ftdm_status_t get_calling_subaddr(ftdm_channel_t *ftdmchan, CgPtySad *cgPtySad)
{
	char subaddress[100];
	
	if (cgPtySad->eh.pres != PRSNT_NODEF) {
		return FTDM_FAIL;
	}
	memset(subaddress, 0, sizeof(subaddress));
	if(cgPtySad->sadInfo.len >= sizeof(subaddress)) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Calling Party Subaddress exceeds local size limit (len:%d max:%d)\n", cgPtySad->sadInfo.len, sizeof(subaddress));
		cgPtySad->sadInfo.len = sizeof(subaddress)-1;
	}
		
	memcpy(subaddress, (char*)cgPtySad->sadInfo.val, cgPtySad->sadInfo.len);
	subaddress[cgPtySad->sadInfo.len] = '\0';
	sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.calling_subaddr", subaddress);
	return FTDM_SUCCESS;
}
ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf)
{
	ftdm_status_t status = FTDM_SUCCESS;
	sngisdn_chan_data_t  *sngisdn_info = ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	char *dtmf_digits = sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits;
	ftdm_size_t dtmf_digits_len = strlen(dtmf_digits);

	dtmf_digits_len += sprintf(&dtmf_digits[dtmf_digits_len], "%s", dtmf);
	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer response digits:%s\n", dtmf_digits);
	if (dtmf_digits_len == 3) {
		if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF)) {
			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_CP_DROP_OFF;
		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED)) {
			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED;
		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_OK)) {
			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_OK;
		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM)) {
			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_NUM;
		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND)) {
			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_COMMAND;
		} else {
			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID;
		}
		if (signal_data->transfer_timeout) {
			ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_CHAN_TIMER_ATT_TRANSFER]);
		}

		if (sngisdn_info->transfer_data.response == FTDM_TRANSFER_RESPONSE_OK &&
			sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
			sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC);
			ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING;
			sngisdn_snd_disconnect(ftdmchan);
		}
		/* Network side will send disconnect in case of NO-DATA Transfer */
		att_courtesy_transfer_complete(sngisdn_info, sngisdn_info->transfer_data.response);
	}

	if (signal_data->att_remove_dtmf != SNGISDN_OPT_FALSE) {
		/* If we return FTDM_BREAK, dtmf event is not queue'ed to user */
		status = FTDM_BREAK;
	}
	return status;
}
ftdm_status_t set_calling_subaddr(ftdm_channel_t *ftdmchan, CgPtySad *cgPtySad)
{
	const char* clg_subaddr = NULL;
	clg_subaddr = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "isdn.calling_subaddr");
	if ((clg_subaddr != NULL) && (*clg_subaddr)) {
		unsigned len = strlen (clg_subaddr);
		cgPtySad->eh.pres = PRSNT_NODEF;
		cgPtySad->typeSad.pres = 1;
		cgPtySad->typeSad.val = 0; /* NSAP */
		cgPtySad->oddEvenInd.pres = 1;
		cgPtySad->oddEvenInd.val = 0;

		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending Calling Party Subaddress:%s\n", clg_subaddr);
		cgPtySad->sadInfo.pres = 1;
		cgPtySad->sadInfo.len = len;
		memcpy(cgPtySad->sadInfo.val, clg_subaddr, len);
	}
	return FTDM_SUCCESS;
}
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__);
}
Exemple #5
0
static int pri_io_write(struct pri *pri, void *buf, int buflen)
{
    pritap_t *pritap = pri_get_userdata(pri);
    ftdm_size_t len = buflen - 2;

    /* libpri passive q921 raw dump does not work for all frames */
    if (pritap->debug & PRI_DEBUG_Q921_RAW) {
        char hbuf[2048] = { 0 };

        print_hex_bytes(buf, len, hbuf, sizeof(hbuf));
        ftdm_log_chan(pritap->dchan, FTDM_LOG_DEBUG, "WRITE %"FTDM_SIZE_FMT"\n%s\n", len, hbuf);
    }

    if (ftdm_channel_write(pritap->dchan, buf, buflen, &len) != FTDM_SUCCESS) {
        ftdm_log(FTDM_LOG_CRIT, "span %d D channel write failed! [%s]\n", pritap->span->span_id, pritap->dchan->last_error);
        return -1;
    }

    return (int)buflen;
}
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;
}
FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan)
{
	ftdm_channel_state_t state;

	ftdm_assert_return(fchan->span->state_processor, FTDM_FAIL, "Cannot process states without a state processor!\n");
	
	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));
		fchan->span->state_processor(fchan);
		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() */
			fchan->state_status = FTDM_STATE_STATUS_PROCESSED;
		}		
	}

	return FTDM_SUCCESS;
}
/* No one calls this function yet, but it has been implemented to complement TXA messages */
void ft_to_sngss7_itx (ftdm_channel_t * ftdmchan)
{
#ifndef SANGOMA_SPIROU
    SS7_FUNC_TRACE_ENTER (__FUNCTION__);
    ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "ITX message not supported!, please update your libsng_ss7\n");
#else
    const char* var = NULL;
    sngss7_chan_data_t	*sngss7_info = ftdmchan->call_data;
    SiCnStEvnt itx;

    SS7_FUNC_TRACE_ENTER (__FUNCTION__);

    memset (&itx, 0x0, sizeof (itx));

    itx.msgNum.eh.pres = PRSNT_NODEF;
    itx.msgNum.msgNum.pres = PRSNT_NODEF;
    var = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "ss7_itx_msg_num");
    if (!ftdm_strlen_zero(var)) {
        itx.msgNum.msgNum.val = atoi(var);
    } else {
        itx.msgNum.msgNum.val = 0x1;
    }

    itx.chargUnitNum.eh.pres = PRSNT_NODEF;
    itx.chargUnitNum.chargUnitNum.pres = PRSNT_NODEF;
    var = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "ss7_itx_charge_unit");
    if (!ftdm_strlen_zero(var)) {
        itx.chargUnitNum.chargUnitNum.val = atoi(var);
    } else {
        itx.chargUnitNum.chargUnitNum.val = 0x1;
    }

    ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "ITX Charging Unit:%d Msg Num:%d\n", itx.chargUnitNum.chargUnitNum.val, itx.msgNum.msgNum.val);
    sng_cc_con_status  (1, sngss7_info->suInstId, sngss7_info->spInstId, sngss7_info->circuit->id, &itx, CHARGE_UNIT);

    SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Tx ITX\n", sngss7_info->circuit->cic);
#endif
    SS7_FUNC_TRACE_EXIT (__FUNCTION__);
    return;
}
ftdm_status_t get_callref(ftdm_channel_t *ftdmchan, BCCallRef* callRef)
{
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;

	if (signal_data->raw_trace_q931) {
		if (callRef->eh.pres != PRSNT_NODEF || callRef->reference.pres != PRSNT_NODEF) {
			/* Netborder only supports BRI, so we only care for BRI for now */
			if (FTDM_SPAN_IS_BRI(ftdmchan->span) && !sngisdn_info->call_ref) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to obtain call reference\n");
			}
			return FTDM_FAIL;
		}		
		if (FTDM_SPAN_IS_BRI(ftdmchan->span)) {
			sngisdn_info->call_ref = 0x7F & callRef->reference.val;
		} else {
			sngisdn_info->call_ref = 0x7FFF & callRef->reference.val;
		}
		
		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Call reference:%04x\n", sngisdn_info->call_ref);
	}
	return FTDM_SUCCESS;
}
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__);
}
void sngisdn_set_chan_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status)
{
	ftdm_sigmsg_t sig;
	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;
		sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*)ftdmchan->span->signal_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;
}
Exemple #13
0
/**
 * \brief Initialises a range of wanpipe channels
 * \param span FreeTDM span
 * \param spanno Wanpipe span number
 * \param start Initial wanpipe channel number
 * \param end Final wanpipe channel number
 * \param type FreeTDM channel type
 * \param name FreeTDM span name
 * \param number FreeTDM span number
 * \param cas_bits CAS bits
 * \return number of spans configured
 */
