/* The stacks is wants to transmit a frame */
int16_t sngisdn_rcv_l1_data_req(uint16_t spId, sng_l1_frame_t *l1_frame)
{
	ftdm_status_t status;
	ftdm_wait_flag_t flags = FTDM_WRITE;
	sngisdn_span_data_t	*signal_data = g_sngisdn_data.dchans[spId].spans[1];
	ftdm_size_t length = l1_frame->len;

	ftdm_assert(signal_data, "Received Data request on unconfigured span\n");
	
	do {
		flags = FTDM_WRITE;
		status = signal_data->dchan->fio->wait(signal_data->dchan, &flags, 1000);
		if (status != FTDM_SUCCESS) {
			ftdm_log_chan_msg(signal_data->dchan, FTDM_LOG_WARNING, "transmit timed-out\n");
			return -1;
		}
		
		
		if ((flags & FTDM_WRITE)) {
			status = signal_data->dchan->fio->write(signal_data->dchan, l1_frame->data, (ftdm_size_t*)&length);
			if (status != FTDM_SUCCESS) {
				ftdm_log_chan_msg(signal_data->dchan, FTDM_LOG_CRIT, "Failed to transmit frame\n");
				return -1;
			}
			break;
		/* On WIN32, it is possible for poll to return without FTDM_WRITE flag set, so we try to retransmit */
#ifndef WIN32
		} else {
			ftdm_log_chan_msg(signal_data->dchan, FTDM_LOG_WARNING, "Failed to poll for d-channel\n");
			return -1;
#endif
		}
	} while(1);
	return 0;
}
void sngisdn_process_disc_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;
	
	DiscEvnt *discEvnt = &sngisdn_event->event.discEvnt;

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing DISCONNECT (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");
	switch (ftdmchan->state) {
		case FTDM_CHANNEL_STATE_RING:
		case FTDM_CHANNEL_STATE_DIALING:
		case FTDM_CHANNEL_STATE_PROGRESS:
		case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
		case FTDM_CHANNEL_STATE_UP:
			if (discEvnt->causeDgn[0].eh.pres && discEvnt->causeDgn[0].causeVal.pres) {
				ftdmchan->caller_data.hangup_cause = discEvnt->causeDgn[0].causeVal.val;
			} else {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "DISCONNECT did not have a cause code\n");
				ftdmchan->caller_data.hangup_cause = 0;
			}
			sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_REL);
			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
			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_DOWN:
			/* somehow we are in down, nothing we can do locally */
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Received DISCONNECT but we are in DOWN state\n");
			break;
		case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
			/* This is a race condition. We just sent a DISCONNECT, on this channel */
			/* Do nothing */
			break;
		default:
			ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Received DISCONNECT in an invalid state (%s)\n",
						  ftdm_channel_state2str(ftdmchan->state));
			/* start reset procedure */

			/* Start the release procedure */
			ftdm_set_flag(sngisdn_info, FLAG_REMOTE_REL);
			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
			break;
	}

	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
