From 568013bc6085d17a97db07822ff26df40cab3788 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 23 Dec 2010 15:39:20 -0500 Subject: [PATCH 01/13] freetdm: initial changes to make FreeTDM APIs non-blocking --- libs/freetdm/src/ftdm_io.c | 193 ++++++++++++++---- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 3 + libs/freetdm/src/include/freetdm.h | 65 ++++-- libs/freetdm/src/include/ftdm_declare.h | 10 +- libs/freetdm/src/include/private/ftdm_core.h | 1 + libs/freetdm/src/include/private/ftdm_types.h | 9 + 6 files changed, 220 insertions(+), 61 deletions(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 451441ec97..89e9321fcf 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -304,6 +304,9 @@ FTDM_STR2ENUM(ftdm_str2ftdm_usr_layer1_prot, ftdm_user_layer1_prot2str, ftdm_use FTDM_ENUM_NAMES(CALLING_PARTY_CATEGORY_NAMES, CALLING_PARTY_CATEGORY_STRINGS) FTDM_STR2ENUM(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t, CALLING_PARTY_CATEGORY_NAMES, FTDM_CPC_INVALID) +FTDM_ENUM_NAMES(INDICATION_NAMES, INDICATION_STRINGS) +FTDM_STR2ENUM(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID) + static ftdm_status_t ftdm_group_add_channels(ftdm_span_t* span, int currindex, const char* name); static const char *cut_path(const char *in) @@ -1549,6 +1552,11 @@ end: goto done; } + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { + /* the channel should not block waiting for state processing */ + goto done; + } + /* there is an inherent race here between set and check of the change flag but we do not care because * the flag should never last raised for more than a few ms for any state change */ while (waitrq && waitms > 0) { @@ -1575,6 +1583,7 @@ end: if (waitms <= 0) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); + ok = 0; } done: return ok ? FTDM_SUCCESS : FTDM_FAIL; @@ -2124,6 +2133,27 @@ FT_DECLARE(void) ftdm_span_set_trunk_type(ftdm_span_t *span, ftdm_trunk_type_t t span->trunk_type = type; } +FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, ftdm_bool_t enabled) +{ + ftdm_channel_t *fchan = NULL; + ftdm_iterator_t *citer = NULL; + ftdm_iterator_t *curr = NULL; + + citer = ftdm_span_get_chan_iterator(span, NULL); + if (!citer) { + return FTDM_ENOMEM; + } + for (curr = citer ; curr; curr = ftdm_iterator_next(curr)) { + fchan = ftdm_iterator_current(curr); + if (enabled) { + ftdm_clear_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK); + } else { + ftdm_set_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK); + } + } + return FTDM_SUCCESS; +} + FT_DECLARE(ftdm_trunk_type_t) ftdm_span_get_trunk_type(const ftdm_span_t *span) { return span->trunk_type; @@ -2210,19 +2240,37 @@ FT_DECLARE(ftdm_bool_t) ftdm_channel_call_check_done(const ftdm_channel_t *ftdmc FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { + ftdm_status_t status; ftdm_channel_lock(ftdmchan); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_HOLD); - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0); ftdm_channel_unlock(ftdmchan); - return FTDM_SUCCESS; + + return status; } FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { + ftdm_status_t status; + ftdm_channel_lock(ftdmchan); - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0); + + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0); + ftdm_channel_unlock(ftdmchan); - return FTDM_SUCCESS; + + return status; +} + +FT_DECLARE(void) ftdm_ack_indication(const ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status) +{ + ftdm_sigmsg_t msg; + memset(&msg, 0, sizeof(msg)); + msg.event_id = FTDM_SIGEVENT_INDICATION_COMPLETED; + msg.ev_data.indication_completed.indication = indication; + msg.ev_data.indication_completed.status = status; + ftdm_span_send_signal(fchan->span, &msg); } FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) @@ -2233,46 +2281,61 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n"); + status = FTDM_ECANCELED; + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED); - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA); - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + status = FTDM_EINVAL; + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) { /* We will fail RFC's if we not skip states, but some modules apart from ftmod_sangoma_isdn * expect the call to always to go PROGRESS and PROGRESS MEDIA state before going to UP, so - * use FTDM_SPAN_USE_SKIP_STATESfor now while we update the sig modules */ + * use FTDM_SPAN_USE_SKIP_STATES for now while we update the sig modules */ if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); + goto done; + } } /* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */ if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n"); + status = FTDM_ECANCELED; + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); + goto done; + } } /* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */ if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to UP\n"); + status = FTDM_ECANCELED; + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } } - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1); + + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); + } done: - ftdm_channel_unlock(ftdmchan); return status; } @@ -2280,6 +2343,8 @@ done: /* lock must be acquired by the caller! */ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const char *func, int line) { + ftdm_status_t status = FTDM_SUCCESS; + ftdm_set_flag(chan, FTDM_CHANNEL_USER_HANGUP); ftdm_set_echocancel_call_end(chan); @@ -2292,7 +2357,7 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c if (chan->hangup_timer) { ftdm_sched_cancel_timer(globals.timingsched, chan->hangup_timer); } - ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1); + status = ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1); } else { /* the signaling stack did not touch the state, * core is responsible from clearing flags and stuff, however, because ftmod_analog @@ -2305,28 +2370,34 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c ftdm_channel_close(&chan); } } - return FTDM_SUCCESS; + return status; } FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t cause) { + ftdm_status_t status = FTDM_SUCCESS; ftdm_channel_lock(ftdmchan); ftdmchan->caller_data.hangup_cause = cause; - call_hangup(ftdmchan, file, func, line); + status = call_hangup(ftdmchan, file, func, line); ftdm_channel_unlock(ftdmchan); - return FTDM_SUCCESS; + return status; } FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { + ftdm_status_t status = FTDM_SUCCESS; + ftdm_channel_lock(ftdmchan); + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; - call_hangup(ftdmchan, file, func, line); + + status = call_hangup(ftdmchan, file, func, line); + ftdm_channel_unlock(ftdmchan); - return FTDM_SUCCESS; + return status; } FT_DECLARE(const char *) ftdm_channel_get_last_error(const ftdm_channel_t *ftdmchan) @@ -2411,13 +2482,31 @@ FT_DECLARE(uint32_t) ftdm_channel_get_ph_span_id(const ftdm_channel_t *ftdmchan) return id; } +/* + * Every user requested indication *MUST* be acknowledged with the proper status (ftdm_status_t) + * If the indication fails before we notify the signaling stack, we *MUST* acknowledge ourselves, + * but if we already notified the signaling stack about the indication, the signaling stack is + * responsible for the acknowledge. + * */ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication) { ftdm_status_t status = FTDM_SUCCESS; + ftdm_channel_lock(ftdmchan); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in outgoing channel in state %s\n", + ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state)); + status = FTDM_EINVAL; + ftdm_ack_indication(ftdmchan, indication, status); + goto done; + } + if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n"); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Ignoring indication %s because the call is in %s state\n", + ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state)); + status = FTDM_ECANCELED; + ftdm_ack_indication(ftdmchan, indication, status); goto done; } @@ -2425,55 +2514,71 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch /* FIXME: ring and busy cannot be used with all signaling stacks * (particularly isdn stacks I think, we should emulate or just move to hangup with busy cause) */ case FTDM_CHANNEL_INDICATE_RINGING: - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, indication, status); + } break; case FTDM_CHANNEL_INDICATE_BUSY: - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, indication, status); + } break; case FTDM_CHANNEL_INDICATE_PROCEED: - if (ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) { - if (ftdmchan->state == FTDM_CHANNEL_STATE_RING) { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1); - } + if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) { + ftdm_ack_indication(ftdmchan, indication, status); + goto done; + } + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, indication, status); } break; case FTDM_CHANNEL_INDICATE_PROGRESS: - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); - } else { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, indication, status); } break; case FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA: - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA); - } else { - if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) { - if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); - } - - /* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */ - if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n"); + if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) { + if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, indication, status); goto done; } } - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); + /* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */ + if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n"); + status = FTDM_ECANCELED; + ftdm_ack_indication(ftdmchan, indication, status); + goto done; + } } + ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); + if (status != FTDM_SUCCESS) { + ftdm_ack_indication(ftdmchan, indication, status); + } + break; + case FTDM_CHANNEL_INDICATE_ANSWER: + /* _ftdm_channel_call_indicate takes care of the indication ack */ + status = _ftdm_channel_call_answer(file, func, line, ftdmchan); break; default: ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Do not know how to indicate %d\n", indication); status = FTDM_FAIL; + ftdm_ack_indication(ftdmchan, indication, status); break; } done: ftdm_channel_unlock(ftdmchan); - return FTDM_SUCCESS; + return status; } FT_DECLARE(ftdm_status_t) _ftdm_channel_call_send_msg(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_sigmsg_t *sigmsg) diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 097ffcac02..3809b224c4 100644 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -1595,6 +1595,9 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) /* use signals queue */ ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE); + /* we can skip states (going straight from RING to UP) */ + ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES); + /* setup the scheduler */ snprintf(schedname, sizeof(schedname), "ftmod_r2_%s", span->name); ftdm_assert(ftdm_sched_create(&r2data->sched, schedname) == FTDM_SUCCESS, "Failed to create schedule!\n"); diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index d27353ddb0..68c9fdc7c4 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -349,11 +349,13 @@ typedef enum { FTDM_SIGEVENT_FACILITY, /*!< In call facility event */ FTDM_SIGEVENT_TRACE, /*! Date: Thu, 23 Dec 2010 17:59:05 -0500 Subject: [PATCH 02/13] fretdm: remove m3ua unused stuff --- libs/freetdm/conf/m3ua.conf | 38 - libs/freetdm/src/ftdm_m3ua.c | 692 ------------------- libs/freetdm/src/include/private/ftdm_m3ua.h | 134 ---- libs/freetdm/src/m3ua/mstm3ua.c | 62 -- libs/freetdm/src/m3ua/mstm3ua.h | 96 --- libs/freetdm/src/m3ua_client.c | 333 --------- libs/freetdm/src/m3ua_client.h | 164 ----- libs/freetdm/src/testm3ua.c | 60 -- 8 files changed, 1579 deletions(-) delete mode 100644 libs/freetdm/conf/m3ua.conf delete mode 100644 libs/freetdm/src/ftdm_m3ua.c delete mode 100644 libs/freetdm/src/include/private/ftdm_m3ua.h delete mode 100644 libs/freetdm/src/m3ua/mstm3ua.c delete mode 100644 libs/freetdm/src/m3ua/mstm3ua.h delete mode 100644 libs/freetdm/src/m3ua_client.c delete mode 100644 libs/freetdm/src/m3ua_client.h delete mode 100644 libs/freetdm/src/testm3ua.c diff --git a/libs/freetdm/conf/m3ua.conf b/libs/freetdm/conf/m3ua.conf deleted file mode 100644 index e3eeed3a4a..0000000000 --- a/libs/freetdm/conf/m3ua.conf +++ /dev/null @@ -1,38 +0,0 @@ -;M3UA SS7 Links Config -; -;ss7box-m3ua_mode => true -;local_sctp_ip => localhost -;local sctp_port => 30000 -;remote_sctp_ip => localhost -;remote_sctp_port => 30001 -;opc => 0-0-0 -;dpc => 0-0-0 - - -; AP Specific Stuff. This will likely move later. - -; CNAM Gateways -cnam1_dpc => 0-0-0 -cnam1_ssn => 253 -cnam2_dpc => 0-0-0 -cnam2_ssn => 253 -cnam3_dpc => 0-0-0 -cnam3_ssn => 253 - -;LNP Gateways -lnp1_dpc => 0-0-0 -lnp1_ssn => 253 -lnp2_dpc => 0-0-0 -lnp2_ssn => 253 -lnp3_dpc => 0-0-0 -lnp3_ssn => 253 - -;LNP Gateways -sms8001_dpc => 0-0-0 -sms8001_ssn => 253 -sms8002_dpc => 0-0-0 -sms8002_ssn => 253 -sms8003_dpc => 0-0-0 -sms8003_ssn => 253 - - diff --git a/libs/freetdm/src/ftdm_m3ua.c b/libs/freetdm/src/ftdm_m3ua.c deleted file mode 100644 index 8d3e00213a..0000000000 --- a/libs/freetdm/src/ftdm_m3ua.c +++ /dev/null @@ -1,692 +0,0 @@ -/* - * ftdm_m3ua.c - * freetdm - * - * Created by Shane Burrell on 4/3/08. - * Copyright 2008 Shane Burrell. All rights reserved. - * - * - * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#include "freetdm.h" -#include "m3ua_client.h" -#include "ftdm_m3ua.h" - -#define MAX_REQ_ID MAX_PENDING_CALLS -typedef uint16_t m3ua_request_id_t; - -typedef enum { - BST_FREE, - BST_WAITING, - BST_READY, - BST_FAIL -} m3ua_request_status_t; - -typedef struct { - m3ua_request_status_t status; - m3uac_event_t event; - ftdm_span_t *span; - ftdm_channel_t *ftdmchan; -} m3ua_request_t; - - -struct general_config { - uint32_t region; -}; -typedef struct general_config general_config_t; - - -struct m3ua_channel_profile { - char name[80]; - int cust_span; - unsigned char opc[3]; - unsigned char dpc[3]; - int local_ip[4]; - int local_port; - int remote_ip[4]; - int remote_port; - int m3ua_mode; -}; -typedef struct m3ua_channel_profile m3ua_channel_profile_t; - -static struct { - ftdm_hash_t *profile_hash; - general_config_t general_config; -} globals; - -struct m3ua_span_data { - uint32_t boardno; - uint32_t flags; -}; -typedef struct m3ua_span_data m3ua_span_data_t; - -struct m3ua_chan_data { - ftdm_buffer_t *digit_buffer; - ftdm_mutex_t *digit_mutex; - ftdm_size_t dtmf_len; - uint32_t flags; - uint32_t hdlc_bytes; -}; -typedef struct m3ua_chan_data m3ua_chan_data_t; - -static ftdm_mutex_t *request_mutex = NULL; -static ftdm_mutex_t *signal_mutex = NULL; - -static uint8_t req_map[MAX_REQ_ID+1] = { 0 }; - -static void release_request_id(m3ua_request_id_t r) -{ - ftdm_mutex_lock(request_mutex); - req_map[r] = 0; - ftdm_mutex_unlock(request_mutex); -} - -/*static m3ua_request_id_t next_request_id(void) -{ - m3ua_request_id_t r = 0; - int ok = 0; - - while(!ok) { - ftdm_mutex_lock(request_mutex); - for (r = 1; r <= MAX_REQ_ID; r++) { - if (!req_map[r]) { - ok = 1; - req_map[r] = 1; - break; - } - } - ftdm_mutex_unlock(request_mutex); - if (!ok) { - ftdm_sleep(5); - } - } - return r; -} -*/ - -static __inline__ void state_advance(ftdm_channel_t *ftdmchan) -{ - - m3ua_data_t *m3ua_data = ftdmchan->span->signal_data; - m3uac_connection_t *mcon = &m3ua_data->mcon; - ftdm_sigmsg_t sig; - ftdm_status_t status; - - ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); - - memset(&sig, 0, sizeof(sig)); - sig.chan_id = ftdmchan->chan_id; - sig.span_id = ftdmchan->span_id; - sig.channel = ftdmchan; - - switch (ftdmchan->state) { - case FTDM_CHANNEL_STATE_DOWN: - { - if (ftdmchan->extra_id) { - release_request_id((m3ua_request_id_t)ftdmchan->extra_id); - ftdmchan->extra_id = 0; - } - ftdm_channel_close(&ftdmchan); - } - break; - case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: - case FTDM_CHANNEL_STATE_PROGRESS: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; - if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } else { - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_START_ACK, - 0); - } - } - break; - case FTDM_CHANNEL_STATE_RING: - { - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_START; - if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } - - } - break; - case FTDM_CHANNEL_STATE_RESTART: - { - if (ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP && ftdmchan->last_state != FTDM_CHANNEL_STATE_DOWN) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } else { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - } - } - break; - case FTDM_CHANNEL_STATE_UP: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - sig.event_id = FTDM_SIGEVENT_UP; - if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } - } else { - if (!(ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA))) { - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_START_ACK, - 0); - } - - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_ANSWERED, - 0); - } - } - break; - case FTDM_CHANNEL_STATE_DIALING: - { - } - break; - case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: - { - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - } - break; - case FTDM_CHANNEL_STATE_HANGUP: - { - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA)) { - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_STOPPED, - ftdmchan->caller_data.hangup_cause); - } else { - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_START_NACK, - ftdmchan->caller_data.hangup_cause); - } - } - break; - case FTDM_CHANNEL_STATE_CANCEL: - { - sig.event_id = FTDM_SIGEVENT_STOP; - status = m3ua_data->signal_cb(&sig); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_START_NACK_ACK, - 0); - } - break; - case FTDM_CHANNEL_STATE_TERMINATING: - { - sig.event_id = FTDM_SIGEVENT_STOP; - status = m3ua_data->signal_cb(&sig); - ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - m3uac_exec_command(mcon, - ftdmchan->physical_span_id-1, - ftdmchan->physical_chan_id-1, - 0, - SIGBOOST_EVENT_CALL_STOPPED_ACK, - 0); - } - break; - default: - break; - } -} - - -static __inline__ void check_state(ftdm_span_t *span) -{ - if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) { - uint32_t j; - ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); - for(j = 1; j <= span->chan_count; j++) { - if (ftdm_test_flag((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_clear_flag_locked((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); - state_advance(&span->channels[j]); - ftdm_channel_complete_state(&span->channels[j]); - } - } - } -} - - -static int parse_ss7_event(ftdm_span_t *span, m3uac_connection_t *mcon, m3uac_event_t *event) -{ - ftdm_mutex_lock(signal_mutex); - - if (!ftdm_running()) { - ftdm_log(FTDM_LOG_WARNING, "System is shutting down.\n"); - goto end; - } - - - if (ftdm_test_flag(span, FTDM_SPAN_SUSPENDED) && - event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && event->event_id != SIGBOOST_EVENT_HEARTBEAT) { - - ftdm_log(FTDM_LOG_WARNING, - "INVALID EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", - m3uac_event_id_name(event->event_id), - event->event_id, - event->span+1, - event->chan+1, - event->release_cause, - event->call_setup_id, - event->fseqno, - (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), - (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") - ); - - goto end; - } - - - ftdm_log(FTDM_LOG_DEBUG, - "RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", - m3uac_event_id_name(event->event_id), - event->event_id, - event->span+1, - event->chan+1, - event->release_cause, - event->call_setup_id, - event->fseqno, - (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), - (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") - ); - - - - switch(event->event_id) { - - case SIGBOOST_EVENT_CALL_START: - //handle_call_start(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_STOPPED: - //handle_call_stop(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_START_ACK: - //handle_call_start_ack(mcon, event); - break; - case SIGBOOST_EVENT_CALL_START_NACK: - //handle_call_start_nack(span, mcon, event); - break; - case SIGBOOST_EVENT_CALL_ANSWERED: - //handle_call_answer(span, mcon, event); - break; - case SIGBOOST_EVENT_HEARTBEAT: - //handle_heartbeat(mcon, event); - break; - case SIGBOOST_EVENT_CALL_STOPPED_ACK: - case SIGBOOST_EVENT_CALL_START_NACK_ACK: - //handle_call_done(span, mcon, event); - break; - case SIGBOOST_EVENT_INSERT_CHECK_LOOP: - //handle_call_loop_start(event); - break; - case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: - //handle_call_stop(event); - break; - case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: - //handle_restart_ack(mcon, span, event); - break; - case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: - //handle_gap_abate(event); - break; - default: - ftdm_log(FTDM_LOG_WARNING, "No handler implemented for [%s]\n", m3uac_event_id_name(event->event_id)); - break; - } - - end: - - ftdm_mutex_unlock(signal_mutex); - - return 0; -} - -static FIO_CONFIGURE_FUNCTION(m3ua_configure) -{ - m3ua_channel_profile_t *profile = NULL; - - int ok = 1; - - if (!(profile = (m3ua_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) { - profile = ftdm_malloc(sizeof(*profile)); - memset(profile, 0, sizeof(*profile)); - ftdm_set_string(profile->name, category); - hashtable_insert(globals.profile_hash, (void *)profile->name, profile); - ftdm_log(FTDM_LOG_INFO, "creating profile [%s]\n", category); - } - -// ftdm_set_string(m3ua_data->mcon. cfg.local_ip, local_ip); - if (!strcasecmp(var, "local_sctp_port")) { - profile->local_port = 30000 ; - profile->remote_port = 30000; - profile->cust_span++; - } - ok = 1; - - - if (ok) { - ftdm_log(FTDM_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category); - } else { - ftdm_log(FTDM_LOG_ERROR, "unknown param [%s]\n", var); - } - - return FTDM_SUCCESS; -} - -static FIO_CONFIGURE_SPAN_FUNCTION(m3ua_configure_span) -{ - - return FTDM_FAIL; -} - -static FIO_OPEN_FUNCTION(m3ua_open) -{ - - return FTDM_FAIL; -} - -static FIO_CLOSE_FUNCTION(m3ua_close) -{ - - return FTDM_FAIL; -} - -/*static FIO_SET_INTERVAL_FUNCTION(m3ua_set_interval) -{ - - return 0; -}*/ - -static FIO_WAIT_FUNCTION(m3ua_wait) -{ - - return FTDM_FAIL; -} - -static FIO_READ_FUNCTION(m3ua_read) -{ - - return FTDM_FAIL; -} - -static FIO_WRITE_FUNCTION(m3ua_write) -{ - - return FTDM_FAIL; -} - -static FIO_COMMAND_FUNCTION(m3ua_command) -{ - return FTDM_FAIL; -} - -static FIO_SPAN_POLL_EVENT_FUNCTION(m3ua_poll_event) -{ - return FTDM_FAIL; -} - -static FIO_SPAN_NEXT_EVENT_FUNCTION(m3ua_next_event) -{ - return FTDM_FAIL; -} - - -static FIO_SPAN_DESTROY_FUNCTION(m3ua_span_destroy) -{ - m3ua_span_data_t *span_data = (m3ua_span_data_t *) span->io_data; - - if (span_data) { - ftdm_safe_free(span_data); - } - - return FTDM_SUCCESS; -} -static FIO_CHANNEL_DESTROY_FUNCTION(m3ua_channel_destroy) -{ - m3ua_chan_data_t *chan_data = (m3ua_chan_data_t *) ftdmchan->io_data; - m3ua_span_data_t *span_data = (m3ua_span_data_t *) ftdmchan->span->io_data; - - if (!chan_data) { - return FTDM_FAIL; - } - - - - - - - ftdm_mutex_destroy(&chan_data->digit_mutex); - ftdm_buffer_destroy(&chan_data->digit_buffer); - - - ftdm_safe_free(chan_data); - - if (span_data) { - ftdm_safe_free(span_data); - } - - - return FTDM_SUCCESS; -} - - - -static FIO_GET_ALARMS_FUNCTION(m3ua_get_alarms) -{ - return FTDM_FAIL; -} - -static ftdm_io_interface_t m3ua_interface; - -ftdm_status_t m3ua_init(ftdm_io_interface_t **zint) -{ - assert(zint != NULL); - memset(&m3ua_interface, 0, sizeof(m3ua_interface)); - - m3ua_interface.name = "m3ua"; - m3ua_interface.configure = m3ua_configure; - m3ua_interface.configure_span = m3ua_configure_span; - m3ua_interface.open = m3ua_open; - m3ua_interface.close = m3ua_close; - m3ua_interface.wait = m3ua_wait; - m3ua_interface.read = m3ua_read; - m3ua_interface.write = m3ua_write; - m3ua_interface.command = m3ua_command; - m3ua_interface.poll_event = m3ua_poll_event; - m3ua_interface.next_event = m3ua_next_event; - m3ua_interface.channel_destroy = m3ua_channel_destroy; - m3ua_interface.span_destroy = m3ua_span_destroy; - m3ua_interface.get_alarms = m3ua_get_alarms; - *zint = &m3ua_interface; - - return FTDM_FAIL; -} - -ftdm_status_t m3ua_destroy(void) -{ - return FTDM_FAIL; -} - - -static void *m3ua_run(ftdm_thread_t *me, void *obj) -{ - ftdm_span_t *span = (ftdm_span_t *) obj; - m3ua_data_t *m3ua_data = span->signal_data; - m3uac_connection_t *mcon, *pcon; - uint32_t ms = 10, too_long = 60000; - - - m3ua_data->pcon = m3ua_data->mcon; - - if (m3uac_connection_open(&m3ua_data->mcon, - m3ua_data->mcon.cfg.local_ip, - m3ua_data->mcon.cfg.local_port, - m3ua_data->mcon.cfg.remote_ip, - m3ua_data->mcon.cfg.remote_port) < 0) { - ftdm_log(FTDM_LOG_DEBUG, "Error: Opening MCON Socket [%d] %s\n", m3ua_data->mcon.socket, strerror(errno)); - goto end; - } - - if (m3uac_connection_open(&m3ua_data->pcon, - m3ua_data->pcon.cfg.local_ip, - ++m3ua_data->pcon.cfg.local_port, - m3ua_data->pcon.cfg.remote_ip, - m3ua_data->pcon.cfg.remote_port) < 0) { - ftdm_log(FTDM_LOG_DEBUG, "Error: Opening PCON Socket [%d] %s\n", m3ua_data->pcon.socket, strerror(errno)); - goto end; - } - - mcon = &m3ua_data->mcon; - pcon = &m3ua_data->pcon; - - top: - - //init_outgoing_array(); - - m3uac_exec_command(mcon, - 0, - 0, - -1, - SIGBOOST_EVENT_SYSTEM_RESTART, - 0); - - while (ftdm_test_flag(m3ua_data, FTDM_M3UA_RUNNING)) { - fd_set rfds, efds; - struct timeval tv = { 0, ms * 1000 }; - int max, activity, i = 0; - m3uac_event_t *event = NULL; - - if (!ftdm_running()) { - m3uac_exec_command(mcon, - 0, - 0, - -1, - SIGBOOST_EVENT_SYSTEM_RESTART, - 0); - break; - } - - FD_ZERO(&rfds); - FD_ZERO(&efds); - FD_SET(mcon->socket, &rfds); - FD_SET(mcon->socket, &efds); - FD_SET(pcon->socket, &rfds); - FD_SET(pcon->socket, &efds); - - max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1; - - if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) { - goto error; - } - - if (activity) { - if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) { - goto error; - } - - if (FD_ISSET(pcon->socket, &rfds)) { - if ((event = m3uac_connection_readp(pcon, i))) { - parse_ss7_event(span, mcon, event); - } else goto top; - } - - if (FD_ISSET(mcon->socket, &rfds)) { - if ((event = m3uac_connection_read(mcon, i))) { - parse_ss7_event(span, mcon, event); - } else goto top; - } - } - - check_state(span); - mcon->hb_elapsed += ms; - - if (mcon->hb_elapsed >= too_long && (mcon->up || !ftdm_test_flag(span, FTDM_SPAN_SUSPENDED))) { - ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART); - ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED); - mcon->up = 0; - ftdm_log(FTDM_LOG_CRIT, "Lost Heartbeat!\n"); - } - - } - - goto end; - - error: - ftdm_log(FTDM_LOG_CRIT, "Socket Error!\n"); - - end: - - m3uac_connection_close(&m3ua_data->mcon); - m3uac_connection_close(&m3ua_data->pcon); - - ftdm_clear_flag(m3ua_data, FTDM_M3UA_RUNNING); - - ftdm_log(FTDM_LOG_DEBUG, "M3UA thread ended.\n"); - return NULL; -} -ftdm_status_t m3ua_start(ftdm_span_t *span) -{ - m3ua_data_t *m3ua_data = span->signal_data; - ftdm_set_flag(m3ua_data, FTDM_M3UA_RUNNING); - return ftdm_thread_create_detached(m3ua_run, span); -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: -*/ diff --git a/libs/freetdm/src/include/private/ftdm_m3ua.h b/libs/freetdm/src/include/private/ftdm_m3ua.h deleted file mode 100644 index 1bf830853c..0000000000 --- a/libs/freetdm/src/include/private/ftdm_m3ua.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * ftdm_m3ua.h - * freetdm - * - * Created by Shane Burrell on 4/3/08. - * Copyright 2008 Shane Burrell. All rights reserved. - * - * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -//#include "m3ua_client.h" -#include "freetdm.h" - -#ifdef __cplusplus -extern "C" { -#endif -enum e_sigboost_event_id_values -{ - SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ - SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/ - SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/ - SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/ - SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/ - SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/ - SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/ - SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/ - SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/ - /* Following IDs are ss7boost to sangoma_mgd only. */ - SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/ - SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/ - SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/ - SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/ -}; -enum e_sigboost_release_cause_values -{ - SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0, - SIGBOOST_RELEASE_CAUSE_NORMAL = 16, - SIGBOOST_RELEASE_CAUSE_BUSY = 17, - /* probable elimination */ - //SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */ - //SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */ - //SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */ - //SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */ -}; - -enum e_sigboost_call_setup_ack_nack_cause_values -{ - SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* unused Q.850 value */ - SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 118, /* unused Q.850 value */ - SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28, - /* probable elimination */ - //SIGBOOST_CALL_SETUP_RESERVED = 0x00, - //SIGBOOST_CALL_SETUP_CIRCUIT_RESET = 0x10, - //SIGBOOST_CALL_SETUP_NACK_CKT_START_TIMEOUT = 0x11, - //SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP = 0x17, -}; -typedef enum { - M3UA_SPAN_SIGNALING_M3UA, - M3UA_SPAN_SIGNALING_SS7BOX, - -} M3UA_TSpanSignaling; -#define M3UA_SPAN_STRINGS "M3UA", "SS7BOX" -FTDM_STR2ENUM_P(m3ua_str2span, m3ua_span2str, M3UA_TSpanSignaling) - - - -typedef enum { - FTDM_M3UA_RUNNING = (1 << 0) -} ftdm_m3uat_flag_t; - -/*typedef struct m3ua_data { - m3uac_connection_t mcon; - m3uac_connection_t pcon; - fio_signal_cb_t signal_cb; - uint32_t flags; -} m3ua_data_t; - -*/ -/*typedef struct mu3a_link { - ss7bc_connection_t mcon; - ss7bc_connection_t pcon; - fio_signal_cb_t signal_cb; - uint32_t flags; -} ftdm_m3ua_data_t; -*/ - -ftdm_status_t m3ua_init(ftdm_io_interface_t **zint); -ftdm_status_t m3ua_destroy(void); -ftdm_status_t m3ua_start(ftdm_span_t *span); - -#ifdef __cplusplus -} -#endif - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ - diff --git a/libs/freetdm/src/m3ua/mstm3ua.c b/libs/freetdm/src/m3ua/mstm3ua.c deleted file mode 100644 index 1d8179c58d..0000000000 --- a/libs/freetdm/src/m3ua/mstm3ua.c +++ /dev/null @@ -1,62 +0,0 @@ -/* WARNING WORK IN PROGRESS - * mstm3ua.c - * mstss7d port - * - * Created by Shane Burrell on 2/2/08. - * Copyright 2008 Shane Burrell. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "mstm3ua.h" - - - - - -int build_m3ua_hdr(unsigned char len,unsigned char *bytemsg) - -{ - - *bytemsg++ = M_VERSION_REL1; // 1 Verison - //bytemsg[1] = 0x00; // 2 RESERVED - //bytemsg[2] = M_CLASS_XFER; // 3 Msg Class - //SS7 BOX Kludge - *bytemsg++ = 0x01; // 2 RESERVED - *bytemsg++ = 0x00; // 2 RESERVED - - *bytemsg++ = M_TYPE_DATA ; // 4 Msg Type - - *bytemsg++ = len; // 5 Msg LENGTH 81 32bit field - *bytemsg++ = 0x00; // 6 - *bytemsg++ = 0x00; // 7 - *bytemsg++ = 0x00; // 8 - return(0); - -}; \ No newline at end of file diff --git a/libs/freetdm/src/m3ua/mstm3ua.h b/libs/freetdm/src/m3ua/mstm3ua.h deleted file mode 100644 index 13527dac35..0000000000 --- a/libs/freetdm/src/m3ua/mstm3ua.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * mstm3ua.h - * mstss7d - * - * Created by Shane Burrell on 3/2/08. - * Copyright 2008 Shane Burrell. All rights reserved. - * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -typedef unsigned long m3ua_ulong; -typedef unsigned short m3ua_ushort; -typedef unsigned char m3ua_uchar; - -typedef unsigned char u8; -typedef unsigned short u16; /* Note: multi-byte values are little-endian */ -typedef unsigned long u32; - - - - -#define M_TAG_NETWORK_APPEARANCE 1 -#define M_TAG_PROTOCOL_DATA 3 -#define M_TAG_INFO_STRING 4 -#define M_TAG_AFFECTED_DPC 5 -#define M_TAG_ROUTING_CONTEXT 6 -#define M_TAG_DIAGNOSTIC_INFORMATION 7 -#define M_TAG_HEARTBEAT_DATA 8 -#define M_TAG_UNAVAILABILITY_CAUSE 9 -#define M_TAG_REASON 10 -#define M_TAG_TRAFFIC_MODE_TYPE 11 -#define M_TAG_ERROR_CODE 12 -#define M_TAG_STATUS_TYPE 13 -#define M_TAG_CONGESTED_INDICATIONS 14 - -#define M_VERSION_REL1 1 - -#define M_CLASS_MGMT 0x00 -#define M_CLASS_XFER 0x01 -#define M_CLASS_SSNM 0x02 -#define M_CLASS_ASPSM 0x03 -#define M_CLASS_ASPTM 0x04 -#define M_CLASS_RKM 0x09 - -#define M_TYPE_ERR (0|M_CLASS_MGMT - -#define M_TYPE_NTFY (1|M_CLASS_XFER) -#define M_TYPE_DATA (1|M_CLASS_XFER) - -#define M_TYPE_DUNA (1|M_CLASS_SSNM) -#define M_TYPE_DAVA (2|M_CLASS_SSNM) -#define M_TYPE_DUAD (3|M_CLASS_SSNM) -#define M_TYPE_SCON (4|M_CLASS_SSNM) -#define M_TYPE_DUPU (5|M_CLASS_SSNM) - -#define M_TYPE_UP (1|M_CLASS_ASPSM) -#define M_TYPE_DOWN (2|M_CLASS_ASPSM) -#define M_TYPE_BEAT (3|M_CLASS_ASPSM) -#define M_TYPE_UP_ACK (4|M_CLASS_ASPSM) -#define M_TYPE_DOWN_ACK (5|M_CLASS_ASPSM) -#define M_TYPE_BEAT_ACK (6|M_CLASS_ASPSM) - -#define M_TYPE_ACTIVE (1|M_CLASS_ASPTM) -#define M_TYPE_INACTIVE (2|M_CLASS_ASPTM) -#define M_TYPE_ACTIVE_ACK (3|M_CLASS_ASPTM) -#define M_TYPE_INACTIVE_ACK (4|M_CLASS_ASPTM) - -#define M_CLASS_MASK 0xff00 -#define M_TYPE_MASK 0x00ff - diff --git a/libs/freetdm/src/m3ua_client.c b/libs/freetdm/src/m3ua_client.c deleted file mode 100644 index 7608183896..0000000000 --- a/libs/freetdm/src/m3ua_client.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * m3ua_client.c - * freetdm - * - * Created by Shane Burrell on 4/3/08. - * Copyright 2008 Shane Burrell. All rights reserved. - * - * - * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#if HAVE_NETDB_H -#include -#endif - -#include "freetdm.h" -#include - - -#ifndef HAVE_GETHOSTBYNAME_R -extern int gethostbyname_r (const char *__name, - struct hostent *__result_buf, - char *__buf, size_t __buflen, - struct hostent **__result, - int *__h_errnop); -#endif - -struct m3uac_map { - uint32_t event_id; - const char *name; -}; - -static struct m3uac_map m3uac_table[] = { - {M3UA_EVENT_CALL_START, "CALL_START"}, - {M3UA_EVENT_CALL_START_ACK, "CALL_START_ACK"}, - {M3UA_EVENT_CALL_START_NACK, "CALL_START_NACK"}, - {M3UA_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"}, - {M3UA_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, - {M3UA_EVENT_CALL_STOPPED, "CALL_STOPPED"}, - {M3UA_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, - {M3UA_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, - {M3UA_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, - {M3UA_EVENT_HEARTBEAT, "HEARTBEAT"}, - {M3UA_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, - {M3UA_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"} -}; - - - -static int create_conn_socket(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) -{ - int rc; - struct hostent *result, *local_result; - char buf[512], local_buf[512]; - int err = 0; - - memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp)); - memset(&mcon->local_hp, 0, sizeof(mcon->local_hp)); - mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); - - ftdm_log(FTDM_LOG_DEBUG, "Creating L=%s:%d R=%s:%d\n", - local_ip,local_port,ip,port); - - if (mcon->socket >= 0) { - int flag; - - flag = 1; - gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err); - gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &err); - if (result && local_result) { - mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype; - memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length); - mcon->remote_addr.sin_port = htons(port); - - mcon->local_addr.sin_family = mcon->local_hp.h_addrtype; - memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length); - mcon->local_addr.sin_port = htons(local_port); - - - setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, (char *)&flag, sizeof(int)); - - rc=listen(mcon->socket,100); - if (rc) { - close(mcon->socket); - mcon->socket = -1; - - } - } - } - - ftdm_mutex_create(&mcon->mutex); - - return mcon->socket; -} - -int m3uac_connection_close(m3uac_connection_t *mcon) -{ - if (mcon->socket > -1) { - close(mcon->socket); - } - - ftdm_mutex_lock(mcon->mutex); - ftdm_mutex_unlock(mcon->mutex); - ftdm_mutex_destroy(&mcon->mutex); - memset(mcon, 0, sizeof(*mcon)); - mcon->socket = -1; - - return 0; -} - -int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port) -{ - create_conn_socket(mcon, local_ip, local_port, ip, port); - return mcon->socket; -} - - -int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause) -{ - m3uac_event_t oevent; - int retry = 5; - - m3uac_event_init(&oevent, cmd, chan, span); - oevent.release_cause = cause; - - if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART) { - mcon->rxseq_reset = 1; - mcon->txseq = 0; - mcon->rxseq = 0; - mcon->txwindow = 0; - } - - if (id >= 0) { - oevent.call_setup_id = id; - } - - while (m3uac_connection_write(mcon, &oevent) <= 0) { - if (--retry <= 0) { - ftdm_log(FTDM_LOG_CRIT, "Failed to tx on M3UA socket: %s\n", strerror(errno)); - return -1; - } else { - ftdm_log(FTDM_LOG_WARNING, "Failed to tx on M3UA socket: %s :retry %i\n", strerror(errno), retry); - ftdm_sleep(1); - } - } - - return 0; -} - - - -m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration) -{ - unsigned int fromlen = sizeof(struct sockaddr_in); - int bytes = 0; - - bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, - (struct sockaddr *) &mcon->local_addr, &fromlen); - - if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) { - - if (mcon->rxseq_reset) { - if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) { - ftdm_log(FTDM_LOG_DEBUG, "Rx sync ok\n"); - mcon->rxseq = mcon->event.fseqno; - return &mcon->event; - } - errno=EAGAIN; - ftdm_log(FTDM_LOG_DEBUG, "Waiting for rx sync...\n"); - return NULL; - } - - mcon->txwindow = mcon->txseq - mcon->event.bseqno; - mcon->rxseq++; - - if (mcon->rxseq != mcon->event.fseqno) { - ftdm_log(FTDM_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno); - return NULL; - } - - return &mcon->event; - } else { - if (iteration == 0) { - ftdm_log(FTDM_LOG_CRIT, "Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); - return NULL; - } - } - - return NULL; -} - -m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration) -{ - unsigned int fromlen = sizeof(struct sockaddr_in); - int bytes = 0; - - bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen); - - if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) { - return &mcon->event; - } else { - if (iteration == 0) { - ftdm_log(FTDM_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event)); - return NULL; - } - } - - return NULL; -} - - -int m3uac_connection_write(m3uac_connection_t *mcon, ss7bc_event_t *event) -{ - int err; - - if (!event || mcon->socket < 0 || !mcon->mutex) { - ftdm_log(FTDM_LOG_DEBUG, "Critical Error: No Event Device\n"); - return -EINVAL; - } - - if (event->span > 16 || event->chan > 31) { - ftdm_log(FTDM_LOG_CRIT, "Critical Error: TX Cmd=%s Invalid Span=%i Chan=%i\n", m3uac_event_id_name(event->event_id), event->span,event->chan); - return -1; - } - - gettimeofday(&event->tv,NULL); - - ftdm_mutex_lock(mcon->mutex); - event->fseqno = mcon->txseq++; - event->bseqno = mcon->rxseq; - err = sendto(mcon->socket, event, sizeof(m3uac_event_t), 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); - ftdm_mutex_unlock(mcon->mutex); - - if (err != sizeof(m3uac_event_t)) { - err = -1; - } - - ftdm_log(FTDM_LOG_DEBUG, "TX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n", - m3uac_event_id_name(event->event_id), - event->event_id, - event->span+1, - event->chan+1, - event->release_cause, - event->call_setup_id, - event->fseqno, - (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"), - (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A") - ); - - return err; -} - -void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id) -{ - memset(event, 0, sizeof(m3uac_event_t)); - event->event_id = M3UA_EVENT_CALL_START; - - if (calling) { - strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1); - event->calling_number_digits_count = strlen(calling); - } - - if (called) { - strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1); - event->called_number_digits_count = strlen(called); - } - - event->call_setup_id = setup_id; - -} - -void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span) -{ - memset(event, 0, sizeof(ss7bc_event_t)); - event->event_id = event_id; - event->chan = chan; - event->span = span; -} - -const char *m3uac_event_id_name(uint32_t event_id) -{ - unsigned int x; - const char *ret = NULL; - - for (x = 0 ; x < sizeof(m3uac_table)/sizeof(struct m3uac_map); x++) { - if (m3uac_table[x].event_id == event_id) { - ret = m3uac_table[x].name; - break; - } - } - - return ret; -} - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ - - diff --git a/libs/freetdm/src/m3ua_client.h b/libs/freetdm/src/m3ua_client.h deleted file mode 100644 index e451156a41..0000000000 --- a/libs/freetdm/src/m3ua_client.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * m3ua_client.h - * freetdm - * - * Created by Shane Burrell on 4/3/08. - * Copyright 2008 Shane Burrell. All rights reserved. - * - * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of the original author; nor the names of any contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Fix this for portability -#include -//#include -#include -#include -#include -//#include -#include - -#define MAX_DIALED_DIGITS 31 -#define MAX_CALLING_NAME 31 - -/* Next two defines are used to create the range of values for call_setup_id - * in the t_sigboost structure. - * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */ -#define CORE_MAX_SPANS 200 -#define CORE_MAX_CHAN_PER_SPAN 30 -#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN -/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */ -#define SIZE_RDNIS 80 - -//#undef MSGWINDOW -#define MSGWINDOW - - -typedef struct -{ - uint32_t event_id; - uint32_t fseqno; -#ifdef MSGWINDOW - uint32_t bseqno; -#endif - uint16_t call_setup_id; - uint32_t trunk_group; - uint32_t span; - uint32_t chan; - uint8_t called_number_digits_count; - char called_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ - uint8_t calling_number_digits_count; /* it's an array */ - char calling_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */ - uint8_t release_cause; - struct timeval tv; - /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ - uint8_t calling_number_screening_ind; - uint8_t calling_number_presentation; - char redirection_string [SIZE_RDNIS]; /* it's a null terminated string */ - -} t_m3ua; - -typedef t_m3ua m3uac_event_t; -typedef uint32_t m3uac_event_id_t; - - -typedef struct m3uac_ip_cfg -{ - char local_ip[25]; - int local_port; - char remote_ip[25]; - int remote_port; -}m3uac_ip_cfg_t; - -struct m3uac_connection { - ftdm_socket_t socket; - struct sockaddr_in local_addr; - struct sockaddr_in remote_addr; - m3uac_event_t event; - struct hostent remote_hp; - struct hostent local_hp; - unsigned int flags; - ftdm_mutex_t *mutex; - FILE *log; - unsigned int txseq; - unsigned int rxseq; - unsigned int txwindow; - unsigned int rxseq_reset; - m3uac_ip_cfg_t cfg; - uint32_t hb_elapsed; - int up; -}; - -typedef enum { - MSU_FLAG_EVENT = (1 << 0) -} m3uac_flag_t; - -typedef struct m3uac_connection m3uac_connection_t; - -static inline void sctp_no_nagle(int socket) -{ - //int flag = 1; - //setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int)); -} - -int m3uac_connection_close(m3uac_connection_t *mcon); -int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port); -m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration); -m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration); -int m3uac_connection_write(m3uac_connection_t *mcon, m3uac_event_t *event); -void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span); -void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id); -const char *m3uac_event_id_name(uint32_t event_id); -int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause); - - - - -/* For Emacs: - * Local Variables: - * mode:c - * indent-tabs-mode:t - * tab-width:4 - * c-basic-offset:4 - * End: - * For VIM: - * vim:set softtabstop=4 shiftwidth=4 tabstop=4: - */ diff --git a/libs/freetdm/src/testm3ua.c b/libs/freetdm/src/testm3ua.c deleted file mode 100644 index 5848470e7a..0000000000 --- a/libs/freetdm/src/testm3ua.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * testm3ua.c - * freetdm - * - * Created by Shane Burrell on 4/8/08. - * Copyright 2008 __MyCompanyName__. All rights reserved. - * - */ - -#include "testm3ua.h" -#include "freetdm.h" -#include "ftdm_m3ua.h" - -static FIO_SIGNAL_CB_FUNCTION(on_signal) -{ - return FTDM_FAIL; -} - -int main(int argc, char *argv[]) -{ - ftdm_span_t *span; - //m3ua_data_t *data; - - ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); - - if (argc < 5) { - printf("more args needed\n"); - exit(-1); - } - - if (ftdm_global_init() != FTDM_SUCCESS) { - fprintf(stderr, "Error loading FreeTDM\n"); - exit(-1); - } - - printf("FreeTDM loaded\n"); - - if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { - fprintf(stderr, "Error finding FreeTDM span\n"); - goto done; - } - - - if (ftdm_m3ua_configure_span(span) == FTDM_SUCCESS) { - //data = span->signal_data; - ftdm_m3ua_start(span); - } else { - fprintf(stderr, "Error starting M3UA\n"); - goto done; - } - - //while(ftdm_test_flag(data, FTDM_M3UA_RUNNING)) { - // ftdm_sleep(1 * 1000); - //} - - done: - - ftdm_global_destroy(); - -} From 82297de7532cbc6043d77f541772f942cde2039f Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 23 Dec 2010 19:50:13 -0500 Subject: [PATCH 03/13] freetdm: state refactoring in progress *shrug* --- libs/freetdm/src/ftdm_io.c | 344 ------------------------------------- 1 file changed, 344 deletions(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 89e9321fcf..161ccdcc18 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -268,9 +268,6 @@ FTDM_STR2ENUM(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_ FTDM_ENUM_NAMES(SIGNAL_NAMES, SIGNAL_STRINGS) FTDM_STR2ENUM(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t, SIGNAL_NAMES, FTDM_SIGEVENT_INVALID) -FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS) -FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID) - FTDM_ENUM_NAMES(MDMF_TYPE_NAMES, MDMF_STRINGS) FTDM_STR2ENUM(ftdm_str2ftdm_mdmf_type, ftdm_mdmf_type2str, ftdm_mdmf_type_t, MDMF_TYPE_NAMES, MDMF_INVALID) @@ -1352,243 +1349,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_add_token(ftdm_channel_t *ftdmchan, char } -FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan) -{ - ftdm_channel_state_t state = ftdmchan->state; - - if (state == FTDM_CHANNEL_STATE_PROGRESS) { - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); - } else if (state == FTDM_CHANNEL_STATE_UP) { - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA); - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED); - } else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS); - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA); - } - - return FTDM_SUCCESS; -} - -static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map) -{ - int x = 0, ok = 0; - ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND; - - for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) { - int i = 0, proceed = 0; - if (!state_map->nodes[x].type) { - break; - } - - if (state_map->nodes[x].direction != direction) { - continue; - } - - if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) { - proceed = 1; - } else { - for(i = 0; i < FTDM_MAP_MAX; i++) { - if (state_map->nodes[x].check_states[i] == ftdmchan->state) { - proceed = 1; - break; - } - } - } - - if (!proceed) { - continue; - } - - for(i = 0; i < FTDM_MAP_MAX; i++) { - ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE); - if (state_map->nodes[x].states[i] == FTDM_END) { - break; - } - if (state_map->nodes[x].states[i] == state) { - ok = !ok; - goto end; - } - } - } - end: - - return ok; -} - -/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */ -#define DEFAULT_WAIT_TIME 1000 -FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) -{ - int ok = 1; - int waitms = DEFAULT_WAIT_TIME; - - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n", - ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); - return FTDM_FAIL; - } - - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the previous state change has not been processed yet\n", - ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); - return FTDM_FAIL; - } - - if (ftdmchan->state == state) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); - return FTDM_FAIL; - } - - if (ftdmchan->span->state_map) { - ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); - goto end; - } - - /* basic core state validation (by-passed if the signaling module provides a state_map) */ - switch(ftdmchan->state) { - case FTDM_CHANNEL_STATE_HANGUP: - case FTDM_CHANNEL_STATE_TERMINATING: - { - ok = 0; - switch(state) { - case FTDM_CHANNEL_STATE_DOWN: - case FTDM_CHANNEL_STATE_BUSY: - case FTDM_CHANNEL_STATE_RESTART: - ok = 1; - break; - default: - break; - } - } - break; - case FTDM_CHANNEL_STATE_UP: - { - ok = 1; - switch(state) { - case FTDM_CHANNEL_STATE_PROGRESS: - case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: - case FTDM_CHANNEL_STATE_RING: - ok = 0; - break; - default: - break; - } - } - break; - case FTDM_CHANNEL_STATE_DOWN: - { - ok = 0; - - switch(state) { - case FTDM_CHANNEL_STATE_DIALTONE: - case FTDM_CHANNEL_STATE_COLLECT: - case FTDM_CHANNEL_STATE_DIALING: - case FTDM_CHANNEL_STATE_RING: - case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: - case FTDM_CHANNEL_STATE_PROGRESS: - case FTDM_CHANNEL_STATE_IDLE: - case FTDM_CHANNEL_STATE_GET_CALLERID: - case FTDM_CHANNEL_STATE_GENRING: - ok = 1; - break; - default: - break; - } - } - break; - case FTDM_CHANNEL_STATE_BUSY: - { - switch(state) { - case FTDM_CHANNEL_STATE_UP: - ok = 0; - break; - default: - break; - } - } - break; - case FTDM_CHANNEL_STATE_RING: - { - switch(state) { - case FTDM_CHANNEL_STATE_UP: - ok = 1; - break; - default: - break; - } - } - break; - default: - break; - } - -end: - - if (ok) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); - ftdmchan->last_state = ftdmchan->state; - ftdmchan->state = state; - ftdmchan->history[ftdmchan->hindex].file = file; - ftdmchan->history[ftdmchan->hindex].func = func; - ftdmchan->history[ftdmchan->hindex].line = line; - ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state; - ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state; - ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms(); - ftdmchan->hindex++; - if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) { - ftdmchan->hindex = 0; - } - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); - - ftdm_mutex_lock(ftdmchan->span->mutex); - ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); - if (ftdmchan->span->pendingchans) { - ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); - } - ftdm_mutex_unlock(ftdmchan->span->mutex); - } else { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); - goto done; - } - - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { - /* the channel should not block waiting for state processing */ - goto done; - } - - /* there is an inherent race here between set and check of the change flag but we do not care because - * the flag should never last raised for more than a few ms for any state change */ - while (waitrq && waitms > 0) { - /* give a chance to the signaling stack to process it */ - ftdm_mutex_unlock(ftdmchan->mutex); - - ftdm_sleep(10); - waitms -= 10; - - ftdm_mutex_lock(ftdmchan->mutex); - - /* if the flag is no longer set, the state change was processed (or is being processed) */ - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - break; - } - - /* if the state is no longer what we set, the state change was - * obviously processed (and the current state change flag is for other state change) */ - if (ftdmchan->state != state) { - break; - } - } - - if (waitms <= 0) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", - ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); - ok = 0; - } -done: - return ok ? FTDM_SUCCESS : FTDM_FAIL; -} - FT_DECLARE(uint32_t) ftdm_group_get_id(const ftdm_group_t *group) { return group->group_id; @@ -1948,17 +1708,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direc return status; } - -FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan) -{ - if (ftdmchan->init_state != FTDM_CHANNEL_STATE_DOWN) { - ftdm_set_state(ftdmchan, ftdmchan->init_state); - ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN; - } - - return FTDM_SUCCESS; -} - FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan) { ftdm_status_t status = FTDM_FAIL; @@ -2415,42 +2164,6 @@ FT_DECLARE(ftdm_caller_data_t *) ftdm_channel_get_caller_data(ftdm_channel_t *ft return &ftdmchan->caller_data; } -FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan) -{ - int state; - ftdm_channel_lock(ftdmchan); - state = ftdmchan->state; - ftdm_channel_unlock(ftdmchan); - return state; -} - -FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan) -{ - const char *state; - ftdm_channel_lock(ftdmchan); - state = ftdm_channel_state2str(ftdmchan->state); - ftdm_channel_unlock(ftdmchan); - return state; -} - -FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan) -{ - int last_state; - ftdm_channel_lock(ftdmchan); - last_state = ftdmchan->last_state; - ftdm_channel_unlock(ftdmchan); - return last_state; -} - -FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan) -{ - const char *state; - ftdm_channel_lock(ftdmchan); - state = ftdm_channel_state2str(ftdmchan->last_state); - ftdm_channel_unlock(ftdmchan); - return state; -} - FT_DECLARE(ftdm_channel_t *) ftdm_span_get_channel(const ftdm_span_t *span, uint32_t chanid) { ftdm_channel_t *chan; @@ -6184,63 +5897,6 @@ FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen) return new; } - -static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime) -{ - char func[255]; - char line[255]; - char states[255]; - const char *filename = NULL; - snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state)); - snprintf(func, sizeof(func), "[%s]", fchan->history[i].func); - filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR); - if (!filename) { - filename = fchan->history[i].file; - } else { - filename++; - } - if (!(*prevtime)) { - *prevtime = fchan->history[i].time; - } - snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line); - stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime)); - *prevtime = fchan->history[i].time; -} - -FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan) -{ - uint8_t i = 0; - ftdm_time_t currtime = 0; - ftdm_time_t prevtime = 0; - - ftdm_stream_handle_t stream = { 0 }; - FTDM_STANDARD_STREAM(stream); - if (!fchan->history[0].file) { - stream.write_function(&stream, "-- No state history --\n"); - return stream.data; - } - - stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", - "-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n"); - - for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) { - if (!fchan->history[i].file) { - break; - } - write_history_entry(fchan, &stream, i, &prevtime); - } - - for (i = 0; i < fchan->hindex; i++) { - write_history_entry(fchan, &stream, i, &prevtime); - } - - currtime = ftdm_current_time_in_ms(); - - stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime)); - - return stream.data; -} - static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data) { uint32_t current_call_id; From 4268bf84b06a66c98803e4998116390ea5d24da7 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 24 Dec 2010 15:58:04 -0500 Subject: [PATCH 04/13] freetdm: more core state refactoring, still untested --- libs/freetdm/Makefile.am | 1 + libs/freetdm/src/ftdm_state.c | 458 ++++++++++++++++++ libs/freetdm/src/include/freetdm.h | 3 +- libs/freetdm/src/include/private/ftdm_core.h | 53 +- libs/freetdm/src/include/private/ftdm_state.h | 218 +++++++++ libs/freetdm/src/include/private/ftdm_types.h | 63 +-- 6 files changed, 685 insertions(+), 111 deletions(-) create mode 100644 libs/freetdm/src/ftdm_state.c create mode 100644 libs/freetdm/src/include/private/ftdm_state.h diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index 5e804b7505..2ab5c29e18 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -73,6 +73,7 @@ libfreetdm_la_SOURCES = \ $(SRC)/hashtable.c \ $(SRC)/hashtable_itr.c \ $(SRC)/ftdm_io.c \ + $(SRC)/ftdm_state.c \ $(SRC)/ftdm_queue.c \ $(SRC)/ftdm_sched.c \ $(SRC)/ftdm_call_utils.c \ diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c new file mode 100644 index 0000000000..2b544756ec --- /dev/null +++ b/libs/freetdm/src/ftdm_state.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2010, Sangoma Technologies + * Moises Silva + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "private/ftdm_core.h" + +FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID) + +/* This function is only needed for boost and we should get rid of it at the next refactoring */ +FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan) +{ + ftdm_channel_lock(fchan); + + if (fchan->init_state != FTDM_CHANNEL_STATE_DOWN) { + ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, fchan, fchan->init_state, 1); + fchan->init_state = FTDM_CHANNEL_STATE_DOWN; + } + + ftdm_channel_unlock(fchan); + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan) +{ + uint8_t hindex = 0; + ftdm_channel_state_t state = fchan->state; + + if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) { + return FTDM_SUCCESS; + } + + if (state == FTDM_CHANNEL_STATE_PROGRESS) { + ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); + } else if (state == FTDM_CHANNEL_STATE_UP) { + ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA); + ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED); + } else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { + ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); + ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA); + } + + /* if there is a pending ack for an indication */ + if (ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) { + ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS); + ftdm_clear_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING); + } + + ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s\n", ftdm_channel_state2str(fchan->state), ftdm_channel_state2str(state)); + hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (hindex - 1); + + ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n"); + + fchan->history[hindex].end_time = ftdm_current_time_in_ms(); + + /* FIXME: broadcast condition to wake up anyone waiting on state completion if the channel + * is blocking (FTDM_CHANNEL_NONBLOCK is not set) */ + + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line, + ftdm_channel_t *fchan, ftdm_channel_state_t state) +{ + if (fchan->state_status == FTDM_STATE_STATUS_NEW) { + /* the current state is new, setting a new state from a signaling module + when the current state is new is equivalent to implicitly acknowledging + the current state */ + _ftdm_channel_complete_state(file, func, line, fchan); + } + return ftdm_channel_set_state(file, func, line, fchan, state, 0); +} + +static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map) +{ + int x = 0, ok = 0; + ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND; + + for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) { + int i = 0, proceed = 0; + if (!state_map->nodes[x].type) { + break; + } + + if (state_map->nodes[x].direction != direction) { + continue; + } + + if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) { + proceed = 1; + } else { + for(i = 0; i < FTDM_MAP_MAX; i++) { + if (state_map->nodes[x].check_states[i] == ftdmchan->state) { + proceed = 1; + break; + } + } + } + + if (!proceed) { + continue; + } + + for(i = 0; i < FTDM_MAP_MAX; i++) { + ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE); + if (state_map->nodes[x].states[i] == FTDM_END) { + break; + } + if (state_map->nodes[x].states[i] == state) { + ok = !ok; + goto end; + } + } + } + end: + + return ok; +} + +/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */ +#define DEFAULT_WAIT_TIME 1000 +FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) +{ + int ok = 1; + int waitms = DEFAULT_WAIT_TIME; + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n", + ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + return FTDM_FAIL; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the previous state change has not been processed yet\n", + ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + return FTDM_FAIL; + } + + if (ftdmchan->state == state) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + return FTDM_FAIL; + } + + if (ftdmchan->span->state_map) { + ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); + goto end; + } + + /* basic core state validation (by-passed if the signaling module provides a state_map) */ + switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_HANGUP: + case FTDM_CHANNEL_STATE_TERMINATING: + { + ok = 0; + switch(state) { + case FTDM_CHANNEL_STATE_DOWN: + case FTDM_CHANNEL_STATE_BUSY: + case FTDM_CHANNEL_STATE_RESTART: + ok = 1; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_UP: + { + ok = 1; + switch(state) { + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + case FTDM_CHANNEL_STATE_RING: + ok = 0; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_DOWN: + { + ok = 0; + + switch(state) { + case FTDM_CHANNEL_STATE_DIALTONE: + case FTDM_CHANNEL_STATE_COLLECT: + case FTDM_CHANNEL_STATE_DIALING: + case FTDM_CHANNEL_STATE_RING: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_IDLE: + case FTDM_CHANNEL_STATE_GET_CALLERID: + case FTDM_CHANNEL_STATE_GENRING: + ok = 1; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_BUSY: + { + switch(state) { + case FTDM_CHANNEL_STATE_UP: + ok = 0; + break; + default: + break; + } + } + break; + case FTDM_CHANNEL_STATE_RING: + { + switch(state) { + case FTDM_CHANNEL_STATE_UP: + ok = 1; + break; + default: + break; + } + } + break; + default: + break; + } + +end: + + if (ok) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + ftdmchan->last_state = ftdmchan->state; + ftdmchan->state = state; + ftdmchan->state_status = FTDM_STATE_STATUS_NEW; + ftdmchan->history[ftdmchan->hindex].file = file; + ftdmchan->history[ftdmchan->hindex].func = func; + ftdmchan->history[ftdmchan->hindex].line = line; + ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state; + ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state; + ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms(); + ftdmchan->history[ftdmchan->hindex].end_time = 0; + ftdmchan->hindex++; + if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) { + ftdmchan->hindex = 0; + } + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + + ftdm_mutex_lock(ftdmchan->span->mutex); + ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + if (ftdmchan->span->pendingchans) { + ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); + } + ftdm_mutex_unlock(ftdmchan->span->mutex); + } else { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + goto done; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { + /* the channel should not block waiting for state processing */ + goto done; + } + + /* there is an inherent race here between set and check of the change flag but we do not care because + * the flag should never last raised for more than a few ms for any state change */ + while (waitrq && waitms > 0) { + /* give a chance to the signaling stack to process it */ + ftdm_mutex_unlock(ftdmchan->mutex); + + ftdm_sleep(10); + waitms -= 10; + + ftdm_mutex_lock(ftdmchan->mutex); + + /* if the flag is no longer set, the state change was processed (or is being processed) */ + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { + break; + } + + /* if the state is no longer what we set, the state change was + * obviously processed (and the current state change flag is for other state change) */ + if (ftdmchan->state != state) { + break; + } + } + + if (waitms <= 0) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", + ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); + ok = 0; + } +done: + return ok ? FTDM_SUCCESS : FTDM_FAIL; +} + +FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan) +{ + int state; + ftdm_channel_lock(ftdmchan); + state = ftdmchan->state; + ftdm_channel_unlock(ftdmchan); + return state; +} + +FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan) +{ + const char *state; + ftdm_channel_lock(ftdmchan); + state = ftdm_channel_state2str(ftdmchan->state); + ftdm_channel_unlock(ftdmchan); + return state; +} + +FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan) +{ + int last_state; + ftdm_channel_lock(ftdmchan); + last_state = ftdmchan->last_state; + ftdm_channel_unlock(ftdmchan); + return last_state; +} + +FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan) +{ + const char *state; + ftdm_channel_lock(ftdmchan); + state = ftdm_channel_state2str(ftdmchan->last_state); + ftdm_channel_unlock(ftdmchan); + return state; +} + +static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime) +{ + char func[255]; + char line[255]; + char states[255]; + const char *filename = NULL; + snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state)); + snprintf(func, sizeof(func), "[%s]", fchan->history[i].func); + filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR); + if (!filename) { + filename = fchan->history[i].file; + } else { + filename++; + } + if (!(*prevtime)) { + *prevtime = fchan->history[i].time; + } + snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line); + stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime)); + *prevtime = fchan->history[i].time; +} + +FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan) +{ + uint8_t i = 0; + ftdm_time_t currtime = 0; + ftdm_time_t prevtime = 0; + + ftdm_stream_handle_t stream = { 0 }; + FTDM_STANDARD_STREAM(stream); + if (!fchan->history[0].file) { + stream.write_function(&stream, "-- No state history --\n"); + return stream.data; + } + + stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", + "-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n"); + + for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) { + if (!fchan->history[i].file) { + break; + } + write_history_entry(fchan, &stream, i, &prevtime); + } + + for (i = 0; i < fchan->hindex; i++) { + write_history_entry(fchan, &stream, i, &prevtime); + } + + currtime = ftdm_current_time_in_ms(); + + stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime)); + + return stream.data; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan, ftdm_channel_state_processor_t state_processor) +{ + ftdm_channel_state_t state; + + ftdm_channel_lock(fchan); + while (fchan->state_status == FTDM_STATE_STATUS_NEW) { + state = fchan->state; + state_processor(fchan); + if (state == fchan->state) { + /* if the state did not change, the state status must go to PROCESSED + * otherwise we don't touch it since is a new state and the old state was + * already completed implicitly by the state_processor() function via some internal + * call to ftdm_set_state() */ + fchan->state_status = FTDM_STATE_STATUS_PROCESSED; + } + } + ftdm_channel_unlock(fchan); + return FTDM_SUCCESS; +} + +FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state) +{ + uint32_t j; + for(j = 1; j <= span->chan_count; j++) { + if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) { + return 0; + } + } + return 1; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 68c9fdc7c4..30fe6ca4f5 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -440,6 +440,7 @@ typedef struct { * This is used during incoming calls when you want to request the signaling stack * to notify about indications occurring locally. See ftdm_channel_call_indicate for more info */ typedef enum { + FTDM_CHANNEL_INDICATE_NONE, FTDM_CHANNEL_INDICATE_RINGING, FTDM_CHANNEL_INDICATE_PROCEED, FTDM_CHANNEL_INDICATE_PROGRESS, @@ -449,7 +450,7 @@ typedef enum { FTDM_CHANNEL_INDICATE_ANSWER, FTDM_CHANNEL_INDICATE_INVALID, } ftdm_channel_indication_t; -#define INDICATION_STRINGS "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "INVALID" +#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "INVALID" /*! \brief Move from string to ftdm_channel_indication_t and viceversa */ FTDM_STR2ENUM_P(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t) diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index dd70dd10ed..8f97456fb7 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -192,17 +192,6 @@ extern "C" { #define ftdm_clear_sflag_locked(obj, flag) assert(obj->mutex != NULL); ftdm_mutex_lock(obj->mutex); (obj)->sflags &= ~(flag); ftdm_mutex_unlock(obj->mutex); -#define ftdm_set_state(obj, s) ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \ - -#define ftdm_set_state_locked(obj, s) \ - do { \ - ftdm_channel_lock(obj); \ - ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \ - ftdm_channel_unlock(obj); \ - } while(0); - -#define ftdm_set_state_r(obj, s, r) r = ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); - #ifdef _MSC_VER /* The while(0) below throws a conditional expression is constant warning */ #pragma warning(disable:4127) @@ -363,15 +352,6 @@ typedef struct { ftdm_mutex_t *mutex; } ftdm_dtmf_debug_t; -typedef struct { - const char *file; - const char *func; - int line; - ftdm_channel_state_t state; - ftdm_channel_state_t last_state; - ftdm_time_t time; -} ftdm_channel_history_entry_t; - typedef enum { FTDM_IOSTATS_ERROR_CRC = (1 << 0), FTDM_IOSTATS_ERROR_FRAME = (1 << 1), @@ -424,9 +404,11 @@ struct ftdm_channel { uint32_t native_interval; uint32_t packet_len; ftdm_channel_state_t state; + ftdm_state_status_t state_status; ftdm_channel_state_t last_state; ftdm_channel_state_t init_state; - ftdm_channel_history_entry_t history[10]; + ftdm_channel_indication_t indication; + ftdm_state_history_entry_t history[10]; uint8_t hindex; ftdm_mutex_t *mutex; teletone_dtmf_detect_state_t dtmf_detect; @@ -572,9 +554,6 @@ FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_checksum(ftdm_fsk_data_state_t *stat FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_sdmf(ftdm_fsk_data_state_t *state, const char *date, char *number); FT_DECLARE(ftdm_status_t) ftdm_channel_send_fsk_data(ftdm_channel_t *ftdmchan, ftdm_fsk_data_state_t *fsk_data, float db_level); -FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, - ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait); - FT_DECLARE(ftdm_status_t) ftdm_span_load_tones(ftdm_span_t *span, const char *mapname); FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void); @@ -589,8 +568,6 @@ FT_DECLARE(void) print_hex_bytes(uint8_t *data, ftdm_size_t dlen, char *buf, ftd FT_DECLARE_NONSTD(int) ftdm_hash_equalkeys(void *k1, void *k2); FT_DECLARE_NONSTD(uint32_t) ftdm_hash_hashfromstring(void *ky); -FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan); - FT_DECLARE(int) ftdm_load_modules(void); FT_DECLARE(ftdm_status_t) ftdm_unload_modules(void); @@ -707,30 +684,6 @@ static __inline__ void ftdm_abort(void) #endif } -static __inline__ void ftdm_set_state_all(ftdm_span_t *span, ftdm_channel_state_t state) -{ - uint32_t j; - ftdm_mutex_lock(span->mutex); - for(j = 1; j <= span->chan_count; j++) { - if (!FTDM_IS_DCHAN(span->channels[j])) { - ftdm_set_state_locked((span->channels[j]), state); - } - } - ftdm_mutex_unlock(span->mutex); -} - -static __inline__ int ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state) -{ - uint32_t j; - for(j = 1; j <= span->chan_count; j++) { - if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) { - return 0; - } - } - - return 1; -} - static __inline__ int16_t ftdm_saturated_add(int16_t sample1, int16_t sample2) { int addres; diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h new file mode 100644 index 0000000000..79a3df07e9 --- /dev/null +++ b/libs/freetdm/src/include/private/ftdm_state.h @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2010, Sangoma Technologies + * Moises Silva + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __FTDM_STATE_H__ +#define __FTDM_STATE_H__ + +/*! \file + * \brief State handling definitions + * \note Most, if not all of the state handling functions assume you have a lock acquired. Touching the channel + * state is a sensitive matter that requires checks and careful thought and is typically a process that + * is not encapsulated within a single function, therefore the lock must be explicitly acquired by the + * caller (most of the time, signaling modules), process states, set a new state and process it, and + * finally unlock the channel. See docs/locking.txt fore more info + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FTDM_CHANNEL_STATE_DOWN, + FTDM_CHANNEL_STATE_HOLD, + FTDM_CHANNEL_STATE_SUSPENDED, + FTDM_CHANNEL_STATE_DIALTONE, + FTDM_CHANNEL_STATE_COLLECT, + FTDM_CHANNEL_STATE_RING, + FTDM_CHANNEL_STATE_RINGING, + FTDM_CHANNEL_STATE_BUSY, + FTDM_CHANNEL_STATE_ATTN, + FTDM_CHANNEL_STATE_GENRING, + FTDM_CHANNEL_STATE_DIALING, + FTDM_CHANNEL_STATE_GET_CALLERID, + FTDM_CHANNEL_STATE_CALLWAITING, + FTDM_CHANNEL_STATE_RESTART, + FTDM_CHANNEL_STATE_PROCEED, + FTDM_CHANNEL_STATE_PROGRESS, + FTDM_CHANNEL_STATE_PROGRESS_MEDIA, + FTDM_CHANNEL_STATE_UP, + FTDM_CHANNEL_STATE_IDLE, + FTDM_CHANNEL_STATE_TERMINATING, + FTDM_CHANNEL_STATE_CANCEL, + FTDM_CHANNEL_STATE_HANGUP, + FTDM_CHANNEL_STATE_HANGUP_COMPLETE, + FTDM_CHANNEL_STATE_IN_LOOP, + FTDM_CHANNEL_STATE_RESET, + FTDM_CHANNEL_STATE_INVALID +} ftdm_channel_state_t; +#define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \ + "RING", "RINGING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \ + "RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", \ + "HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t) + +typedef struct { + const char *file; + const char *func; + int line; + ftdm_channel_state_t state; + ftdm_channel_state_t last_state; + ftdm_time_t time; + ftdm_time_t end_time; +} ftdm_state_history_entry_t; + +typedef ftdm_status_t (*ftdm_channel_state_processor_t)(ftdm_channel_t *fchan); + +FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan, ftdm_channel_state_processor_t processor); +FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *function, int line, ftdm_channel_t *fchan); +#define ftdm_channel_complete_state(obj) _ftdm_channel_complete_state(__FILE__, __FUNCTION__, __LINE__, obj) +FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state); + +/*! + * \brief Status of the current channel state + * \note A given state goes thru several status (yes, states for the state!) + * The order is always FTDM_STATE_STATUS_NEW -> FTDM_STATE_STATUS_PROCESSED -> FTDM_STATUS_COMPLETED + * However, is possible to go from NEW -> COMPLETED directly when the signaling module explicitly changes + * the state of the channel in the middle of processing the current state by calling the ftdm_set_state() API + * + * FTDM_STATE_STATUS_NEW - + * Someone just set the state of the channel, either the signaling module or the user (implicitly through a call API). + * This is accomplished by calling ftdm_channel_set_state() which changes the 'state' and 'last_state' memebers of + * the ftdm_channel_t structure. + * + * FTDM_STATE_STATUS_PROCESSED - + * The signaling module did something based on the new state. + * + * This is accomplished via ftdm_channel_advance_states() + * + * When ftdm_channel_advance_states(), at the very least, if the channel has its state in FTDM_STATE_STATUS_NEW, it + * will move to FTDM_STATE_STATUS_PROCESSED, depending on what the signaling module does during the processing + * the state may move to FTDM_STATE_STATUS_COMPLETED right after or wait for a signaling specific event to complete it. + * It is also possible that more state transitions occur during the execution of ftdm_channel_advance_states() if one + * state processing/completion leads to another state change, the function will not return until the chain of events + * lead to a state that is not in FTDM_STATE_STATUS_NEW + * + * FTDM_STATE_STATUS_COMPLETED - + * The signaling module completed the processing of the state and there is nothing further to be done for this state. + * + * This is accomplished either explicitly by the signaling module by calling ftdm_channel_complete_state() or by + * the signaling module implicitly by trying to set the state of the channel to a new state via ftdm_set_state() + * + * When working with blocking channels (FTDM_CHANNEL_NONBLOCK flag not set), the user thread is signaled and unblocked + * so it can continue. + * + * When a state moves to this status is also possible for a signal FTDM_SIGEVENT_INDICATION_COMPLETED to be delivered + * by the core if the state change was associated to an indication requested by the user, + */ +typedef enum { + FTDM_STATE_STATUS_NEW, + FTDM_STATE_STATUS_PROCESSED, + FTDM_STATE_STATUS_COMPLETED +} ftdm_state_status_t; + +typedef enum { + ZSM_NONE, + ZSM_UNACCEPTABLE, + ZSM_ACCEPTABLE +} ftdm_state_map_type_t; + +typedef enum { + ZSD_INBOUND, + ZSD_OUTBOUND, +} ftdm_state_direction_t; + +#define FTDM_MAP_NODE_SIZE 512 +#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2 + +struct ftdm_state_map_node { + ftdm_state_direction_t direction; + ftdm_state_map_type_t type; + ftdm_channel_state_t check_states[FTDM_MAP_MAX]; + ftdm_channel_state_t states[FTDM_MAP_MAX]; +}; +typedef struct ftdm_state_map_node ftdm_state_map_node_t; + +struct ftdm_state_map { + ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE]; +}; +typedef struct ftdm_state_map ftdm_state_map_t; + +FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, + ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait); + +/*!\brief Set the state of a channel immediately and implicitly complete the previous state */ +FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line, + ftdm_channel_t *fchan, ftdm_channel_state_t state); +#define ftdm_set_state(obj, s) _ftdm_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s); \ + +/*!\brief This macro is deprecated, signaling modules should always lock the channel themselves anyways since they must + * process first the user pending state changes then set a new state before releasing the lock + */ +#define ftdm_set_state_locked(obj, s) \ + do { \ + ftdm_channel_lock(obj); \ + ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \ + ftdm_channel_unlock(obj); \ + } while(0); + +#define ftdm_set_state_r(obj, s, r) r = ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); + +#define ftdm_set_state_all(span, state) \ + do { \ + uint32_t _j; \ + ftdm_mutex_lock((span)->mutex); \ + for(_j = 1; _j <= (span)->chan_count; _j++) { \ + if (!FTDM_IS_DCHAN(span->channels[_j])) { \ + ftdm_set_state_locked((span->channels[_j]), state); \ + } \ + } \ + ftdm_mutex_unlock((span)->mutex); \ + } while (0); + +#ifdef __cplusplus +} +#endif + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h index d70bc69b3a..c4d48a893d 100644 --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -204,40 +204,6 @@ typedef enum { FTDM_CHANNEL_FEATURE_IO_STATS = (1<<9), /*!< Channel supports IO statistics (HDLC channels only) */ } ftdm_channel_feature_t; -typedef enum { - FTDM_CHANNEL_STATE_DOWN, - FTDM_CHANNEL_STATE_HOLD, - FTDM_CHANNEL_STATE_SUSPENDED, - FTDM_CHANNEL_STATE_DIALTONE, - FTDM_CHANNEL_STATE_COLLECT, - FTDM_CHANNEL_STATE_RING, - FTDM_CHANNEL_STATE_RINGING, - FTDM_CHANNEL_STATE_BUSY, - FTDM_CHANNEL_STATE_ATTN, - FTDM_CHANNEL_STATE_GENRING, - FTDM_CHANNEL_STATE_DIALING, - FTDM_CHANNEL_STATE_GET_CALLERID, - FTDM_CHANNEL_STATE_CALLWAITING, - FTDM_CHANNEL_STATE_RESTART, - FTDM_CHANNEL_STATE_PROCEED, - FTDM_CHANNEL_STATE_PROGRESS, - FTDM_CHANNEL_STATE_PROGRESS_MEDIA, - FTDM_CHANNEL_STATE_UP, - FTDM_CHANNEL_STATE_IDLE, - FTDM_CHANNEL_STATE_TERMINATING, - FTDM_CHANNEL_STATE_CANCEL, - FTDM_CHANNEL_STATE_HANGUP, - FTDM_CHANNEL_STATE_HANGUP_COMPLETE, - FTDM_CHANNEL_STATE_IN_LOOP, - FTDM_CHANNEL_STATE_RESET, - FTDM_CHANNEL_STATE_INVALID -} ftdm_channel_state_t; -#define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \ - "RING", "RINGING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \ - "RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", \ - "HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID" -FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t) - /*!< Channel flags. This used to be an enum but we reached the 32bit limit for enums, is safer this way */ #define FTDM_CHANNEL_CONFIGURED (1ULL << 0) #define FTDM_CHANNEL_READY (1ULL << 1) @@ -282,33 +248,10 @@ FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channe #define FTDM_CHANNEL_CALL_STARTED (1ULL << 32) /*!< The user wants non-blocking operations in the channel */ #define FTDM_CHANNEL_NONBLOCK (1ULL << 33) +/*!< There is a pending acknowledge for an indication */ +#define FTDM_CHANNEL_IND_ACK_PENDING (1ULL << 34) -typedef enum { - ZSM_NONE, - ZSM_UNACCEPTABLE, - ZSM_ACCEPTABLE -} ftdm_state_map_type_t; - -typedef enum { - ZSD_INBOUND, - ZSD_OUTBOUND, -} ftdm_state_direction_t; - -#define FTDM_MAP_NODE_SIZE 512 -#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2 - -struct ftdm_state_map_node { - ftdm_state_direction_t direction; - ftdm_state_map_type_t type; - ftdm_channel_state_t check_states[FTDM_MAP_MAX]; - ftdm_channel_state_t states[FTDM_MAP_MAX]; -}; -typedef struct ftdm_state_map_node ftdm_state_map_node_t; - -struct ftdm_state_map { - ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE]; -}; -typedef struct ftdm_state_map ftdm_state_map_t; +#include "ftdm_state.h" typedef enum ftdm_channel_hw_link_status { FTDM_HW_LINK_DISCONNECTED = 0, From 090864fa3997cf697f046200e1ea813464ec79d8 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 29 Dec 2010 13:38:43 -0500 Subject: [PATCH 05/13] freetdm: completed most of the code for the new core state processing use new core state processing functions in ftmod_r2 --- libs/freetdm/mod_freetdm/mod_freetdm.c | 15 +- libs/freetdm/src/ftdm_io.c | 63 ++-- libs/freetdm/src/ftdm_state.c | 44 ++- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 350 ++++++++---------- libs/freetdm/src/include/freetdm.h | 14 +- libs/freetdm/src/include/ftdm_declare.h | 1 + libs/freetdm/src/include/private/ftdm_core.h | 2 +- libs/freetdm/src/include/private/ftdm_state.h | 5 +- 8 files changed, 250 insertions(+), 244 deletions(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index d9c26db4fe..e08d4c1f3b 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -421,16 +421,18 @@ static switch_status_t channel_on_routing(switch_core_session_t *session) private_t *tech_pvt = NULL; channel = switch_core_session_get_channel(session); - assert(channel != NULL); + switch_assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); - assert(tech_pvt != NULL); + switch_assert(tech_pvt != NULL); - assert(tech_pvt->ftdmchan != NULL); + switch_assert(tech_pvt->ftdmchan != NULL); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel)); - ftdm_channel_call_indicate(tech_pvt->ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED); + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) { + ftdm_channel_call_indicate(tech_pvt->ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED); + } return SWITCH_STATUS_SUCCESS; } @@ -441,10 +443,10 @@ static switch_status_t channel_on_execute(switch_core_session_t *session) private_t *tech_pvt = NULL; channel = switch_core_session_get_channel(session); - assert(channel != NULL); + switch_assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); - assert(tech_pvt != NULL); + switch_assert(tech_pvt != NULL); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel)); @@ -2094,6 +2096,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal) break; case FTDM_SIGEVENT_PROCEED:{} break; + case FTDM_SIGEVENT_INDICATION_COMPLETED:{} break; default: { diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 161ccdcc18..0c25054816 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -1022,6 +1022,8 @@ FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t } ftdm_set_flag(new_chan, FTDM_CHANNEL_CONFIGURED | FTDM_CHANNEL_READY); + new_chan->state = FTDM_CHANNEL_STATE_DOWN; + new_chan->state_status = FTDM_STATE_STATUS_COMPLETED; *chan = new_chan; return FTDM_SUCCESS; } @@ -2012,10 +2014,14 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char return status; } -FT_DECLARE(void) ftdm_ack_indication(const ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status) +FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status) { ftdm_sigmsg_t msg; + ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n", + ftdm_channel_indication2str(indication), ftdm_channel_state2str(fchan->state), status); + ftdm_clear_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING); memset(&msg, 0, sizeof(msg)); + msg.channel = fchan; msg.event_id = FTDM_SIGEVENT_INDICATION_COMPLETED; msg.ev_data.indication_completed.indication = indication; msg.ev_data.indication_completed.status = status; @@ -2197,21 +2203,39 @@ FT_DECLARE(uint32_t) ftdm_channel_get_ph_span_id(const ftdm_channel_t *ftdmchan) /* * Every user requested indication *MUST* be acknowledged with the proper status (ftdm_status_t) - * If the indication fails before we notify the signaling stack, we *MUST* acknowledge ourselves, + * However, if the indication fails before we notify the signaling stack, we don't need to ack * but if we already notified the signaling stack about the indication, the signaling stack is - * responsible for the acknowledge. + * responsible for the acknowledge. Bottom line is, whenever this function returns FTDM_SUCCESS + * someone *MUST* acknowledge the indication, either the signaling stack, this function or the core + * at some later point * */ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication) { ftdm_status_t status = FTDM_SUCCESS; + ftdm_assert_return(ftdmchan, FTDM_FAIL, "Null channel\n"); + + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Indicating %s in state %s\n", + ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state)); + ftdm_channel_lock(ftdmchan); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_IND_ACK_PENDING)) { + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in channel with indication %s still pending in state %s\n", + ftdm_channel_indication2str(indication), + ftdm_channel_indication2str(ftdmchan->indication), + ftdm_channel_state2str(ftdmchan->state)); + status = FTDM_EBUSY; + goto done; + } + + ftdmchan->indication = indication; + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_IND_ACK_PENDING); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in outgoing channel in state %s\n", ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state)); status = FTDM_EINVAL; - ftdm_ack_indication(ftdmchan, indication, status); goto done; } @@ -2219,7 +2243,6 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Ignoring indication %s because the call is in %s state\n", ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state)); status = FTDM_ECANCELED; - ftdm_ack_indication(ftdmchan, indication, status); goto done; } @@ -2228,15 +2251,9 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch * (particularly isdn stacks I think, we should emulate or just move to hangup with busy cause) */ case FTDM_CHANNEL_INDICATE_RINGING: status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1); - if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, indication, status); - } break; case FTDM_CHANNEL_INDICATE_BUSY: status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1); - if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, indication, status); - } break; case FTDM_CHANNEL_INDICATE_PROCEED: if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) { @@ -2244,47 +2261,34 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch goto done; } status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1); - if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, indication, status); - } break; case FTDM_CHANNEL_INDICATE_PROGRESS: status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); - if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, indication, status); - } break; case FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA: if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) { if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, indication, status); goto done; } } /* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */ if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n"); - status = FTDM_ECANCELED; - ftdm_ack_indication(ftdmchan, indication, status); + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring progress media because the call is terminating\n"); goto done; } } - ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); - if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, indication, status); - } + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); break; case FTDM_CHANNEL_INDICATE_ANSWER: - /* _ftdm_channel_call_indicate takes care of the indication ack */ + /* _ftdm_channel_call_answer takes care of the indication ack */ status = _ftdm_channel_call_answer(file, func, line, ftdmchan); break; default: ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Do not know how to indicate %d\n", indication); - status = FTDM_FAIL; - ftdm_ack_indication(ftdmchan, indication, status); + status = FTDM_EINVAL; break; } @@ -2463,6 +2467,7 @@ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan) ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN; ftdmchan->state = FTDM_CHANNEL_STATE_DOWN; + ftdmchan->state_status = FTDM_STATE_STATUS_COMPLETED; ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DEBUG_DTMF, NULL); ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_INPUT_DUMP, NULL); @@ -5023,7 +5028,7 @@ FT_DECLARE(ftdm_status_t) ftdm_configure_span_signaling(ftdm_span_t *span, const ftdm_assert_return(parameters != NULL, FTDM_FAIL, "No parameters"); if (!span->chan_count) { - ftdm_log(FTDM_LOG_WARNING, "Cannot configure signaling on span with no channels\n"); + ftdm_log(FTDM_LOG_WARNING, "Cannot configure signaling on span %s with no channels\n", span->name); return FTDM_FAIL; } diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index 2b544756ec..12dc37957a 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -37,6 +37,9 @@ FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS) FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID) +FTDM_ENUM_NAMES(CHANNEL_STATE_STATUS_NAMES, CHANNEL_STATE_STATUS_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t, CHANNEL_STATE_STATUS_NAMES, FTDM_STATE_STATUS_INVALID) + /* This function is only needed for boost and we should get rid of it at the next refactoring */ FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan) { @@ -54,12 +57,17 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan) FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan) { uint8_t hindex = 0; + ftdm_time_t diff = 0; ftdm_channel_state_t state = fchan->state; if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) { + ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL, + "State change flag set but state is not completed\n"); return FTDM_SUCCESS; } + ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE); + if (state == FTDM_CHANNEL_STATE_PROGRESS) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); } else if (state == FTDM_CHANNEL_STATE_UP) { @@ -71,19 +79,28 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA); } - /* if there is a pending ack for an indication */ + /* if there is a pending ack for an indication + * MAINTENANCE WARNING: we're assuming an indication performed + * via state change will involve a single state change + */ if (ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) { ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS); - ftdm_clear_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING); } - ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s\n", ftdm_channel_state2str(fchan->state), ftdm_channel_state2str(state)); - hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (hindex - 1); + hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1); ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n"); fchan->history[hindex].end_time = ftdm_current_time_in_ms(); + fchan->state_status = FTDM_STATE_STATUS_COMPLETED; + + diff = fchan->history[hindex].end_time - fchan->history[hindex].time; + + ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %llums\n", + ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff); + + /* FIXME: broadcast condition to wake up anyone waiting on state completion if the channel * is blocking (FTDM_CHANNEL_NONBLOCK is not set) */ @@ -93,9 +110,9 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line, ftdm_channel_t *fchan, ftdm_channel_state_t state) { - if (fchan->state_status == FTDM_STATE_STATUS_NEW) { - /* the current state is new, setting a new state from a signaling module - when the current state is new is equivalent to implicitly acknowledging + if (fchan->state_status != FTDM_STATE_STATUS_COMPLETED) { + /* the current state is not completed, setting a new state from a signaling module + when the current state is not completed is equivalent to implicitly acknowledging the current state */ _ftdm_channel_complete_state(file, func, line, fchan); } @@ -161,9 +178,11 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *f return FTDM_FAIL; } - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the previous state change has not been processed yet\n", - ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, + "Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n", + ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state), + ftdm_state_status2str(ftdmchan->state_status)); return FTDM_FAIL; } @@ -422,9 +441,10 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan, ftd ftdm_channel_lock(fchan); while (fchan->state_status == FTDM_STATE_STATUS_NEW) { state = fchan->state; + ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Executing state processor for %s\n", ftdm_channel_state2str(fchan->state)); state_processor(fchan); - if (state == fchan->state) { - /* if the state did not change, the state status must go to PROCESSED + if (state == fchan->state && fchan->state_status == FTDM_STATE_STATUS_NEW) { + /* if the state did not change and is still NEW, the state status must go to PROCESSED * otherwise we don't touch it since is a new state and the old state was * already completed implicitly by the state_processor() function via some internal * call to ftdm_set_state() */ diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 3809b224c4..530bb2b9b2 100644 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -73,7 +73,6 @@ typedef struct ftdm_r2_call_t { int disconnect_rcvd:1; int ftdm_call_started:1; int protocol_error:1; - ftdm_channel_state_t chanstate; ftdm_size_t dnis_index; ftdm_size_t ani_index; char logname[255]; @@ -168,8 +167,7 @@ static ftdm_hash_t *g_mod_data_hash; /* IO interface for the command API */ static ftdm_io_interface_t g_ftdm_r2_interface; -static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan); -static void ftdm_r2_state_advance_all(ftdm_channel_t *ftdmchan); +static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan); /* whether R2 call accept process is pending */ #define IS_ACCEPTING_PENDING(ftdmchan) \ @@ -349,7 +347,6 @@ static void ft_r2_clean_call(ftdm_r2_call_t *call) call->disconnect_rcvd = 0; call->ftdm_call_started = 0; call->protocol_error = 0; - call->chanstate = FTDM_CHANNEL_STATE_DOWN; call->dnis_index = 0; call->ani_index = 0; call->name[0] = 0; @@ -479,7 +476,6 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call) } R2CALL(ftdmchan)->ftdm_call_started = 1; - R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN; ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING); ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS); @@ -624,7 +620,7 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan) ftdm_sched_cancel_timer(r2data->sched, r2call->protocol_error_recovery_timer); ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Cancelled protocol error recovery timer\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - ftdm_r2_state_advance_all(ftdmchan); + ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); } } @@ -658,7 +654,6 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan) ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_OUTPUT_DUMP, &r2data->mf_dump_size); } - R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN; ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS); ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_TX_BUFFERS, NULL); @@ -708,12 +703,10 @@ static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, cons static void clear_accept_pending(ftdm_channel_t *fchan) { if (IS_ACCEPTING_PENDING(fchan)) { - ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_channel_complete_state(fchan); } else if (ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) { ftdm_log_chan(fchan, FTDM_LOG_CRIT, "State change flag set in state %s, last state = %s\n", ftdm_channel_state2str(fchan->state), ftdm_channel_state2str(fchan->last_state)); - ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_channel_complete_state(fchan); } } @@ -821,7 +814,7 @@ static void ftdm_r2_on_call_end(openr2_chan_t *r2chan) ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); /* in some circumstances openr2 can call on_call_init right after this, so let's advance the state right here */ - ftdm_r2_state_advance_all(ftdmchan); + ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); } static void ftdm_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen) @@ -853,7 +846,7 @@ static void ftdm_r2_recover_from_protocol_error(void *data) goto done; } ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - ftdm_r2_state_advance_all(ftdmchan); + ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); done: ftdm_channel_unlock(ftdmchan); } @@ -1620,10 +1613,10 @@ fail: } /* the channel must be locked when calling this function */ -static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan) +static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan) { ftdm_sigmsg_t sigev; - int ret; + ftdm_status_t ret; ftdm_r2_call_t *r2call = R2CALL(ftdmchan); openr2_chan_t *r2chan = r2call->r2chan; ftdm_r2_data_t *r2data = ftdmchan->span->signal_data; @@ -1633,172 +1626,161 @@ static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan) sigev.span_id = ftdmchan->span_id; sigev.channel = ftdmchan; - ret = 0; + ret = FTDM_SUCCESS; - /* because we do not always acknowledge the state change (clearing the FTDM_CHANNEL_STATE_CHANGE flag) due to the accept - * procedure described below, we need the chanstate member to NOT process some states twice, so is valid entering this - * function with the FTDM_CHANNEL_STATE_CHANGE flag set but with a state that was already processed and is just waiting - * to complete (the processing is media-bound) - * */ - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE) - && (r2call->chanstate != ftdmchan->state)) { + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state)); - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state)); - r2call->chanstate = ftdmchan->state; - - if (IS_ACCEPTING_PENDING(ftdmchan)) { - /* - Moving to PROGRESS, PROGRESS_MEDIA or UP means that we must accept the call first, and accepting - the call in R2 means sending a tone, then waiting for the acknowledge from the other end, - since all of that requires sending and detecting tones, it takes a few milliseconds (I'd say around 100) - which means during that time the user should not try to perform any operations like answer, hangup or anything - else, therefore we DO NOT clear the FTDM_CHANNEL_STATE_CHANGE flag here, we rely on ftdm_io.c to block - the user thread until we're done with the accept (see on_call_accepted callback) and then we clear the state change flag, - otherwise we have a race condition between freetdm calling openr2_chan_answer_call and openr2 accepting the call first, - if freetdm calls openr2_chan_answer_call before the accept cycle completes, openr2 will fail to answer the call */ - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "State ack for state %s will have to wait a bit\n", ftdm_channel_state2str(ftdmchan->state)); - } else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){ - ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); - ftdm_channel_complete_state(ftdmchan); - } - - switch (ftdmchan->state) { - - /* starting an incoming call */ - case FTDM_CHANNEL_STATE_COLLECT: - { - uint32_t interval = 0; - ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); - ftdm_assert(interval != 0, "Invalid interval!"); - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting processing of incoming call with interval %d\n", interval); - openr2_chan_enable_read(r2chan); - } - break; - - /* starting an outgoing call */ - case FTDM_CHANNEL_STATE_DIALING: - { - uint32_t interval = 0; - ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); - ftdm_assert(interval != 0, "Invalid interval!"); - ftdm_log_chan(ftdmchan, - FTDM_LOG_DEBUG, "Starting processing of outgoing call in channel with interval %d\n", interval); - openr2_chan_enable_read(r2chan); - } - break; - - /* incoming call was offered */ - case FTDM_CHANNEL_STATE_RING: - - /* notify the user about the new call */ - sigev.event_id = FTDM_SIGEVENT_START; - - ftdm_span_send_signal(ftdmchan->span, &sigev); - r2call->ftdm_call_started = 1; - - break; - - /* the call is making progress */ - case FTDM_CHANNEL_STATE_PROGRESS: - case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: - { - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - if (!r2call->accepted) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Accepting call\n"); - ft_r2_accept_call(ftdmchan); - } - } else { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying progress\n"); - sigev.event_id = FTDM_SIGEVENT_PROCEED; - ftdm_span_send_signal(ftdmchan->span, &sigev); - - sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; - ftdm_span_send_signal(ftdmchan->span, &sigev); - } - } - break; - - /* the call was answered */ - case FTDM_CHANNEL_STATE_UP: - { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call was answered\n"); - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - if (!r2call->accepted) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call has not been accepted, need to accept first\n"); - // the answering will be done in the on_call_accepted handler - ft_r2_accept_call(ftdmchan); - r2call->answer_pending = 1; - } else { - ft_r2_answer_call(ftdmchan); - } - } else { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying of call answered\n"); - sigev.event_id = FTDM_SIGEVENT_UP; - ftdm_span_send_signal(ftdmchan->span, &sigev); - } - } - break; - - /* just got hangup */ - case FTDM_CHANNEL_STATE_HANGUP: - { - if (!r2call->disconnect_rcvd) { - openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan); - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause)); - /* this will disconnect the call, but need to wait for the call end before moving to DOWN */ - openr2_chan_disconnect_call(r2chan, disconnect_cause); - } else if (!r2call->protocol_error) { - /* just ack the hangup, on_call_end will be called by openr2 right after */ - openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING); - } else { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Clearing call due to protocol error\n"); - /* do not set to down yet, give some time for recovery */ - ftdm_sched_timer(r2data->sched, "protocolerr_recover", 100, - ftdm_r2_recover_from_protocol_error, r2chan, &r2call->protocol_error_recovery_timer); - } - } - break; - - case FTDM_CHANNEL_STATE_TERMINATING: - { - /* if the call has not been started yet we must go to HANGUP right here */ - if (!r2call->ftdm_call_started) { - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); - } else { - openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan); - ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause)); - /* notify the user of the call terminating and we wait for the user to move us to hangup */ - sigev.event_id = FTDM_SIGEVENT_STOP; - ftdm_span_send_signal(ftdmchan->span, &sigev); - } - } - break; - - /* finished call for good */ - case FTDM_CHANNEL_STATE_DOWN: - { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n"); - ret = 1; - } - break; - - /* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */ - case FTDM_CHANNEL_STATE_RINGING: - { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n"); - } - break; - - default: - { - ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state)); - } - break; - - } + if (IS_ACCEPTING_PENDING(ftdmchan)) { + /* + Moving to PROGRESS, PROGRESS_MEDIA or UP means that we must accept the call first, and accepting + the call in R2 means sending a tone, then waiting for the acknowledge from the other end, + since all of that requires sending and detecting tones, it takes a few milliseconds (I'd say around 100) + which means during that time the user should not try to perform any operations like answer, hangup or anything + else, therefore we DO NOT clear the FTDM_CHANNEL_STATE_CHANGE flag here, we rely on ftdm_io.c to block + the user thread until we're done with the accept (see on_call_accepted callback) and then we clear the state change flag, + otherwise we have a race condition between freetdm calling openr2_chan_answer_call and openr2 accepting the call first, + if freetdm calls openr2_chan_answer_call before the accept cycle completes, openr2 will fail to answer the call */ + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "State ack for state %s will have to wait a bit\n", ftdm_channel_state2str(ftdmchan->state)); + } else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){ + ftdm_channel_complete_state(ftdmchan); } - if (ret) { + switch (ftdmchan->state) { + + /* starting an incoming call */ + case FTDM_CHANNEL_STATE_COLLECT: + { + uint32_t interval = 0; + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); + ftdm_assert(interval != 0, "Invalid interval!"); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting processing of incoming call with interval %d\n", interval); + openr2_chan_enable_read(r2chan); + } + break; + + /* starting an outgoing call */ + case FTDM_CHANNEL_STATE_DIALING: + { + uint32_t interval = 0; + ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval); + ftdm_assert(interval != 0, "Invalid interval!"); + ftdm_log_chan(ftdmchan, + FTDM_LOG_DEBUG, "Starting processing of outgoing call in channel with interval %d\n", interval); + openr2_chan_enable_read(r2chan); + } + break; + + /* incoming call was offered */ + case FTDM_CHANNEL_STATE_RING: + + /* notify the user about the new call */ + sigev.event_id = FTDM_SIGEVENT_START; + + ftdm_span_send_signal(ftdmchan->span, &sigev); + r2call->ftdm_call_started = 1; + + break; + + /* the call is making progress */ + case FTDM_CHANNEL_STATE_PROGRESS: + case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: + { + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + if (!r2call->accepted) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Accepting call\n"); + ft_r2_accept_call(ftdmchan); + } + } else { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying progress\n"); + sigev.event_id = FTDM_SIGEVENT_PROCEED; + ftdm_span_send_signal(ftdmchan->span, &sigev); + + sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; + ftdm_span_send_signal(ftdmchan->span, &sigev); + } + } + break; + + /* the call was answered */ + case FTDM_CHANNEL_STATE_UP: + { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call was answered\n"); + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + if (!r2call->accepted) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call has not been accepted, need to accept first\n"); + // the answering will be done in the on_call_accepted handler + ft_r2_accept_call(ftdmchan); + r2call->answer_pending = 1; + } else { + ft_r2_answer_call(ftdmchan); + } + } else { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying of call answered\n"); + sigev.event_id = FTDM_SIGEVENT_UP; + ftdm_span_send_signal(ftdmchan->span, &sigev); + } + } + break; + + /* just got hangup */ + case FTDM_CHANNEL_STATE_HANGUP: + { + if (!r2call->disconnect_rcvd) { + openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause)); + /* this will disconnect the call, but need to wait for the call end before moving to DOWN */ + openr2_chan_disconnect_call(r2chan, disconnect_cause); + } else if (!r2call->protocol_error) { + /* just ack the hangup, on_call_end will be called by openr2 right after */ + openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING); + } else { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Clearing call due to protocol error\n"); + /* do not set to down yet, give some time for recovery */ + ftdm_sched_timer(r2data->sched, "protocolerr_recover", 100, + ftdm_r2_recover_from_protocol_error, r2chan, &r2call->protocol_error_recovery_timer); + } + } + break; + + case FTDM_CHANNEL_STATE_TERMINATING: + { + /* if the call has not been started yet we must go to HANGUP right here */ + if (!r2call->ftdm_call_started) { + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP); + } else { + openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause)); + /* notify the user of the call terminating and we wait for the user to move us to hangup */ + sigev.event_id = FTDM_SIGEVENT_STOP; + ftdm_span_send_signal(ftdmchan->span, &sigev); + } + } + break; + + /* finished call for good */ + case FTDM_CHANNEL_STATE_DOWN: + { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n"); + ret = FTDM_BREAK; + } + break; + + /* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */ + case FTDM_CHANNEL_STATE_RINGING: + { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n"); + } + break; + + default: + { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state)); + } + break; + + } + + if (ret == FTDM_BREAK) { ftdm_channel_t *closed_chan; closed_chan = ftdmchan; ftdm_channel_close(&closed_chan); @@ -1807,20 +1789,6 @@ static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan) return ret; } -/* the channel must be locked when calling this function */ -static void ftdm_r2_state_advance_all(ftdm_channel_t *ftdmchan) -{ - /* because we do not always acknowledge the state change (clearing the FTDM_CHANNEL_STATE_CHANGE flag) due to the accept - * procedure described below, we need the chanstate member to NOT process some states twice, so is valid entering this - * function with the FTDM_CHANNEL_STATE_CHANGE flag set but with a state that was already processed and is just waiting - * to complete (the processing is media-bound) - * */ - while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE) - && (R2CALL(ftdmchan)->chanstate != ftdmchan->state)) { - ftdm_r2_state_advance(ftdmchan); - } -} - static void *ftdm_r2_run(ftdm_thread_t *me, void *obj) { openr2_chan_t *r2chan = NULL; @@ -1949,12 +1917,12 @@ static void *ftdm_r2_run(ftdm_thread_t *me, void *obj) ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED); - ftdm_r2_state_advance_all(ftdmchan); + ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); r2chan = call->r2chan; openr2_chan_process_signaling(r2chan); - ftdm_r2_state_advance_all(ftdmchan); + ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); if (!call->accepted) { /* if the call is not accepted we do not want users reading */ diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 30fe6ca4f5..ff129636d9 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -777,11 +777,17 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan); /*! \brief Indicate a new condition in an incoming call + * * \note Every indication request will result in FTDM_SIGEVENT_INDICATION_COMPLETED event being delivered with - * the proper status that will inform you if the request was successful or not. - * Be aware there is no guarantee of whether the event will arrive after or before your execution thread returns - * from ftdm_channel_call_indicate. This means you could get FTDM_SIGEVENT_INDICATION_COMPLETED even before - * your execution thread returns from the ftdm_channel_call_indicate() API + * the proper status that will inform you if the request was successful or not. The exception is if this + * function returns something different to FTDM_SUCCESS, in which case the request failed right away and no + * further FTDM_SIGEVENT_INDICATION_COMPLETED will be delivered + * Be aware there is no guarantee of whether the completion event will arrive after or before your execution + * thread returns from ftdm_channel_call_indicate. This means you could get FTDM_SIGEVENT_INDICATION_COMPLETED + * even before your execution thread returns from the ftdm_channel_call_indicate() API + * + * \note You cannot send more than one indication at the time. You must wait for the completed event before + * calling this function again (unless the return code was different than FTDM_SUCCESS) */ #define ftdm_channel_call_indicate(ftdmchan, indication) _ftdm_channel_call_indicate(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (indication)) diff --git a/libs/freetdm/src/include/ftdm_declare.h b/libs/freetdm/src/include/ftdm_declare.h index 2e41281baf..88a76930f7 100644 --- a/libs/freetdm/src/include/ftdm_declare.h +++ b/libs/freetdm/src/include/ftdm_declare.h @@ -192,6 +192,7 @@ typedef enum { FTDM_EINVAL, /*!< Invalid argument */ FTDM_ECANCELED, /*!< Operation cancelled */ + FTDM_EBUSY, /*!< Device busy */ } ftdm_status_t; /*! \brief FreeTDM bool type. */ diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 8f97456fb7..605ab8df15 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -583,7 +583,7 @@ FT_DECLARE(int) ftdm_vasprintf(char **ret, const char *fmt, va_list ap); FT_DECLARE(ftdm_status_t) ftdm_span_close_all(void); FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan); -FT_DECLARE(void) ftdm_ack_indication(const ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_status_t status); +FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_status_t status); /*! * \brief Retrieves an event from the span diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h index 79a3df07e9..36dcc2bef8 100644 --- a/libs/freetdm/src/include/private/ftdm_state.h +++ b/libs/freetdm/src/include/private/ftdm_state.h @@ -138,8 +138,11 @@ FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t sta typedef enum { FTDM_STATE_STATUS_NEW, FTDM_STATE_STATUS_PROCESSED, - FTDM_STATE_STATUS_COMPLETED + FTDM_STATE_STATUS_COMPLETED, + FTDM_STATE_STATUS_INVALID } ftdm_state_status_t; +#define CHANNEL_STATE_STATUS_STRINGS "NEW", "PROCESSED", "COMPLETED", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t) typedef enum { ZSM_NONE, From c854fe518adb53683cb0336a9f1f722b9208e42c Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 29 Dec 2010 14:04:31 -0500 Subject: [PATCH 06/13] freetdm: update 2008 freetdm vcproj to include ftdm_state header and C file --- libs/freetdm/msvc/freetdm.2008.vcproj | 152 ++++++++++++++------------ 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/libs/freetdm/msvc/freetdm.2008.vcproj b/libs/freetdm/msvc/freetdm.2008.vcproj index c72891e525..0539ff3f42 100644 --- a/libs/freetdm/msvc/freetdm.2008.vcproj +++ b/libs/freetdm/msvc/freetdm.2008.vcproj @@ -94,78 +94,6 @@ Name="VCPostBuildEventTool" /> - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -452,6 +456,10 @@ RelativePath="..\src\ftdm_sched.c" > + + From 6fb1e5d9c1221d4943b52a284668c7435596268f Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 29 Dec 2010 14:08:18 -0500 Subject: [PATCH 07/13] freetdm: free the iterator when setting channels to non-block --- libs/freetdm/src/ftdm_io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 0c25054816..5c900b91dd 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -1902,6 +1902,7 @@ FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, f ftdm_set_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK); } } + ftdm_iterator_free(citer); return FTDM_SUCCESS; } From 903d13648184bbc94030ce381ffbf9b7702e7dce Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 30 Dec 2010 10:23:56 -0500 Subject: [PATCH 08/13] freetdm: remove busy-waiting and use ftdm interrupt to wait for state change completion --- libs/freetdm/src/ftdm_state.c | 96 +++++++++---------- libs/freetdm/src/ftdm_threadmutex.c | 13 ++- libs/freetdm/src/include/private/ftdm_core.h | 1 + libs/freetdm/src/include/private/ftdm_types.h | 2 + 4 files changed, 61 insertions(+), 51 deletions(-) diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index 12dc37957a..21bb3b40c7 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -101,8 +101,10 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff); - /* FIXME: broadcast condition to wake up anyone waiting on state completion if the channel - * is blocking (FTDM_CHANNEL_NONBLOCK is not set) */ + if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) { + ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING); + ftdm_interrupt_signal(fchan->state_change_notify); + } return FTDM_SUCCESS; } @@ -169,6 +171,7 @@ static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t s #define DEFAULT_WAIT_TIME 1000 FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) { + ftdm_status_t status; int ok = 1; int waitms = DEFAULT_WAIT_TIME; @@ -191,6 +194,16 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *f return FTDM_FAIL; } + if (!ftdmchan->state_change_notify) { + status = ftdm_interrupt_create(&ftdmchan->state_change_notify, FTDM_INVALID_SOCKET); + if (status != FTDM_SUCCESS) { + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, + "Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + return status; + } + } + + if (ftdmchan->span->state_map) { ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); goto end; @@ -276,67 +289,54 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *f end: - if (ok) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); - ftdmchan->last_state = ftdmchan->state; - ftdmchan->state = state; - ftdmchan->state_status = FTDM_STATE_STATUS_NEW; - ftdmchan->history[ftdmchan->hindex].file = file; - ftdmchan->history[ftdmchan->hindex].func = func; - ftdmchan->history[ftdmchan->hindex].line = line; - ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state; - ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state; - ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms(); - ftdmchan->history[ftdmchan->hindex].end_time = 0; - ftdmchan->hindex++; - if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) { - ftdmchan->hindex = 0; - } - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); - - ftdm_mutex_lock(ftdmchan->span->mutex); - ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); - if (ftdmchan->span->pendingchans) { - ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); - } - ftdm_mutex_unlock(ftdmchan->span->mutex); - } else { + if (!ok) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); goto done; } + ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); + ftdmchan->last_state = ftdmchan->state; + ftdmchan->state = state; + ftdmchan->state_status = FTDM_STATE_STATUS_NEW; + ftdmchan->history[ftdmchan->hindex].file = file; + ftdmchan->history[ftdmchan->hindex].func = func; + ftdmchan->history[ftdmchan->hindex].line = line; + ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state; + ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state; + ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms(); + ftdmchan->history[ftdmchan->hindex].end_time = 0; + ftdmchan->hindex++; + if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) { + ftdmchan->hindex = 0; + } + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + + ftdm_mutex_lock(ftdmchan->span->mutex); + ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + if (ftdmchan->span->pendingchans) { + ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); + } + ftdm_mutex_unlock(ftdmchan->span->mutex); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { /* the channel should not block waiting for state processing */ goto done; } - /* there is an inherent race here between set and check of the change flag but we do not care because - * the flag should never last raised for more than a few ms for any state change */ - while (waitrq && waitms > 0) { - /* give a chance to the signaling stack to process it */ - ftdm_mutex_unlock(ftdmchan->mutex); + ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING); - ftdm_sleep(10); - waitms -= 10; + ftdm_mutex_unlock(ftdmchan->mutex); - ftdm_mutex_lock(ftdmchan->mutex); - - /* if the flag is no longer set, the state change was processed (or is being processed) */ - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - break; - } + status = ftdm_interrupt_wait(ftdmchan->state_change_notify, waitms); - /* if the state is no longer what we set, the state change was - * obviously processed (and the current state change flag is for other state change) */ - if (ftdmchan->state != state) { - break; - } - } + ftdm_mutex_lock(ftdmchan->mutex); - if (waitms <= 0) { - ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", + if (status != FTDM_SUCCESS) { + ftdm_log_chan_ex(ftdmchan, file, func, line, + FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); ok = 0; + goto done; } done: return ok ? FTDM_SUCCESS : FTDM_FAIL; diff --git a/libs/freetdm/src/ftdm_threadmutex.c b/libs/freetdm/src/ftdm_threadmutex.c index b1884ec587..6efa27714c 100644 --- a/libs/freetdm/src/ftdm_threadmutex.c +++ b/libs/freetdm/src/ftdm_threadmutex.c @@ -56,7 +56,11 @@ struct ftdm_interrupt { /* for generic interruption */ HANDLE event; #else - /* for generic interruption */ + /* In theory we could be using thread conditions for generic interruption, + * however, Linux does not have a primitive like Windows WaitForMultipleObjects + * to wait for both thread condition and file descriptors, therefore we decided + * to use a dummy pipe for generic interruption/condition logic + * */ int readfd; int writefd; #endif @@ -243,6 +247,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex) FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device) { + ftdm_status_t status = FTDM_SUCCESS; ftdm_interrupt_t *interrupt = NULL; #ifndef WIN32 int fds[2]; @@ -253,7 +258,7 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, interrupt = ftdm_calloc(1, sizeof(*interrupt)); if (!interrupt) { ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n"); - return FTDM_FAIL; + return FTDM_ENOMEM; } interrupt->device = device; @@ -261,11 +266,13 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!interrupt->event) { ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n"); + status = FTDM_ENOMEM; goto failed; } #else if (pipe(fds)) { ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno)); + status = FTDM_FAIL; goto failed; } interrupt->readfd = fds[0]; @@ -287,7 +294,7 @@ failed: #endif ftdm_safe_free(interrupt); } - return FTDM_FAIL; + return status; } #define ONE_BILLION 1000000000 diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 605ab8df15..3b69144e54 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -462,6 +462,7 @@ struct ftdm_channel { ftdm_dtmf_debug_t dtmfdbg; ftdm_io_dump_t rxdump; ftdm_io_dump_t txdump; + ftdm_interrupt_t *state_change_notify; /*!< Notify when a state change is terminated */ int32_t txdrops; int32_t rxdrops; }; diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h index c4d48a893d..41711f743d 100644 --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -250,6 +250,8 @@ typedef enum { #define FTDM_CHANNEL_NONBLOCK (1ULL << 33) /*!< There is a pending acknowledge for an indication */ #define FTDM_CHANNEL_IND_ACK_PENDING (1ULL << 34) +/*!< There is someone blocking in the channel waiting for state completion */ +#define FTDM_CHANNEL_BLOCKING (1ULL << 35) #include "ftdm_state.h" From 39d58e0452f9af4b67f4c9d1056a49cc765ef000 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 31 Dec 2010 09:42:15 -0500 Subject: [PATCH 09/13] freetdm: - Update ftmod_sangoma_isdn to use core state advance - Added locking documentation (docs/locking.txt) - Updated core state advance to use state processor in span rather than pushing the processor in the function arguments --- libs/freetdm/docs/locking.txt | 125 ++++++++++++++++++ libs/freetdm/src/ftdm_state.c | 7 +- libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c | 11 +- .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.c | 40 +++--- libs/freetdm/src/include/freetdm.h | 14 +- libs/freetdm/src/include/private/ftdm_core.h | 8 +- libs/freetdm/src/include/private/ftdm_state.h | 28 +++- 7 files changed, 189 insertions(+), 44 deletions(-) create mode 100644 libs/freetdm/docs/locking.txt diff --git a/libs/freetdm/docs/locking.txt b/libs/freetdm/docs/locking.txt new file mode 100644 index 0000000000..851d045b41 --- /dev/null +++ b/libs/freetdm/docs/locking.txt @@ -0,0 +1,125 @@ +Last Updated: Fri 30 Dec, 2010 + +== Background == + +FreeTDM is a threaded library. As such, locking considerations must be taken when using it and when writing code inside the library. + +At the moment locks are not exposed to users. This means API users cannot acquire a lock on a channel or span structure. There is no +need for users to lock channels or spans since all their interactions with those structures should be done thru the FreeTDM API which +can (and in most cases must) internally lock on their behalf. + +Internally, locking can be done either by the core or the signaling modules. To better understand the locking considerations we must +understand first the threading model of FreeTDM. + +== Threading Model == + +At startup, when the user calls ftdm_global_init(), just one timing thread is created to dispatch internal timers. If you write +a signaling module or any other code using the scheduling API, you can choose to run your schedule in this timing thread or in +a thread of your choice. This is the only thread launched at initialization. + +If the application decides to use ftdm_global_configuration(), which reads freetdm.conf to create the spans and channels data +structures, then possibly another thread will be launched for CPU usage monitoring (only if enabled in the configuration cpu_monitor=yes +This thread sole purpose is to check the CPU and raise an alarm if reaches a configurable threshold, the alarm then is checked to avoid +placing or receiving further calls. + +At this point FreeTDM has initialized and configured its channels input output configuration. + +The user is then supposed to configure the signaling via ftdm_configure_span_signaling() and then start the signaling work +using ftdm_span_start(). This will typically launch at least 1 thread per span. Some signaling modules (actually just the analog one) +launches another thread per channel when receiving a call. The rest of the signaling modules currently launch only one thread per +span and the signaling for all channels within the span is handled in that thread. We call that thread 'the signaling thread'. + +At this point the user can start placing calls using the FreeTDM call API ftdm_channel_call_place(). Any of the possible threads in +which the user calls the FreeTDM API is called 'the user thread', depending on the application thread model (the application using FreeTDM) +this user thread may be different each time or the same all the time, we cannot make any assumptions. In the case of FreeSWITCH, the most +common user of FreeTDM, the user thread is most of the cases a thread for each new call leg. + +At this point we have identified 4 types of threads. + +1. The timing thread (the core thread that triggers timers). + Its responsibility is simply check for timers that were scheduled and trigger them when the time comes. This means that if you decide + to use the scheduling API in freerun mode (where you use the core timer thread) you callbacks will be executed in this global thread + and you MUST not block at all since there might be other events waiting. + +2. The CPU thread (we don't really care about this one as it does not interact with channels or spans). + +3. The signaling thread. + There is one thread of this per span. This thread takes care of reading signaling specific messages from the network (ISDN network, etc) and + changing the channel call state accordingly and processing state changes caused by user API calls (like ftdm_channel_call_hangup for example). + +4. The user thread. + This is a thread in which the user decides to execute FreeTDM APIs, in some cases it might even be the same than the signaling thread (because + most SIGEVENT notifications are delivered by the signaling thread, however we are advicing users to not use FreeTDM unsafe APIs from the + thread where they receive SIGEVENT notifications as some APIs may block for a few milliseconds, effectively blocking the whole signaling thread + that is servicing a span. + +== Application Locking == + +Users of the FreeTDM API will typically have locking of their own for their own application-specific data structures (in the case of FreeSWITCH, the +session lock for example). Other application-specific locks may be involved. + +== DeadLocks == + +As soon as we think of application locks, and we mix them with the FreeTDM internal locks, the possibility of deadlocks arise. + +A typical deadlock scenario when 2 locks are involved is: + +- User Thread - - Signaling Thread - +1. Application locks applock. 1. A network message is received for a channel. + +2. Aplication invokes a FreeTDM call API (ie: ftdm_channel_call_hangup()). 2. The involved channel is locked. + +3. The FreeTDM API attempts to acquire the channel lock and stalls because 3. The message processing results in a notification + the signaling thread just acquired it. to be delivered to the user via the callback function + provided for that purpose. The callback is then called. + +4. The thread is now deadlocked because the signaling thread will never 4. The application callback attempts to acquire its application + release the channel lock. lock but deadlocks because the user thread already has it. + +To avoid this signaling modules should not deliver signals to the user while holding the channel lock. An easy way to avoid this is +to not deliver signals while processing a state change, but rather defer them until the channel lock is released. Most new signaling modules +accomplish this by setting the span flag FTDM_SPAN_USE_SIGNALS_QUEUE, this flag tells the core to enqueue signals (ie FTDM_SIGEVENT_START) +when ftdm_span_send_signal() is called and not deliver them until ftdm_span_trigger_signals() is called, which is done by the signaling module +in its signaling thread when no channel lock is being held. + +== State changes while locking == + +Only 2 types of threads should be performing state changes. + +User threads. +The user thread is a random thread that was crated by the API user. We do not know what threading model users of FreeTDM will follow +and therefore cannot make assumptions about it. The user should be free to call FreeTDM APIs from any thread, except threads that +are under our control, like the signaling threads. Although it may work in most situations, is discouraged for users to try +to use FreeTDM APIs from the signaling thread, that is, the thread where the signaling callback provided during configuration +is called (the callback where FTDM_SIGEVENT_XXX signals are delivered). + +A user thread may request state changes implicitly through calls to FreeTDM API's. The idea of state changes is internal to freetdm +and should not be exposed to users of the API (except for debugging purposes, like the ftdm_channel_get_state, ftdm_channel_get_state_str etc) + +This is an example of the API's that implicitly request a state change. + +ftdm_channel_call_answer() + +Signaling modules should guarantee that upon releasing a lock on a channel, any state changes will be already processed and +not deferred to other threads, otherwise that leads to a situation where a state change requested by the signaling module is pending +to be serviced by another signaling module thread but a user thread wins the channel lock and attempts to perform a state change which will +fail because another state change is pending (and user threads are not meant to process signaling states). + +ONLY one signaling thread per channel should try to perform state changes and processing of the states, +otherwise complexity arises and is not worth it! + +At some point before we stablished this policies we could have 3 different threads doing state changes. + +1. A user random thread could implcitly try to change the state in response to a call API. +2. The ftmod signaling thread could try to change the state in response to other state changes. +3. The lower level signaling stack threads could try to change the state in response to stack events. + +As a result, lower level signaling stack thread could set a state and then let the signaling thread to +process it, but when unlocking the channel, the user thread may win the lock over the signaling thread and +may try to set a state change of its own and fail (due to the unprocessed state change)! + +The rule is, the signaling module should never unlock a channel with states pending to process this way the user, +when acquiring a channel lock (inside ftdm_channel_call_answer for example) it will always find a consistent state +for the channel and not in the middle of state processing. + + diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index 21bb3b40c7..2f42282756 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -434,15 +434,14 @@ FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan) return stream.data; } -FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan, ftdm_channel_state_processor_t state_processor) +FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan) { ftdm_channel_state_t state; - ftdm_channel_lock(fchan); while (fchan->state_status == FTDM_STATE_STATUS_NEW) { state = fchan->state; ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Executing state processor for %s\n", ftdm_channel_state2str(fchan->state)); - state_processor(fchan); + fchan->span->state_processor(fchan); if (state == fchan->state && fchan->state_status == FTDM_STATE_STATUS_NEW) { /* if the state did not change and is still NEW, the state status must go to PROCESSED * otherwise we don't touch it since is a new state and the old state was @@ -451,7 +450,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan, ftd fchan->state_status = FTDM_STATE_STATUS_PROCESSED; } } - ftdm_channel_unlock(fchan); + return FTDM_SUCCESS; } diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 530bb2b9b2..37c760ce94 100644 --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@ -620,7 +620,7 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan) ftdm_sched_cancel_timer(r2data->sched, r2call->protocol_error_recovery_timer); ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Cancelled protocol error recovery timer\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); + ftdm_channel_advance_states(ftdmchan); } } @@ -814,7 +814,7 @@ static void ftdm_r2_on_call_end(openr2_chan_t *r2chan) ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); /* in some circumstances openr2 can call on_call_init right after this, so let's advance the state right here */ - ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); + ftdm_channel_advance_states(ftdmchan); } static void ftdm_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen) @@ -846,7 +846,7 @@ static void ftdm_r2_recover_from_protocol_error(void *data) goto done; } ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); + ftdm_channel_advance_states(ftdmchan); done: ftdm_channel_unlock(ftdmchan); } @@ -1584,6 +1584,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling) span->set_channel_sig_status = ftdm_r2_set_channel_sig_status; span->state_map = &r2_state_map; + span->state_processor = ftdm_r2_state_advance; /* use signals queue */ ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE); @@ -1917,12 +1918,12 @@ static void *ftdm_r2_run(ftdm_thread_t *me, void *obj) ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED); - ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); + ftdm_channel_advance_states(ftdmchan); r2chan = call->r2chan; openr2_chan_process_signaling(r2chan); - ftdm_channel_advance_states(ftdmchan, ftdm_r2_state_advance); + ftdm_channel_advance_states(ftdmchan); if (!call->accepted) { /* if the call is not accepted we do not want users reading */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c index bb29b4f4a5..259bf18b81 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c @@ -46,10 +46,9 @@ static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span); static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span); ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event); -static void ftdm_sangoma_isdn_advance_chan_states(ftdm_channel_t *ftdmchan); static void ftdm_sangoma_isdn_poll_events(ftdm_span_t *span); static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event); -static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan); +static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan); static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event); static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan); static void ftdm_sangoma_isdn_dchan_set_queue_size(ftdm_channel_t *ftdmchan); @@ -270,13 +269,6 @@ ftdm_state_map_t sangoma_isdn_state_map = { } }; -static __inline__ void ftdm_sangoma_isdn_advance_chan_states(ftdm_channel_t *ftdmchan) -{ - while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_sangoma_isdn_process_state_change(ftdmchan); - } -} - static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event) { sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) span->signal_data; @@ -457,7 +449,7 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj) while ((ftdmchan = ftdm_queue_dequeue(span->pendingchans))) { /* double check that this channel has a state change pending */ ftdm_channel_lock(ftdmchan); - ftdm_sangoma_isdn_advance_chan_states(ftdmchan); + ftdm_channel_advance_states(ftdmchan); ftdm_channel_unlock(ftdmchan); } @@ -470,11 +462,11 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj) /* twiddle */ break; case FTDM_FAIL: - ftdm_log(FTDM_LOG_ERROR,"%s:ftdm_interrupt_wait returned error!\n", span->name); + ftdm_log(FTDM_LOG_ERROR, "%s: ftdm_interrupt_wait returned error!\n", span->name); break; default: - ftdm_log(FTDM_LOG_ERROR,"%s:ftdm_interrupt_wait returned with unknown code\n", span->name); + ftdm_log(FTDM_LOG_ERROR, "%s: ftdm_interrupt_wait returned with unknown code\n", span->name); break; } @@ -536,7 +528,7 @@ ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisd break; } ftdm_channel_lock(ftdmchan); - ftdm_sangoma_isdn_advance_chan_states(ftdmchan); + ftdm_channel_advance_states(ftdmchan); return ftdmchan; } @@ -600,13 +592,14 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev sngisdn_process_rst_ind(sngisdn_event); break; } - if(ftdmchan != NULL) { - ftdm_sangoma_isdn_advance_chan_states(ftdmchan); + if (ftdmchan != NULL) { + ftdm_channel_advance_states(ftdmchan); ftdm_channel_unlock(ftdmchan); } } -static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan) +/* this function is called with the channel already locked by the core */ +static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan) { ftdm_sigmsg_t sigev; ftdm_channel_state_t initial_state; @@ -618,13 +611,12 @@ static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan) sigev.span_id = ftdmchan->span_id; sigev.channel = ftdmchan; - /*first lock the channel*/ - ftdm_channel_lock(ftdmchan); - /*clear the state change flag...since we might be setting a new state*/ - ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + /* Acknowledge the state change */ + ftdm_channel_complete_state(ftdmchan); + #ifdef FTDM_DEBUG_CHAN_MEMORY if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) { - ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ)==0, "Failed to mprotect"); + ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ) == 0, "Failed to mprotect"); } #endif @@ -879,11 +871,10 @@ static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan) } #ifdef FTDM_DEBUG_CHAN_MEMORY if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) { - ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ|PROT_WRITE)==0, "Failed to mprotect"); + ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ|PROT_WRITE) == 0, "Failed to mprotect"); } #endif - ftdm_channel_unlock(ftdmchan); - return; + return FTDM_SUCCESS; } static FIO_CHANNEL_SEND_MSG_FUNCTION(ftdm_sangoma_isdn_send_msg) @@ -1098,6 +1089,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config) span->get_span_sig_status = ftdm_sangoma_isdn_get_span_sig_status; span->set_span_sig_status = ftdm_sangoma_isdn_set_span_sig_status; span->state_map = &sangoma_isdn_state_map; + span->state_processor = ftdm_sangoma_isdn_process_state_change; ftdm_set_flag(span, FTDM_SPAN_USE_CHAN_QUEUE); ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE); ftdm_set_flag(span, FTDM_SPAN_USE_PROCEED_STATE); diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index ff129636d9..3e8ad287bd 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -356,7 +356,6 @@ typedef enum { "PROGRESS_MEDIA", "ALARM_TRAP", "ALARM_CLEAR", \ "COLLECTED_DIGIT", "ADD_CALL", "RESTART", "SIGSTATUS_CHANGED", "COLLISION", "FACILITY", \ "TRACE", "TRACE_RAW", "INDICATION_COMPLETED", "INVALID" - /*! \brief Move from string to ftdm_signal_event_t and viceversa */ FTDM_STR2ENUM_P(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t) @@ -653,7 +652,20 @@ typedef ftdm_status_t (*fio_span_get_sig_status_t) FIO_SPAN_GET_SIG_STATUS_ARGS; typedef ftdm_status_t (*fio_span_poll_event_t) FIO_SPAN_POLL_EVENT_ARGS ; typedef ftdm_status_t (*fio_span_next_event_t) FIO_SPAN_NEXT_EVENT_ARGS ; typedef ftdm_status_t (*fio_channel_next_event_t) FIO_CHANNEL_NEXT_EVENT_ARGS ; + +/*! \brief Callback for signal delivery (FTDM_SIGEVENT_START and friends) + * \note This callback is provided by the user during ftdm_configure_span_signaling + * + * \note You must NOT do any blocking during this callback since this function is + * most likely called in an internal signaling thread that can potentially be + * shared for all the channels in a span and blocking will delay processing + * (sometimes even audio processing) for other channels + * + * \note Although some simple FreeTDM APIs can work (ie: ftdm_span_get_id etc), the + * use of any FreeTDM call API (ie ftdm_channel_call_answer) is discouraged + */ typedef ftdm_status_t (*fio_signal_cb_t) FIO_SIGNAL_CB_ARGS ; + typedef ftdm_status_t (*fio_event_cb_t) FIO_EVENT_CB_ARGS ; typedef ftdm_status_t (*fio_configure_span_t) FIO_CONFIGURE_SPAN_ARGS ; typedef ftdm_status_t (*fio_configure_t) FIO_CONFIGURE_ARGS ; diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 3b69144e54..829b4069a1 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -500,15 +500,15 @@ struct ftdm_span { ftdm_span_stop_t stop; ftdm_channel_sig_read_t sig_read; ftdm_channel_sig_write_t sig_write; - /* Private I/O data per span. Do not touch unless you are an I/O module */ - void *io_data; + ftdm_channel_state_processor_t state_processor; /*!< This guy is called whenever state processing is required */ + void *io_data; /*!< Private I/O data per span. Do not touch unless you are an I/O module */ char *type; char *dtmf_hangup; size_t dtmf_hangup_len; ftdm_state_map_t *state_map; ftdm_caller_data_t default_caller_data; - ftdm_queue_t *pendingchans; - ftdm_queue_t *pendingsignals; + ftdm_queue_t *pendingchans; /*!< Channels pending of state processing */ + ftdm_queue_t *pendingsignals; /*!< Signals pending from being delivered to the user */ struct ftdm_span *next; }; diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h index 36dcc2bef8..7de015b72b 100644 --- a/libs/freetdm/src/include/private/ftdm_state.h +++ b/libs/freetdm/src/include/private/ftdm_state.h @@ -86,15 +86,21 @@ typedef struct { const char *file; const char *func; int line; - ftdm_channel_state_t state; - ftdm_channel_state_t last_state; - ftdm_time_t time; - ftdm_time_t end_time; + ftdm_channel_state_t state; /*!< Current state (processed or not) */ + ftdm_channel_state_t last_state; /*!< Previous state */ + ftdm_time_t time; /*!< Time the state was set */ + ftdm_time_t end_time; /*!< Time the state processing was completed */ } ftdm_state_history_entry_t; typedef ftdm_status_t (*ftdm_channel_state_processor_t)(ftdm_channel_t *fchan); -FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan, ftdm_channel_state_processor_t processor); +/*! + * \brief Process channel states by invoking the channel state processing routine + * it will keep calling the processing routine while the state status + * is FTDM_STATE_STATUS_NEW, it will not do anything otherwise + */ +FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan); + FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *function, int line, ftdm_channel_t *fchan); #define ftdm_channel_complete_state(obj) _ftdm_channel_complete_state(__FILE__, __FUNCTION__, __LINE__, obj) FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state); @@ -171,16 +177,26 @@ struct ftdm_state_map { }; typedef struct ftdm_state_map ftdm_state_map_t; +/*!\brief Set the state for a channel (the channel must be locked when calling this function) + * \note Signaling modules should use ftdm_set_state macro instead + * \note If this function is called with the wait parameter set to a non-zero value, the recursivity + * of the channel lock must be == 1 because the channel will be unlocked/locked when waiting */ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait); -/*!\brief Set the state of a channel immediately and implicitly complete the previous state */ +/*!\brief Set the state of a channel immediately and implicitly complete the previous state if needed + * \note FTDM_SIGEVENT_INDICATION_COMPLETED will be sent if the state change + * is associated to some indication (ie FTDM_CHANNEL_INDICATE_PROCEED) + * \note The channel must be locked when calling this function + * */ FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line, ftdm_channel_t *fchan, ftdm_channel_state_t state); #define ftdm_set_state(obj, s) _ftdm_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s); \ /*!\brief This macro is deprecated, signaling modules should always lock the channel themselves anyways since they must * process first the user pending state changes then set a new state before releasing the lock + * this macro is here for backwards compatibility, DO NOT USE IT in new code since it is *always* wrong to set + * a state in a signaling module without checking and processing the current state first (and for that you must lock the channel) */ #define ftdm_set_state_locked(obj, s) \ do { \ From fe0d8d1ec508d96d6edc08ec610f142633e896bf Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 31 Dec 2010 13:44:20 -0500 Subject: [PATCH 10/13] freetdm: - updated all signaling modules (except for ftmod_isdn and ftmod_sangoma_ss7) to adapt to new core state processing - fixed bug in configure.ac to detect ftmod_pritap compilation - ignore FTDM_SIGEVENT_RELEASED and FTDM_SIGEVENT_INDICATION_COMPLETED in mod_freetdm - Destroy the state completed interrupt on channel destroy - Fix analog polarity reversal bug when using 3-way calling or call-swap --- libs/freetdm/configure.ac | 2 +- libs/freetdm/mod_freetdm/mod_freetdm.c | 14 ++++---- libs/freetdm/src/ftdm_io.c | 3 ++ libs/freetdm/src/ftdm_state.c | 18 +++++++--- .../src/ftmod/ftmod_analog/ftmod_analog.c | 14 ++++---- .../ftmod/ftmod_analog_em/ftmod_analog_em.c | 1 - .../src/ftmod/ftmod_libpri/ftmod_libpri.c | 15 ++++----- .../src/ftmod/ftmod_pritap/ftmod_pritap.c | 29 ++++++++-------- .../ftmod_sangoma_boost/ftmod_sangoma_boost.c | 33 ++++++------------- libs/freetdm/src/include/freetdm.h | 2 +- libs/freetdm/src/include/private/ftdm_core.h | 2 +- 11 files changed, 65 insertions(+), 68 deletions(-) diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index e26f10b0b2..a070e994a3 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -160,7 +160,7 @@ AC_ARG_WITH([pritap], [AS_HELP_STRING([--with-pritap], [Install ftmod_pritap])], [case "${withval}" in no) enable_pritap="no" ;; - *) enable_pritab="yes" ;; + *) enable_pritap="yes" ;; esac], [enable_pritap="no"] ) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index e08d4c1f3b..7bbdef6dae 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -1642,6 +1642,14 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal) } return FTDM_SUCCESS; } + + case FTDM_SIGEVENT_RELEASED: + case FTDM_SIGEVENT_INDICATION_COMPLETED: + { + /* Swallow these events */ + return FTDM_BREAK; + } + break; default: return FTDM_SUCCESS; break; @@ -1732,7 +1740,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal) } } break; - case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break; case FTDM_SIGEVENT_SIGSTATUS_CHANGED: { /* twiddle */ } break; default: @@ -1788,7 +1795,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal) } } break; - case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break; case FTDM_SIGEVENT_STOP: { @@ -2015,8 +2021,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal) } break; - case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break; - /* on DNIS received from the R2 forward side, return status == FTDM_BREAK to stop requesting DNIS */ case FTDM_SIGEVENT_COLLECTED_DIGIT: { @@ -2135,8 +2139,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) } break; - case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break; - case FTDM_SIGEVENT_STOP: case FTDM_SIGEVENT_RESTART: { diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 5c900b91dd..a1c5ceea88 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -615,6 +615,9 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan) ftdm_mutex_destroy(&ftdmchan->mutex); ftdm_mutex_destroy(&ftdmchan->pre_buffer_mutex); + if (ftdmchan->state_completed_interrupt) { + ftdm_interrupt_destroy(&ftdmchan->state_completed_interrupt); + } } return FTDM_SUCCESS; diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c index 2f42282756..0221fa2a1e 100644 --- a/libs/freetdm/src/ftdm_state.c +++ b/libs/freetdm/src/ftdm_state.c @@ -103,7 +103,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) { ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING); - ftdm_interrupt_signal(fchan->state_change_notify); + ftdm_interrupt_signal(fchan->state_completed_interrupt); } return FTDM_SUCCESS; @@ -194,8 +194,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *f return FTDM_FAIL; } - if (!ftdmchan->state_change_notify) { - status = ftdm_interrupt_create(&ftdmchan->state_change_notify, FTDM_INVALID_SOCKET); + if (!ftdmchan->state_completed_interrupt) { + status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET); if (status != FTDM_SUCCESS) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, "Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); @@ -323,17 +323,23 @@ end: goto done; } + if (!waitrq) { + /* no waiting was requested */ + goto done; + } + + /* let's wait for the state change to be completed by the signaling stack */ ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING); ftdm_mutex_unlock(ftdmchan->mutex); - status = ftdm_interrupt_wait(ftdmchan->state_change_notify, waitms); + status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms); ftdm_mutex_lock(ftdmchan->mutex); if (status != FTDM_SUCCESS) { ftdm_log_chan_ex(ftdmchan, file, func, line, - FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", + FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); ok = 0; goto done; @@ -437,6 +443,8 @@ FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan) FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan) { ftdm_channel_state_t state; + + ftdm_assert_return(fchan->span->state_processor, FTDM_FAIL, "Cannot process states without a state processor!\n"); while (fchan->state_status == FTDM_STATE_STATUS_NEW) { state = fchan->state; diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c index 981df5dcc9..818f1c5754 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c +++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c @@ -585,8 +585,8 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) if (done) { ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP); - ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); + ftdm_channel_complete_state(ftdmchan); ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0; } } @@ -628,7 +628,6 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) break; } } else { - ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); ftdm_channel_complete_state(ftdmchan); indicate = 0; @@ -672,11 +671,12 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj) !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) && ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) { ftdm_polarity_t polarity = FTDM_POLARITY_REVERSE; - if (ftdmchan->polarity != FTDM_POLARITY_FORWARD) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Polarity is already reversed on answer??\n"); - } else { + if (ftdmchan->polarity == FTDM_POLARITY_FORWARD) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on answer\n"); ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity); + } else { + /* the polarity may be already reversed if this is the second time we + * answer (ie, due to 2 calls being on the same line) */ } } @@ -1036,8 +1036,8 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) { if (event->channel->state == FTDM_CHANNEL_STATE_HANGUP && ftdm_test_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_clear_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE); /* we do not need to process HANGUP since the device also hangup already */ + ftdm_channel_complete_state(event->channel); } ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_DOWN); } @@ -1052,8 +1052,8 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e { if (event->channel->state == FTDM_CHANNEL_STATE_CALLWAITING) { ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_UP); - ftdm_clear_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE); ftdm_clear_flag(event->channel->span, FTDM_SPAN_STATE_CHANGE); + ftdm_channel_complete_state(event->channel); event->channel->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0; } diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c index 843ac484a5..31c2421b9b 100644 --- a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c +++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c @@ -355,7 +355,6 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj) break; } } else { - ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); ftdm_channel_complete_state(ftdmchan); indicate = 0; diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c index 1d3c76d2e2..8be6d579e1 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -497,7 +497,7 @@ static ftdm_state_map_t isdn_state_map = { * \param ftdmchan Channel to handle * \note This function MUST be called with the channel locked */ -static __inline__ void state_advance(ftdm_channel_t *chan) +static ftdm_status_t state_advance(ftdm_channel_t *chan) { ftdm_libpri_data_t *isdn_data = chan->span->signal_data; q931_call *call = (q931_call *)chan->call_data; @@ -511,6 +511,8 @@ static __inline__ void state_advance(ftdm_channel_t *chan) sig.chan_id = ftdm_channel_get_id(chan); sig.span_id = ftdm_channel_get_span_id(chan); sig.channel = chan; + + ftdm_channel_complete_state(chan); switch (ftdm_channel_get_state(chan)) { case FTDM_CHANNEL_STATE_DOWN: @@ -625,7 +627,7 @@ static __inline__ void state_advance(ftdm_channel_t *chan) ftdm_channel_get_span_id(chan), ftdm_channel_get_id(chan)); /* TODO: set hangup cause? */ ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RESTART); - return; + return FTDM_SUCCESS; } ton = caller_data->dnis.type; @@ -708,6 +710,7 @@ static __inline__ void state_advance(ftdm_channel_t *chan) default: break; } + return FTDM_SUCCESS; } /** @@ -723,13 +726,8 @@ static __inline__ void check_state(ftdm_span_t *span) for (j = 1; j <= ftdm_span_get_chan_count(span); j++) { ftdm_channel_t *chan = ftdm_span_get_channel(span, j); - ftdm_channel_lock(chan); - while (ftdm_test_flag(chan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_clear_flag(chan, FTDM_CHANNEL_STATE_CHANGE); - state_advance(chan); - ftdm_channel_complete_state(chan); - } + ftdm_channel_advance_states(chan); ftdm_channel_unlock(chan); } } @@ -1910,6 +1908,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span) span->outgoing_call = isdn_outgoing_call; span->state_map = &isdn_state_map; + span->state_processor = state_advance; span->get_channel_sig_status = isdn_get_channel_sig_status; span->get_span_sig_status = isdn_get_span_sig_status; diff --git a/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c b/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c index 27fbe2139f..48a2f012eb 100644 --- a/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c +++ b/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c @@ -265,7 +265,7 @@ static ftdm_state_map_t pritap_state_map = { } }; -static __inline__ void state_advance(ftdm_channel_t *ftdmchan) +static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) { ftdm_status_t status; ftdm_sigmsg_t sig; @@ -278,6 +278,8 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan) sig.span_id = ftdmchan->span_id; sig.channel = ftdmchan; + ftdm_channel_complete_state(ftdmchan); + switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_DOWN: { @@ -321,24 +323,20 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan) break; } - return; + return FTDM_SUCCESS; } static __inline__ void pritap_check_state(ftdm_span_t *span) { - if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) { - uint32_t j; - ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); - for(j = 1; j <= span->chan_count; j++) { - if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_mutex_lock(span->channels[j]->mutex); - ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE); - state_advance(span->channels[j]); - ftdm_channel_complete_state(span->channels[j]); - ftdm_mutex_unlock(span->channels[j]->mutex); - } - } - } + if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) { + uint32_t j; + ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE); + for(j = 1; j <= span->chan_count; j++) { + ftdm_mutex_lock(span->channels[j]->mutex); + ftdm_channel_advance_states(span->channels[j]); + ftdm_mutex_unlock(span->channels[j]->mutex); + } + } } static int pri_io_read(struct pri *pri, void *buf, int buflen) @@ -896,6 +894,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_pritap_configure_span) span->get_span_sig_status = pritap_get_span_sig_status; span->state_map = &pritap_state_map; + span->state_processor = state_advance; return FTDM_SUCCESS; } diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c index 97e242dacb..d0bc14c8d8 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c @@ -951,7 +951,6 @@ static void handle_call_answer(ftdm_span_t *span, sangomabc_connection_t *mcon, } } -static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan); static __inline__ void stop_loop(ftdm_channel_t *ftdmchan); /** @@ -1002,7 +1001,7 @@ tryagain: } else if (ftdmchan->state == FTDM_CHANNEL_STATE_IN_LOOP && retry) { retry = 0; stop_loop(ftdmchan); - advance_chan_states(ftdmchan); + ftdm_channel_advance_states(ftdmchan); goto tryagain; } else { ftdm_log(FTDM_LOG_ERROR, "s%dc%d: rejecting incoming call in channel state %s\n", @@ -1267,7 +1266,7 @@ static ftdm_channel_t* event_process_states(ftdm_span_t *span, sangomabc_short_e } ftdm_mutex_lock(ftdmchan->mutex); - advance_chan_states(ftdmchan); + ftdm_channel_advance_states(ftdmchan); return ftdmchan; } @@ -1354,11 +1353,11 @@ static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon, } if(ftdmchan != NULL) { - advance_chan_states(ftdmchan); + ftdm_channel_advance_states(ftdmchan); ftdm_mutex_unlock(ftdmchan->mutex); } - return 0; + return 0; } @@ -1366,7 +1365,7 @@ static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon, * \brief Handler for channel state change * \param ftdmchan Channel to handle */ -static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) +static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) { ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data; sangomabc_connection_t *mcon = &sangoma_boost_data->mcon; @@ -1374,12 +1373,6 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) ftdm_status_t status; - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); - } else { - return FTDM_SUCCESS; - } - ftdm_assert_return(ftdmchan->last_state != ftdmchan->state, FTDM_FAIL, "Channel state already processed\n"); ftdm_log(FTDM_LOG_DEBUG, "%d:%d PROCESSING STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state)); @@ -1389,6 +1382,8 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) sig.span_id = ftdmchan->span_id; sig.channel = ftdmchan; + ftdm_channel_complete_state(ftdmchan); + switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_DOWN: { @@ -1640,24 +1635,15 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan) default: break; } - ftdm_channel_complete_state(ftdmchan); return FTDM_SUCCESS; } -static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan) -{ - while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - state_advance(ftdmchan); - } -} - /** * \brief Initialises outgoing requests array */ static __inline__ void init_outgoing_array(void) { memset(&OUTBOUND_REQUESTS, 0, sizeof(OUTBOUND_REQUESTS)); - } /** @@ -1685,7 +1671,7 @@ static __inline__ void check_state(ftdm_span_t *span) if (susp && span->channels[j]->state != FTDM_CHANNEL_STATE_DOWN) { ftdm_set_state(span->channels[j], FTDM_CHANNEL_STATE_RESTART); } - state_advance(span->channels[j]); + ftdm_channel_advance_states(span->channels[j]); ftdm_mutex_unlock(span->channels[j]->mutex); } } @@ -1695,7 +1681,7 @@ static __inline__ void check_state(ftdm_span_t *span) * but without taking the chan out of the queue, so check th * flag before advancing the state */ ftdm_mutex_lock(ftdmchan->mutex); - state_advance(ftdmchan); + ftdm_channel_advance_states(ftdmchan); ftdm_mutex_unlock(ftdmchan->mutex); } } @@ -2687,6 +2673,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_boost_configure_span) span->get_span_sig_status = sangoma_boost_get_span_sig_status; span->set_span_sig_status = sangoma_boost_set_span_sig_status; span->state_map = &boost_state_map; + span->state_processor = state_advance; sangoma_boost_data->mcon.debuglevel = FTDM_LOG_LEVEL_DEBUG; sangoma_boost_data->pcon.debuglevel = FTDM_LOG_LEVEL_DEBUG; ftdm_clear_flag(span, FTDM_SPAN_SUGGEST_CHAN_ID); diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 3e8ad287bd..38bc61188b 100644 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -583,10 +583,10 @@ typedef enum { FTDM_COMMAND_GET_LINK_STATUS, FTDM_COMMAND_ENABLE_LOOP, FTDM_COMMAND_DISABLE_LOOP, - FTDM_COMMAND_COUNT, FTDM_COMMAND_SET_RX_QUEUE_SIZE, FTDM_COMMAND_SET_TX_QUEUE_SIZE, FTDM_COMMAND_SET_POLARITY, + FTDM_COMMAND_COUNT, } ftdm_command_t; typedef enum { diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 829b4069a1..55fb8ba032 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -462,7 +462,7 @@ struct ftdm_channel { ftdm_dtmf_debug_t dtmfdbg; ftdm_io_dump_t rxdump; ftdm_io_dump_t txdump; - ftdm_interrupt_t *state_change_notify; /*!< Notify when a state change is terminated */ + ftdm_interrupt_t *state_completed_interrupt; /*!< Notify when a state change is completed */ int32_t txdrops; int32_t rxdrops; }; From 6f5a0e9ff5984aed4b99b7a04a6cb1339b86e3b1 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Fri, 31 Dec 2010 15:06:49 -0500 Subject: [PATCH 11/13] freetdm: typo --- libs/freetdm/src/ftdm_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index a1c5ceea88..e55839a784 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -4234,7 +4234,7 @@ static void print_core_usage(ftdm_stream_handle_t *stream) stream->write_function(stream, "--------------------------------------------------------------------------------\n" "ftdm core state [!] - List all channels in or not in the given state\n" - "ftdm core flag - List all channels with the fiven flag value set\n" + "ftdm core flag - List all channels with the given flag value set\n" "ftdm core calls - List all known calls to the FreeTDM core\n" "--------------------------------------------------------------------------------\n"); } From 4a6b4e86f16a25074cc0f77fc7ddbda36f5cbfc4 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 4 Jan 2011 09:30:49 -0500 Subject: [PATCH 12/13] freetdm: add non-locking answer function to handle answer indication export ftdm_current_time_in_ms to users update testr2 to test non-blocking API (no waiting on indications) --- libs/freetdm/src/ftdm_io.c | 49 +++--- libs/freetdm/src/include/ftdm_os.h | 5 + libs/freetdm/src/include/private/ftdm_core.h | 1 - libs/freetdm/src/include/private/ftdm_types.h | 2 - libs/freetdm/src/testr2.c | 144 ++++++++++++++---- 5 files changed, 147 insertions(+), 54 deletions(-) diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index e55839a784..b60e72f605 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -2032,22 +2032,22 @@ FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indicat ftdm_span_send_signal(fchan->span, &msg); } -FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +/*! \brief Answer call without locking the channel. The caller must have locked first + * \note This function was added because ftdm_channel_call_indicate needs to answer the call + * when its already locking the channel, ftdm_channel_set_state cannot be called with the same + * lock locked once or more (recursive lock) and wait for the result */ +static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) { ftdm_status_t status = FTDM_SUCCESS; - ftdm_channel_lock(ftdmchan); + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { + status = FTDM_EINVAL; + goto done; + } if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n"); status = FTDM_ECANCELED; - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); - goto done; - } - - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { - status = FTDM_EINVAL; - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } @@ -2059,7 +2059,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) { status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1); if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); + status = FTDM_ECANCELED; goto done; } } @@ -2068,14 +2068,13 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n"); status = FTDM_ECANCELED; - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) { status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1); if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); + status = FTDM_ECANCELED; goto done; } } @@ -2084,23 +2083,35 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to UP\n"); status = FTDM_ECANCELED; - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); goto done; } } status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1); if (status != FTDM_SUCCESS) { - ftdm_ack_indication(ftdmchan, FTDM_CHANNEL_INDICATE_ANSWER, status); + status = FTDM_ECANCELED; + goto done; } done: + + return status; +} + +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan) +{ + ftdm_status_t status = FTDM_SUCCESS; + + ftdm_channel_lock(ftdmchan); + + status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan); + ftdm_channel_unlock(ftdmchan); return status; } /* lock must be acquired by the caller! */ -static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const char *func, int line) +static ftdm_status_t _ftdm_channel_call_hangup_nl(ftdm_channel_t *chan, const char *file, const char *func, int line) { ftdm_status_t status = FTDM_SUCCESS; @@ -2139,7 +2150,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, ftdmchan->caller_data.hangup_cause = cause; - status = call_hangup(ftdmchan, file, func, line); + status = _ftdm_channel_call_hangup_nl(ftdmchan, file, func, line); ftdm_channel_unlock(ftdmchan); return status; @@ -2153,7 +2164,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; - status = call_hangup(ftdmchan, file, func, line); + status = _ftdm_channel_call_hangup_nl(ftdmchan, file, func, line); ftdm_channel_unlock(ftdmchan); return status; @@ -2288,7 +2299,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch break; case FTDM_CHANNEL_INDICATE_ANSWER: /* _ftdm_channel_call_answer takes care of the indication ack */ - status = _ftdm_channel_call_answer(file, func, line, ftdmchan); + status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan); break; default: ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Do not know how to indicate %d\n", indication); @@ -5292,7 +5303,7 @@ static void execute_safety_hangup(void *data) fchan->hangup_timer = 0; if (fchan->state == FTDM_CHANNEL_STATE_TERMINATING) { ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Forcing hangup since the user did not confirmed our hangup after %dms\n", FORCE_HANGUP_TIMER); - call_hangup(fchan, __FILE__, __FUNCTION__, __LINE__); + _ftdm_channel_call_hangup_nl(fchan, __FILE__, __FUNCTION__, __LINE__); } else { ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Not performing safety hangup, channel state is %s\n", ftdm_channel_state2str(fchan->state)); } diff --git a/libs/freetdm/src/include/ftdm_os.h b/libs/freetdm/src/include/ftdm_os.h index f3ebee9ea2..a4605c3371 100644 --- a/libs/freetdm/src/include/ftdm_os.h +++ b/libs/freetdm/src/include/ftdm_os.h @@ -51,6 +51,9 @@ extern "C" { #include #endif +/*! \brief time data type */ +typedef uint64_t ftdm_time_t; + /*! \brief sleep x amount of milliseconds */ #ifdef __WINDOWS__ #define ftdm_sleep(x) Sleep(x) @@ -114,6 +117,8 @@ FT_DECLARE(char *) ftdm_strdup(const char *str); /*! \brief Duplicate string with limit */ FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen); +/*! \brief Get the current time in milliseconds */ +FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void); #ifdef __cplusplus } /* extern C */ diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index 55fb8ba032..a5862e5d15 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -556,7 +556,6 @@ FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_sdmf(ftdm_fsk_data_state_t *state, c FT_DECLARE(ftdm_status_t) ftdm_channel_send_fsk_data(ftdm_channel_t *ftdmchan, ftdm_fsk_data_state_t *fsk_data, float db_level); FT_DECLARE(ftdm_status_t) ftdm_span_load_tones(ftdm_span_t *span, const char *mapname); -FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void); FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan); diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h index 41711f743d..2d683e3098 100644 --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -69,8 +69,6 @@ extern "C" { #define FTDM_END -1 #define FTDM_ANY_STATE -1 -typedef uint64_t ftdm_time_t; - typedef enum { FTDM_ENDIAN_BIG = 1, FTDM_ENDIAN_LITTLE = -1 diff --git a/libs/freetdm/src/testr2.c b/libs/freetdm/src/testr2.c index 8ac90c59fd..72d98020bc 100644 --- a/libs/freetdm/src/testr2.c +++ b/libs/freetdm/src/testr2.c @@ -2,78 +2,158 @@ #include #include -static int R = 0; -static ftdm_mutex_t *mutex = NULL; +static volatile int running = 0; +static ftdm_mutex_t *the_mutex = NULL; +static ftdm_channel_t *fchan = NULL; +static ftdm_channel_indication_t indication = FTDM_CHANNEL_INDICATE_NONE; static FIO_SIGNAL_CB_FUNCTION(on_r2_signal) { int chanid = ftdm_channel_get_ph_id(sigmsg->channel); - ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), chanid); - return FTDM_SUCCESS; + ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), chanid); + switch (sigmsg->event_id) { + case FTDM_SIGEVENT_START: + { + ftdm_mutex_lock(the_mutex); + if (!fchan) { + fchan = sigmsg->channel; + indication = FTDM_CHANNEL_INDICATE_PROCEED; + } + ftdm_mutex_unlock(the_mutex); + } + break; + case FTDM_SIGEVENT_INDICATION_COMPLETED: + { + ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE; + if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROCEED) { + ftdm_log(FTDM_LOG_DEBUG, "Proceed indication result = %d\n", sigmsg->ev_data.indication_completed.status); + ind = FTDM_CHANNEL_INDICATE_PROGRESS; + } else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS) { + ftdm_log(FTDM_LOG_DEBUG, "Progress indication result = %d\n", sigmsg->ev_data.indication_completed.status); + ind = FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA; + } else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA) { + ftdm_log(FTDM_LOG_DEBUG, "Progress media indication result = %d\n", sigmsg->ev_data.indication_completed.status); + ind = FTDM_CHANNEL_INDICATE_ANSWER; + } else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_ANSWER) { + ftdm_log(FTDM_LOG_DEBUG, "Answer indication result = %d\n", sigmsg->ev_data.indication_completed.status); + } else { + ftdm_log(FTDM_LOG_DEBUG, "Unexpected indication, result = %d\n", sigmsg->ev_data.indication_completed.status); + exit(1); + } + ftdm_mutex_lock(the_mutex); + if (fchan) { + indication = ind; + } + ftdm_mutex_unlock(the_mutex); + } + break; + case FTDM_SIGEVENT_STOP: + { + ftdm_channel_call_hangup(sigmsg->channel); + } + break; + case FTDM_SIGEVENT_RELEASED: + { + ftdm_mutex_lock(the_mutex); + if (fchan && fchan == sigmsg->channel) { + fchan = NULL; + } + ftdm_mutex_unlock(the_mutex); + } + break; + default: + break; + } + return FTDM_SUCCESS; } -static void handle_SIGINT(int sig) +static void stop_test(int sig) { - ftdm_mutex_lock(mutex); - R = 0; - ftdm_mutex_unlock(mutex); - return; + running = 0; } int main(int argc, char *argv[]) { ftdm_span_t *span; - ftdm_mutex_create(&mutex); - - ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + ftdm_conf_parameter_t parameters[20]; + + ftdm_mutex_create(&the_mutex); if (argc < 2) { printf("umm no\n"); - exit(-1); + exit(1); } + ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); + if (ftdm_global_init() != FTDM_SUCCESS) { fprintf(stderr, "Error loading FreeTDM\n"); - exit(-1); + exit(1); } + ftdm_global_configuration(); + printf("FreeTDM loaded\n"); - if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { - fprintf(stderr, "Error finding FreeTDM span\n"); + if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) { + fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); goto done; } + /* testing non-blocking operation */ + //ftdm_span_set_blocking_mode(span, FTDM_FALSE); + parameters[0].var = "variant"; + parameters[0].val = "br"; - if (ftdm_configure_span(span, "r2", on_r2_signal, - "variant", "mx", - "max_ani", 10, - "max_dnis", 4, - "logging", "all", - FTDM_TAG_END) == FTDM_SUCCESS) { - + parameters[1].var = "max_ani"; + parameters[1].val = "4"; + parameters[2].var = "max_dnis"; + parameters[2].val = "4"; + + parameters[3].var = "logging"; + parameters[3].val = "all"; + + parameters[4].var = NULL; + parameters[4].val = NULL; + + if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, parameters) == FTDM_SUCCESS) { ftdm_span_start(span); } else { fprintf(stderr, "Error starting R2 span\n"); goto done; } - signal(SIGINT, handle_SIGINT); - ftdm_mutex_lock(mutex); - R = 1; - ftdm_mutex_unlock(mutex); - while(R) { - ftdm_sleep(1 * 1000); + running = 1; + signal(SIGINT, stop_test); + while(running) { + ftdm_sleep(20); + if (fchan && indication != FTDM_CHANNEL_INDICATE_NONE) { + ftdm_channel_t *lchan = NULL; + ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE; + ftdm_time_t start, stop, diff; + + ftdm_mutex_lock(the_mutex); + ind = indication; + indication = FTDM_CHANNEL_INDICATE_NONE; + lchan = fchan; + ftdm_mutex_unlock(the_mutex); + + start = ftdm_current_time_in_ms(); + ftdm_channel_call_indicate(lchan, ind); + stop = ftdm_current_time_in_ms(); + diff = stop - start; + ftdm_log(FTDM_LOG_DEBUG, "Setting indication %s took %llums\n", + ftdm_channel_indication2str(ind), diff); + } } - done: +done: ftdm_global_destroy(); - return 1; - + return 0; } /* For Emacs: From 5c02639b5acf48ead80045f50e841c1c43f36d34 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Tue, 4 Jan 2011 10:37:20 -0500 Subject: [PATCH 13/13] freetdm: update ftmod_sangoma_ss7 to use new state advance API --- .../ftmod_sangoma_ss7_main.c | 20 +++++++------------ .../ftmod_sangoma_ss7_main.h | 2 +- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c index 05a325b913..9016771671 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c @@ -46,7 +46,6 @@ ftdm_sngss7_data_t g_ftdm_sngss7_data; /* PROTOTYPES *****************************************************************/ static void *ftdm_sangoma_ss7_run (ftdm_thread_t * me, void *obj); -void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan); static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_event); static ftdm_status_t ftdm_sangoma_ss7_stop (ftdm_span_t * span); @@ -308,9 +307,7 @@ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj) ftdm_mutex_lock(ftdmchan->mutex); /* process state changes for this channel until they are all done */ - while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_sangoma_ss7_process_state_change (ftdmchan); - } + ftdm_channel_advance_states(ftdmchan); /* unlock the channel */ ftdm_mutex_unlock (ftdmchan->mutex); @@ -403,9 +400,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev ftdm_mutex_lock(ftdmchan->mutex); /* while there's a state change present on this channel process it */ - while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_sangoma_ss7_process_state_change(ftdmchan); - } + ftdm_channel_advance_states(ftdmchan); /* figure out the type of event and send it to the right handler */ switch (sngss7_event->event_id) { @@ -468,9 +463,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev } /* switch (sngss7_event->event_id) */ /* while there's a state change present on this channel process it */ - while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) { - ftdm_sangoma_ss7_process_state_change(ftdmchan); - } + ftdm_channel_advance_states(ftdmchan); /* unlock the channel */ ftdm_mutex_unlock(ftdmchan->mutex); @@ -479,7 +472,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev } /******************************************************************************/ -void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) +ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) { sngss7_chan_data_t *sngss7_info = ftdmchan->call_data; sng_isup_inf_t *isup_intf = NULL; @@ -495,7 +488,7 @@ void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan) SS7_DEBUG_CHAN(ftdmchan, "ftmod_sangoma_ss7 processing state %s\n", ftdm_channel_state2str (ftdmchan->state)); /* clear the state change flag...since we might be setting a new state */ - ftdm_clear_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE); + ftdm_channel_complete_state(ftdmchan); /*check what state we are supposed to be in */ switch (ftdmchan->state) { @@ -1212,7 +1205,7 @@ suspend_goto_restart: /**************************************************************************/ }/*switch (ftdmchan->state) */ - return; + return FTDM_SUCCESS; } /******************************************************************************/ @@ -1476,6 +1469,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_ss7_span_config) span->get_channel_sig_status = ftdm_sangoma_ss7_get_sig_status; span->set_channel_sig_status = ftdm_sangoma_ss7_set_sig_status; span->state_map = &sangoma_ss7_state_map; + span->state_processor = ftdm_sangoma_ss7_process_state_change; span->signal_data = ss7_span_info; /* set the flag to indicate that this span uses channel state change queues */ diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h index fe4b6f45c4..f28547f9fe 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h @@ -452,7 +452,7 @@ extern int cmbLinkSetId; /* PROTOTYPES *****************************************************************/ /* in ftmod_sangoma_ss7_main.c */ -void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan); +ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan); /* in ftmod_sangoma_ss7_logger.c */ void handle_sng_log(uint8_t level, char *fmt,...);