ftdm_status_t sngisdn_activate_trace(ftdm_span_t *span, sngisdn_tracetype_t trace_opt)
{
	sngisdn_span_data_t *signal_data = sngisdn_dchan((sngisdn_span_data_t*)span->signal_data);

	if (!signal_data) {
		ftdm_log(FTDM_LOG_ERROR, "%s:Span is not used by signalling module\n", span->name);
		return FTDM_FAIL;
	}

	switch (trace_opt) {
		case SNGISDN_TRACE_DISABLE:
			if (sngisdn_test_trace_flag(signal_data, SNGISDN_TRACE_Q921)) {
				ftdm_log(FTDM_LOG_INFO, "%s:Disabling q921 trace\n", signal_data->ftdm_span->name);
				sngisdn_clear_trace_flag(signal_data, SNGISDN_TRACE_Q921);
				
				if (sngisdn_cntrl_q921(signal_data->ftdm_span, ADISIMM, SATRC) != FTDM_SUCCESS) {
					ftdm_log(FTDM_LOG_INFO, "%s:Failed to disable q921 trace\n", signal_data->ftdm_span->name);
				}
			}
			if (sngisdn_test_trace_flag(signal_data, SNGISDN_TRACE_Q931)) {
				ftdm_log(FTDM_LOG_INFO, "%s:Disabling q921 trace\n", signal_data->ftdm_span->name);
				sngisdn_clear_trace_flag(signal_data, SNGISDN_TRACE_Q931);

				if (sngisdn_cntrl_q931(signal_data->ftdm_span, ADISIMM, SATRC) != FTDM_SUCCESS) {
					ftdm_log(FTDM_LOG_INFO, "%s:Failed to disable q921 trace\n", signal_data->ftdm_span->name);
				}
			}
			break;
		case SNGISDN_TRACE_Q921:
			if (!sngisdn_test_trace_flag(signal_data, SNGISDN_TRACE_Q921)) {
				ftdm_log(FTDM_LOG_INFO, "%s:Enabling q921 trace\n", signal_data->ftdm_span->name);
				sngisdn_set_trace_flag(signal_data, SNGISDN_TRACE_Q921);

				if (sngisdn_cntrl_q921(signal_data->ftdm_span, AENA, SATRC) != FTDM_SUCCESS) {
					ftdm_log(FTDM_LOG_INFO, "%s:Failed to enable q921 trace\n", signal_data->ftdm_span->name);
				}
			}
			break;
		case SNGISDN_TRACE_Q931:
			if (!sngisdn_test_trace_flag(signal_data, SNGISDN_TRACE_Q931)) {
				ftdm_log(FTDM_LOG_INFO, "s%d Enabling q931 trace\n", signal_data->link_id);
				sngisdn_set_trace_flag(signal_data, SNGISDN_TRACE_Q931);
				
				if (sngisdn_cntrl_q931(signal_data->ftdm_span, AENA, SATRC) != FTDM_SUCCESS) {
					ftdm_log(FTDM_LOG_INFO, "%s:Failed to enable q931 trace\n", signal_data->ftdm_span->name);
				}
			}
			break;
	}
	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_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;
}
/* 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;
}
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_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;
}