static FIO_SIGNAL_CB_FUNCTION(on_r2_signal) { int chanid = ftdm_channel_get_ph_id(sigmsg->channel); ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel %d\n", ftdm_signal_event2str(sigmsg->event_id), chanid); switch (sigmsg->event_id) { case FTDM_SIGEVENT_START: { ftdm_mutex_lock(the_mutex); if (!fchan) { fchan = sigmsg->channel; indication = FTDM_CHANNEL_INDICATE_PROCEED; } ftdm_mutex_unlock(the_mutex); } break; case FTDM_SIGEVENT_INDICATION_COMPLETED: { ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE; if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROCEED) { ftdm_log(FTDM_LOG_DEBUG, "Proceed indication result = %d\n", sigmsg->ev_data.indication_completed.status); ind = FTDM_CHANNEL_INDICATE_PROGRESS; } else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS) { ftdm_log(FTDM_LOG_DEBUG, "Progress indication result = %d\n", sigmsg->ev_data.indication_completed.status); ind = FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA; } else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA) { ftdm_log(FTDM_LOG_DEBUG, "Progress media indication result = %d\n", sigmsg->ev_data.indication_completed.status); ind = FTDM_CHANNEL_INDICATE_ANSWER; } else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_ANSWER) { ftdm_log(FTDM_LOG_DEBUG, "Answer indication result = %d\n", sigmsg->ev_data.indication_completed.status); } else { ftdm_log(FTDM_LOG_DEBUG, "Unexpected indication, result = %d\n", sigmsg->ev_data.indication_completed.status); exit(1); } ftdm_mutex_lock(the_mutex); if (fchan) { indication = ind; } ftdm_mutex_unlock(the_mutex); } break; case FTDM_SIGEVENT_STOP: { ftdm_channel_call_hangup(sigmsg->channel); } break; case FTDM_SIGEVENT_RELEASED: { ftdm_mutex_lock(the_mutex); if (fchan && fchan == sigmsg->channel) { fchan = NULL; } ftdm_mutex_unlock(the_mutex); } break; default: break; } return FTDM_SUCCESS; }
FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **insched) { ftdm_sched_t *sched = NULL; ftdm_timer_t *timer; ftdm_timer_t *deltimer; ftdm_assert_return(insched != NULL, FTDM_EINVAL, "sched is null!\n"); ftdm_assert_return(*insched != NULL, FTDM_EINVAL, "sched is null!\n"); sched = *insched; /* since destroying a sched may affect the global list, we gotta check */ ftdm_mutex_lock(sched_globals.mutex); /* if we're head, replace head with our next (whatever our next is, even null will do) */ if (sched == sched_globals.freeruns) { sched_globals.freeruns = sched->next; } /* if we have a previous member (then we were not head) set our previous next to our next */ if (sched->prev) { sched->prev->next = sched->next; } /* if we have a next then set their prev to our prev (if we were head prev will be null and sched->next is already the new head) */ if (sched->next) { sched->next->prev = sched->prev; } ftdm_mutex_unlock(sched_globals.mutex); /* now grab the sched mutex */ ftdm_mutex_lock(sched->mutex); timer = sched->timers; while (timer) { deltimer = timer; timer = timer->next; ftdm_safe_free(deltimer); } ftdm_log(FTDM_LOG_DEBUG, "Destroying schedule %s\n", sched->name); ftdm_mutex_unlock(sched->mutex); ftdm_mutex_destroy(&sched->mutex); ftdm_safe_free(sched); *insched = NULL; return FTDM_SUCCESS; }
static void tap_pri_put_pcall(pritap_t *pritap, void *callref) { int i; int crv; int tstcrv; if (!callref) { ftdm_log(FTDM_LOG_ERROR, "Cannot put pcall for null callref in span %s\n", pritap->span->name); return; } ftdm_mutex_lock(pritap->pcalls_lock); crv = tap_pri_get_crv(pritap->pri, callref); for (i = 0; i < ftdm_array_len(pritap->pcalls); i++) { if (!pritap->pcalls[i].callref) { continue; } tstcrv = tap_pri_get_crv(pritap->pri, pritap->pcalls[i].callref); if (tstcrv == crv) { if (pritap->pcalls[i].inuse) { ftdm_log(FTDM_LOG_DEBUG, "releasing slot %d in span %s used by callref %d/%p\n", i, pritap->span->name, crv, pritap->pcalls[i].callref); pritap->pcalls[i].inuse = 0; } } } ftdm_mutex_unlock(pritap->pcalls_lock); }
static void handle_SIGINT(int sig) { ftdm_mutex_lock(mutex); R = 0; ftdm_mutex_unlock(mutex); return; }
/* FUNCTIONS ******************************************************************/ void handle_isup_t35(void *userdata) { SS7_FUNC_TRACE_ENTER(__FUNCTION__); sngss7_timer_data_t *timer = userdata; sngss7_chan_data_t *sngss7_info = timer->sngss7_info; ftdm_channel_t *ftdmchan = sngss7_info->ftdmchan; /* now that we have the right channel...put a lock on it so no-one else can use it */ ftdm_mutex_lock(ftdmchan->mutex); SS7_ERROR("[Call-Control] Timer 35 expired on CIC = %d\n", sngss7_info->circuit->cic); /* set the flag to indicate this hangup is started from the local side */ sngss7_set_ckt_flag(sngss7_info, FLAG_LOCAL_REL); /* hang up on timer expiry */ ftdmchan->caller_data.hangup_cause = 28; /* end the call */ ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); /*unlock*/ ftdm_mutex_unlock(ftdmchan->mutex); SS7_FUNC_TRACE_EXIT(__FUNCTION__); return; }
void sngisdn_rcv_con_ind (int16_t suId, uint32_t suInstId, uint32_t spInstId, ConEvnt *conEvnt, int16_t dChan, uint8_t ces) { uint8_t bchan_no = 0; sngisdn_chan_data_t *sngisdn_info = NULL; sngisdn_event_data_t *sngisdn_event = NULL; ISDN_FUNC_TRACE_ENTER(__FUNCTION__); ftdm_assert(g_sngisdn_data.ccs[suId].activation_done != 0, "Con Ind on unconfigured cc\n"); ftdm_assert(g_sngisdn_data.dchans[dChan].num_spans != 0, "Con Ind on unconfigured dchan\n"); if (conEvnt->chanId.eh.pres != PRSNT_NODEF) { /* TODO: Implement me */ ftdm_log(FTDM_LOG_ERROR, "Incoming call without Channel Id not supported yet\n"); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } if (conEvnt->chanId.chanNmbSlotMap.pres) { bchan_no = conEvnt->chanId.chanNmbSlotMap.val[0]; } else if (conEvnt->chanId.infoChanSel.pres) { bchan_no = conEvnt->chanId.infoChanSel.val; } if (!bchan_no) { ftdm_log(FTDM_LOG_ERROR, "Failed to obtain b-channel number from SETUP message\n"); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } if (g_sngisdn_data.dchans[dChan].channels[bchan_no] == NULL) { ftdm_log(FTDM_LOG_ERROR, "Incoming call on unconfigured b-channel:%d\n", bchan_no); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } sngisdn_info = g_sngisdn_data.dchans[dChan].channels[bchan_no]; ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Received SETUP (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); sngisdn_event = ftdm_malloc(sizeof(*sngisdn_event)); ftdm_assert(sngisdn_event, "Failed to allocate memory\n"); memset(sngisdn_event, 0, sizeof(*sngisdn_event)); sngisdn_event->event_id = SNGISDN_EVENT_CON_IND; sngisdn_event->sngisdn_info = sngisdn_info; sngisdn_event->suId = suId; sngisdn_event->spInstId = spInstId; sngisdn_event->dChan = dChan; sngisdn_event->ces = ces; ftdm_mutex_lock(g_sngisdn_data.ccs[suId].mutex); g_sngisdn_data.ccs[suId].active_spInstIds[spInstId] = sngisdn_info; ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); memcpy(&sngisdn_event->event.conEvnt, conEvnt, sizeof(*conEvnt)); ftdm_queue_enqueue(((sngisdn_span_data_t*)sngisdn_info->ftdmchan->span->signal_data)->event_queue, sngisdn_event); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); }
static passive_call_t *tap_pri_get_pcall_bycrv(pritap_t *pritap, int crv) { int i; int tstcrv; ftdm_mutex_lock(pritap->pcalls_lock); for (i = 0; i < ftdm_array_len(pritap->pcalls); i++) { tstcrv = pritap->pcalls[i].callref ? tap_pri_get_crv(pritap->pri, pritap->pcalls[i].callref) : 0; if (pritap->pcalls[i].callref && tstcrv == crv) { if (pritap->pcalls[i].inuse) { ftdm_mutex_unlock(pritap->pcalls_lock); return &pritap->pcalls[i]; } /* This just means the crv is being re-used in another call before this one was destroyed */ ftdm_log(FTDM_LOG_DEBUG, "Found crv %d in slot %d of span %s with call %p but is no longer in use\n", crv, i, pritap->span->name, pritap->pcalls[i].callref); } } ftdm_log(FTDM_LOG_DEBUG, "crv %d was not found active in span %s\n", crv, pritap->span->name); ftdm_mutex_unlock(pritap->pcalls_lock); return NULL; }
FT_DECLARE(ftdm_status_t) ftdm_sched_get_time_to_next_timer(const ftdm_sched_t *sched, int32_t *timeto) { ftdm_status_t status = FTDM_FAIL; int res = -1; int ms = 0; struct timeval currtime; ftdm_timer_t *current = NULL; ftdm_timer_t *winner = NULL; /* forever by default */ *timeto = -1; ftdm_mutex_lock(sched->mutex); res = gettimeofday(&currtime, NULL); if (-1 == res) { ftdm_log(FTDM_LOG_ERROR, "Failed to get next event time\n"); goto done; } status = FTDM_SUCCESS; current = sched->timers; while (current) { /* if no winner, set this as winner */ if (!winner) { winner = current; } current = current->next; /* if no more timers, return the winner */ if (!current) { ms = (((winner->time.tv_sec - currtime.tv_sec) * 1000) + ((winner->time.tv_usec - currtime.tv_usec) / 1000)); /* if the timer is expired already, return 0 to attend immediately */ if (ms < 0) { *timeto = 0; break; } *timeto = ms; break; } /* if the winner timer is after the current timer, then we have a new winner */ if (winner->time.tv_sec > current->time.tv_sec || (winner->time.tv_sec == current->time.tv_sec && winner->time.tv_usec > current->time.tv_usec)) { winner = current; } } done: ftdm_mutex_unlock(sched->mutex); #ifdef __WINDOWS__ UNREFERENCED_PARAMETER(timeto); UNREFERENCED_PARAMETER(sched); #endif return status; }
static void *run_main_schedule(ftdm_thread_t *thread, void *data) { int32_t timeto; int32_t sleepms; ftdm_status_t status; ftdm_sched_t *current = NULL; #ifdef __WINDOWS__ UNREFERENCED_PARAMETER(data); UNREFERENCED_PARAMETER(thread); #endif while (ftdm_running()) { sleepms = SCHED_MAX_SLEEP; ftdm_mutex_lock(sched_globals.mutex); if (!sched_globals.freeruns) { /* there are no free runs, wait a bit and check again (FIXME: use ftdm_interrupt_t for this) */ ftdm_mutex_unlock(sched_globals.mutex); if (ftdm_running()) { ftdm_sleep(sleepms); } } for (current = sched_globals.freeruns; current; current = current->next) { if (!ftdm_running()) { break; } /* first run the schedule */ ftdm_sched_run(current); /* now find out how much time to sleep until running them again */ status = ftdm_sched_get_time_to_next_timer(current, &timeto); if (status != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_WARNING, "Failed to get time to next timer for schedule %s, skipping\n", current->name); continue; } /* if timeto == -1 we don't want to sleep forever, so keep the last sleepms */ if (timeto != -1 && sleepms > timeto) { sleepms = timeto; } } ftdm_mutex_unlock(sched_globals.mutex); if (ftdm_running()) { ftdm_sleep(sleepms); } } ftdm_log(FTDM_LOG_NOTICE, "Main scheduling thread going out ...\n"); sched_globals.running = 0; return NULL; }
FT_DECLARE(ftdm_status_t) ftdm_sched_free_run(ftdm_sched_t *sched) { ftdm_status_t status = FTDM_FAIL; ftdm_assert_return(sched != NULL, FTDM_EINVAL, "invalid pointer\n"); ftdm_mutex_lock(sched->mutex); ftdm_mutex_lock(sched_globals.mutex); if (sched->freerun) { ftdm_log(FTDM_LOG_ERROR, "Schedule %s is already running in free run\n", sched->name); goto done; } sched->freerun = 1; if (sched_globals.running == FTDM_FALSE) { ftdm_log(FTDM_LOG_NOTICE, "Launching main schedule thread\n"); status = ftdm_thread_create_detached(run_main_schedule, NULL); if (status != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_CRIT, "Failed to launch main schedule thread\n"); goto done; } sched_globals.running = FTDM_TRUE; } ftdm_log(FTDM_LOG_DEBUG, "Running schedule %s in the main schedule thread\n", sched->name); status = FTDM_SUCCESS; /* Add the schedule to the global list of free runs */ if (!sched_globals.freeruns) { sched_globals.freeruns = sched; } else { sched->next = sched_globals.freeruns; sched_globals.freeruns->prev = sched; sched_globals.freeruns = sched; } done: ftdm_mutex_unlock(sched_globals.mutex); ftdm_mutex_unlock(sched->mutex); return status; }
void sngisdn_delayed_setup(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; ftdm_mutex_lock(ftdmchan->mutex); sngisdn_snd_setup(ftdmchan); ftdm_mutex_unlock(ftdmchan->mutex); return; }
int main(int argc, char *argv[]) { ftdm_span_t *span; ftdm_mutex_create(&mutex); ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); if (argc < 2) { printf("umm no\n"); exit(-1); } if (ftdm_global_init() != FTDM_SUCCESS) { fprintf(stderr, "Error loading FreeTDM\n"); exit(-1); } printf("FreeTDM loaded\n"); if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) { fprintf(stderr, "Error finding FreeTDM span\n"); goto done; } if (ftdm_configure_span("r2", span, on_r2_signal, "variant", "mx", "max_ani", 10, "max_dnis", 4, "logging", "all", TAG_END) == FTDM_SUCCESS) { ftdm_span_start(span); } else { fprintf(stderr, "Error starting R2 span\n"); goto done; } signal(SIGINT, handle_SIGINT); ftdm_mutex_lock(mutex); R = 1; ftdm_mutex_unlock(mutex); while(R) { ftdm_sleep(1 * 1000); } done: ftdm_global_destroy(); return 1; }
void sngisdn_rcv_cnst_ind (int16_t suId, uint32_t suInstId, uint32_t spInstId, CnStEvnt *cnStEvnt, uint8_t evntType, int16_t dChan, uint8_t ces) { sngisdn_chan_data_t *sngisdn_info = NULL; sngisdn_event_data_t *sngisdn_event = NULL; ISDN_FUNC_TRACE_ENTER(__FUNCTION__); ftdm_assert(g_sngisdn_data.ccs[suId].activation_done != 0, "Cnst Ind on unconfigured cc\n"); ftdm_assert(g_sngisdn_data.dchans[dChan].num_spans != 0, "Cnst Ind on unconfigured dchan\n"); if (get_ftdmchan_by_suInstId(suId, suInstId, &sngisdn_info) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_CRIT, "Could not find matching call suId:%u suInstId:%u spInstId:%u\n", suId, suInstId, spInstId); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } if (!sngisdn_info->spInstId) { ftdm_mutex_lock(g_sngisdn_data.ccs[suId].mutex); sngisdn_info->spInstId = spInstId; g_sngisdn_data.ccs[suId].active_spInstIds[spInstId] = sngisdn_info; ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); } ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Received %s (suId:%u suInstId:%u spInstId:%u ces:%d)\n", (evntType == MI_ALERTING)?"ALERT": (evntType == MI_CALLPROC)?"PROCEED": (evntType == MI_PROGRESS)?"PROGRESS": (evntType == MI_SETUPACK)?"SETUP ACK": (evntType == MI_NOTIFY)?"NOTIFY": (evntType == MI_INFO)?"INFO":"UNKNOWN", suId, suInstId, spInstId, ces); sngisdn_event = ftdm_malloc(sizeof(*sngisdn_event)); ftdm_assert(sngisdn_event, "Failed to allocate memory\n"); memset(sngisdn_event, 0, sizeof(*sngisdn_event)); sngisdn_event->event_id = SNGISDN_EVENT_CNST_IND; sngisdn_event->sngisdn_info = sngisdn_info; sngisdn_event->suId = suId; sngisdn_event->suInstId = suInstId; sngisdn_event->spInstId = spInstId; sngisdn_event->dChan = dChan; sngisdn_event->ces = ces; sngisdn_event->evntType = evntType; memcpy(&sngisdn_event->event.cnStEvnt, cnStEvnt, sizeof(*cnStEvnt)); ftdm_queue_enqueue(((sngisdn_span_data_t*)sngisdn_info->ftdmchan->span->signal_data)->event_queue, sngisdn_event); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); }
void sngisdn_snd_setup(ftdm_channel_t *ftdmchan) { ConEvnt conEvnt; sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID}; ftdm_assert((!sngisdn_info->suInstId && !sngisdn_info->spInstId), "Trying to call out, but call data was not cleared\n"); sngisdn_info->suInstId = get_unique_suInstId(signal_data->cc_id); sngisdn_info->spInstId = 0; ftdm_mutex_lock(g_sngisdn_data.ccs[signal_data->cc_id].mutex); g_sngisdn_data.ccs[signal_data->cc_id].active_suInstIds[sngisdn_info->suInstId] = sngisdn_info; ftdm_mutex_unlock(g_sngisdn_data.ccs[signal_data->cc_id].mutex); memset(&conEvnt, 0, sizeof(conEvnt)); if (signal_data->switchtype == SNGISDN_SWITCH_EUROISDN || signal_data->force_sending_complete == SNGISDN_OPT_TRUE) { conEvnt.sndCmplt.eh.pres = PRSNT_NODEF; } if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP && signal_data->signalling == SNGISDN_SIGNALING_NET) { sngisdn_info->ces = CES_MNGMNT; } ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Outgoing call: Called No:[%s] Calling No:[%s]\n", ftdmchan->caller_data.dnis.digits, ftdmchan->caller_data.cid_num.digits); set_chan_id_ie(ftdmchan, &conEvnt.chanId); set_bear_cap_ie(ftdmchan, &conEvnt.bearCap[0]); set_called_num(ftdmchan, &conEvnt.cdPtyNmb); set_calling_num(ftdmchan, &conEvnt.cgPtyNmb); set_calling_num2(ftdmchan, &conEvnt.cgPtyNmb2); set_calling_subaddr(ftdmchan, &conEvnt.cgPtySad); set_called_subaddr(ftdmchan, &conEvnt.cdPtySad); set_redir_num(ftdmchan, &conEvnt.redirNmb); set_calling_name(ftdmchan, &conEvnt); set_network_specific_fac(ftdmchan, &conEvnt.netFac[0]); /* set_facility_ie will overwrite Calling Name for NI-2 if user specifies custom Facility IE */ set_facility_ie(ftdmchan, &conEvnt.facilityStr); set_prog_ind_ie(ftdmchan, &conEvnt.progInd, prog_ind); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending SETUP (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces); if (sng_isdn_con_request(signal_data->cc_id, sngisdn_info->suInstId, &conEvnt, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused SETUP request\n"); } return; }
void sngisdn_delayed_connect(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_mutex_lock(ftdmchan->mutex); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending delayed CONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); sngisdn_snd_connect(ftdmchan); ftdm_mutex_unlock(ftdmchan->mutex); return; }
void clear_call_data(sngisdn_chan_data_t *sngisdn_info) { uint32_t cc_id = ((sngisdn_span_data_t*)sngisdn_info->ftdmchan->span->signal_data)->cc_id; ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_DEBUG, "Clearing call data (suId:%u suInstId:%u spInstId:%u)\n", cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId); ftdm_mutex_lock(g_sngisdn_data.ccs[cc_id].mutex); g_sngisdn_data.ccs[cc_id].active_spInstIds[sngisdn_info->spInstId]=NULL; g_sngisdn_data.ccs[cc_id].active_suInstIds[sngisdn_info->suInstId]=NULL; ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].mutex); sngisdn_info->suInstId = 0; sngisdn_info->spInstId = 0; sngisdn_info->globalFlg = 0; sngisdn_info->flags = 0; return; }
void sngisdn_facility_timeout(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_mutex_lock(ftdmchan->mutex); if (ftdmchan->state == FTDM_CHANNEL_STATE_GET_CALLERID) { ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Facility timeout reached proceeding with call (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->spInstId, sngisdn_info->suInstId); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); } ftdm_mutex_unlock(ftdmchan->mutex); return; }
void att_courtesy_transfer_timeout(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_mutex_lock(ftdmchan->mutex); if (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_NONE) { /* Call was already cleared */ ftdm_mutex_unlock(ftdmchan->mutex); return; } ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "AT&T Courtesy Transfer timeout (%d)\n", signal_data->transfer_timeout); att_courtesy_transfer_complete(sngisdn_info, FTDM_TRANSFER_RESPONSE_TIMEOUT); ftdm_mutex_unlock(ftdmchan->mutex); return; }
void sngisdn_rcv_con_cfm (int16_t suId, uint32_t suInstId, uint32_t spInstId, CnStEvnt *cnStEvnt, int16_t dChan, uint8_t ces) { sngisdn_chan_data_t *sngisdn_info = NULL; sngisdn_event_data_t *sngisdn_event = NULL; ISDN_FUNC_TRACE_ENTER(__FUNCTION__); ftdm_assert(g_sngisdn_data.ccs[suId].activation_done != 0, "Con Cfm on unconfigured cc\n"); ftdm_assert(g_sngisdn_data.dchans[dChan].num_spans != 0, "Con Cfm on unconfigured dchan\n"); if (get_ftdmchan_by_suInstId(suId, suInstId, &sngisdn_info) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_CRIT, "Could not find matching call suId:%u suInstId:%u spInstId:%u\n", suId, suInstId, spInstId); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } if (!sngisdn_info->spInstId) { ftdm_mutex_lock(g_sngisdn_data.ccs[suId].mutex); sngisdn_info->spInstId = spInstId; g_sngisdn_data.ccs[suId].active_spInstIds[spInstId] = sngisdn_info; ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); } ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Received CONNECT/CONNECT ACK (suId:%u suInstId:%u spInstId:%u ces:%d)\n", suId, suInstId, spInstId, ces); sngisdn_event = ftdm_malloc(sizeof(*sngisdn_event)); ftdm_assert(sngisdn_event, "Failed to allocate memory\n"); memset(sngisdn_event, 0, sizeof(*sngisdn_event)); sngisdn_event->event_id = SNGISDN_EVENT_CON_CFM; sngisdn_event->sngisdn_info = sngisdn_info; sngisdn_event->suId = suId; sngisdn_event->suInstId = suInstId; sngisdn_event->spInstId = spInstId; sngisdn_event->dChan = dChan; sngisdn_event->ces = ces; memcpy(&sngisdn_event->event.cnStEvnt, cnStEvnt, sizeof(*cnStEvnt)); ftdm_queue_enqueue(((sngisdn_span_data_t*)sngisdn_info->ftdmchan->span->signal_data)->event_queue, sngisdn_event); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); }
/* * This is a tricky function with some side effects, some explanation needed ... * * The libpri stack process HDLC frames, then finds Q921 frames and Q931 events, each time * it finds a new Q931 event, checks if the crv of that event matches a known call in the internal * list found in the PRI control block (for us, one control block per span), if it does not find * the call, allocates a new one and then sends the event up to the user (us, ftmod_pritap in this case) * * The user is then expected to destroy the call when done with it (on hangup), but things get tricky here * because in ftmod_pritap we do not destroy the call right away to be sure we only destroy it when no one * else needs that pointer, therefore we decide to delay the destruction of the call pointer until later * when a new call comes which triggers the garbage collecting code in this function * * Now, what happens if a new call arrives right away with the same crv than the last call? the pri stack * does *not* allocate a new call pointer because is still a known call and we must therefore re-use the * same call pointer * * This function accepts a pointer to a callref, even a NULL one. When callref is NULL we search for an * available slot so the caller of this function can use it to store a new callref pointer. In the process * we also scan for slots that still have a callref pointer but are no longer in use (inuse=0) and we * destroy that callref and clear the slot (memset). The trick is, we only do this if the callref to * be garbage collected is NOT the one provided by the parameter callref, of course! otherwise we may * be freeing a pointer to a callref for a new call that used an old (recycled) callref! */ static passive_call_t *tap_pri_get_pcall(pritap_t *pritap, void *callref) { int i; int crv; ftdm_mutex_lock(pritap->pcalls_lock); for (i = 0; i < ftdm_array_len(pritap->pcalls); i++) { /* If this slot has a call reference * and it is different than the *callref provided to us * and is no longer in use, * then it is time to garbage collect it ... */ if (pritap->pcalls[i].callref && callref != pritap->pcalls[i].callref && !pritap->pcalls[i].inuse) { crv = tap_pri_get_crv(pritap->pri, pritap->pcalls[i].callref); /* garbage collection */ ftdm_log(FTDM_LOG_DEBUG, "Garbage collecting callref %d/%p from span %s in slot %d\n", crv, pritap->pcalls[i].callref, pritap->span->name, i); pri_passive_destroycall(pritap->pri, pritap->pcalls[i].callref); memset(&pritap->pcalls[i], 0, sizeof(pritap->pcalls[0])); } if (callref == pritap->pcalls[i].callref) { if (callref == NULL) { pritap->pcalls[i].inuse = 1; ftdm_log(FTDM_LOG_DEBUG, "Enabling callref slot %d in span %s\n", i, pritap->span->name); } else if (!pritap->pcalls[i].inuse) { crv = tap_pri_get_crv(pritap->pri, callref); ftdm_log(FTDM_LOG_DEBUG, "Recyclying callref slot %d in span %s for callref %d/%p\n", i, pritap->span->name, crv, callref); memset(&pritap->pcalls[i], 0, sizeof(pritap->pcalls[0])); pritap->pcalls[i].callref = callref; pritap->pcalls[i].inuse = 1; } ftdm_mutex_unlock(pritap->pcalls_lock); return &pritap->pcalls[i]; } } ftdm_mutex_unlock(pritap->pcalls_lock); return NULL; }
uint32_t get_unique_suInstId(uint8_t cc_id) { uint32_t suInstId; ftdm_mutex_lock(g_sngisdn_data.ccs[cc_id].request_mutex); suInstId = g_sngisdn_data.ccs[cc_id].last_suInstId; while(1) { if (++suInstId == MAX_INSTID) { suInstId = 1; } if (g_sngisdn_data.ccs[cc_id].active_suInstIds[suInstId] == NULL) { g_sngisdn_data.ccs[cc_id].last_suInstId = suInstId; ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].request_mutex); return suInstId; } } /* Should never reach here */ ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].request_mutex); return 0; }
uint32_t get_unique_suInstId(int16_t cc_id) { uint32_t suInstId; ftdm_assert_return((cc_id > 0 && cc_id <=MAX_VARIANTS), FTDM_FAIL, "Invalid cc_id\n"); ftdm_mutex_lock(g_sngisdn_data.ccs[cc_id].mutex); suInstId = g_sngisdn_data.ccs[cc_id].last_suInstId; while(1) { if (++suInstId == MAX_INSTID) { suInstId = 1; } if (g_sngisdn_data.ccs[cc_id].active_suInstIds[suInstId] == NULL) { g_sngisdn_data.ccs[cc_id].last_suInstId = suInstId; ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].mutex); return suInstId; } } /* Should never reach here */ ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].mutex); return 0; }
void sngisdn_t3_timeout(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Timer T3 expired (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); ftdm_mutex_lock(ftdmchan->mutex); if (ftdm_test_flag(sngisdn_info, FLAG_ACTIVATING)){ /* PHY layer timed-out, need to clear the call */ ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Failed to Wake-Up line (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NO_ROUTE_DESTINATION; ftdm_clear_flag(sngisdn_info, FLAG_ACTIVATING); ftdm_set_flag(sngisdn_info, FLAG_LOCAL_ABORT); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); } ftdm_mutex_unlock(ftdmchan->mutex); }
void sngisdn_delayed_disconnect(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_mutex_lock(ftdmchan->mutex); if (ftdmchan->caller_data.hangup_cause == IN_CCNORTTODEST || ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending delayed DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); sngisdn_snd_disconnect(ftdmchan); if (ftdmchan->caller_data.hangup_cause == IN_CCNORTTODEST) { ftdm_channel_t *close_chan = ftdmchan; ftdm_channel_close(&close_chan); } } ftdm_mutex_unlock(ftdmchan->mutex); return; }
void sngisdn_delayed_release(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_mutex_lock(ftdmchan->mutex); if (ftdm_test_flag(sngisdn_info, FLAG_DELAYED_REL)) { ftdm_clear_flag(sngisdn_info, FLAG_DELAYED_REL); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending delayed RELEASE (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); sngisdn_snd_release(ftdmchan, 1); clear_call_glare_data(sngisdn_info); } else { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Call was already released (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); } ftdm_mutex_unlock(ftdmchan->mutex); return; }
void clear_call_glare_data(sngisdn_chan_data_t *sngisdn_info) { ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_DEBUG, "Clearing glare data (suId:%d suInstId:%u spInstId:%u actv-suInstId:%u actv-spInstId:%u)\n", sngisdn_info->glare.suId, sngisdn_info->glare.suInstId, sngisdn_info->glare.spInstId, sngisdn_info->suInstId, sngisdn_info->spInstId); ftdm_mutex_lock(g_sngisdn_data.ccs[sngisdn_info->glare.suId].mutex); if (sngisdn_info->glare.spInstId != sngisdn_info->spInstId) { g_sngisdn_data.ccs[sngisdn_info->glare.suId].active_spInstIds[sngisdn_info->glare.spInstId]=NULL; } g_sngisdn_data.ccs[sngisdn_info->glare.suId].active_suInstIds[sngisdn_info->glare.suInstId]=NULL; ftdm_mutex_unlock(g_sngisdn_data.ccs[sngisdn_info->glare.suId].mutex); ftdm_clear_flag(sngisdn_info, FLAG_GLARE); memset(&sngisdn_info->glare.setup, 0, sizeof(ConEvnt)); sngisdn_info->glare.suId = 0; sngisdn_info->glare.suInstId = 0; sngisdn_info->glare.spInstId = 0; sngisdn_info->glare.dChan = 0; sngisdn_info->glare.ces = 0; return; }
void sngisdn_rcv_sta_cfm (int16_t suId, uint32_t suInstId, uint32_t spInstId, StaEvnt *staEvnt) { sngisdn_chan_data_t *sngisdn_info; sngisdn_event_data_t *sngisdn_event = NULL; ISDN_FUNC_TRACE_ENTER(__FUNCTION__); /* We sometimes receive a STA CFM after receiving a RELEASE/RELEASE COMPLETE, so we need to lock here in case we are calling clear_call_data at the same time this function is called */ ftdm_mutex_lock(g_sngisdn_data.ccs[suId].mutex); if (!(spInstId && get_ftdmchan_by_spInstId(suId, spInstId, &sngisdn_info) == FTDM_SUCCESS) && !(suInstId && get_ftdmchan_by_suInstId(suId, suInstId, &sngisdn_info) == FTDM_SUCCESS)) { ftdm_log(FTDM_LOG_CRIT, "Could not find matching call suId:%u suInstId:%u spInstId:%u\n", suId, suInstId, spInstId); ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); return; } ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Received STATUS CONFIRM (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); sngisdn_event = ftdm_malloc(sizeof(*sngisdn_event)); ftdm_assert(sngisdn_event != NULL, "Failed to allocate memory\n"); memset(sngisdn_event, 0, sizeof(*sngisdn_event)); sngisdn_event->event_id = SNGISDN_EVENT_STA_CFM; sngisdn_event->sngisdn_info = sngisdn_info; sngisdn_event->suId = suId; sngisdn_event->suInstId = suInstId; sngisdn_event->spInstId = spInstId; memcpy(&sngisdn_event->event.staEvnt, staEvnt, sizeof(*staEvnt)); ftdm_queue_enqueue(((sngisdn_span_data_t*)sngisdn_info->ftdmchan->span->signal_data)->event_queue, sngisdn_event); ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); }
FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_timer_id_t timerid) { ftdm_status_t status = FTDM_FAIL; ftdm_timer_t *timer; ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n"); if (!timerid) { return FTDM_SUCCESS; } ftdm_mutex_lock(sched->mutex); /* look for the timer and destroy it */ for (timer = sched->timers; timer; timer = timer->next) { if (timer->id == timerid) { if (timer == sched->timers) { /* it's the head timer, put a new head */ sched->timers = timer->next; } if (timer->prev) { timer->prev->next = timer->next; } if (timer->next) { timer->next->prev = timer->prev; } ftdm_safe_free(timer); status = FTDM_SUCCESS; break; } } ftdm_mutex_unlock(sched->mutex); return status; }
static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) { ftdm_status_t status; int ok = 1; int waitms = DEFAULT_WAIT_TIME; if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); return FTDM_FAIL; } 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; } if (ftdmchan->state == state) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); return FTDM_FAIL; } if (!ftdmchan->state_completed_interrupt) { status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET); if (status != FTDM_SUCCESS) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, "Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); return status; } } if (ftdmchan->span->state_map) { ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); goto end; } /* basic core state validation (by-passed if the signaling module provides a state_map) */ switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_HANGUP: case FTDM_CHANNEL_STATE_TERMINATING: { ok = 0; switch(state) { case FTDM_CHANNEL_STATE_DOWN: case FTDM_CHANNEL_STATE_BUSY: case FTDM_CHANNEL_STATE_RESTART: ok = 1; break; default: break; } } break; case FTDM_CHANNEL_STATE_UP: { ok = 1; switch(state) { case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_RING: ok = 0; break; default: break; } } break; case FTDM_CHANNEL_STATE_DOWN: { ok = 0; switch(state) { case FTDM_CHANNEL_STATE_DIALTONE: case FTDM_CHANNEL_STATE_COLLECT: case FTDM_CHANNEL_STATE_DIALING: case FTDM_CHANNEL_STATE_RING: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_IDLE: case FTDM_CHANNEL_STATE_GET_CALLERID: case FTDM_CHANNEL_STATE_GENRING: ok = 1; break; default: break; } } break; case FTDM_CHANNEL_STATE_BUSY: { switch(state) { case FTDM_CHANNEL_STATE_UP: ok = 0; break; default: break; } } break; case FTDM_CHANNEL_STATE_RING: { switch(state) { case FTDM_CHANNEL_STATE_UP: ok = 1; break; default: break; } } break; default: break; } end: if (!ok) { ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); goto done; } ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); ftdmchan->last_state = ftdmchan->state; ftdmchan->state = state; ftdmchan->state_status = FTDM_STATE_STATUS_NEW; ftdmchan->history[ftdmchan->hindex].file = file; ftdmchan->history[ftdmchan->hindex].func = func; ftdmchan->history[ftdmchan->hindex].line = line; ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state; ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state; ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms(); ftdmchan->history[ftdmchan->hindex].end_time = 0; ftdmchan->hindex++; if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) { ftdmchan->hindex = 0; } ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE); ftdm_mutex_lock(ftdmchan->span->mutex); ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE); if (ftdmchan->span->pendingchans) { ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan); } ftdm_mutex_unlock(ftdmchan->span->mutex); if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { /* the channel should not block waiting for state processing */ goto done; } if (!waitrq) { /* no waiting was requested */ goto done; } /* let's wait for the state change to be completed by the signaling stack */ ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING); ftdm_mutex_unlock(ftdmchan->mutex); status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms); ftdm_mutex_lock(ftdmchan->mutex); if (status != FTDM_SUCCESS) { ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_BLOCKING); ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); ok = 0; goto done; } done: return ok ? FTDM_SUCCESS : FTDM_FAIL; }
int main(int argc, char *argv[]) { ftdm_span_t *span; ftdm_conf_parameter_t parameters[20]; ftdm_mutex_create(&the_mutex); if (argc < 2) { printf("umm no\n"); exit(1); } ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG); if (ftdm_global_init() != FTDM_SUCCESS) { fprintf(stderr, "Error loading FreeTDM\n"); exit(1); } ftdm_global_configuration(); printf("FreeTDM loaded\n"); if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) { fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]); goto done; } /* testing non-blocking operation */ //ftdm_span_set_blocking_mode(span, FTDM_FALSE); parameters[0].var = "variant"; parameters[0].val = "br"; parameters[1].var = "max_ani"; parameters[1].val = "4"; parameters[2].var = "max_dnis"; parameters[2].val = "4"; parameters[3].var = "logging"; parameters[3].val = "all"; parameters[4].var = NULL; parameters[4].val = NULL; if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, parameters) == FTDM_SUCCESS) { ftdm_span_start(span); } else { fprintf(stderr, "Error starting R2 span\n"); goto done; } running = 1; signal(SIGINT, stop_test); while(running) { ftdm_sleep(20); if (fchan && indication != FTDM_CHANNEL_INDICATE_NONE) { ftdm_channel_t *lchan = NULL; ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE; ftdm_time_t start, stop, diff; ftdm_mutex_lock(the_mutex); ind = indication; indication = FTDM_CHANNEL_INDICATE_NONE; lchan = fchan; ftdm_mutex_unlock(the_mutex); start = ftdm_current_time_in_ms(); ftdm_channel_call_indicate(lchan, ind); stop = ftdm_current_time_in_ms(); diff = stop - start; ftdm_log(FTDM_LOG_DEBUG, "Setting indication %s took %"FTDM_TIME_FMT" ms\n", ftdm_channel_indication2str(ind), diff); } } done: ftdm_global_destroy(); return 0; }