void sngisdn_snd_event(sngisdn_span_data_t *signal_data, ftdm_oob_event_t event)
{
	sng_l1_event_t l1_event;

	if (!signal_data->dchan) {
		return;
	}
	memset(&l1_event, 0, sizeof(l1_event));

	switch(event) {
		case FTDM_OOB_ALARM_CLEAR:
			l1_event.type = SNG_L1EVENT_ALARM_OFF;
			sng_isdn_event_ind(signal_data->link_id, &l1_event);

			if (!signal_data->dl_request_pending) {
				signal_data->dl_request_pending = 1;
				ftdm_sched_timer(signal_data->sched, "delayed_dl_req", 8000, sngisdn_delayed_dl_req, (void*) signal_data, NULL);
			}
			break;
		case FTDM_OOB_ALARM_TRAP:
			l1_event.type = SNG_L1EVENT_ALARM_ON;
			sng_isdn_event_ind(signal_data->link_id, &l1_event);
			break;
		default:
			/* We do not care about the other OOB events for now */
			return;
	}
	return;
}
Example #2
0
void sngisdn_set_chan_sig_status(ftdm_channel_t *ftdmchan, ftdm_signaling_status_t status)
{
	ftdm_sigmsg_t sig;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*)ftdmchan->span->signal_data;
	
	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Signalling link status changed to %s\n", ftdm_signaling_status2str(status));

	memset(&sig, 0, sizeof(sig));
	sig.chan_id = ftdmchan->chan_id;
	sig.span_id = ftdmchan->span_id;
	sig.channel = ftdmchan;
	sig.event_id = FTDM_SIGEVENT_SIGSTATUS_CHANGED;
	sig.ev_data.sigstatus.status = status;
	ftdm_span_send_signal(ftdmchan->span, &sig);

	if (FTDM_SPAN_IS_BRI(ftdmchan->span)) {		
		sngisdn_chan_data_t		*sngisdn_info = ftdmchan->call_data;
		if (ftdm_test_flag(sngisdn_info, FLAG_ACTIVATING)) {
			ftdm_clear_flag(sngisdn_info, FLAG_ACTIVATING);

			ftdm_sched_timer(signal_data->sched, "delayed_setup", 1000, sngisdn_delayed_setup, (void*) sngisdn_info, NULL);
		}
	}
	return;
}
Example #3
0
int main(int argc, char *argv[])
{
	ftdm_status_t status;
	custom_data_t data;

	ftdm_sched_t *sched;
	signal(SIGINT, trap);
	
	ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);

	ftdm_cpu_monitor_disable();

	if (ftdm_global_init() != FTDM_SUCCESS) {
		fprintf(stderr, "Error loading FreeTDM\n");
		exit(-1);
	}

	status = ftdm_sched_create(&sched, "testsched");
	if (status != FTDM_SUCCESS) {
		fprintf(stderr, "Error creating sched\n");
		exit(-1);
	}

	data.sched = sched;
	data.counter = 10;
	data.beat = 5000;
	data.callback = handle_heartbeat;
	status = ftdm_sched_timer(sched, "heartbeat", data.beat, data.callback, &data, &data.heartbeat_timer);
	if (status != FTDM_SUCCESS) {
		fprintf(stderr, "Error creating heartbeat timer\n");
		exit(-1);
	}

	ftdm_sched_free_run(sched);

	while (running) {
		ftdm_sleep(10);
	}

	ftdm_global_destroy();

	printf("Done, press any key to die!\n");

	getchar();
	return 0;
}
Example #4
0
void handle_heartbeat(void *usrdata)
{
	ftdm_status_t status;
	custom_data_t *data = usrdata;

	printf("beep (elapsed %dms count= %d)\n", data->beat, data->counter);
	if (data->beat > 1000) {
		data->beat -= 1000;
	} else if (data->beat <= 1000 && data->beat > 200) {
		data->beat -= 100;
	} else if (data->beat <= 200 && data->beat > 100) {
		if (!data->counter--) {
			data->counter = 5;
			data->beat -= 100;
		}
	} else if (data->beat <= 100 && data->beat > 10) {
		if (!data->counter--) {
			data->counter = 10;
			data->beat -= 10;
			if (data->beat == 10) {
				data->counter = 200;
			}
		}
	} else {
		if (!data->counter--) {
			data->counter = 5;
			data->beat--;
		}
	}

	if (!data->beat) {
		printf("beeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep you're dead!\n");
		return;
	}

	data->heartbeat_timer = NULL;
	status = ftdm_sched_timer(data->sched, "heartbeat", data->beat, data->callback, data, &data->heartbeat_timer);
	if (status != FTDM_SUCCESS) {
		fprintf(stderr, "Error creating heartbeat timer\n");
		running = 0;
		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;
}
/* Remote side transmit a SETUP */
void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
{
	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);
	
	int16_t suId = sngisdn_event->suId;
	uint32_t suInstId = sngisdn_event->suInstId;
	uint32_t spInstId = sngisdn_event->spInstId;
	int16_t dChan = sngisdn_event->dChan;
	uint8_t ces = sngisdn_event->ces;
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;	
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	ConEvnt *conEvnt = &sngisdn_event->event.conEvnt;

	ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n");
	
	ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_DEBUG, "Processing SETUP (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId);
	
	switch (ftdmchan->state) {
		case FTDM_CHANNEL_STATE_DOWN: /* Proper state to receive a SETUP */
			if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE) ||
				ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) {

				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Received SETUP but channel is in USE, saving call for later processing\n");
				/* the flag the channel as having a collision */
				sngisdn_set_flag(sngisdn_info, FLAG_GLARE);

				/* save the SETUP for processing once the channel has gone to DOWN */
				memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt));
				sngisdn_info->glare.suId = suId;
				sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */
				sngisdn_info->glare.spInstId = spInstId;
				sngisdn_info->glare.dChan = dChan;
				sngisdn_info->glare.ces = ces;
				break;
			}
			
			sngisdn_info->suInstId = get_unique_suInstId(suId);
			sngisdn_info->spInstId = spInstId;

			/* If this is a glared call that was previously saved, we moved
			all the info to the current call, so clear the glared saved data */
			if (sngisdn_info->glare.spInstId == spInstId) {
				clear_call_glare_data(sngisdn_info);
			}			

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

			ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND);

			if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP &&
				signal_data->signalling == SNGISDN_SIGNALING_NET) {
				sngisdn_info->ces = ces;
			}

			/* try to open the channel */
			if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Failed to open channel");
				sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_REL);
				ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE;
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL);
				break;
			}