/* The stacks is wants to transmit a frame */
int16_t sngisdn_rcv_l1_data_req(uint16_t spId, sng_l1_frame_t *l1_frame)
{
	ftdm_status_t status;
	ftdm_wait_flag_t flags = FTDM_WRITE;
	sngisdn_span_data_t	*signal_data = g_sngisdn_data.dchans[spId].spans[1];
	ftdm_size_t length = l1_frame->len;

	ftdm_assert(signal_data, "Received Data request on unconfigured span\n");
	
	do {
		flags = FTDM_WRITE;
		status = signal_data->dchan->fio->wait(signal_data->dchan, &flags, 1000);
		switch(status) {
			case FTDM_SUCCESS:
				break;
			case FTDM_TIMEOUT:
				continue;
			case FTDM_FAIL:
			default:
				ftdm_log_chan_msg(signal_data->dchan, FTDM_LOG_WARNING, "failed to poll for channel\n");
				return -1;
		}
		
		
		/* status = FTDM_SUCCESS */	
		if ((flags & FTDM_WRITE)) {
#if 0
			int i;
			char string [2000];
			unsigned string_len = 0;
			for (i = 0; i < length; i++) {
				string_len += sprintf(&string[string_len], "0x%02x ", l1_frame->data[i]);
			}

			ftdm_log_chan(signal_data->dchan, FTDM_LOG_CRIT, "\nL1 TX [%s]\n", string);
#endif
			
			status = signal_data->dchan->fio->write(signal_data->dchan, l1_frame->data, (ftdm_size_t*)&length);
			if (status != FTDM_SUCCESS) {
				ftdm_log_chan_msg(signal_data->dchan, FTDM_LOG_CRIT, "Failed to transmit frame\n");
				return -1;
			}
			break;
		/* On WIN32, it is possible for poll to return without FTDM_WRITE flag set, so we try to retransmit */
#ifndef WIN32
		} else {
			ftdm_log_chan_msg(signal_data->dchan, FTDM_LOG_WARNING, "Failed to poll for d-channel\n");
			return -1;
#endif
		}
	} while(1);
	return 0;
}
void sngisdn_snd_release(ftdm_channel_t *ftdmchan, uint8_t glare)
{
	RelEvnt relEvnt;
	uint32_t suInstId, spInstId;

	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

 	if (!sngisdn_info->suInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending RELEASE, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);

		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
		return;
	}
	
	memset(&relEvnt, 0, sizeof(relEvnt));
	
	/* Fill relEvnt here */
  	relEvnt.causeDgn[0].eh.pres = PRSNT_NODEF;
	relEvnt.causeDgn[0].location.pres = PRSNT_NODEF;
	relEvnt.causeDgn[0].location.val = IN_LOC_PRIVNETLU;
	relEvnt.causeDgn[0].codeStand3.pres = PRSNT_NODEF;
	relEvnt.causeDgn[0].codeStand3.val = IN_CSTD_CCITT;

	relEvnt.causeDgn[0].causeVal.pres = PRSNT_NODEF;
	relEvnt.causeDgn[0].causeVal.val = ftdmchan->caller_data.hangup_cause;
	relEvnt.causeDgn[0].recommend.pres = NOTPRSNT;
	relEvnt.causeDgn[0].dgnVal.pres = NOTPRSNT;

	if (glare) {
		suInstId = sngisdn_info->glare.suInstId;
		spInstId = sngisdn_info->glare.spInstId;
	} else {
		suInstId = sngisdn_info->suInstId;
		spInstId = sngisdn_info->spInstId;
	}

	set_facility_ie(ftdmchan, &relEvnt.facilityStr);
	
	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending RELEASE/RELEASE COMPLETE (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, suInstId, spInstId);

	if (glare) {
		if (sng_isdn_release_request(signal_data->cc_id, suInstId, spInstId, &relEvnt)) {
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused RELEASE/RELEASE COMPLETE request\n");
		}
	} else {	
		if (sng_isdn_release_request(signal_data->cc_id, suInstId, spInstId, &relEvnt)) {
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused RELEASE/RELEASE COMPLETE request\n");
		}
	}	
	return;
}
void sngisdn_process_fac_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;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	FacEvnt *facEvnt = &sngisdn_event->event.facEvnt;

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing FACILITY IND (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId);

	switch (ftdmchan->state) {
		case FTDM_CHANNEL_STATE_GET_CALLERID:
			/* Update the caller ID Name */
			if (facEvnt->facElmt.facStr.pres) {
				char retrieved_str[255];
				
				/* 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 (sng_isdn_retrieve_facility_caller_name(&facEvnt->facElmt.facStr.val[2], facEvnt->facElmt.facStr.len, retrieved_str) == 0) {
					strcpy(ftdmchan->caller_data.cid_name, retrieved_str);
				} else {
					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to retrieve Caller Name from Facility IE\n");
				}
				if (signal_data->facility_timeout) {
					/* Cancel facility timeout */
					ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_TIMER_FACILITY]);
				}
			}

			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
			break;
		case FTDM_CHANNEL_STATE_RING:
			/* We received the caller ID Name in FACILITY, but its too late, facility-timeout already occurred */
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "FACILITY received, but we already proceeded with call\n");
			break;
		default:
			/* We do not support other FACILITY types for now, so do nothing */
			break;
	}
	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
