Example #1
0
void on_wat_con_sts(unsigned char span_id, uint8_t call_id, wat_con_status_t *status)
{

	ftdm_span_t *span = NULL;
	//ftdm_status_t ftdm_status = FTDM_FAIL;
	ftdm_gsm_span_data_t *gsm_data = NULL;

	if(!(span = GetSpanByID(span_id, &gsm_data))) {
		return;
	}



	switch(status->type) {

		case WAT_CON_STATUS_TYPE_RINGING:
			ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts -  WAT_CON_STATUS_TYPE_RINGING\r\n");
			ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_RINGING);
		break;		
	
		case WAT_CON_STATUS_TYPE_ANSWER:
			ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts -  WAT_CON_STATUS_TYPE_ANSWER\r\n");
			ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
		break;
		default:
			ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - Unhandled state %d\n", span_id);

	};
	
	
	return;
}
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;
}
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;
}
Example #4
0
void on_wat_rel_cfm(unsigned char span_id, uint8_t call_id)
{
	ftdm_log(FTDM_LOG_INFO, "s%d: Call hangup complete (id:%d)\n", span_id, call_id);
	ftdm_span_t *span = NULL;
	//ftdm_status_t ftdm_status = FTDM_FAIL;
	ftdm_gsm_span_data_t *gsm_data = NULL;
  
	if(!(span = GetSpanByID(span_id, &gsm_data))) {
		return;
	}

	switch(gsm_data->dchan->state) {
	case FTDM_CHANNEL_STATE_UP:
		ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_HANGUP);
		break;
	default:
		ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_DOWN);
		break;
	}

}
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;
}
Example #6
0
void on_wat_con_ind(unsigned char span_id, uint8_t call_id, wat_con_event_t *con_event)
{
	//fprintf(stdout, "s%d: Incoming call (id:%d) Calling Number:%s  Calling Name:\"%s\" type:%d plan:%d\n", span_id, call_id, con_event->calling_num.digits, con_event->calling_name, con_event->calling_num.type, con_event->calling_num.plan);
	
	ftdm_log(FTDM_LOG_INFO, "s%d: Incoming call (id:%d) Calling Number:%s  Calling Name:\"%s\" type:%d plan:%d\n", span_id, call_id, con_event->calling_num.digits, con_event->calling_name, con_event->calling_num.type, con_event->calling_num.plan);
	
	ftdm_span_t *span = NULL;
	//ftdm_status_t ftdm_status = FTDM_FAIL;
	ftdm_gsm_span_data_t *gsm_data = NULL;

	if(!(span = GetSpanByID(span_id, &gsm_data))) {
		return;
	}	  

	gsm_data->call_id = call_id;			
	
	#define ZERO_ARRAY(arr) memset(arr, 0, sizeof(arr))
	// cid date
	{
		time_t t;
		struct tm *tmp;
		t = time(NULL);
		tmp = localtime(&t);
		if (tmp == NULL) {
			ZERO_ARRAY(gsm_data->bchan->caller_data.cid_date);
			strftime(gsm_data->bchan->caller_data.cid_date, sizeof(gsm_data->bchan->caller_data.cid_date), "%y/%m/%d", tmp);
		}

	}

	// cid name
	ZERO_ARRAY(gsm_data->bchan->caller_data.cid_name);
	strncpy(gsm_data->bchan->caller_data.cid_name, con_event->calling_name,sizeof(gsm_data->bchan->caller_data.cid_name));

	// dnis
	ZERO_ARRAY(gsm_data->bchan->caller_data.dnis.digits);
	strncpy(gsm_data->bchan->caller_data.dnis.digits, con_event->calling_num.digits, FTDM_DIGITS_LIMIT);
	
	//collected
	ZERO_ARRAY(gsm_data->bchan->caller_data.collected);
	strncpy(gsm_data->bchan->caller_data.collected, con_event->calling_num.digits, FTDM_DIGITS_LIMIT);
	
	ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_RING);

	if (ftdm_channel_open_chan(gsm_data->bchan) != FTDM_SUCCESS) {
		ftdm_log_chan(gsm_data->bchan, FTDM_LOG_ERROR, "Failed to open GSM b-channel of span %s!\n", span->name);
	}
	
}
Example #7
0
void on_wat_rel_ind(unsigned char span_id, uint8_t call_id, wat_rel_event_t *rel_event)
{
	ftdm_log(FTDM_LOG_INFO, "s%d: Call hangup (id:%d) cause:%d\n", span_id, call_id, rel_event->cause);

	ftdm_span_t *span = NULL;
	//ftdm_status_t ftdm_status = FTDM_FAIL;
	ftdm_gsm_span_data_t *gsm_data = NULL;

	if(!(span = GetSpanByID(span_id, &gsm_data))) {
		return;
	}

	ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_HANGUP);

	
}
void sngisdn_facility_timeout(void* p_sngisdn_info)
{
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info;
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;	

	ftdm_mutex_lock(ftdmchan->mutex);
	if (ftdmchan->state == FTDM_CHANNEL_STATE_GET_CALLERID) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Facility timeout reached proceeding with call (suId:%d suInstId:%u spInstId:%u)\n",
					  signal_data->cc_id, sngisdn_info->spInstId, sngisdn_info->suInstId);
		
		ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
	}
	
	ftdm_mutex_unlock(ftdmchan->mutex);
	return;
}
void sngisdn_t3_timeout(void* p_sngisdn_info)
{
	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info;
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Timer T3 expired (suId:%d suInstId:%u spInstId:%u)\n",
				  signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId);
	ftdm_mutex_lock(ftdmchan->mutex);
	if (ftdm_test_flag(sngisdn_info, FLAG_ACTIVATING)){
		/* PHY layer timed-out, need to clear the call */
		ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Failed to Wake-Up line (suId:%d suInstId:%u spInstId:%u)\n",
					  signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId);

		ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NO_ROUTE_DESTINATION;
		ftdm_clear_flag(sngisdn_info, FLAG_ACTIVATING);
		ftdm_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
		ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
	}	
	ftdm_mutex_unlock(ftdmchan->mutex);
}
void sngisdn_process_sta_cfm (sngisdn_event_data_t *sngisdn_event)
{
	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);
	
	int16_t suId = sngisdn_event->suId;
	uint32_t suInstId = sngisdn_event->suInstId;
	uint32_t spInstId = sngisdn_event->spInstId;
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	
	StaEvnt *staEvnt = &sngisdn_event->event.staEvnt;
	
	uint8_t call_state = 0;

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

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

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

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

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

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

	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
