From: Moises Silva Date: Tue, 4 Jan 2011 17:32:30 +0000 (-0500) Subject: Merge branch 'moy.nonblocking-api' X-Git-Tag: v1.2-rc1~223^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1334d02ac0913370a5ee77d63dd7b64fbb0a3f89;p=thirdparty%2Ffreeswitch.git Merge branch 'moy.nonblocking-api' Conflicts: libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c --- 1334d02ac0913370a5ee77d63dd7b64fbb0a3f89 diff --cc libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c index 8792b519d0,37c760ce94..f1ef61ce9f --- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c +++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c @@@ -1656,183 -1627,161 +1653,173 @@@ static ftdm_status_t ftdm_r2_state_adva 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; - - /* 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; + 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; - /* incoming call was offered */ - case FTDM_CHANNEL_STATE_RING: + /* 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; - /* notify the user about the new call */ - sigev.event_id = FTDM_SIGEVENT_START; + /* incoming call was offered */ + case FTDM_CHANNEL_STATE_RING: - ftdm_span_send_signal(ftdmchan->span, &sigev); - r2call->ftdm_call_started = 1; + /* notify the user about the new call */ + sigev.event_id = FTDM_SIGEVENT_START; - break; + ftdm_span_send_signal(ftdmchan->span, &sigev); + r2call->ftdm_call_started = 1; - /* 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); + break; - sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA; - 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); } - 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; - - 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: - { - if (ftdmchan->last_state != FTDM_CHANNEL_STATE_RESET) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n"); - } - ret = 1; + 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; - /* 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"); + /* finished call for good */ + case FTDM_CHANNEL_STATE_DOWN: + { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n"); ++ if (ftdmchan->last_state != FTDM_CHANNEL_STATE_RESET) { ++ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n"); ++ } else { ++ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Reset Complete\n"); + } - break; + ret = FTDM_BREAK; + } + break; - /* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */ - case FTDM_CHANNEL_STATE_RESET: - { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n"); - openr2_chan_set_idle(r2chan); - ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); - } - 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; ++ /* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */ ++ case FTDM_CHANNEL_STATE_RESET: ++ { ++ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n"); ++ openr2_chan_set_idle(r2chan); ++ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); ++ } ++ 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);