/* Unsed only for overlap receive */
void sngisdn_snd_setup_ack(ftdm_channel_t *ftdmchan)
{
	CnStEvnt cnStEvnt;
	
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending SETUP ACK , but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
		return;
	}
	
	memset(&cnStEvnt, 0, sizeof(cnStEvnt));	



	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending SETUP ACK (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);

	if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, MI_SETUPACK, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused SETUP ACK request\n");
	}
	return;
}
/**
 * \brief Reads data from a Wanpipe channel
 * \param ftdmchan Channel to read from
 * \param data Data buffer
 * \param datalen Size of data buffer
 * \return Success, failure or timeout
 */
static FIO_READ_FUNCTION(wanpipe_read)
{
	int rx_len = 0;
	int myerrno = 0;
	wp_tdm_api_rx_hdr_t hdrframe;

	memset(&hdrframe, 0, sizeof(hdrframe));

	rx_len = sangoma_readmsg_tdm(ftdmchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (int)*datalen, 0);
	*datalen = rx_len;

	if (rx_len == 0) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Read 0 bytes\n");
		return FTDM_TIMEOUT;
	}

	if (rx_len < 0) {
		myerrno = errno;
		snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno));
		ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Failed to read from sangoma device: %s (%d)\n", strerror(errno), rx_len);
		return FTDM_FAIL;
	} 


	return FTDM_SUCCESS;
}
void sngisdn_snd_disconnect(ftdm_channel_t *ftdmchan)
{	
	DiscEvnt discEvnt;

	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending DISCONNECT, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);

		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
		return;
	}

	memset(&discEvnt, 0, sizeof(discEvnt));

	set_cause_ie(ftdmchan, &discEvnt.causeDgn[0]);
	set_facility_ie(ftdmchan, &discEvnt.facilityStr);
	set_user_to_user_ie(ftdmchan, &discEvnt.usrUsr);

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
	if (sng_isdn_disc_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &discEvnt)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused DISCONNECT request\n");
	}
	return;
}
/**
 * \brief Destroys a Wanpipe Channel
 * \param ftdmchan Channel to destroy
 * \return Success
 */
static FIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy)
{
#ifdef LIBSANGOMA_VERSION
	if (ftdmchan->mod_data) {
		sangoma_wait_obj_t *sangoma_wait_obj;
		sangoma_wait_obj = ftdmchan->mod_data;
		ftdmchan->mod_data = NULL;
		sangoma_wait_obj_delete(&sangoma_wait_obj);
	}
#endif

	if (ftdmchan->sockfd != FTDM_INVALID_SOCKET) {
		/* enable HW DTMF. As odd as it seems. Why enable when the channel is being destroyed and won't be used anymore?
		 * because that way we can transfer the DTMF state back to the driver, if we're being restarted we will set again
		 * the FEATURE_DTMF flag and use HW DTMF, if we don't enable here, then on module restart we won't see
		 * HW DTMF available and will use software */
		if (ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
			wanpipe_tdm_api_t tdm_api;
			int err;
			memset(&tdm_api, 0, sizeof(tdm_api));
			err = sangoma_tdm_enable_dtmf_events(ftdmchan->sockfd, &tdm_api);
			if (err) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed enabling Sangoma HW DTMF failed on channel destroy\n");
			}
		}
		sangoma_close(&ftdmchan->sockfd);
	}

	return FTDM_SUCCESS;
}
void sngisdn_snd_fac_req(ftdm_channel_t *ftdmchan)
{
	FacEvnt facEvnt;
	
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	if (!sngisdn_info->suInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending FACILITY, but no call data, ignoring (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
		return;
	}
		
	memset(&facEvnt, 0, sizeof(facEvnt));
	
	if (set_facility_ie_str(ftdmchan, &facEvnt.facElmt.facStr.val[2], (uint8_t*)&facEvnt.facElmt.facStr.len) != FTDM_SUCCESS) {
		/* No point in sending a FACILITY message if there is no Facility IE to transmit */
		return;
	}
	
	facEvnt.facElmt.eh.pres = PRSNT_NODEF;
	facEvnt.facElmt.facStr.pres = PRSNT_NODEF;
	facEvnt.facElmt.facStr.val[0] = 0x1C;
	facEvnt.facElmt.facStr.val[1] = (uint8_t)facEvnt.facElmt.facStr.len;
	facEvnt.facElmt.facStr.len +=2; /* Need to include the size of identifier + len */

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending FACILITY (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);

	if (sng_isdn_facility_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &facEvnt, MI_FACIL, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused FACILITY request\n");
	}
	return;
}
void sngisdn_snd_connect(ftdm_channel_t *ftdmchan)
{
	CnStEvnt cnStEvnt;
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_NETE_ISDN};

	if (sngisdn_test_flag(sngisdn_info, FLAG_SENT_CONNECT)) {
		return;
	}
	sngisdn_set_flag(sngisdn_info, FLAG_SENT_CONNECT);

	if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending CONNECT, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
		return;
	}
	
	memset(&cnStEvnt, 0, sizeof(cnStEvnt));

	/* Indicate channel ID only in first response */
	if (!ftdm_test_flag(sngisdn_info, FLAG_SENT_CHAN_ID)) {
		set_chan_id_ie(ftdmchan, &cnStEvnt.chanId);
	}
	set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind);
	set_facility_ie(ftdmchan, &cnStEvnt.facilityStr);

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending CONNECT (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);
	if (sng_isdn_con_response(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused CONNECT request\n");
	}
	return;
}
void sngisdn_snd_alert(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_ind)
{
	CnStEvnt cnStEvnt;
	
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending ALERT, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
		return;
	}	

	memset(&cnStEvnt, 0, sizeof(cnStEvnt));

	set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind);
	set_facility_ie(ftdmchan, &cnStEvnt.facilityStr);

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending ALERT (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);

	if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId,&cnStEvnt, MI_ALERTING, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused ALERT request\n");
	}
	return;
}
void sngisdn_snd_progress(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_ind)
{
	CnStEvnt cnStEvnt;
	
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending PROGRESS, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
		return;
	}	
	
	if (signal_data->switchtype == SNGISDN_SWITCH_INSNET) {
		/* Trillium Q931 layer complains of invalid event when receiving PROGRESS in
			INSNET variant, so PROGRESS event is probably invalid */
		return;
	}

	memset(&cnStEvnt, 0, sizeof(cnStEvnt));	
	set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind);
	set_facility_ie(ftdmchan, &cnStEvnt.facilityStr);

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending PROGRESS (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);
	if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId,&cnStEvnt, MI_PROGRESS, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused PROGRESS request\n");
	}
	return;
}
/* Used only for BRI PTMP - This function is used when the NT side makes a call out,
	and one or multiple TE's reply, then NT assigns the call by sending a con_complete*/