static unsigned wp_open_range(ftdm_span_t *span, unsigned spanno, unsigned start, unsigned end, ftdm_chan_type_t type, char *name, char *number, unsigned char cas_bits)
{
	unsigned configured = 0, x;
#ifdef LIBSANGOMA_VERSION
	sangoma_status_t sangstatus;
	sangoma_wait_obj_t *sangoma_wait_obj;
#endif

	if (type == FTDM_CHAN_TYPE_CAS) {
		ftdm_log(FTDM_LOG_DEBUG, "Configuring Wanpipe CAS channels with abcd == 0x%X\n", cas_bits);
	}	
	for(x = start; x < end; x++) {
		ftdm_channel_t *chan;
		ftdm_socket_t sockfd = FTDM_INVALID_SOCKET;
		const char *dtmf = "none";
		if (!strncasecmp(span->name, "smg_prid_nfas", 8) && span->trunk_type == FTDM_TRUNK_T1 && x == 24) {
#ifdef LIBSANGOMA_VERSION
			sockfd = __tdmv_api_open_span_chan(spanno, x);
#else
			ftdm_log(FTDM_LOG_ERROR, "span %d channel %d cannot be configured as smg_prid_nfas, you need to compile freetdm with newer libsangoma\n", spanno, x);
#endif
		} else {
			sockfd = tdmv_api_open_span_chan(spanno, x);
		}

		if (sockfd == FTDM_INVALID_SOCKET) {
			ftdm_log(FTDM_LOG_ERROR, "Failed to open wanpipe device span %d channel %d\n", spanno, x);
			continue;
		}
		
		if (ftdm_span_add_channel(span, sockfd, type, &chan) == FTDM_SUCCESS) {
			wanpipe_tdm_api_t tdm_api;
			memset(&tdm_api, 0, sizeof(tdm_api));
#ifdef LIBSANGOMA_VERSION
			/* we need SANGOMA_DEVICE_WAIT_OBJ_SIG and not SANGOMA_DEVICE_WAIT_OBJ alone because we need to call 
			 * sangoma_wait_obj_sig to wake up any I/O waiters when closing the channel (typically on ftdm shutdown)
			 * this adds an extra pair of file descriptors to the waitable object
			 * */
			sangstatus = sangoma_wait_obj_create(&sangoma_wait_obj, sockfd, SANGOMA_DEVICE_WAIT_OBJ_SIG);
			if (sangstatus != SANG_STATUS_SUCCESS) {
				ftdm_log(FTDM_LOG_ERROR, "failure create waitable object for s%dc%d\n", spanno, x);
				continue;
			}
			chan->mod_data = sangoma_wait_obj;
#endif
			
			chan->physical_span_id = spanno;
			chan->physical_chan_id = x;
			chan->rate = 8000;
			
			if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO || type == FTDM_CHAN_TYPE_B) {
				int err;
				
				dtmf = "software";

				err = sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api);

				if (tdm_api.wp_tdm_cmd.hw_tdm_coding) {
					chan->native_codec = chan->effective_codec = FTDM_CODEC_ALAW;
				} else {
					chan->native_codec = chan->effective_codec = FTDM_CODEC_ULAW;
				}

				err = sangoma_tdm_get_hw_dtmf(chan->sockfd, &tdm_api);
				if (err > 0) {
					ftdm_channel_set_feature(chan, FTDM_CHANNEL_FEATURE_DTMF_DETECT);
					dtmf = "hardware";
				}
			}

#ifdef LIBSANGOMA_VERSION
			if (type == FTDM_CHAN_TYPE_FXS) {
				if (sangoma_tdm_disable_ring_trip_detect_events(chan->sockfd, &tdm_api)) {
					/* we had problems of on-hook/off-hook detection due to how ring trip events were handled
					 * if this fails, I believe we will still work ok as long as we dont handle them incorrectly */
					ftdm_log(FTDM_LOG_WARNING, "Failed to disable ring trip events in channel s%dc%d\n", spanno, x);
				}
			}
#endif
#if 0
            if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO) {
                /* Enable FLASH/Wink Events */
                int err=sangoma_set_rm_rxflashtime(chan->sockfd, &tdm_api, wp_globals.flash_ms);
                if (err == 0) {
			        ftdm_log(FTDM_LOG_ERROR, "flash enabled s%dc%d\n", spanno, x);
                } else {
			        ftdm_log(FTDM_LOG_ERROR, "flash disabled s%dc%d\n", spanno, x);
                }
            }
#endif

			if (type == FTDM_CHAN_TYPE_CAS || type == FTDM_CHAN_TYPE_EM) {
#ifdef LIBSANGOMA_VERSION
				sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,chan->physical_chan_id, wanpipe_swap_bits(cas_bits));

				/* this should probably be done for old libsangoma but I am not sure if the API is available and I'm lazy to check,
				   The poll rate is hard coded to 100 per second (done in the driver, is the max rate of polling allowed by wanpipe)
				 */
				if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) {
					ftdm_log(FTDM_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd);
					continue;
				}
				/* probably done by the driver but lets write defensive code this time */
				sangoma_flush_bufs(chan->sockfd, &tdm_api);
#else
				/* 
				 * With wanpipe 3.4.4.2 I get failure even though the events are enabled, /var/log/messages said:
				 * wanpipe4: WARNING: Event type 9 is already pending!
				 * wanpipe4: Failed to add new fe event 09 ch_map=FFFFFFFF!
				 * may be we should not send an error until that is fixed in the driver
				 */
				if (sangoma_tdm_enable_rbs_events(chan->sockfd, &tdm_api, 100)) {
					ftdm_log(FTDM_LOG_ERROR, "Failed to enable RBS/CAS events in device %d:%d fd:%d\n", chan->span_id, chan->chan_id, sockfd);
				}
				/* probably done by the driver but lets write defensive code this time */
				sangoma_tdm_flush_bufs(chan->sockfd, &tdm_api);
				sangoma_tdm_write_rbs(chan->sockfd,&tdm_api, wanpipe_swap_bits(cas_bits));
