}
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;
}
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;
/*
* 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;
}
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;
}
* (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)) {
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;
}
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);
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;
}
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)
{
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) {
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) */
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);
}
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;
}
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() */
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];
/* 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) \
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;
}
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);
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);
}
}
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);
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);
}
}
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)
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);
}
}
/* 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;
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);
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;
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 */