static ftdm_status_t ftdm_pritap_start(ftdm_span_t *span) { ftdm_status_t ret; pritap_t *pritap = span->signal_data; pritap_t *p_pritap = pritap->peerspan->signal_data; if (ftdm_test_flag(pritap, PRITAP_RUNNING)) { return FTDM_FAIL; } ftdm_mutex_create(&pritap->pcalls_lock); ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD); ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD); ftdm_set_flag(pritap, PRITAP_RUNNING); if (p_pritap && ftdm_test_flag(p_pritap, PRITAP_RUNNING)) { /* our peer already started, we're the master */ ftdm_set_flag(pritap, PRITAP_MASTER); } ret = ftdm_thread_create_detached(ftdm_pritap_run, span); if (ret != FTDM_SUCCESS) { return ret; } return ret; }
FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char *func, int line, ftdm_channel_t *fchan) { ftdm_time_t diff; ftdm_channel_state_t state; ftdm_channel_state_t last_state; uint8_t hindex = 0; if (!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) { ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Cannot cancel state change from %s to %s, it was already processed\n", ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state)); return FTDM_FAIL; } if (fchan->state_status != FTDM_STATE_STATUS_NEW) { ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Failed to cancel state change from %s to %s, state is not new anymore\n", ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state)); return FTDM_FAIL; } /* compute the last history index */ hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1); diff = fchan->history[hindex].end_time - fchan->history[hindex].time; /* go back in time and revert the state to the previous state */ state = fchan->state; last_state = fchan->last_state; fchan->state = fchan->last_state; fchan->state_status = FTDM_STATE_STATUS_COMPLETED; fchan->last_state = fchan->history[hindex].last_state; fchan->hindex = hindex; /* clear the state change flag */ ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE); /* ack any pending indications as cancelled */ ftdm_ack_indication(fchan, fchan->indication, FTDM_ECANCELED); /* wake up anyone sleeping waiting for the state change to complete, it won't ever be completed */ if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) { ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING); ftdm_interrupt_signal(fchan->state_completed_interrupt); } /* NOTE * we could potentially also take out the channel from the pendingchans queue, but I believe is easier just leave it, * the only side effect will be a call to ftdm_channel_advance_states() for a channel that has nothing to advance */ ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Cancelled state change from %s to %s in %llums\n", ftdm_channel_state2str(last_state), ftdm_channel_state2str(state), diff); return FTDM_SUCCESS; }
void sngisdn_set_chan_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status) { ftdm_sigmsg_t sig; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*)ftdmchan->span->signal_data; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Signalling link status changed to %s\n", ftdm_signaling_status2str(status)); memset(&sig, 0, sizeof(sig)); sig.chan_id = ftdmchan->chan_id; sig.span_id = ftdmchan->span_id; sig.channel = ftdmchan; sig.event_id = FTDM_SIGEVENT_SIGSTATUS_CHANGED; sig.ev_data.sigstatus.status = status; ftdm_span_send_signal(ftdmchan->span, &sig); if (FTDM_SPAN_IS_BRI(ftdmchan->span)) { sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; if (ftdm_test_flag(sngisdn_info, FLAG_ACTIVATING)) { ftdm_clear_flag(sngisdn_info, FLAG_ACTIVATING); ftdm_sched_timer(signal_data->sched, "delayed_setup", 1000, sngisdn_delayed_setup, (void*) sngisdn_info, NULL); } } return; }
static int __pri_lpwrap_read(struct pri *pri, void *buf, int buflen) { struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); ftdm_size_t len = buflen; int res; ftdm_status_t zst; if ((zst = ftdm_channel_read(spri->dchan, buf, &len)) != FTDM_SUCCESS) { if (zst == FTDM_FAIL) { ftdm_log(FTDM_LOG_CRIT, "span %d D-READ FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); spri->errs++; } else { ftdm_log(FTDM_LOG_CRIT, "span %d D-READ TIMEOUT\n", spri->span->span_id); } ftdm_clear_flag(spri, LPWRAP_PRI_READY); return -1; } spri->errs = 0; res = (int)len; memset(&((unsigned char*)buf)[res],0,2); res+=2; #ifdef IODEBUG { char bb[2048] = { 0 }; print_hex_bytes(buf, res - 2, bb, sizeof(bb)); ftdm_log(FTDM_LOG_DEBUG, "READ %d\n", res-2); } #endif return res; }
void sngisdn_rcv_q921_ind(BdMngmt *status) { ftdm_span_t *ftdmspan; sngisdn_span_data_t *signal_data = g_sngisdn_data.dchans[status->t.usta.lnkNmb].spans[1]; if (!signal_data) { ftdm_log(FTDM_LOG_INFO, "Received q921 status on unconfigured span (lnkNmb:%d)\n", status->t.usta.lnkNmb); return; } ftdmspan = signal_data->ftdm_span; if (!ftdmspan) { ftdm_log(FTDM_LOG_INFO, "Received q921 status on unconfigured span (lnkNmb:%d)\n", status->t.usta.lnkNmb); return; } switch (status->t.usta.alarm.category) { case (LCM_CATEGORY_PROTOCOL): ftdm_log(FTDM_LOG_DEBUG, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n", ftdmspan->name, DECODE_LCM_CATEGORY(status->t.usta.alarm.category), DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event, DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause); if (FTDM_SPAN_IS_BRI(ftdmspan) && (status->t.usta.alarm.event == PROT_ST_DN)) { /* Q.921 link is down - This is a line where the Q.921 stops transmitting after the line goes idle. Do not drop current calls, but set sigstatus do down so that we can try to re-initialize link before trying new outbound calls */ sngisdn_set_span_sig_status(ftdmspan, FTDM_SIG_STATE_DOWN); sngisdn_set_span_avail_rate(ftdmspan, SNGISDN_AVAIL_PWR_SAVING); } break; default: ftdm_log(FTDM_LOG_INFO, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n", ftdmspan->name, DECODE_LCM_CATEGORY(status->t.usta.alarm.category), DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event, DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause); switch (status->t.usta.alarm.event) { case ENTR_CONG: /* Entering Congestion */ ftdm_log(FTDM_LOG_WARNING, "s%d: Entering Congestion\n", ftdmspan->span_id); ftdm_set_flag(ftdmspan, FTDM_SPAN_SUSPENDED); break; case EXIT_CONG: /* Exiting Congestion */ ftdm_log(FTDM_LOG_WARNING, "s%d: Exiting Congestion\n", ftdmspan->span_id); ftdm_clear_flag(ftdmspan, FTDM_SPAN_SUSPENDED); break; } break; } return; }
void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response) { ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED); ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DTMF_DETECT, NULL); sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE; sngisdn_info->transfer_data.response = response; ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Transfer Complete:%s\n", ftdm_transfer_response2str(sngisdn_info->transfer_data.response)); sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_TRANSFER_COMPLETED); ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_RX_BUFFERS, NULL); return; }
void sngisdn_rcv_q921_ind(BdMngmt *status) { ftdm_span_t *ftdmspan; sngisdn_span_data_t *signal_data = g_sngisdn_data.dchans[status->t.usta.lnkNmb].spans[1]; if (!signal_data) { ftdm_log(FTDM_LOG_INFO, "Received q921 status on unconfigured span (lnkNmb:%d)\n", status->t.usta.lnkNmb); return; } ftdmspan = signal_data->ftdm_span; if (!ftdmspan) { ftdm_log(FTDM_LOG_INFO, "Received q921 status on unconfigured span (lnkNmb:%d)\n", status->t.usta.lnkNmb); return; } switch (status->t.usta.alarm.category) { case (LCM_CATEGORY_PROTOCOL): ftdm_log(FTDM_LOG_DEBUG, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n", ftdmspan->name, DECODE_LCM_CATEGORY(status->t.usta.alarm.category), DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event, DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause); break; default: ftdm_log(FTDM_LOG_INFO, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n", ftdmspan->name, DECODE_LCM_CATEGORY(status->t.usta.alarm.category), DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event, DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause); switch (status->t.usta.alarm.event) { case ENTR_CONG: /* Entering Congestion */ ftdm_log(FTDM_LOG_WARNING, "s%d: Entering Congestion\n", ftdmspan->span_id); ftdm_set_flag(ftdmspan, FTDM_SPAN_SUSPENDED); break; case EXIT_CONG: /* Exiting Congestion */ ftdm_log(FTDM_LOG_WARNING, "s%d: Exiting Congestion\n", ftdmspan->span_id); ftdm_clear_flag(ftdmspan, FTDM_SPAN_SUSPENDED); break; } break; } return; }
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); }
static int __pri_lpwrap_write(struct pri *pri, void *buf, int buflen) { struct lpwrap_pri *spri = (struct lpwrap_pri *) pri_get_userdata(pri); ftdm_size_t len = buflen -2; if (ftdm_channel_write(spri->dchan, buf, buflen, &len) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_CRIT, "span %d D-WRITE FAIL! [%s]\n", spri->span->span_id, spri->dchan->last_error); ftdm_clear_flag(spri, LPWRAP_PRI_READY); return -1; } #ifdef IODEBUG { char bb[2048] = { 0 }; print_hex_bytes(buf, buflen - 2, bb, sizeof(bb)); ftdm_log(FTDM_LOG_DEBUG, "WRITE %d\n", (int)buflen-2); } #endif return (int) buflen; }
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; }
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_usrmsg_free(&fchan->usrmsg); 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_PROGRESS_MEDIA) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); ftdm_test_and_set_media(fchan); } else if (state == FTDM_CHANNEL_STATE_UP) { ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS); ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED); ftdm_test_and_set_media(fchan); } else if (state == FTDM_CHANNEL_STATE_DIALING) { ftdm_sigmsg_t msg; memset(&msg, 0, sizeof(msg)); msg.channel = fchan; msg.event_id = FTDM_SIGEVENT_DIALING; ftdm_span_send_signal(fchan->span, &msg); } else if (state == FTDM_CHANNEL_STATE_HANGUP) { ftdm_set_echocancel_call_end(fchan); } /* MAINTENANCE WARNING * we're assuming an indication performed * via state change will involve a single state change */ ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS); 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); if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) { ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING); ftdm_interrupt_signal(fchan->state_completed_interrupt); } return FTDM_SUCCESS; }
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; }
void sngisdn_process_rel_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; RelEvnt *relEvnt = &sngisdn_event->event.relEvnt; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing RELEASE/RELEASE COMPLETE (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); if ((suInstId && (sngisdn_info->glare.suInstId == suInstId)) || (spInstId && (sngisdn_info->glare.spInstId == spInstId))) { /* This hangup is for a glared saved call */ ftdm_clear_flag(sngisdn_info, FLAG_DELAYED_REL); clear_call_glare_data(sngisdn_info); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } /* check whether the ftdm channel is in a state to accept a call */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* go to DOWN */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); break; case FTDM_CHANNEL_STATE_DOWN: /* do nothing, just drop the message */ break; case FTDM_CHANNEL_STATE_DIALING: /* Remote side rejected our SETUP message on outbound call */ if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP)) { sng_isdn_set_avail_rate(ftdmchan->span, SNGISDN_AVAIL_DOWN); } /* fall-through */ case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_UP: case FTDM_CHANNEL_STATE_RING: /* If we previously had a glare on this channel, this RELEASE could be for the previous call. Confirm whether call_data has not changed while we were waiting for ftdmchan->mutex by comparing suInstId's */ if (((sngisdn_chan_data_t*)ftdmchan->call_data)->suInstId == suInstId || ((sngisdn_chan_data_t*)ftdmchan->call_data)->spInstId == spInstId) { if (relEvnt->causeDgn[0].eh.pres && relEvnt->causeDgn[0].causeVal.pres) { ftdmchan->caller_data.hangup_cause = relEvnt->causeDgn[0].causeVal.val; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "cause:%d\n", ftdmchan->caller_data.hangup_cause); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "RELEASE COMPLETE did not have a cause code\n"); ftdmchan->caller_data.hangup_cause = 0; } sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); } else { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "RELEASE was for previous call (suInstId:%u spInstId:%u)\n", suInstId, spInstId); } break; case FTDM_CHANNEL_STATE_COLLECT: case FTDM_CHANNEL_STATE_GET_CALLERID: ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); break; case FTDM_CHANNEL_STATE_TERMINATING: if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE) && sngisdn_info->glare.suInstId != suInstId) { /* This release if for the outbound call that we already started clearing */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Received RELEASE for local glared call\n"); sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Received release before we could clear local call\n"); /* FS core took too long to respond to the SIG STOP event */ sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); /* set abort flag so that we do not transmit another release complete on this channel once FS core is done */ } break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Received RELEASE in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); break; } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
/* Remote side transmit a SETUP */ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; int16_t dChan = sngisdn_event->dChan; uint8_t ces = sngisdn_event->ces; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ConEvnt *conEvnt = &sngisdn_event->event.conEvnt; ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_DEBUG, "Processing SETUP (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_DOWN: /* Proper state to receive a SETUP */ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE) || ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Received SETUP but channel is in USE, saving call for later processing\n"); /* the flag the channel as having a collision */ sngisdn_set_flag(sngisdn_info, FLAG_GLARE); /* save the SETUP for processing once the channel has gone to DOWN */ memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt)); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */ sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; break; } sngisdn_info->suInstId = get_unique_suInstId(suId); sngisdn_info->spInstId = spInstId; /* If this is a glared call that was previously saved, we moved all the info to the current call, so clear the glared saved data */ if (sngisdn_info->glare.spInstId == spInstId) { clear_call_glare_data(sngisdn_info); } ftdm_mutex_lock(g_sngisdn_data.ccs[suId].mutex); g_sngisdn_data.ccs[suId].active_suInstIds[sngisdn_info->suInstId] = sngisdn_info; ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP && signal_data->signalling == SNGISDN_SIGNALING_NET) { sngisdn_info->ces = ces; } /* try to open the channel */ if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Failed to open channel"); sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_REL); ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE; ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); break; } #if 0 /* Export ftdmchan variables here if we need to */ ftdm_channel_add_var(ftdmchan, "isdn_specific_var", "1"); ftdm_channel_add_var(ftdmchan, "isdn_crap", "morecrap"); ftdm_channel_add_var(ftdmchan, "isdn_stuff", "s"); ftdm_channel_add_var(ftdmchan, "isdn_d", "asdsadasdasdsad"); #endif /* Fill in call information */ cpy_calling_num_from_stack(&ftdmchan->caller_data, &conEvnt->cgPtyNmb); cpy_called_num_from_stack(&ftdmchan->caller_data, &conEvnt->cdPtyNmb); cpy_calling_name_from_stack(&ftdmchan->caller_data, &conEvnt->display); if (conEvnt->bearCap[0].eh.pres) { ftdmchan->caller_data.bearer_layer1 = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].usrInfoLyr1Prot.val); ftdmchan->caller_data.bearer_capability = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].infoTranCap.val); } if (signal_data->switchtype == SNGISDN_SWITCH_NI2) { if (conEvnt->shift11.eh.pres && conEvnt->ni2OctStr.eh.pres) { if (conEvnt->ni2OctStr.str.len == 4 && conEvnt->ni2OctStr.str.val[0] == 0x37) { snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", conEvnt->ni2OctStr.str.val[3]); } } if (signal_data->facility == SNGISDN_OPT_TRUE && conEvnt->facilityStr.eh.pres) { /* Verify whether the Caller Name will come in a subsequent FACILITY message */ uint16_t ret_val; char retrieved_str[255]; ret_val = sng_isdn_retrieve_facility_caller_name(conEvnt->facilityStr.facilityStr.val, conEvnt->facilityStr.facilityStr.len, retrieved_str); /* return values for "sng_isdn_retrieve_facility_information_following": If there will be no information following, or fails to decode IE, returns -1 If there will be no information following, but current FACILITY IE contains a caller name, returns 0 If there will be information following, returns 1 */ if (ret_val == 1) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Expecting Caller name in FACILITY\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_GET_CALLERID); /* Launch timer in case we never get a FACILITY msg */ if (signal_data->facility_timeout) { ftdm_sched_timer(signal_data->sched, "facility_timeout", signal_data->facility_timeout, sngisdn_facility_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_TIMER_FACILITY]); } break; } else if (ret_val == 0) { strcpy(ftdmchan->caller_data.cid_name, retrieved_str); } } } if (signal_data->overlap_dial == SNGISDN_OPT_TRUE && !conEvnt->sndCmplt.eh.pres) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); } else { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); } break; case FTDM_CHANNEL_STATE_TERMINATING: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Processing SETUP in TERMINATING state, saving SETUP info for later processing\n"); ftdm_assert(!sngisdn_test_flag(sngisdn_info, FLAG_GLARE), "Trying to save GLARE info, but we already had a glare\n"); sngisdn_set_flag(sngisdn_info, FLAG_GLARE); /* save the SETUP for processing once the channel has gone to DOWN */ memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt)); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */ sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; break; case FTDM_CHANNEL_STATE_DIALING: /* glare */ if (signal_data->signalling == SNGISDN_SIGNALING_NET) { /* Save inbound call info so we can send a RELEASE when this channel goes to a different state */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Processing SETUP in DIALING state, rejecting inbound call\n"); sngisdn_set_flag(sngisdn_info, FLAG_DELAYED_REL); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = get_unique_suInstId(suId); sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; ftdmchan->caller_data.hangup_cause = 0x2C; /* Channel requested not available */ ftdm_sched_timer(signal_data->sched, "delayed_release", 1, sngisdn_delayed_release, (void*) sngisdn_info, NULL); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Processing SETUP in DIALING state, saving SETUP info for later processing\n"); /* the flag the channel as having a collision */ ftdm_assert(!sngisdn_test_flag(sngisdn_info, FLAG_GLARE), "Trying to save GLARE info, but we already had a glare"); sngisdn_set_flag(sngisdn_info, FLAG_GLARE); /* save the SETUP for processing once the channel has gone to DOWN */ memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt)); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */ sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); } break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing SETUP in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); break; } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
static void *ftdm_pritap_run(ftdm_thread_t *me, void *obj) { ftdm_span_t *span = (ftdm_span_t *) obj; ftdm_span_t *peer = NULL; pritap_t *pritap = span->signal_data; pritap_t *p_pritap = NULL; pri_event *event = NULL; struct pollfd dpoll[2]; int rc = 0; ftdm_log(FTDM_LOG_DEBUG, "Tapping PRI thread started on span %s\n", span->name); pritap->span = span; ftdm_set_flag(span, FTDM_SPAN_IN_THREAD); if (ftdm_channel_open(span->span_id, pritap->dchan->chan_id, &pritap->dchan) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "Failed to open D-channel for span %s\n", span->name); goto done; } if ((pritap->pri = pri_new_cb(pritap->dchan->sockfd, PRI_NETWORK, PRI_SWITCH_NI2, pri_io_read, pri_io_write, pritap))) { pri_set_debug(pritap->pri, pritap->debug); } else { ftdm_log(FTDM_LOG_CRIT, "Failed to create tapping PRI\n"); goto done; } /* The last span starting runs the show ... * This simplifies locking and avoid races by having multiple threads for a single tapped link * Since both threads really handle a single tapped link there is no benefit on multi-threading, just complications ... */ peer = pritap->peerspan; p_pritap = peer->signal_data; if (!ftdm_test_flag(pritap, PRITAP_MASTER)) { ftdm_log(FTDM_LOG_DEBUG, "Running dummy thread on span %s\n", span->name); while (ftdm_running() && !ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) { poll(NULL, 0, 100); } } else { memset(&dpoll, 0, sizeof(dpoll)); dpoll[0].fd = pritap->dchan->sockfd; dpoll[1].fd = p_pritap->dchan->sockfd; ftdm_log(FTDM_LOG_DEBUG, "Master tapping thread on span %s (fd1=%d, fd2=%d)\n", span->name, pritap->dchan->sockfd, p_pritap->dchan->sockfd); while (ftdm_running() && !ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) { pritap_check_state(span); pritap_check_state(peer); dpoll[0].revents = 0; dpoll[0].events = POLLIN; dpoll[1].revents = 0; dpoll[1].events = POLLIN; rc = poll(&dpoll[0], 2, 10); if (rc < 0) { if (errno == EINTR) { ftdm_log(FTDM_LOG_DEBUG, "D-channel waiting interrupted, continuing ...\n"); continue; } ftdm_log(FTDM_LOG_ERROR, "poll failed: %s\n", strerror(errno)); continue; } pri_schedule_run(pritap->pri); pri_schedule_run(p_pritap->pri); pritap_check_state(span); pritap_check_state(peer); if (rc) { if (dpoll[0].revents & POLLIN) { event = pri_read_event(pritap->pri); if (event) { handle_pri_passive_event(pritap, event); pritap_check_state(span); } } if (dpoll[1].revents & POLLIN) { event = pri_read_event(p_pritap->pri); if (event) { handle_pri_passive_event(p_pritap, event); pritap_check_state(peer); } } } } } done: ftdm_log(FTDM_LOG_DEBUG, "Tapping PRI thread ended on span %s\n", span->name); ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD); ftdm_clear_flag(pritap, PRITAP_RUNNING); ftdm_clear_flag(pritap, PRITAP_MASTER); return NULL; }