#endif
			}
			
			if (!ftdm_strlen_zero(name)) {
				ftdm_copy_string(chan->chan_name, name, sizeof(chan->chan_name));
			}

			if (!ftdm_strlen_zero(number)) {
				ftdm_copy_string(chan->chan_number, number, sizeof(chan->chan_number));
			}
			configured++;
			ftdm_log_chan(chan, FTDM_LOG_INFO, "configured wanpipe device s%dc%d as FreeTDM channel %d:%d fd:%d DTMF: %s\n",
				spanno, x, chan->span_id, chan->chan_id, sockfd, dtmf);

		} else {
			ftdm_log(FTDM_LOG_ERROR, "ftdm_span_add_channel failed for wanpipe span %d channel %d\n", spanno, x);
		}
	}
	
	return configured;
}
Exemple #14
0
static void *ftdm_gsm_run(ftdm_thread_t *me, void *obj)
{

	ftdm_log(FTDM_LOG_INFO,"ftdm_gsm_run\r\n");

	ftdm_channel_t *ftdmchan = NULL;
	ftdm_span_t *span = (ftdm_span_t *) obj;
	ftdm_gsm_span_data_t *gsm_data = NULL;
	ftdm_interrupt_t *data_sources[2] = {NULL, NULL};
	int waitms = 0;
	
	gsm_data = span->signal_data;

	ftdm_assert_return(gsm_data != NULL, NULL, "No gsm data attached to span\n");

	ftdm_log(FTDM_LOG_DEBUG, "GSM monitor thread for span %s started\n", span->name);
	if (!gsm_data->dchan || ftdm_channel_open_chan(gsm_data->dchan) != FTDM_SUCCESS) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to open GSM d-channel of span %s!\n", span->name);
		gsm_data->dchan = NULL;
		goto done;
	}




	while (ftdm_running()) {

		wat_span_run(span->span_id);

		waitms = wat_span_schedule_next(span->span_id);
		if (waitms > GSM_POLL_INTERVAL_MS) {
			waitms = GSM_POLL_INTERVAL_MS;
		}

/////////////////////
		

		{
			ftdm_wait_flag_t flags = FTDM_READ | FTDM_EVENTS;
			ftdm_status_t status = ftdm_channel_wait(gsm_data->dchan, &flags, waitms);
			
	
			/* double check that this channel has a state change pending */
			ftdm_channel_lock(gsm_data->bchan);
			ftdm_channel_advance_states(gsm_data->bchan);
					
			if(FTDM_SUCCESS == status ) {
		
				if(flags &FTDM_READ ) {
					char buffer[1025];
					int n = 0, m = 0;
					memset(buffer, 0, sizeof(buffer));

					n = read_channel(gsm_data->dchan, buffer, sizeof(buffer)-1);
					m = strlen(buffer);	
					wat_span_process_read(span->span_id, buffer, m);
#if 	 LOG_SIG_DATA
						printf("<<======================= incomming data len = %d, %s\r\n", n, buffer);
#endif

				}
			}
			
			ftdm_channel_advance_states(gsm_data->bchan);
			
			ftdm_channel_unlock(gsm_data->bchan);
			

		}


		

		ftdm_span_trigger_signals(span);


	}