#if 0
			/* Export ftdmchan variables here if we need to */
			ftdm_channel_add_var(ftdmchan, "isdn_specific_var", "1");
			ftdm_channel_add_var(ftdmchan, "isdn_crap", "morecrap");
			ftdm_channel_add_var(ftdmchan, "isdn_stuff", "s");
			ftdm_channel_add_var(ftdmchan, "isdn_d", "asdsadasdasdsad");
#endif
			/* Fill in call information */
			cpy_calling_num_from_stack(&ftdmchan->caller_data, &conEvnt->cgPtyNmb);
			cpy_called_num_from_stack(&ftdmchan->caller_data, &conEvnt->cdPtyNmb);
			cpy_calling_name_from_stack(&ftdmchan->caller_data, &conEvnt->display);

			if (conEvnt->bearCap[0].eh.pres) {
				ftdmchan->caller_data.bearer_layer1 = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].usrInfoLyr1Prot.val);
				ftdmchan->caller_data.bearer_capability = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].infoTranCap.val);
			}

			if (signal_data->switchtype == SNGISDN_SWITCH_NI2) {
				if (conEvnt->shift11.eh.pres && conEvnt->ni2OctStr.eh.pres) {
					if (conEvnt->ni2OctStr.str.len == 4 && conEvnt->ni2OctStr.str.val[0] == 0x37) {
						snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", conEvnt->ni2OctStr.str.val[3]);
					}
				}

				
				if (signal_data->facility == SNGISDN_OPT_TRUE && conEvnt->facilityStr.eh.pres) {
					/* Verify whether the Caller Name will come in a subsequent FACILITY message */
					uint16_t ret_val;
					char retrieved_str[255];
					
					ret_val = sng_isdn_retrieve_facility_caller_name(conEvnt->facilityStr.facilityStr.val, conEvnt->facilityStr.facilityStr.len, retrieved_str);
					/*
						return values for "sng_isdn_retrieve_facility_information_following":
						If there will be no information following, or fails to decode IE, returns -1
						If there will be no information following, but current FACILITY IE contains a caller name, returns 0
						If there will be information following, returns 1
					*/

					if (ret_val == 1) {
						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Expecting Caller name in FACILITY\n");
						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_GET_CALLERID);
						/* Launch timer in case we never get a FACILITY msg */
						if (signal_data->facility_timeout) {
							ftdm_sched_timer(signal_data->sched, "facility_timeout", signal_data->facility_timeout, 
									sngisdn_facility_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_TIMER_FACILITY]);
						}
						break;
					} else if (ret_val == 0) {
						strcpy(ftdmchan->caller_data.cid_name, retrieved_str);
					}
				}
			}

			if (signal_data->overlap_dial == SNGISDN_OPT_TRUE && !conEvnt->sndCmplt.eh.pres) {
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
			} else {
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
			}
			break;
		case FTDM_CHANNEL_STATE_TERMINATING:
			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Processing SETUP in TERMINATING state, saving SETUP info for later processing\n");
			ftdm_assert(!sngisdn_test_flag(sngisdn_info, FLAG_GLARE), "Trying to save GLARE info, but we already had a glare\n");
			
			sngisdn_set_flag(sngisdn_info, FLAG_GLARE);

			/* save the SETUP for processing once the channel has gone to DOWN */
			memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt));
			sngisdn_info->glare.suId = suId;
			sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */
			sngisdn_info->glare.spInstId = spInstId;
			sngisdn_info->glare.dChan = dChan;
			sngisdn_info->glare.ces = ces;
			
			break;
		case FTDM_CHANNEL_STATE_DIALING:	/* glare */
			if (signal_data->signalling == SNGISDN_SIGNALING_NET) {
				/* Save inbound call info so we can send a RELEASE when this channel goes to a different state */
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Processing SETUP in DIALING state, rejecting inbound call\n");
				sngisdn_set_flag(sngisdn_info, FLAG_DELAYED_REL);

				sngisdn_info->glare.suId = suId;
				sngisdn_info->glare.suInstId = get_unique_suInstId(suId);
				sngisdn_info->glare.spInstId = spInstId;

				sngisdn_info->glare.dChan = dChan;
				sngisdn_info->glare.ces = ces;
				ftdmchan->caller_data.hangup_cause = 0x2C; /* Channel requested not available */
				ftdm_sched_timer(signal_data->sched, "delayed_release", 1, sngisdn_delayed_release, (void*) sngisdn_info, NULL);
			} else {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Processing SETUP in DIALING state, saving SETUP info for later processing\n");
				
				/* the flag the channel as having a collision */
				ftdm_assert(!sngisdn_test_flag(sngisdn_info, FLAG_GLARE), "Trying to save GLARE info, but we already had a glare");
				sngisdn_set_flag(sngisdn_info, FLAG_GLARE);

				/* save the SETUP for processing once the channel has gone to DOWN */
				memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt));
				sngisdn_info->glare.suId = suId;
				sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */
				sngisdn_info->glare.spInstId = spInstId;
				sngisdn_info->glare.dChan = dChan;
				sngisdn_info->glare.ces = ces;

				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
			}
			break;
		default:
			ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing SETUP in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state));
			break;
	}
	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
static 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;
}