void sngisdn_process_rel_ind (sngisdn_event_data_t *sngisdn_event)
{
	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);
	int16_t suId = sngisdn_event->suId;
	uint32_t suInstId = sngisdn_event->suInstId;
	uint32_t spInstId = sngisdn_event->spInstId;
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;	
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	
	RelEvnt *relEvnt = &sngisdn_event->event.relEvnt;

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing RELEASE/RELEASE COMPLETE (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId);
	
	ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n");
	
	if ((suInstId && (sngisdn_info->glare.suInstId == suInstId)) ||
		(spInstId && (sngisdn_info->glare.spInstId == spInstId))) {

		/* This hangup is for a glared saved call */
		ftdm_clear_flag(sngisdn_info, FLAG_DELAYED_REL);
		clear_call_glare_data(sngisdn_info);

		ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
		return;
	}

	/* check whether the ftdm channel is in a state to accept a call */
	switch (ftdmchan->state) {
		case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
			/* go to DOWN */
			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
			break;
		case FTDM_CHANNEL_STATE_DOWN:
			/* do nothing, just drop the message */
			break;
		case FTDM_CHANNEL_STATE_DIALING:
			/* Remote side rejected our SETUP message on outbound call */
			if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP)) {
				sng_isdn_set_avail_rate(ftdmchan->span, SNGISDN_AVAIL_DOWN);
			}
			/* fall-through */
		case FTDM_CHANNEL_STATE_PROGRESS:
		case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
		case FTDM_CHANNEL_STATE_UP:
		case FTDM_CHANNEL_STATE_RING:
			/* If we previously had a glare on this channel,
			this RELEASE could be for the previous call.  Confirm whether call_data has
			not changed while we were waiting for ftdmchan->mutex by comparing suInstId's */
			if (((sngisdn_chan_data_t*)ftdmchan->call_data)->suInstId == suInstId ||
									((sngisdn_chan_data_t*)ftdmchan->call_data)->spInstId == spInstId) {
				if (relEvnt->causeDgn[0].eh.pres && relEvnt->causeDgn[0].causeVal.pres) {
					ftdmchan->caller_data.hangup_cause = relEvnt->causeDgn[0].causeVal.val;
					ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "cause:%d\n", ftdmchan->caller_data.hangup_cause);
				} else {
					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "RELEASE COMPLETE did not have a cause code\n");
					ftdmchan->caller_data.hangup_cause = 0;
				}

				sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT);
				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);

			} else {
				ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "RELEASE was for previous call (suInstId:%u spInstId:%u)\n", suInstId, spInstId);
			}
			break;
		case FTDM_CHANNEL_STATE_COLLECT:
		case FTDM_CHANNEL_STATE_GET_CALLERID:
			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL);
			break;
		case FTDM_CHANNEL_STATE_TERMINATING:
			if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE) &&
								sngisdn_info->glare.suInstId != suInstId) {
				/* This release if for the outbound call that we already started clearing */

				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Received RELEASE for local glared call\n");
				sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT);
			} else {
				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Received release before we could clear local call\n");
				/* FS core took too long to respond to the SIG STOP event */
				sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT);
				/* set abort flag so that we do not transmit another release complete on this channel once FS core is done */
			}
			break;
		default:
			ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Received RELEASE in an invalid state (%s)\n",
							ftdm_channel_state2str(ftdmchan->state));

			break;
	}


	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