done:
	if (data_sources[0]) {
		ftdm_interrupt_destroy(&data_sources[0]);
	}

	ftdm_log(FTDM_LOG_DEBUG, "GSM thread ending.\n");

	return NULL;
}
Exemple #15
0
static ftdm_status_t ftdm_pritap_sig_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size)
{
    ftdm_status_t status;
    fio_codec_t codec_func;
    ftdm_channel_t *peerchan = ftdmchan->call_data;
    pritap_t *pritap = ftdmchan->span->signal_data;
    int16_t chanbuf[size];
    int16_t peerbuf[size];
    int16_t mixedbuf[size];
    int i = 0;
    ftdm_size_t sizeread = size;

    if (!FTDM_IS_VOICE_CHANNEL(ftdmchan) || !ftdmchan->call_data) {
        return FTDM_SUCCESS;
    }

    if (pritap->mixaudio == PRITAP_MIX_SELF) {
        return FTDM_SUCCESS;
    }

    if (pritap->mixaudio == PRITAP_MIX_PEER) {
        /* start out by clearing the self audio to make sure we don't return audio we were
         * not supposed to in an error condition  */
        memset(data, FTDM_SILENCE_VALUE(ftdmchan), size);
    }

    if (ftdmchan->native_codec != peerchan->native_codec) {
        ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Invalid peer channel with format %d, ours = %d\n",
                      peerchan->native_codec, ftdmchan->native_codec);
        return FTDM_FAIL;
    }

    memcpy(chanbuf, data, size);
    status = peerchan->fio->read(peerchan, peerbuf, &sizeread);
    if (status != FTDM_SUCCESS) {
        ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to read from peer channel!\n");
        return FTDM_FAIL;
    }
    if (sizeread != size) {
        ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "read from peer channel only %"FTDM_SIZE_FMT" bytes!\n", sizeread);
        return FTDM_FAIL;
    }

    if (pritap->mixaudio == PRITAP_MIX_PEER) {
        /* only the peer audio is requested */
        memcpy(data, peerbuf, size);
        return FTDM_SUCCESS;
    }

    codec_func = peerchan->native_codec == FTDM_CODEC_ULAW ? fio_ulaw2slin : peerchan->native_codec == FTDM_CODEC_ALAW ? fio_alaw2slin : NULL;
    if (codec_func) {
        sizeread = size;
        codec_func(chanbuf, sizeof(chanbuf), &sizeread);
        sizeread = size;
        codec_func(peerbuf, sizeof(peerbuf), &sizeread);
    }

    for (i = 0; i < size; i++) {
        mixedbuf[i] = ftdm_saturated_add(chanbuf[i], peerbuf[i]);
    }

    codec_func = peerchan->native_codec == FTDM_CODEC_ULAW ? fio_slin2ulaw : peerchan->native_codec == FTDM_CODEC_ALAW ? fio_slin2alaw : NULL;
    if (codec_func) {
        size = sizeof(mixedbuf);
        codec_func(mixedbuf, size, &size);
    }
    memcpy(data, mixedbuf, size);
    return FTDM_SUCCESS;
}
Exemple #16
0
static void handle_pri_passive_event(pritap_t *pritap, pri_event *e)
{
    passive_call_t *pcall = NULL;
    passive_call_t *peerpcall = NULL;
    ftdm_channel_t *fchan = NULL;
    ftdm_channel_t *peerfchan = NULL;
    int layer1, transcap = 0;
    int crv = 0;
    pritap_t *peertap = pritap->peerspan->signal_data;

    switch (e->e) {

    case PRI_EVENT_RING:
        /* we cannot use ftdm_channel_t because we still dont know which channel will be used
         * (ie, flexible channel was requested), thus, we need our own list of call references */
        crv = tap_pri_get_crv(pritap->pri, e->ring.call);
        ftdm_log(FTDM_LOG_DEBUG, "Ring on channel %s:%d:%d with callref %d\n",
                 pritap->span->name, PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), crv);
        pcall = tap_pri_get_pcall_bycrv(pritap, crv);
        if (pcall) {
            ftdm_log(FTDM_LOG_WARNING, "There is a call with callref %d already, ignoring duplicated ring event\n", crv);
            break;
        }

        /* Try to get a recycled call (ie, e->ring.call is a call that the PRI stack allocated previously and then
         * re-used for the next RING event because we did not destroy it fast enough) */
        pcall = tap_pri_get_pcall(pritap, e->ring.call);
        if (!pcall) {
            /* ok so the call is really not known to us, let's get a new one */
            pcall = tap_pri_get_pcall(pritap, NULL);
            if (!pcall) {
                ftdm_log(FTDM_LOG_ERROR, "Failed to get a free passive PRI call slot for callref %d, this is a bug!\n", crv);
                break;
            }
        }
        pcall->callref = e->ring.call;
        ftdm_set_string(pcall->callingnum.digits, e->ring.callingnum);
        ftdm_set_string(pcall->callingani.digits, e->ring.callingani);
        ftdm_set_string(pcall->callednum.digits, e->ring.callednum);
        ftdm_set_string(pcall->callingname, e->ring.callingname);
        break;

    case PRI_EVENT_PROGRESS:
        crv = tap_pri_get_crv(pritap->pri, e->proceeding.call);
        ftdm_log(FTDM_LOG_DEBUG, "Progress on channel %s:%d:%d with callref %d\n",
                 pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv);
        break;

    case PRI_EVENT_PROCEEDING:
        crv = tap_pri_get_crv(pritap->pri, e->proceeding.call);
        /* at this point we should know the real b chan that will be used and can therefore proceed to notify about the call, but
         * only if a couple of call tests are passed first */
        ftdm_log(FTDM_LOG_DEBUG, "Proceeding on channel %s:%d:%d with callref %d\n",
                 pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv);

        /* check that we already know about this call in the peer PRI (which was the one receiving the PRI_EVENT_RING event) */
        if (!(pcall = tap_pri_get_pcall_bycrv(peertap, crv))) {
            ftdm_log(FTDM_LOG_DEBUG,
                     "ignoring proceeding in channel %s:%d:%d for callref %d since we don't know about it\n",
                     pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv);
            break;
        }
        if (pcall->proceeding) {
            ftdm_log(FTDM_LOG_DEBUG, "Ignoring duplicated proceeding with callref %d\n", crv);
            break;
        }
        pcall->proceeding = 1;

        /* This call should not be known to this PRI yet ... */
        if ((peerpcall = tap_pri_get_pcall_bycrv(pritap, crv))) {
            ftdm_log(FTDM_LOG_ERROR,
                     "ignoring proceeding in channel %s:%d:%d for callref %d, dup???\n",
                     pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv);
            break;
        }

        /* Check if the call pointer is being recycled */
        peerpcall = tap_pri_get_pcall(pritap, e->proceeding.call);
        if (!peerpcall) {
            peerpcall = tap_pri_get_pcall(pritap, NULL);
            if (!peerpcall) {
                ftdm_log(FTDM_LOG_ERROR, "Failed to get a free peer PRI passive call slot for callref %d in span %s, this is a bug!\n",
                         crv, pritap->span->name);
                break;
            }
            peerpcall->callref = e->proceeding.call;
        }

        /* check that the layer 1 and trans capability are supported */
        layer1 = pri_get_layer1(peertap->pri, pcall->callref);
        transcap = pri_get_transcap(peertap->pri, pcall->callref);

        if (PRI_LAYER_1_ULAW != layer1 && PRI_LAYER_1_ALAW != layer1) {
            ftdm_log(FTDM_LOG_NOTICE, "Not monitoring callref %d with unsupported layer 1 format %d\n", crv, layer1);
            break;
        }

        if (transcap != PRI_TRANS_CAP_SPEECH && transcap != PRI_TRANS_CAP_3_1K_AUDIO && transcap != PRI_TRANS_CAP_7K_AUDIO) {
            ftdm_log(FTDM_LOG_NOTICE, "Not monitoring callref %d with unsupported capability %d\n", crv, transcap);
            break;
        }

        fchan = tap_pri_get_fchan(pritap, pcall, e->proceeding.channel);
        if (!fchan) {
            ftdm_log(FTDM_LOG_ERROR, "Proceeding requested on odd/unavailable channel %s:%d:%d for callref %d\n",
                     pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv);
            break;
        }

        peerfchan = tap_pri_get_fchan(peertap, pcall, e->proceeding.channel);
        if (!peerfchan) {
            ftdm_log(FTDM_LOG_ERROR, "Proceeding requested on odd/unavailable channel %s:%d:%d for callref %d\n",
                     peertap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv);
            break;
        }
        pcall->fchan = fchan;
        peerpcall->fchan = fchan;

        ftdm_log_chan(fchan, FTDM_LOG_NOTICE, "Starting new tapped call with callref %d\n", crv);

        ftdm_channel_lock(fchan);
        fchan->call_data = peerfchan;
        ftdm_set_state(fchan, FTDM_CHANNEL_STATE_RING);
        ftdm_channel_unlock(fchan);

        ftdm_channel_lock(peerfchan);
        peerfchan->call_data = fchan;
        ftdm_channel_unlock(peerfchan);

        break;

    case PRI_EVENT_ANSWER:
        crv = tap_pri_get_crv(pritap->pri, e->answer.call);
        ftdm_log(FTDM_LOG_DEBUG, "Answer on channel %s:%d:%d with callref %d\n",
                 pritap->span->name, PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), crv);
        if (!(pcall = tap_pri_get_pcall_bycrv(pritap, crv))) {
            ftdm_log(FTDM_LOG_DEBUG,
                     "ignoring answer in channel %s:%d:%d for callref %d since we don't know about it\n",
                     pritap->span->name, PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->proceeding.channel), crv);
            break;
        }
        if (!pcall->fchan) {
            ftdm_log(FTDM_LOG_ERROR,
                     "Received answer in channel %s:%d:%d for callref %d but we never got a channel\n",
                     pritap->span->name, PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), crv);
            break;
        }
        ftdm_channel_lock(pcall->fchan);
        ftdm_log_chan(pcall->fchan, FTDM_LOG_NOTICE, "Tapped call was answered in state %s\n", ftdm_channel_state2str(pcall->fchan->state));
        ftdm_set_pflag(pcall->fchan, PRITAP_NETWORK_ANSWER);
        ftdm_set_state(pcall->fchan, FTDM_CHANNEL_STATE_UP);
        ftdm_channel_unlock(pcall->fchan);
        break;

    case PRI_EVENT_HANGUP_REQ:
        crv = tap_pri_get_crv(pritap->pri, e->hangup.call);

        ftdm_log(FTDM_LOG_DEBUG, "Hangup on channel %s:%d:%d with callref %d\n",
                 pritap->span->name, PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), crv);

        if (!(pcall = tap_pri_get_pcall_bycrv(pritap, crv))) {
            ftdm_log(FTDM_LOG_DEBUG,
                     "ignoring hangup in channel %s:%d:%d for callref %d since we don't know about it",
                     pritap->span->name, PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), crv);
            break;
        }

        if (pcall->fchan) {
            fchan = pcall->fchan;
            ftdm_channel_lock(fchan);
            if (fchan->state < FTDM_CHANNEL_STATE_TERMINATING) {
                ftdm_set_state(fchan, FTDM_CHANNEL_STATE_TERMINATING);
            }
            pcall->fchan = NULL; /* after this event we're not supposed to need to do anything with the channel anymore */
            ftdm_channel_unlock(fchan);
        }

        tap_pri_put_pcall(pritap, e->hangup.call);
        tap_pri_put_pcall(peertap, e->hangup.call);
        break;

    case PRI_EVENT_HANGUP_ACK:
        crv = tap_pri_get_crv(pritap->pri, e->hangup.call);
        ftdm_log(FTDM_LOG_DEBUG, "Hangup ack on channel %s:%d:%d with callref %d\n",
                 pritap->span->name, PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), crv);
        tap_pri_put_pcall(pritap, e->hangup.call);
        tap_pri_put_pcall(peertap, e->hangup.call);
        break;

    default:
        ftdm_log(FTDM_LOG_DEBUG, "Ignoring passive event %s on span %s\n", pri_event2str(e->gen.e), pritap->span->name);
        break;

    }
}
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;
}
void sngisdn_process_sta_cfm (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;
	
	StaEvnt *staEvnt = &sngisdn_event->event.staEvnt;
	
	uint8_t call_state = 0;

	if (staEvnt->callSte.eh.pres && staEvnt->callSte.callGlblSte.pres) {
		call_state = staEvnt->callSte.callGlblSte.val;
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing STATUS CONFIRM (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 (staEvnt->causeDgn[0].eh.pres && staEvnt->causeDgn[0].causeVal.pres) {
		if (staEvnt->callSte.eh.pres && staEvnt->callSte.callGlblSte.pres) {
			call_state = staEvnt->callSte.callGlblSte.val;
		} else {
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Received STATUS without call state\n");
			ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
			return;
		}
		switch (staEvnt->causeDgn[0].causeVal.val) {
			case FTDM_CAUSE_RESPONSE_TO_STATUS_ENQUIRY:
				ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Status Check OK:%d", call_state);
				ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
				return;
			case FTDM_CAUSE_WRONG_CALL_STATE:
				ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Message incompatible with call state (call_state:%d channel-state:%s cause:%d) (suId:%u suInstId:%u spInstId:%u)\n", call_state, ftdm_channel_state2str(ftdmchan->state), staEvnt->causeDgn[0].causeVal.val, suId, suInstId, spInstId);
				break;
			case FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE:
				ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Recovery on timer expire (call_state:%d channel-state:%s cause:%d) (suId:%u suInstId:%u spInstId:%u)\n", call_state, ftdm_channel_state2str(ftdmchan->state), staEvnt->causeDgn[0].causeVal.val, suId, suInstId, spInstId);
				break;
			default:
				ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "STATUS CONFIRM (call_state:%d channel-state:%s cause:%d) (suId:%u suInstId:%u spInstId:%u)\n", call_state, ftdm_channel_state2str(ftdmchan->state), staEvnt->causeDgn[0].causeVal.val, suId, suInstId, spInstId);
				break;
		}

		/* Section 4.3.30 from INT Interface - Service Definition */
		ftdmchan->caller_data.hangup_cause = staEvnt->causeDgn[0].causeVal.val;
		
		switch(call_state) {
			/* Sere ITU-T Q931 for definition of call states */
			case 0:	/* Remote switch thinks there are no calls on this channel */
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_COLLECT:
					case FTDM_CHANNEL_STATE_DIALING:
					case FTDM_CHANNEL_STATE_UP:
						sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT);
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
						break;
					case FTDM_CHANNEL_STATE_TERMINATING:
						/* We are in the process of clearing local states,
						just make sure we will not send any messages to remote switch */
						sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT);
						break;
					case FTDM_CHANNEL_STATE_HANGUP:
						/* This cannot happen, state_advance always sets
						ftdmchan to STATE_HANGUP_COMPLETE when in STATE_HANGUP
						and we called check_for_state_change earlier so something is very wrong here!!! */
						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "How can we we in FTDM_CHANNEL_STATE_HANGUP after checking for state change?\n");
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
						break;
					case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
						/* We were waiting for remote switch to send RELEASE COMPLETE
						but this will not happen, so just clear local state */
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
						break;
					case FTDM_CHANNEL_STATE_DOWN:
						/* If our local state is down as well, then there is nothing to do */
						break;
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						break;
				}
				break;
			case 1:
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_UP:
						/* Remote side is still waiting for our CONNECT message */
						if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
							ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_connect", 1, sngisdn_delayed_connect, (void*) sngisdn_info, NULL);
							break;
						}
						/* Fall-through */
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						break;
				}
				break;
				case 2: /* overlap sending/receiving */
					switch (ftdmchan->state) {
						case FTDM_CHANNEL_STATE_COLLECT:
							/* T302 Timeout reached */
							/* Send the call to user, and see if they accept it */
							ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "T302 Timer expired, proceeding with call\n");
							ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
							break;
						case FTDM_CHANNEL_STATE_PROGRESS:
						case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
							ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Remote switch expecting OVERLAP receive, but we are already PROCEEDING\n");
							sngisdn_snd_disconnect(ftdmchan);
							break;
						case FTDM_CHANNEL_STATE_DOWN:
						case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
							/* We hung up locally, but remote switch doesn't know send disconnect again*/
							sngisdn_snd_disconnect(ftdmchan);
							break;
						default:
							ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
							break;
					}
					break;
			case 3:
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_PROGRESS:
						/* T310 timer has expired */
						ftdmchan->caller_data.hangup_cause = staEvnt->causeDgn[0].causeVal.val;
						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "T310 Timer expired, hanging up call\n");
						sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC);
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);

						break;
					case FTDM_CHANNEL_STATE_UP:
						/* Remote side is still waiting for our CONNECT message */
						if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
							ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_connect", 1, sngisdn_delayed_connect, (void*) sngisdn_info, NULL);
							break;
						}
						/* Fall-through */
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						break;
				}
				break;
			case 8: /* Remote switch is in "Connect Request state" */
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_UP:
						/* This is ok. We sent a Connect, and we are waiting for a connect ack */
						/* Do nothing */
						break;
					case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
						/* We hung up locally, but remote switch doesn't know send disconnect again*/
						sngisdn_snd_disconnect(ftdmchan);
						break;
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						break;
				}
				break;
			case 9: /* Remote switch is in "Incoming call proceeding" state */
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_PROGRESS:
					case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
					case FTDM_CHANNEL_STATE_GET_CALLERID:
						/* Do nothing */
						break;
					case FTDM_CHANNEL_STATE_UP:
						/* Remote switch missed our CONNECT message, re-send */
						ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_connect", 1, sngisdn_delayed_connect, (void*) sngisdn_info, NULL);
						break;
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						break;
				}
				break;
			case 10: /* Remote switch is in active state */
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_UP:
						/* This is ok, they are in active state and we are in active state */
						/* Do nothing */
						break;
					case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
						/* We sent a disconnect message, but remote side missed it ? */
						ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_disconnect", 1, sngisdn_delayed_disconnect, (void*) sngisdn_info, NULL);
						break;
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						break;
				}
				break;
			case 22:
				switch (ftdmchan->state) {
					case FTDM_CHANNEL_STATE_UP:
						/* Stack is in the process of clearing the call*/
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
						break;
					case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
						/* Do nothing as we will get a RELEASE COMPLETE */
						break;
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						//ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RESTART);
						break;
				}
			default:
				ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state));
						//ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RESTART);
				break;
		}
	}

	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