void sngisdn_snd_con_complete(ftdm_channel_t *ftdmchan)
{
	CnStEvnt cnStEvnt;
	
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending CONNECT COMPL , but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
		sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
		return;
	}
	
	memset(&cnStEvnt, 0, sizeof(cnStEvnt));

	/* Indicate channel ID only in first response */
	if (!ftdm_test_flag(sngisdn_info, FLAG_SENT_CHAN_ID)) {
		set_chan_id_ie(ftdmchan, &cnStEvnt.chanId);
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending CONNECT COMPL (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);

	if(sng_isdn_con_comp(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused CONNECT ACK request\n");
	}
	return;
}
ftdm_status_t set_bear_cap_ie(ftdm_channel_t *ftdmchan, BearCap *bearCap)
{
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	bearCap->eh.pres = PRSNT_NODEF;
	bearCap->infoTranCap.pres = PRSNT_NODEF;
	bearCap->infoTranCap.val = sngisdn_get_infoTranCap_from_user(ftdmchan->caller_data.bearer_capability);

	bearCap->codeStand0.pres = PRSNT_NODEF;
	bearCap->codeStand0.val = IN_CSTD_CCITT;
	bearCap->infoTranRate0.pres = PRSNT_NODEF;
	bearCap->infoTranRate0.val = IN_ITR_64KBIT;
	bearCap->tranMode.pres = PRSNT_NODEF;
	bearCap->tranMode.val = IN_TM_CIRCUIT;

	bearCap->usrInfoLyr1Prot.pres = PRSNT_NODEF;
	bearCap->usrInfoLyr1Prot.val = sngisdn_get_usrInfoLyr1Prot_from_user(ftdmchan->caller_data.bearer_layer1);

	switch (signal_data->switchtype) {
		case SNGISDN_SWITCH_NI2:
		case SNGISDN_SWITCH_4ESS:
		case SNGISDN_SWITCH_5ESS:
		case SNGISDN_SWITCH_DMS100:
		case SNGISDN_SWITCH_INSNET:
			if (bearCap->usrInfoLyr1Prot.val == IN_UIL1_G711ALAW) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Overriding bearer cap to u-law\n");
				bearCap->usrInfoLyr1Prot.val = IN_UIL1_G711ULAW;
			}
			break;
		case SNGISDN_SWITCH_EUROISDN:
		case SNGISDN_SWITCH_QSIG:
			if (bearCap->usrInfoLyr1Prot.val == IN_UIL1_G711ULAW) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Overriding bearer cap to a-law\n");
				bearCap->usrInfoLyr1Prot.val = IN_UIL1_G711ALAW;
			}
			break;
	}

	bearCap->lyr1Ident.pres = PRSNT_NODEF;
	bearCap->lyr1Ident.val = IN_L1_IDENT;
	
	return FTDM_SUCCESS;
}
ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan)
{
	const char* args;
	char *p;
	char *type = NULL;
	char *target = NULL;
	ftdm_status_t status = FTDM_FAIL;
	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
	unsigned i;

	args = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_arg");
	if (ftdm_strlen_zero(args)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because call_transfer_arg variable is not set\n");
		goto done;
	}

	type = ftdm_strdup(args);
	if ((p = strchr(type, '/'))) {
		target = ftdm_strdup(p+1);
		*p = '\0';
	}

	if (ftdm_strlen_zero(type) || ftdm_strlen_zero(target)) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid parameters for transfer %s, expected <type>/<target>\n", args);
		goto done;
	}

	if (sngisdn_info->transfer_data.type != SNGISDN_TRANSFER_NONE) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because an existing transfer transfer is pending (%s)\n", sngisdn_transfer_type2str(sngisdn_info->transfer_data.type));
		goto done;
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer requested type:%s target:%s\n", type, target);
	for (i = 0; i < ftdm_array_len(transfer_interfaces); i++ ) {
		if (!strcasecmp(transfer_interfaces[i].name, type)) {
			/* Depending on the transfer type, the transfer function may change the
			 * channel state to UP, or last_state, but the transfer function will always result in
			 * an immediate state change if FTDM_SUCCESS is returned */

			status = transfer_interfaces[i].func(ftdmchan, transfer_interfaces[i].type, target);
			goto done;
		}
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid transfer type:%s\n", type);

done:
	if (status != FTDM_SUCCESS) {
		ftdm_set_state(ftdmchan, ftdmchan->last_state);
	}

	ftdm_safe_free(type);
	ftdm_safe_free(target);
	return status;
}
ftdm_status_t check_for_state_change(ftdm_channel_t *ftdmchan)
{

#if 0
    ftdm_log_chan_msg(ftdmchan, "Checking for pending state change\n");
#endif
    /* check to see if there are any pending state changes on the channel and give them a sec to happen*/
    ftdm_wait_for_flag_cleared(ftdmchan, FTDM_CHANNEL_STATE_CHANGE, 5000);

    /* check the flag to confirm it is clear now */
    if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
        /* the flag is still up...so we have a problem */
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "FTDM_CHANNEL_STATE_CHANGE set for over 500ms\n");

        /* move the state of the channel to RESTART to force a reset */
        ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_RESTART);

        return FTDM_FAIL;
    }
    return FTDM_SUCCESS;
}
void sngisdn_snd_setup(ftdm_channel_t *ftdmchan)
{
	ConEvnt conEvnt;
	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};	

	ftdm_assert((!sngisdn_info->suInstId && !sngisdn_info->spInstId), "Trying to call out, but call data was not cleared\n");
	
	sngisdn_info->suInstId = get_unique_suInstId(signal_data->cc_id);
	sngisdn_info->spInstId = 0;

	ftdm_mutex_lock(g_sngisdn_data.ccs[signal_data->cc_id].mutex);
	g_sngisdn_data.ccs[signal_data->cc_id].active_suInstIds[sngisdn_info->suInstId] = sngisdn_info;
	ftdm_mutex_unlock(g_sngisdn_data.ccs[signal_data->cc_id].mutex);

	memset(&conEvnt, 0, sizeof(conEvnt));
	if (signal_data->switchtype == SNGISDN_SWITCH_EUROISDN || signal_data->force_sending_complete == SNGISDN_OPT_TRUE) {
		conEvnt.sndCmplt.eh.pres = PRSNT_NODEF;
	}
	
	if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP &&
		signal_data->signalling == SNGISDN_SIGNALING_NET) {
		sngisdn_info->ces = CES_MNGMNT;
	}
	ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_INFO, "Outgoing call: Called No:[%s] Calling No:[%s]\n", ftdmchan->caller_data.dnis.digits, ftdmchan->caller_data.cid_num.digits);

	set_chan_id_ie(ftdmchan, &conEvnt.chanId);	
	set_bear_cap_ie(ftdmchan, &conEvnt.bearCap[0]);
	set_called_num(ftdmchan, &conEvnt.cdPtyNmb);
	set_calling_num(ftdmchan, &conEvnt.cgPtyNmb);
	set_calling_num2(ftdmchan, &conEvnt.cgPtyNmb2);
	set_calling_subaddr(ftdmchan, &conEvnt.cgPtySad);
	set_called_subaddr(ftdmchan, &conEvnt.cdPtySad);
	set_redir_num(ftdmchan, &conEvnt.redirNmb);
	set_calling_name(ftdmchan, &conEvnt);
	set_network_specific_fac(ftdmchan, &conEvnt.netFac[0]);

	/* set_facility_ie will overwrite Calling Name for NI-2 if user specifies custom Facility IE */
	set_facility_ie(ftdmchan, &conEvnt.facilityStr);
	set_prog_ind_ie(ftdmchan, &conEvnt.progInd, prog_ind);

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending SETUP (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);

	if (sng_isdn_con_request(signal_data->cc_id, sngisdn_info->suInstId, &conEvnt, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused SETUP request\n");
	}

	return;
}
/* This is used to request Q.921 to initiate link establishment */
void sngisdn_snd_dl_req(ftdm_channel_t *ftdmchan)
{
	CnStEvnt cnStEvnt;
	
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	memset(&cnStEvnt, 0, sizeof(cnStEvnt));

	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Requesting Link establishment (suId:%d dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);

	if (sng_isdn_con_status(signal_data->cc_id, 0, 0, &cnStEvnt, MI_INFO, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused Link establishment\n");
	}
	return;
}
void sngisdn_snd_status_enq(ftdm_channel_t *ftdmchan)
{
	StaEvnt staEvnt;

	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	//ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Sending STATUS ENQ\n");

	memset(&staEvnt, 0, sizeof(StaEvnt));
	
	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending Status ENQ on suId:%d suInstId:%u spInstId:%d dchan:%d ces:%d\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, sngisdn_dchan(signal_data)->link_id, sngisdn_info->ces);
	if (sng_isdn_status_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &staEvnt, MI_STATENQ)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, 	"stack refused Status ENQ request\n");
	}
	return;
}
void sngisdn_snd_restart(ftdm_channel_t *ftdmchan)
{
	Rst rstEvnt;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	memset(&rstEvnt, 0, sizeof(rstEvnt));

	set_chan_id_ie(ftdmchan, &rstEvnt.chanId);
	set_restart_ind_ie(ftdmchan, &rstEvnt.rstInd);
	
	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending RESTART (suId:%d dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_dchan(signal_data)->link_id, CES_MNGMNT);

	if (sng_isdn_restart_request(signal_data->cc_id, &rstEvnt, sngisdn_dchan(signal_data)->link_id, CES_MNGMNT, IN_SND_RST)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "stack refused RESTART request\n");
	}
	return;
}
/* French SPIROU send Charging Acknowledgement */
void ft_to_sngss7_txa (ftdm_channel_t * ftdmchan)
{
#ifndef SANGOMA_SPIROU
    SS7_FUNC_TRACE_ENTER (__FUNCTION__);
    ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "TXA message not supported!, please update your libsng_ss7\n");
