/* MONITIOR THREADS ***********************************************************/
static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj)
{
- ftdm_interrupt_t *ftdm_sangoma_ss7_int[3];
+ ftdm_interrupt_t *ftdm_sangoma_ss7_int[2];
ftdm_span_t *ftdmspan = (ftdm_span_t *) obj;
ftdm_channel_t *ftdmchan = NULL;
- ftdm_channel_t *peerchan = NULL;
ftdm_event_t *event = NULL;
sngss7_event_data_t *sngss7_event = NULL;
sngss7_span_data_t *sngss7_span = (sngss7_span_data_t *)ftdmspan->signal_data;
goto ftdm_sangoma_ss7_run_exit;
}
- /* get an interrupt queue for this span for peer channel events */
- if (ftdm_queue_get_interrupt (sngss7_span->peer_chans, &ftdm_sangoma_ss7_int[2]) != FTDM_SUCCESS) {
- SS7_CRITICAL ("Failed to get a ftdm_interrupt for span = %d for peer channel events queue!\n", ftdmspan->span_id);
- goto ftdm_sangoma_ss7_run_exit;
- }
-
while (ftdm_running () && !(ftdm_test_flag (ftdmspan, FTDM_SPAN_STOP_THREAD))) {
int x = 0;
if (b_alarm_test) {
/* clean out all pending channel state changes */
while ((ftdmchan = ftdm_queue_dequeue (ftdmspan->pendingchans))) {
+ sngss7_chan_data_t *chan_info = ftdmchan->call_data;
/*first lock the channel */
ftdm_mutex_lock(ftdmchan->mutex);
/* process state changes for this channel until they are all done */
ftdm_channel_advance_states(ftdmchan);
-
- /* unlock the channel */
- ftdm_mutex_unlock (ftdmchan->mutex);
- }
-
- /* clean out all peer pending channel events */
- while ((peerchan = ftdm_queue_dequeue (sngss7_span->peer_chans))) {
- /* note that the channels being dequeued here may not belong to this span
- they may belong to just about any other span that one of our channels
- happens to be bridged to */
- sngss7_chan_data_t *peer_info;
- sngss7_chan_data_t *chan_info;
-
- peer_info = peerchan->call_data;
- if (peer_info) {
- chan_info = peer_info->peer_data;
- if (chan_info) {
- ftdmchan = chan_info->ftdmchan;
- if (ftdmchan) {
-
- /*
- if there is any state changes at all, those will be done in the opposite channel
- to peerchan (where the original event was received), therefore we must lock ftdmchan,
- but do not need to lock peerchan as we only read its event queue, which is already
- locked when dequeueing */
- ftdm_channel_lock(ftdmchan);
-
- /* clean out all pending stack events in the peer channel */
- while ((sngss7_event = ftdm_queue_dequeue(peer_info->event_queue))) {
- ftdm_sangoma_ss7_process_peer_stack_event(ftdmchan, sngss7_event);
- ftdm_safe_free(sngss7_event);
- }
- ftdm_channel_unlock(ftdmchan);
- }
+ if (chan_info->peer_data) {
+ /* clean out all pending stack events in the peer channel */
+ while ((sngss7_event = ftdm_queue_dequeue(chan_info->event_queue))) {
+ ftdm_sangoma_ss7_process_peer_stack_event(ftdmchan, sngss7_event);
+ ftdm_safe_free(sngss7_event);
}
}
+
+ /* unlock the channel */
+ ftdm_mutex_unlock (ftdmchan->mutex);
}
/* clean out all pending stack events */
ftdm_channel_advance_states(ftdmchan);
if (sngss7_event->event_id == SNGSS7_CON_IND_EVENT) {
- /* this is the first event in a call, flush the event queue */
- sngss7_flush_queue(sngss7_info->event_queue);
- /* clear the peer if any */
- sngss7_info->peer_data = NULL;
clone_event++;
}
- /* if the call has already started and the event is not a release confirmation, clone the event */
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED) &&
- sngss7_event->event_id != SNGSS7_REL_CFM_EVENT) {
+ /* If the call has already started (we only bridge events related to calls)
+ * and the event is not a release confirmation, then clone the event.
+ * We do not clone release cfm events because that is the only event (final event) that is not
+ * bridged to the other leg, the first Spirou customer we had explicitly requested to send
+ * release confirm as soon as the release is received and therefore not wait for the other leg
+ * to send release confirm (hence, not need to clone and enqueue in the other leg) */
+ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALL_STARTED) && sngss7_event->event_id != SNGSS7_REL_CFM_EVENT) {
clone_event++;
}
event_clone = ftdm_calloc(1, sizeof(*sngss7_event));
if (event_clone) {
memcpy(event_clone, sngss7_event, sizeof(*sngss7_event));
- ftdm_queue_enqueue(sngss7_info->event_queue, event_clone);
+ /* if we have already a peer channel then enqueue the event in their queue */
if (sngss7_info->peer_data) {
- sngss7_span_data_t *sngss7_peer_span = (sngss7_span_data_t *)sngss7_info->peer_data->ftdmchan->span->signal_data;
+ ftdm_span_t *peer_span = sngss7_info->peer_data->ftdmchan->span;
+ if (sngss7_info->peer_event_transfer_cnt) {
+ sngss7_event_data_t *peer_event = NULL;
+ int qi = 0;
+ /* looks like for the first time we found our peer, transfer any messages we enqueued */
+ for (qi = 0; qi < sngss7_info->peer_event_transfer_cnt; qi++) {
+ peer_event = ftdm_queue_dequeue(sngss7_info->event_queue);
+ if (peer_event) {
+ ftdm_queue_enqueue(sngss7_info->peer_data->event_queue, peer_event);
+ } else {
+ /* This should never happen! */
+ SS7_CRIT_CHAN(ftdmchan,"[CIC:%d]What!? someone stole my messages!\n", sngss7_info->circuit->cic);
+ }
+ }
+ SS7_DEBUG_CHAN(ftdmchan,"[CIC:%d]Transferred %d messages into my peer's queue\n",
+ sngss7_info->circuit->cic, sngss7_info->peer_event_transfer_cnt);
+ sngss7_info->peer_event_transfer_cnt = 0;
+ }
/* we already have a peer attached, wake him up */
- ftdm_queue_enqueue(sngss7_peer_span->peer_chans, sngss7_info->ftdmchan);
+ ftdm_queue_enqueue(sngss7_info->peer_data->event_queue, event_clone);
+ ftdm_queue_enqueue(peer_span->pendingchans, sngss7_info->peer_data->ftdmchan);
+ } else {
+ /* we don't have a peer yet, save the event on our own queue for later
+ * only the first event in this queue is directly consumed by our peer (IAM), subsequent events
+ * must be transferred by us to their queue as soon as we find our peer */
+ ftdm_queue_enqueue(sngss7_info->event_queue, event_clone);
+ if (sngss7_event->event_id != SNGSS7_CON_IND_EVENT) {
+ /* This could be an SAM, save it for transfer once we know who our peer is (if we ever find that) */
+ sngss7_info->peer_event_transfer_cnt++;
+ }
}
}
}
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
break;
case SNGSS7_REL_CFM_EVENT:
- {
- if (sngss7_info->peer_data) {
- ftdm_channel_t *peer_chan = sngss7_info->peer_data->ftdmchan;
- ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
- if (peer_chan) {
- /* we need to unlock our chan or we risk deadlock */
- ftdm_channel_advance_states(ftdmchan);
- ftdm_channel_unlock(ftdmchan);
-
- ftdm_channel_lock(peer_chan);
- if (peer_chan->state != FTDM_CHANNEL_STATE_DOWN) {
- ftdm_set_state(peer_chan, FTDM_CHANNEL_STATE_DOWN);
- }
- ftdm_channel_unlock(peer_chan);
-
- ftdm_channel_lock(ftdmchan);
- }
- }
- }
+ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
break;
default:
break;
sngss7_clear_ckt_flag(sngss7_info, FLAG_SUS_RECVD);
sngss7_clear_ckt_flag(sngss7_info, FLAG_T6_CANCELED);
sngss7_flush_queue(sngss7_info->event_queue);
+ sngss7_info->peer_data = NULL;
ftdm_channel_close (&close_chan);
}
break;
case FTDM_CHANNEL_STATE_TERMINATING:
{
- ft_to_sngss7_rlc(ftdmchan);
+ /* Release confirm is sent immediately, since Spirou customer asked us not to wait for the second call leg
+ * to come back with a release confirm ... */
/* when receiving REL we move to TERMINATING and notify the user that the bridge is ending */
- sngss7_send_signal(sngss7_info, FTDM_SIGEVENT_STOP);
+ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USER_HANGUP)) {
+ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
+ } else {
+ /* Notify the user and wait for their ack before sending RLC */
+ sngss7_send_signal(sngss7_info, FTDM_SIGEVENT_STOP);
+ }
+ }
+ break;
+
+ case FTDM_CHANNEL_STATE_HANGUP:
+ {
+ ft_to_sngss7_rlc(ftdmchan);
+ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
break;
sngss7_info->blk_flags);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE)) {
- /* DIALING is the only state we process normally when doing an outgoing call that is natively bridged */
+ /* DIALING is the only state we process normally when doing an outgoing call that is natively bridged,
+ * all other states are run by a different state machine (and the freetdm core does not do any checking) */
if (ftdmchan->state != FTDM_CHANNEL_STATE_DIALING) {
return ftdm_sangoma_ss7_native_bridge_state_change(ftdmchan);
}
- sngss7_info->peer_data = NULL;
}
/*check what state we are supposed to be in */
if (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_OPEN)) {
ftdm_channel_t *close_chan = ftdmchan;
-
- /* detach native bridging if needed (only the outbound leg is responsible for that)
- Inbound leg was responsible of flushing its queue of events, but peer attach/detach
- is left as an outbound leg responsibility
- */
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
- sngss7_chan_data_t *peer_info = sngss7_info->peer_data;
- if (peer_info) {
- sngss7_info->peer_data = NULL;
- if (peer_info) {
- peer_info->peer_data = NULL;
- }
- }
- }
-
/* close the channel */
SS7_DEBUG_CHAN(ftdmchan,"FTDM Channel Close %s\n", "");
- sngss7_flush_queue(sngss7_info->event_queue);
ftdm_channel_close (&close_chan);
}
return FTDM_FAIL;
}
- /* create an peer channel queue for this span */
- if ((ftdm_queue_create(&(ss7_span_info)->peer_chans, SNGSS7_PEER_CHANS_QUEUE_SIZE)) != FTDM_SUCCESS) {
- SS7_CRITICAL("Unable to create peer chans queue!\n");
- return FTDM_FAIL;
- }
-
/*setup the span structure with the info so far */
g_ftdm_sngss7_data.sig_cb = sig_cb;
span->start = ftdm_sangoma_ss7_start;
SiConEvnt iam;
ftdm_bool_t native_going_up = FTDM_FALSE;
sngss7_chan_data_t *sngss7_info = ftdmchan->call_data;;
+ sngss7_event_data_t *event_clone = NULL;
SS7_FUNC_TRACE_ENTER (__FUNCTION__);
var, peer_span->signal_type);
} else {
peer_info = peer_chan->call_data;
- if (peer_info) {
- SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Starting native bridge with peer CIC %d\n",
- sngss7_info->circuit->cic, peer_info->circuit->cic);
-
- /* make each one of us aware of the native bridge */
- peer_info->peer_data = sngss7_info;
- sngss7_info->peer_data = peer_info;
-
- /* flush our own queue */
- sngss7_flush_queue(sngss7_info->event_queue);
-
- /* Go to up until release comes, note that state processing is done different and much simpler when there is a peer,
- We can't go to UP state right away yet though, so do not set the state to UP here, wait until the end of this function
- because moving from one state to another causes the ftdmchan->usrmsg structure to be wiped
- and we still need those variables for further IAM processing */
- native_going_up = FTDM_TRUE;
- }
+ SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Starting native bridge with peer CIC %d\n",
+ sngss7_info->circuit->cic, peer_info->circuit->cic);
+
+ /* retrieve only first message from the others guys queue (must be IAM) */
+ event_clone = ftdm_queue_dequeue(peer_info->event_queue);
+
+ /* make each one of us aware of the native bridge */
+ peer_info->peer_data = sngss7_info;
+ sngss7_info->peer_data = peer_info;
+
+ /* Go to up until release comes, note that state processing is done different and much simpler when there is a peer,
+ We can't go to UP state right away yet though, so do not set the state to UP here, wait until the end of this function
+ because moving from one state to another causes the ftdmchan->usrmsg structure to be wiped
+ and we still need those variables for further IAM processing */
+ native_going_up = FTDM_TRUE;
}
}
}
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE) && sngss7_info->peer_data) {
- sngss7_span_data_t *span_data = ftdmchan->span->signal_data;
- sngss7_event_data_t *event_clone = ftdm_queue_dequeue(sngss7_info->peer_data->event_queue);
- /* Retrieve IAM from our peer */
+ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE)) {
if (!event_clone) {
- SS7_ERROR_CHAN(ftdmchan, "No event clone in peer queue!%s\n", "");
+ SS7_ERROR_CHAN(ftdmchan, "No IAM event clone in peer queue!%s\n", "");
} else if (event_clone->event_id != SNGSS7_CON_IND_EVENT) {
/* first message in the queue should ALWAYS be an IAM */
SS7_ERROR_CHAN(ftdmchan, "Invalid initial peer message type '%d'\n", event_clone->event_id);
copy_ocn_to_sngss7(ftdmchan, &iam.origCdNum);
}
}
- /* since this is the first time we dequeue an event from the peer, make sure our main thread process any other events,
- this will trigger the interrupt in our span peer_chans queue which will wake up our main thread if it is sleeping */
- ftdm_queue_enqueue(span_data->peer_chans, sngss7_info->peer_data->ftdmchan);
} else if (sngss7_info->circuit->transparent_iam &&
sngss7_retrieve_iam(ftdmchan, &iam) == FTDM_SUCCESS) {
SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Tx IAM (Transparent)\n", sngss7_info->circuit->cic);
the user sending FTDM_SIGEVENT_UP which can cause the application to misbehave (ie, no audio) */
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
ftdm_channel_advance_states(ftdmchan);
- }
+ }
+
+ ftdm_safe_free(event_clone);
SS7_FUNC_TRACE_EXIT (__FUNCTION__);
return;