void sngisdn_process_cnst_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;
	uint8_t ces = sngisdn_event->ces;
	uint8_t evntType = sngisdn_event->evntType;
	
	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;
	
	CnStEvnt *cnStEvnt = &sngisdn_event->event.cnStEvnt;

	ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n");

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing %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_INFO)?"INFO":"UNKNOWN",
															suId, suInstId, spInstId, ces);
	
	switch(evntType) {
		case MI_PROGRESS:
			if (signal_data->switchtype == SNGISDN_SWITCH_NI2 &&
						 cnStEvnt->causeDgn[0].eh.pres && cnStEvnt->causeDgn[0].causeVal.pres) {

				switch(cnStEvnt->causeDgn[0].causeVal.val) {
					case 17:	/* User Busy */
					case 18:	/* No User responding */
					case 19:	/* User alerting, no answer */
					case 21:	/* Call rejected, the called party does not with to accept this call */
					case 27:	/* Destination out of order */
					case 31:	/* Normal, unspecified */
					case 34:	/* Circuit/Channel congestion */
					case 41:	/* Temporary failure */
					case 42:	/* Switching equipment is experiencing a period of high traffic */
					case 47:	/* Resource unavailable */
					case 58:	/* Bearer Capability not available */
					case 63:	/* Service or option not available */
					case 65:	/* Bearer Cap not implemented, not supported */
					case 79:	/* Service or option not implemented, unspecified */
						ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Cause requires disconnect (cause:%d)\n", cnStEvnt->causeDgn[0].causeVal.val);
						ftdmchan->caller_data.hangup_cause = cnStEvnt->causeDgn[0].causeVal.val;
						
						sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC);
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
						goto sngisdn_process_cnst_ind_end;
				}
			}
			/* fall-through */
		case MI_ALERTING:
		case MI_CALLPROC:
		
			switch(ftdmchan->state) {
				case FTDM_CHANNEL_STATE_DIALING:					
					if (evntType == MI_PROGRESS ||
						(cnStEvnt->progInd.eh.pres && cnStEvnt->progInd.progDesc.val == IN_PD_IBAVAIL)) {
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
					} else {
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS);
					}
					break;
				case FTDM_CHANNEL_STATE_PROGRESS:
					if (evntType == MI_PROGRESS ||
						(cnStEvnt->progInd.eh.pres && cnStEvnt->progInd.progDesc.val == IN_PD_IBAVAIL)) {
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
					}
					break;
				case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
					/* We are already in progress media, we can't go to any higher state except up */
					/* Do nothing */
					break;
				default:
					ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing ALERT/PROCEED/PROGRESS in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state));

					/* Start the disconnect procedure */
					ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
					break;
			}
			break;
		case MI_SETUPACK:
			ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Processing SETUP_ACK, but overlap sending not implemented (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId);
			break;
		case MI_INFO:
			ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing INFO (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId);

			if (cnStEvnt->cdPtyNmb.eh.pres) {
				switch(ftdmchan->state) {
					case FTDM_CHANNEL_STATE_COLLECT:
					{
						ftdm_size_t min_digits = ((sngisdn_span_data_t*)ftdmchan->span->signal_data)->min_digits;
						ftdm_size_t num_digits;

						cpy_called_num_from_stack(&ftdmchan->caller_data, &cnStEvnt->cdPtyNmb);
						num_digits = strlen(ftdmchan->caller_data.dnis.digits);

						if (cnStEvnt->sndCmplt.eh.pres || num_digits >= min_digits) {
							ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
						} else {
							ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "received %d of %d digits\n", num_digits, min_digits);
						}
					}
					break;
					case FTDM_CHANNEL_STATE_RING:
					case FTDM_CHANNEL_STATE_PROGRESS:
					case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
					case FTDM_CHANNEL_STATE_UP:
						ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Receiving more digits %s, but we already proceeded with call\n", cnStEvnt->cdPtyNmb.nmbDigits.val);
						break;
					default:
						ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "\n", suId, suInstId, spInstId);
						break;
				}
			}

			break;
		default:
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Unhandled STATUS event\n");
			break;
	}