#else
    SiCnStEvnt txa;
    sngss7_chan_data_t	*sngss7_info = ftdmchan->call_data;
    SS7_FUNC_TRACE_ENTER (__FUNCTION__);

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

    sng_cc_con_status(1, sngss7_info->suInstId, sngss7_info->spInstId, sngss7_info->circuit->id, &txa, CHARGE_ACK);

    SS7_INFO_CHAN(ftdmchan,"[CIC:%d]Tx TXA\n", sngss7_info->circuit->cic);
#endif
    SS7_FUNC_TRACE_EXIT (__FUNCTION__);
    return;
}
ftdm_status_t get_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t data_len)
{
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	
	if (signal_data->facility_ie_decode == SNGISDN_OPT_FALSE) {
		/* Max size of Facility IE is 255 */
		uint8_t my_data [255];
		
		/* Always include Facility IE identifier + len so this can be used as a sanity check by the user */
		my_data[0] = SNGISDN_Q931_FACILITY_IE_ID;
		my_data[1] = data_len;
		memcpy(&my_data[2], data, data_len);

		sngisdn_add_raw_data((sngisdn_chan_data_t*)ftdmchan->call_data, my_data, data_len+2);
		
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Raw Facility IE copied available\n");
	} else {
		/* Call libsng_isdn to process facility IE's here */
	}
	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;
}
Exemple #26
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;
}
/* 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;
}
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;
}
Exemple #29
0
/**
 * \brief Executes an FreeTDM command on a Wanpipe channel
 * \param ftdmchan Channel to execute command on
 * \param command FreeTDM command to execute
 * \param obj Object (unused)
 * \return Success or failure
 */
