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);