/* Remote side transmit a SETUP */
void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
{
	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);
	
	int16_t suId = sngisdn_event->suId;
	uint32_t suInstId = sngisdn_event->suInstId;
	uint32_t spInstId = sngisdn_event->spInstId;
	int16_t dChan = sngisdn_event->dChan;
	uint8_t ces = sngisdn_event->ces;
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;	
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	ConEvnt *conEvnt = &sngisdn_event->event.conEvnt;

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

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

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

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

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

			ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND);

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

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

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

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

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

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

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

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

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

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

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

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

				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
			}
			break;
		default:
			ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing SETUP in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state));
			break;
	}
	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
void sngisdn_process_cnst_ind (sngisdn_event_data_t *sngisdn_event)
{
	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);

	int16_t suId = sngisdn_event->suId;
	uint32_t suInstId = sngisdn_event->suInstId;
	uint32_t spInstId = sngisdn_event->spInstId;
	uint8_t ces = sngisdn_event->ces;
	uint8_t evntType = sngisdn_event->evntType;
	
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	
	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
	
	CnStEvnt *cnStEvnt = &sngisdn_event->event.cnStEvnt;

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

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing %s (suId:%u suInstId:%u spInstId:%u ces:%d)\n",
													(evntType == MI_ALERTING)?"ALERT":
													(evntType == MI_CALLPROC)?"PROCEED":
													(evntType == MI_PROGRESS)?"PROGRESS":
													(evntType == MI_SETUPACK)?"SETUP ACK":
															(evntType == MI_INFO)?"INFO":"UNKNOWN",
															suId, suInstId, spInstId, ces);
	
	switch(evntType) {
		case MI_PROGRESS:
			if (signal_data->switchtype == SNGISDN_SWITCH_NI2 &&
						 cnStEvnt->causeDgn[0].eh.pres && cnStEvnt->causeDgn[0].causeVal.pres) {

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

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

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

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

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

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

sngisdn_process_cnst_ind_end:
	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
/* Remote side transmit a CONNECT or CONNECT ACK */
void sngisdn_process_con_cfm (sngisdn_event_data_t *sngisdn_event)
{
	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);

	int16_t suId = sngisdn_event->suId;
	uint32_t suInstId = sngisdn_event->suInstId;
	uint32_t spInstId = sngisdn_event->spInstId;
	uint8_t ces = sngisdn_event->ces;
	sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info;
	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
	
	/* Function does not require any info from conStEvnt struct for now */
	/* CnStEvnt *cnStEvnt = &sngisdn_event->event.cnStEvnt; */	
				
	ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n");
	
	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing CONNECT/CONNECT ACK (suId:%u suInstId:%u spInstId:%u ces:%d)\n", suId, suInstId, spInstId, ces);

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

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

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

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

	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
	return;
}
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;
}
Example #16
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;
}
Example #17
0
static void handle_pri_passive_event(pritap_t *pritap, pri_event *e)
{
    passive_call_t *pcall = NULL;
    passive_call_t *peerpcall = NULL;
    ftdm_channel_t *fchan = NULL;
    ftdm_channel_t *peerfchan = NULL;
    int layer1, transcap = 0;
    int crv = 0;
    pritap_t *peertap = pritap->peerspan->signal_data;

    switch (e->e) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        break;

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

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

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

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

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

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

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

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

    }
}