sngisdn_process_cnst_ind_end:
	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;
}
/* Remote side transmit a CONNECT or CONNECT ACK */
void sngisdn_process_con_cfm (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;
	uint8_t ces = sngisdn_event->ces;
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	
	/* Function does not require any info from conStEvnt struct for now */
	/* CnStEvnt *cnStEvnt = &sngisdn_event->event.cnStEvnt; */	
				
	ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n");
	
	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing CONNECT/CONNECT ACK (suId:%u suInstId:%u spInstId:%u ces:%d)\n", suId, suInstId, spInstId, ces);

	if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP &&
		((sngisdn_span_data_t*)ftdmchan->span->signal_data)->signalling == SNGISDN_SIGNALING_NET) {

		if(sngisdn_info->ces == CES_MNGMNT) {
			/* We assign the call to the first TE */
			sngisdn_info->ces = ces;
		} else {
			/* We already assigned this call, do nothing */
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call already assigned, ignoring connect\n");
			ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
			return;
		}
	}

	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
		switch(ftdmchan->state) {
			case FTDM_CHANNEL_STATE_PROGRESS:
			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
			case FTDM_CHANNEL_STATE_DIALING:
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
				break;
			default:
				ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing CONNECT/CONNECT ACK in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state));

				/* Start the disconnect procedure */
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
				break;
		}
	} else {
		switch(ftdmchan->state) {
			case FTDM_CHANNEL_STATE_UP:
				/* This is the only valid state we should get a CONNECT ACK on */
				/* do nothing */
				break;
			case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
				/* We just hung up an incoming call right after we sent a CONNECT so ignore this message */
				break;
			default:
				ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing CONNECT/CONNECT ACK in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state));
				
				/* Start the disconnect procedure */
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
				break;
		}
	}

	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