static FIO_COMMAND_FUNCTION(wanpipe_command)
{
	wanpipe_tdm_api_t tdm_api;
	int err = 0;

	memset(&tdm_api, 0, sizeof(tdm_api));

	switch(command) {
	case FTDM_COMMAND_OFFHOOK:
		{
			err=sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api);
			if (err) {
				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "OFFHOOK Failed");
				return FTDM_FAIL;
			}
			ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
		}
		break;
	case FTDM_COMMAND_ONHOOK:
		{
			err=sangoma_tdm_txsig_onhook(ftdmchan->sockfd,&tdm_api);
			if (err) {
				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ONHOOK Failed");
				return FTDM_FAIL;
			}
			ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
		}
		break;
	case FTDM_COMMAND_GENERATE_RING_ON:
		{
			err=sangoma_tdm_txsig_start(ftdmchan->sockfd,&tdm_api);
			if (err) {
				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring Failed");
				return FTDM_FAIL;
			}
			ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING);
			ftdm_set_pflag_locked(ftdmchan, WP_RINGING);
			ftdmchan->ring_time = ftdm_current_time_in_ms() + wp_globals.ring_on_ms;
		}
		break;
	case FTDM_COMMAND_GENERATE_RING_OFF:
		{
			err=sangoma_tdm_txsig_offhook(ftdmchan->sockfd,&tdm_api);
			if (err) {
				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring-off Failed");
				return FTDM_FAIL;
			}
			ftdm_clear_pflag_locked(ftdmchan, WP_RINGING);
			ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING);
		}
		break;
	case FTDM_COMMAND_GET_INTERVAL:
		{
			err=sangoma_tdm_get_usr_period(ftdmchan->sockfd, &tdm_api);
			if (err > 0 ) {
				FTDM_COMMAND_OBJ_INT = err;
				err=0;
			}
		}
		break;
	case FTDM_COMMAND_ENABLE_ECHOCANCEL:
		{
			err=sangoma_tdm_enable_hwec(ftdmchan->sockfd, &tdm_api);
			if (err) {
             			snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HWEC Enable Failed");
				return FTDM_FAIL;
			}
		}
		break;
	case FTDM_COMMAND_DISABLE_ECHOCANCEL:
		{
			err=sangoma_tdm_disable_hwec(ftdmchan->sockfd, &tdm_api);
			if (err) {
             			snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HWEC Disable Failed");
				return FTDM_FAIL;
			}
		}
		break;
	case FTDM_COMMAND_ENABLE_DTMF_DETECT:
		{
#ifdef WP_API_FEATURE_DTMF_EVENTS
			err = sangoma_tdm_enable_dtmf_events(ftdmchan->sockfd, &tdm_api);
			if (err) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Enabling of Sangoma HW DTMF failed\n");
             			snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HW DTMF Enable Failed");
				return FTDM_FAIL;
			}
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Enabled DTMF events\n");
#else
			return FTDM_NOTIMPL;
