]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
freetdm: completed most of the code for the new core state processing
authorMoises Silva <moy@sangoma.com>
Wed, 29 Dec 2010 18:38:43 +0000 (13:38 -0500)
committerMoises Silva <moy@sangoma.com>
Wed, 29 Dec 2010 18:38:43 +0000 (13:38 -0500)
 use new core state processing functions in ftmod_r2

libs/freetdm/mod_freetdm/mod_freetdm.c
libs/freetdm/src/ftdm_io.c
libs/freetdm/src/ftdm_state.c
libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
libs/freetdm/src/include/freetdm.h
libs/freetdm/src/include/ftdm_declare.h
libs/freetdm/src/include/private/ftdm_core.h
libs/freetdm/src/include/private/ftdm_state.h

index d9c26db4fea3a81643575ed6eb4c337cf63c3c38..e08d4c1f3b06545626f9fed5729cd36072ec214a 100755 (executable)
@@ -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:
                {
index 161ccdcc1861f4fbd3c3b5001269a2206b3b66d1..0c2505481614764aef317b6266da99c91d348407 100644 (file)
@@ -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;
        }
 
index 2b544756ec2e23bcba86daad74967b3704ec547a..12dc37957adb5be24677a1863a6d7daefc2bc27f 100644 (file)
@@ -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() */
index 3809b224c4e729a8b82159f58fdc56be6b689e2a..530bb2b9b2fcb1f12377469cf7f59664c2823db0 100644 (file)
@@ -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;
-
-       /* 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));
-               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;
+       ret = FTDM_SUCCESS;
+
+       ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(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_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;
+                       /* 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:
+                       /* incoming call was offered */
+               case FTDM_CHANNEL_STATE_RING:
 
-                               /* notify the user about the new call */
-                               sigev.event_id = FTDM_SIGEVENT_START;
+                       /* 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; 
+                       ftdm_span_send_signal(ftdmchan->span, &sigev);
+                       r2call->ftdm_call_started = 1; 
 
-                               break;
+                       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);
+                       /* 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);
-                                       }
+                                       sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
+                                       ftdm_span_send_signal(ftdmchan->span, &sigev);
                                }
-                               break;
+                       }
+                       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);
-                                               }
+                       /* 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 {
-                                               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);
+                                               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;
+                       }
+                       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);
-                                       }
+                       /* 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;
+                       }
+                       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);
-                                       }
+               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;
+                       }
+                       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;
+                       /* 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;
+                       /* 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;
+               default:
+                       {
+                               ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
+                       }
+                       break;
 
-               }
        }
 
-       if (ret) {
+       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 */
index 30fe6ca4f54e055066da34c774ac4c958091f49d..ff129636d93ef6b2b32356c5fa513b7019ebb371 100644 (file)
@@ -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))
 
index 2e41281baf7b66dcbc141a03d9f02afb1cd65abe..88a76930f7627170ea9f25d53c3f9fa503b8503b 100644 (file)
@@ -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. */
index 8f97456fb7cbd00f4e80b8e823dade107901a34a..605ab8df15b5673a38769ed38ea89441ffa52d89 100644 (file)
@@ -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
index 79a3df07e944e19b86e6cd362763e557336532b1..36dcc2bef86102971dc2c0522bc9a7317d7e9917 100644 (file)
@@ -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,