ftdm_status_t get_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd)
{
	uint8_t val;

	if (!progInd->eh.pres) {
		return FTDM_FAIL;
	}

	if (progInd->progDesc.pres) {
		/* TODO: use get_ftdm_val function and table here */
		switch (progInd->progDesc.val) {
			case IN_PD_NOTETEISDN:
				val = SNGISDN_PROGIND_DESCR_NETE_ISDN;
				break;
			case IN_PD_DSTNOTISDN:
				val = SNGISDN_PROGIND_DESCR_DEST_NISDN;
				break;
			case IN_PD_ORGNOTISDN:
				val = SNGISDN_PROGIND_DESCR_ORIG_NISDN;
				break;
			case IN_PD_CALLRET:
				val = SNGISDN_PROGIND_DESCR_RET_ISDN;
				break;
			case IN_PD_DELRESP:
				val = SNGISDN_PROGIND_DESCR_SERV_CHANGE;
				break;
			case IN_PD_IBAVAIL:
				val = SNGISDN_PROGIND_DESCR_IB_AVAIL;
				break;
			default:
				ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unknown Progress Indicator Description (%d)\n", progInd->progDesc.val);
				val = SNGISDN_PROGIND_DESCR_INVALID;
				break;
		}
		sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.prog_ind.descr", ftdm_sngisdn_progind_descr2str(val));
	}
	
	if (progInd->location.pres) {
		switch (progInd->location.val) {
			case IN_LOC_USER:
				val = SNGISDN_PROGIND_LOC_USER;
				break;
			case IN_LOC_PRIVNETLU:
				val = SNGISDN_PROGIND_LOC_PRIV_NET_LOCAL_USR;
				break;
			case IN_LOC_PUBNETLU:
				val = SNGISDN_PROGIND_LOC_PUB_NET_LOCAL_USR;
				break;
			case IN_LOC_TRANNET:
				val = SNGISDN_PROGIND_LOC_TRANSIT_NET;
				break;
			case IN_LOC_PUBNETRU:
				val = SNGISDN_PROGIND_LOC_PUB_NET_REMOTE_USR;
				break;
			case IN_LOC_PRIVNETRU:
				val = SNGISDN_PROGIND_LOC_PRIV_NET_REMOTE_USR;
				break;
			case IN_LOC_NETINTER:
				val = SNGISDN_PROGIND_LOC_NET_BEYOND_INTRW;
				break;
			default:
				ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unknown Progress Indicator Location (%d)", progInd->location.val);
				val = SNGISDN_PROGIND_LOC_INVALID;
				break;
		}
		sngisdn_add_var((sngisdn_chan_data_t*)ftdmchan->call_data, "isdn.prog_ind.loc", ftdm_sngisdn_progind_loc2str(val));
	}	
	return FTDM_SUCCESS;
}
/* We received an incoming frame on the d-channel, send data to the stack */
void sngisdn_snd_data(ftdm_channel_t *dchan, uint8_t *data, ftdm_size_t len)
{
	sng_l1_frame_t l1_frame;
	ftdm_alarm_flag_t alarmbits = 0;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) dchan->span->signal_data;

	ftdm_channel_get_alarms(dchan, &alarmbits);

	if (alarmbits) {
		ftdm_log_chan_msg(dchan, FTDM_LOG_WARNING, "Dropping incoming data due to L1 alarm\n");
		return;
	}

	if (len > sizeof(l1_frame.data)) {
		ftdm_log_chan(dchan, FTDM_LOG_ERROR, "Received frame of %"FTDM_SIZE_FMT" bytes, exceeding max size of %"FTDM_SIZE_FMT" bytes\n", 
				len, sizeof(l1_frame.data));
		return;
	}
	if (len <= 2) {
		return;
	}

	memset(&l1_frame, 0, sizeof(l1_frame));
	l1_frame.len = len;

	memcpy(&l1_frame.data, data, len);

	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_CRC)) {
		l1_frame.flags |= SNG_L1FRAME_ERROR_CRC;
	}

	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_FRAME)) {
		l1_frame.flags |= SNG_L1FRAME_ERROR_FRAME;
	}
	
	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_ABORT)) {
		l1_frame.flags |= SNG_L1FRAME_ERROR_ABORT;
	}

	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_FIFO)) {
		l1_frame.flags |= SNG_L1FRAME_ERROR_FIFO;
	}

	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_DMA)) {
		l1_frame.flags |= SNG_L1FRAME_ERROR_DMA;
	}

	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_THRES)) {
		/* Should we trigger congestion here? */		
		l1_frame.flags |= SNG_L1FRAME_QUEUE_THRES;
	}

	if (ftdm_test_flag(&(dchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_FULL)) {
		/* Should we trigger congestion here? */
		l1_frame.flags |= SNG_L1FRAME_QUEUE_FULL;
	}
#if 0
	if (1) {
		int i;
		char string [2000];
		unsigned string_len = 0;
		for (i = 0; i < l1_frame.len; i++) {
			string_len += sprintf(&string[string_len], "0x%02x ", l1_frame.data[i]);
		}

		ftdm_log_chan(dchan, FTDM_LOG_CRIT, "\nL1 RX [%s] flags:%x\n", string, l1_frame.flags);
	}
#endif
	sng_isdn_data_ind(signal_data->link_id, &l1_frame);
}
Exemple #24
0
static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
{
    ftdm_status_t status;
    ftdm_sigmsg_t sig;
    pritap_t *pritap = ftdmchan->span->signal_data;
    pritap_t *peer_pritap = pritap->peerspan->signal_data;

    ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "processing state %s\n", ftdm_channel_state2str(ftdmchan->state));

    memset(&sig, 0, sizeof(sig));
    sig.chan_id = ftdmchan->chan_id;
    sig.span_id = ftdmchan->span_id;
    sig.channel = ftdmchan;

    ftdm_channel_complete_state(ftdmchan);

    switch (ftdmchan->state) {
    case FTDM_CHANNEL_STATE_DOWN:
    {
        ftdm_channel_t *fchan = ftdmchan;

        /* Destroy the peer data first */
        if (fchan->call_data) {
            ftdm_channel_t *peerchan = fchan->call_data;
            ftdm_channel_t *pchan = peerchan;

            ftdm_channel_lock(peerchan);

            pchan->call_data = NULL;
            pchan->pflags = 0;
            ftdm_channel_close(&pchan);

            ftdm_channel_unlock(peerchan);
        } else {
            ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "No call data?\n");
        }

        ftdmchan->call_data = NULL;
        ftdmchan->pflags = 0;
        ftdm_channel_close(&fchan);
    }
    break;

    case FTDM_CHANNEL_STATE_PROGRESS:
    case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
    case FTDM_CHANNEL_STATE_HANGUP:
        break;

    case FTDM_CHANNEL_STATE_UP:
    {
        if (ftdm_test_pflag(ftdmchan, PRITAP_NETWORK_ANSWER)) {
            ftdm_clear_pflag(ftdmchan, PRITAP_NETWORK_ANSWER);
            sig.event_id = FTDM_SIGEVENT_UP;
            ftdm_span_send_signal(ftdmchan->span, &sig);
        }
    }
    break;

    case FTDM_CHANNEL_STATE_RING:
    {
        sig.event_id = FTDM_SIGEVENT_START;
        /* The ring interface (where the setup was received) is the peer, since we RING the channel
         * where PROCEED/PROGRESS is received */
        ftdm_sigmsg_add_var(&sig, "pritap_ring_interface", PRITAP_GET_INTERFACE(peer_pritap->iface));
        if ((status = ftdm_span_send_signal(ftdmchan->span, &sig) != FTDM_SUCCESS)) {
            ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
        }
    }
    break;

    case FTDM_CHANNEL_STATE_TERMINATING:
    {
        if (ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP) {
            sig.event_id = FTDM_SIGEVENT_STOP;
            status = ftdm_span_send_signal(ftdmchan->span, &sig);
        }
        ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
    }
    break;

    default:
    {
        ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "ignoring state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(ftdmchan->state));
    }
    break;
    }

    return FTDM_SUCCESS;
}
Exemple #25
0
static ftdm_status_t ftdm_gsm_state_advance(ftdm_channel_t *ftdmchan)
{

	ftdm_log_chan(ftdmchan, STATE_ADVANCE_LOG_LEVEL , "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
	
	ftdm_channel_complete_state(ftdmchan);

		switch (ftdmchan->state) {

		/* starting an incoming call */
		case FTDM_CHANNEL_STATE_COLLECT: 
			{
				
				
			}
			break;

			/* starting an outgoing call */
		case FTDM_CHANNEL_STATE_DIALING:
			{
				uint32_t interval = 0;
				
				ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval);

				{

					ftdm_gsm_span_data_t *gsm_data;
					gsm_data = ftdmchan->span->signal_data;
					gsm_data->call_id = g_outbound_call_id++;				
					wat_con_event_t con_event;
					memset(&con_event, 0, sizeof(con_event));
					ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits);
					ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits);
					wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event);
 
					SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING);
					
					
				}

				
			}
			break;

			/* incoming call was offered */
		case FTDM_CHANNEL_STATE_RING:

			/* notify the user about the new call */

			ftdm_log(FTDM_LOG_INFO, "Answering Incomming Call\r\n");
			SEND_STATE_SIGNAL(FTDM_SIGEVENT_START);
			
			break;

			/* the call is making progress */
		case FTDM_CHANNEL_STATE_PROGRESS:
		case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
			{
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA);
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
			}
			break;

			/* the call was answered */
		case FTDM_CHANNEL_STATE_UP:
			{
				if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
				
					SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP);
				}
				else {
						ftdm_gsm_span_data_t *gsm_data;
						gsm_data = ftdmchan->span->signal_data;
						wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id); 
				}
				
				
			}
			break;

			/* just got hangup */
		case FTDM_CHANNEL_STATE_HANGUP:
			{
				ftdm_gsm_span_data_t *gsm_data;
				gsm_data = ftdmchan->span->signal_data;
				wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id);
				gsm_data->call_id = 0;
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
			}
			break;

		case FTDM_CHANNEL_STATE_TERMINATING:
			{
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
			}
			break;

			/* finished call for good */
		case FTDM_CHANNEL_STATE_DOWN: 
			{
				ftdm_channel_t *closed_chan;
				closed_chan = ftdmchan;
				ftdm_channel_close(&closed_chan);
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n");
				SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP);
			}
			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");
			SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING);
				
			break;

			/* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
		case FTDM_CHANNEL_STATE_RESET:
			{
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
			}
			break;

		default:
			{
				ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
			}
			break;
	}

	
	return FTDM_SUCCESS;
}
static ftdm_status_t sngisdn_map_call(sngisdn_span_data_t *signal_data, sngisdn_frame_info_t frame_info, ftdm_channel_t **found)
{
    sngisdn_chan_data_t *sngisdn_info;
    ftdm_channel_t *ftdmchan = NULL;
    ftdm_iterator_t *chaniter = NULL;
    ftdm_iterator_t *curr = NULL;
    ftdm_status_t status = FTDM_FAIL;
    uint8_t outbound_call = 0;

    if ((!frame_info.call_ref_flag && frame_info.dir == FTDM_TRACE_DIR_OUTGOING) ||
            (frame_info.call_ref_flag && frame_info.dir == FTDM_TRACE_DIR_INCOMING)) {

        /* If this is an outgoing frame and this frame was sent by the originating side
        	of the call (frame_info.call_ref_flag == 0), then this is an outbound call */
        outbound_call = 1;
    } else {
        outbound_call = 0;
    }

    switch (frame_info.msgtype) {
    case PROT_Q931_MSGTYPE_SETUP:
        /* We initiated this outgoing call try to match the call reference with our internal call-id*/
        if (!frame_info.bchan_no) {
            /* We were not able to determine the bchannel on this call, so we will not be able to match it anyway */
            status = FTDM_FAIL;
        }

        chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL);
        for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {
            ftdmchan = (ftdm_channel_t*)(ftdm_iterator_current(curr));
            ftdm_channel_lock(ftdmchan);

            if (outbound_call) {
                sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
                if (sngisdn_info && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
                    if (ftdmchan->caller_data.call_id && ftdmchan->physical_chan_id == frame_info.bchan_no) {

                        sngisdn_info->call_ref = frame_info.call_ref;
                        *found = ftdmchan;
                        status = FTDM_SUCCESS;
                    }
                }
            } else {
                if (ftdmchan->physical_chan_id == frame_info.bchan_no) {
                    *found = ftdmchan;
                    status = FTDM_SUCCESS;
                }
            }
            ftdm_channel_unlock(ftdmchan);
        }
        ftdm_iterator_free(chaniter);
        break;
    case PROT_Q931_MSGTYPE_ALERTING:
    case PROT_Q931_MSGTYPE_PROCEEDING:
    case PROT_Q931_MSGTYPE_PROGRESS:
    case PROT_Q931_MSGTYPE_CONNECT:
    case PROT_Q931_MSGTYPE_SETUP_ACK:
    case PROT_Q931_MSGTYPE_CONNECT_ACK:
    case PROT_Q931_MSGTYPE_USER_INFO:
    case PROT_Q931_MSGTYPE_DISCONNECT:
    case PROT_Q931_MSGTYPE_RELEASE:
    case PROT_Q931_MSGTYPE_RELEASE_ACK:
    case PROT_Q931_MSGTYPE_RELEASE_COMPLETE:
    case PROT_Q931_MSGTYPE_FACILITY:
    case PROT_Q931_MSGTYPE_NOTIFY:
    case PROT_Q931_MSGTYPE_STATUS_ENQUIRY:
    case PROT_Q931_MSGTYPE_INFORMATION:
    case PROT_Q931_MSGTYPE_STATUS:
        /* Look for an outbound call on that span and and try to match the call-id */
        chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL);
        for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {
            ftdmchan = (ftdm_channel_t*)(ftdm_iterator_current(curr));
            ftdm_channel_lock(ftdmchan);
            sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
            if (outbound_call) {
                if (sngisdn_info && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
                    if (sngisdn_info->call_ref == frame_info.call_ref) {

                        *found = ftdmchan;
                        status = FTDM_SUCCESS;
                    }
                }
            } else {
                if (sngisdn_info && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
                    if (sngisdn_info->call_ref && sngisdn_info->call_ref == frame_info.call_ref) {

                        *found = ftdmchan;
                        status = FTDM_SUCCESS;
                    }
                }
            }
            ftdm_channel_unlock(ftdmchan);
        }
        ftdm_iterator_free(chaniter);
        break;
    default:
        /* This frame is not call specific, ignore */
        break;
    }
    if (status == FTDM_SUCCESS) {
        ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Mapped %s with Call Ref:%04x to call-id:%d\n", get_code_2_str(frame_info.msgtype, dcodQ931MsgTypeTable), frame_info.call_ref, (*found)->caller_data.call_id);
    } else {
        /* We could not map this frame to a call-id */
        ftdm_log(FTDM_LOG_DEBUG, "Failed to map %s with Call Ref:%04x to local call\n",
                 get_code_2_str(frame_info.msgtype, dcodQ931MsgTypeTable), frame_info.call_ref);
    }

    return status;
}
static ftdm_status_t att_courtesy_vru(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* args)
{
	char dtmf_digits[64];
	ftdm_status_t status = FTDM_FAIL;
	sngisdn_chan_data_t  *sngisdn_info = ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	char *p = args;
	uint8_t forced_answer = 0;

	switch (signal_data->switchtype) {
		case SNGISDN_SWITCH_5ESS:
		case SNGISDN_SWITCH_4ESS:
			break;
		default:
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "AT&T Courtesy Transfer not supported for switchtype\n");
			return FTDM_FAIL;
	}

	while (!ftdm_strlen_zero(p)) {
		if (!isdigit(*p) && *p != 'w' && *p != 'W') {
			ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot transfer to non-numeric number:%s\n", args);
			return FTDM_FAIL;
		}
		p++;
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Performing AT&T Courtesy Transfer-VRU%s to %s\n", (type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) ?"--data" : "", args);
	sprintf(dtmf_digits, "*8w%s", args);
	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending digits %s\n", dtmf_digits);

	switch (ftdmchan->last_state) {
		case FTDM_CHANNEL_STATE_PROCEED:
		case FTDM_CHANNEL_STATE_PROGRESS:
		case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
			/* Call has to be in answered state - so send a CONNECT message if we did not answer this call yet */
			forced_answer++;
			sngisdn_snd_connect(ftdmchan);
			/* fall-through */
		case FTDM_CHANNEL_STATE_UP:
			memset(&sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits, 0, sizeof(sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits));
			sngisdn_info->transfer_data.type = type;

			/* We will be polling the channel for IO so that we can receive the DTMF events,
			 * Disable user RX otherwise it is a race between who calls channel_read */
			ftdm_set_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED);

			ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL);
			ftdm_channel_command(ftdmchan, FTDM_COMMAND_SEND_DTMF, dtmf_digits);

			if (type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
				/* We need to save transfer data, so we can send it in the disconnect msg */
				const char *val = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_data");
				if (ftdm_strlen_zero(val)) {
					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform data transfer because transfer_data variable is not set\n");
					goto done;
				}
				if (strlen(val) > COURTESY_TRANSFER_MAX_DATA_SIZE) {
					ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Data exceeds max size (len:%"FTDM_SIZE_FMT" max:%d), cannot perform transfer\n", strlen(val), COURTESY_TRANSFER_MAX_DATA_SIZE);
					goto done;
				}
				memcpy(sngisdn_info->transfer_data.tdata.att_courtesy_vru.data, val, strlen(val));
			}

			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
			if (forced_answer) {
				/* Notify the user that we answered the call */
				sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_UP);
			}
			if (signal_data->transfer_timeout) {
				ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "courtesy_transfer_timeout", signal_data->transfer_timeout, att_courtesy_transfer_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_CHAN_TIMER_ATT_TRANSFER]);
			}

			status = FTDM_SUCCESS;
			break;
		default:
			ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer in state %s\n", ftdm_channel_state2str(ftdmchan->state));
			break;

	}
done:
	return status;
}