#endif
		}
		break;
	case FTDM_COMMAND_DISABLE_DTMF_DETECT:
		{
#ifdef WP_API_FEATURE_DTMF_EVENTS
			err = sangoma_tdm_disable_dtmf_events(ftdmchan->sockfd, &tdm_api);
			if (err) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Disabling of Sangoma HW DTMF failed\n");
             			snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "HW DTMF Disable Failed");
				return FTDM_FAIL;
			}
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Disabled DTMF events\n");
#else
			return FTDM_NOTIMPL;
#endif
		}
		break;
	case FTDM_COMMAND_ENABLE_LOOP:
		{
#ifdef WP_API_FEATURE_LOOP
         	err=sangoma_tdm_enable_loop(ftdmchan->sockfd, &tdm_api);
			if (err) {
				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Loop Enable Failed");
				return FTDM_FAIL;
			}
#endif		
		}
		break;
	case FTDM_COMMAND_DISABLE_LOOP:
		{
#ifdef WP_API_FEATURE_LOOP
         	err=sangoma_tdm_disable_loop(ftdmchan->sockfd, &tdm_api);
			if (err) {
				snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Loop Disable Failed");
				return FTDM_FAIL;
			}
#endif	 
		}
		break;
	case FTDM_COMMAND_SET_INTERVAL: 
		{
			err=sangoma_tdm_set_usr_period(ftdmchan->sockfd, &tdm_api, FTDM_COMMAND_OBJ_INT);
			ftdmchan->packet_len = ftdmchan->native_interval * (ftdmchan->effective_codec == FTDM_CODEC_SLIN ? 16 : 8);
		}
		break;
	case FTDM_COMMAND_SET_CAS_BITS:
		{
#ifdef LIBSANGOMA_VERSION
			err = sangoma_tdm_write_rbs(ftdmchan->sockfd,&tdm_api, ftdmchan->physical_chan_id, wanpipe_swap_bits(FTDM_COMMAND_OBJ_INT));
#else
			err = sangoma_tdm_write_rbs(ftdmchan->sockfd, &tdm_api, wanpipe_swap_bits(FTDM_COMMAND_OBJ_INT));
#endif
		}
		break;
	case FTDM_COMMAND_GET_CAS_BITS:
		{
#ifdef LIBSANGOMA_VERSION
			unsigned char rbsbits;
			err = sangoma_tdm_read_rbs(ftdmchan->sockfd, &tdm_api, ftdmchan->physical_chan_id, &rbsbits);
			if (!err) {
				FTDM_COMMAND_OBJ_INT = wanpipe_swap_bits(rbsbits);
			}
#else
			// does sangoma_tdm_read_rbs is available here?
			FTDM_COMMAND_OBJ_INT = ftdmchan->rx_cas_bits;
#endif
		}
		break;
	case FTDM_COMMAND_SET_LINK_STATUS:
		{
			ftdm_channel_hw_link_status_t status = FTDM_COMMAND_OBJ_INT;
			char sangoma_status = status == FTDM_HW_LINK_CONNECTED ? FE_CONNECTED : FE_DISCONNECTED;
			err = sangoma_tdm_set_fe_status(ftdmchan->sockfd, &tdm_api, sangoma_status);
		}
		break;
	case FTDM_COMMAND_GET_LINK_STATUS:
		{
			unsigned char sangoma_status = 0;
			err = sangoma_tdm_get_fe_status(ftdmchan->sockfd, &tdm_api, &sangoma_status);
			if (!err) {
				FTDM_COMMAND_OBJ_INT = sangoma_status == FE_CONNECTED ? FTDM_HW_LINK_CONNECTED : FTDM_HW_LINK_DISCONNECTED;
			}
		}
		break;
	default:
		break;
	};

	if (err) {
		snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno));
		return FTDM_FAIL;
	}


	return FTDM_SUCCESS;
}
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;
}