From c4d8fa6408f7db6d231eeb423eb0dcb17dd7230f Mon Sep 17 00:00:00 2001
From: David Yat Sin <dyatsin@sangoma.com>
Date: Wed, 11 May 2011 17:33:45 -0400
Subject: [PATCH 1/6] freetdm - ISDN fix for callback pointers

---
 .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c        | 5 ++---
 .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c  | 2 +-
 .../src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h  | 4 ++--
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
index 75379eef64..cd09f8606d 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
@@ -1156,10 +1156,9 @@ static FIO_SIG_LOAD_FUNCTION(ftdm_sangoma_isdn_init)
 	g_sngisdn_event_interface.cc.sng_fac_ind 	= sngisdn_rcv_fac_ind;
 	g_sngisdn_event_interface.cc.sng_sta_cfm 	= sngisdn_rcv_sta_cfm;
 	g_sngisdn_event_interface.cc.sng_srv_ind 	= sngisdn_rcv_srv_ind;
-	g_sngisdn_event_interface.cc.sng_srv_ind	= sngisdn_rcv_srv_cfm;
-	g_sngisdn_event_interface.cc.sng_rst_ind 	= sngisdn_rcv_rst_cfm;
+	g_sngisdn_event_interface.cc.sng_srv_cfm	= sngisdn_rcv_srv_cfm;
 	g_sngisdn_event_interface.cc.sng_rst_ind 	= sngisdn_rcv_rst_ind;
-	g_sngisdn_event_interface.cc.sng_rst_cfm	= sngisdn_rcv_rst_cfm;
+	g_sngisdn_event_interface.cc.sng_rst_cfm 	= sngisdn_rcv_rst_cfm;
 
 	g_sngisdn_event_interface.lg.sng_log 		= sngisdn_rcv_sng_log;
 	g_sngisdn_event_interface.lg.sng_assert 	= sngisdn_rcv_sng_assert;
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
index 8c8c8d5159..31994c7c6e 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
@@ -915,7 +915,7 @@ static ftdm_status_t sngisdn_map_call(sngisdn_span_data_t *signal_data, sngisdn_
 		case PROT_Q931_MSGTYPE_USER_INFO:
 		case PROT_Q931_MSGTYPE_DISCONNECT:
 		case PROT_Q931_MSGTYPE_RELEASE:
-		case PROT_Q931_MSGTYPE_RELEASE_ACK:
+		case PROT_Q931_MSGTYPE_RESTART_ACK:
 		case PROT_Q931_MSGTYPE_RELEASE_COMPLETE:
 		case PROT_Q931_MSGTYPE_FACILITY:
 		case PROT_Q931_MSGTYPE_NOTIFY:
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h
index db33cf8083..ca6683d4e3 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h
@@ -211,7 +211,7 @@ struct code2str dcodQ931CallRefLoTable[] = {
 #define PROT_Q931_MSGTYPE_DISCONNECT		69
 #define PROT_Q931_MSGTYPE_RESTART			70
 #define PROT_Q931_MSGTYPE_RELEASE			77
-#define PROT_Q931_MSGTYPE_RELEASE_ACK		78
+#define PROT_Q931_MSGTYPE_RESTART_ACK		78
 #define PROT_Q931_MSGTYPE_RELEASE_COMPLETE	90
 #define PROT_Q931_MSGTYPE_SEGMENT			96
 #define PROT_Q931_MSGTYPE_FACILITY			98
@@ -240,7 +240,7 @@ struct code2str dcodQ931MsgTypeTable[] = {
 	{PROT_Q931_MSGTYPE_DISCONNECT, "DISCONNECT"},
 	{PROT_Q931_MSGTYPE_RESTART, "RESTART"},
 	{PROT_Q931_MSGTYPE_RELEASE, "RELEASE"},
-	{PROT_Q931_MSGTYPE_RELEASE_ACK, "RELEASR ACK"},
+	{PROT_Q931_MSGTYPE_RESTART_ACK, "RESTART ACK"},
 	{PROT_Q931_MSGTYPE_RELEASE_COMPLETE, "RELEASE COMPLETE"},
 	{PROT_Q931_MSGTYPE_SEGMENT, "SEGMENT"},
 	{PROT_Q931_MSGTYPE_FACILITY, "FACILITY"},

From c20f56bad0e0e4cffe1beecddecf58be1e5054ca Mon Sep 17 00:00:00 2001
From: David Yat Sin <dyatsin@sangoma.com>
Date: Fri, 27 May 2011 12:18:06 -0400
Subject: [PATCH 2/6] freetdm - ISDN:Fix for not responding to incoming
 RESTARTs with RESTART ACK if there is an active call on that channel at the
 time the RESTART was received

---
 .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.c   |   3 +-
 .../ftmod_sangoma_isdn_stack_hndl.c           | 163 +++++++++++++-----
 .../ftmod_sangoma_isdn_trace.c                |   4 +-
 .../ftmod_sangoma_isdn_trace.h                |   4 +-
 4 files changed, 129 insertions(+), 45 deletions(-)

diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
index 3cf4876070..94e816e0d0 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
@@ -1156,8 +1156,7 @@ static FIO_SIG_LOAD_FUNCTION(ftdm_sangoma_isdn_init)
 	g_sngisdn_event_interface.cc.sng_fac_ind 	= sngisdn_rcv_fac_ind;
 	g_sngisdn_event_interface.cc.sng_sta_cfm 	= sngisdn_rcv_sta_cfm;
 	g_sngisdn_event_interface.cc.sng_srv_ind 	= sngisdn_rcv_srv_ind;
-	g_sngisdn_event_interface.cc.sng_srv_ind	= sngisdn_rcv_srv_cfm;
-	g_sngisdn_event_interface.cc.sng_rst_ind 	= sngisdn_rcv_rst_cfm;
+	g_sngisdn_event_interface.cc.sng_srv_cfm 	= sngisdn_rcv_srv_cfm;
 	g_sngisdn_event_interface.cc.sng_rst_ind 	= sngisdn_rcv_rst_ind;
 	g_sngisdn_event_interface.cc.sng_rst_cfm	= sngisdn_rcv_rst_cfm;
 
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
index 0789c433f9..e9d0fdc58a 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
@@ -35,6 +35,7 @@
 #include "ftmod_sangoma_isdn.h"
 static ftdm_status_t sngisdn_cause_val_requires_disconnect(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn);
 static void sngisdn_process_restart_confirm(ftdm_channel_t *ftdmchan);
+static ftdm_status_t sngisdn_force_down(ftdm_channel_t *ftdmchan);
 
 /* Remote side transmit a SETUP */
 void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
@@ -191,8 +192,7 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
 					strcpy(ftdmchan->caller_data.cid_name, retrieved_str);
 				}
 			}
-#endif
-			
+#endif			
 			if (signal_data->overlap_dial == SNGISDN_OPT_TRUE && !conEvnt->sndCmplt.eh.pres) {
 				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
 			} else {
@@ -920,36 +920,8 @@ void sngisdn_process_sta_cfm (sngisdn_event_data_t *sngisdn_event)
 		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;
+				if (sngisdn_force_down(ftdmchan) != FTDM_SUCCESS) {
+					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;
 			case 1:
@@ -1159,6 +1131,49 @@ static void sngisdn_process_restart_confirm(ftdm_channel_t *ftdmchan)
 	return;
 }
 
+static ftdm_status_t sngisdn_force_down(ftdm_channel_t *ftdmchan)
+{
+	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
+	
+	ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Forcing channel to DOWN state (%s)\n", ftdm_channel_state2str(ftdmchan->state));
+	
+	ftdm_status_t status = FTDM_SUCCESS;
+	switch (ftdmchan->state) {
+		case FTDM_CHANNEL_STATE_DOWN:
+			/* Do nothing */
+			break;
+		case FTDM_CHANNEL_STATE_RESET:
+			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
+			break;
+		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 already waiting for usr to respond to SIGEVENT stop.
+				FreeTDM already scheduled a timout in case the User does respond to
+				SIGEVENT_STOP, no need to do anything here */			
+			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;
+		default:
+			status = FTDM_FAIL;
+
+	}
+	return status;
+}
 
 void sngisdn_process_rst_cfm (sngisdn_event_data_t *sngisdn_event)
 {
@@ -1171,12 +1186,12 @@ void sngisdn_process_rst_cfm (sngisdn_event_data_t *sngisdn_event)
 	
 	sngisdn_span_data_t	*signal_data = g_sngisdn_data.dchans[dChan].spans[1];
 	if (!signal_data) {
-		ftdm_log(FTDM_LOG_CRIT, "Received RESTART on unconfigured span (suId:%d)\n", suId);
+		ftdm_log(FTDM_LOG_CRIT, "Received RESTART CFM on unconfigured span (suId:%d)\n", suId);
 		return;
 	}
 	
-	if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) {
-		ftdm_log(FTDM_LOG_DEBUG, "Receved RESTART, but Restart Indicator IE not present\n");
+	if (rstEvnt->rstInd.eh.pres != PRSNT_NODEF && rstEvnt->rstInd.rstClass.pres != PRSNT_NODEF) {
+		ftdm_log(FTDM_LOG_DEBUG, "Received RESTART, but Restart Indicator IE not present\n");
 		return;
 	}
 		
@@ -1233,8 +1248,11 @@ void sngisdn_process_rst_cfm (sngisdn_event_data_t *sngisdn_event)
 }
 
 
+/* The remote side sent us a RESTART Msg. Trillium automatically acks with RESTART ACK, but
+	we need to clear our call states if there is a call on this channel */
 void sngisdn_process_rst_ind (sngisdn_event_data_t *sngisdn_event)
 {
+	uint8_t chan_no = 0;
 	int16_t suId = sngisdn_event->suId;
 	int16_t dChan = sngisdn_event->dChan;
 	uint8_t ces = sngisdn_event->ces;
@@ -1242,15 +1260,82 @@ void sngisdn_process_rst_ind (sngisdn_event_data_t *sngisdn_event)
 
 	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);
 
-	/* Function does not require any info from ssHlEvnt struct for now */
-	/*Rst *rstEvnt = &sngisdn_event->event.rstEvnt;*/
-		
-	ftdm_log(FTDM_LOG_DEBUG, "Processing RESTART CFM (suId:%u dChan:%d ces:%d %s)\n", suId, dChan, ces,
+	Rst *rstEvnt = &sngisdn_event->event.rstEvnt;
+	sngisdn_span_data_t	*signal_data = g_sngisdn_data.dchans[dChan].spans[1];
+	if (!signal_data) {
+		ftdm_log(FTDM_LOG_CRIT, "Received RESTART IND on unconfigured span (suId:%d)\n", suId);
+		return;
+	}
+	
+	ftdm_log(FTDM_LOG_DEBUG, "Processing RESTART IND (suId:%u dChan:%d ces:%d %s)\n", suId, dChan, ces,
 													(evntType == IN_LNK_DWN)?"LNK_DOWN":
 													(evntType == IN_LNK_UP)?"LNK_UP":
 													(evntType == IN_INDCHAN)?"b-channel":
 													(evntType == IN_LNK_DWN_DM_RLS)?"NFAS service procedures":
 													(evntType == IN_SWCHD_BU_DCHAN)?"NFAS switchover to backup":"Unknown");
+
+	if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) {
+		ftdm_log(FTDM_LOG_DEBUG, "Received RESTART IND, but Restart Indicator IE not present\n");
+		return;
+	}
+
+	switch(rstEvnt->rstInd.rstClass.val) {
+		case IN_CL_INDCHAN: /* Indicated b-channel */
+			if (rstEvnt->chanId.eh.pres) {
+				if (rstEvnt->chanId.intType.val == IN_IT_BASIC) {
+					if (rstEvnt->chanId.infoChanSel.pres == PRSNT_NODEF) {
+						chan_no = rstEvnt->chanId.infoChanSel.val;
+					}
+				} else if (rstEvnt->chanId.intType.val == IN_IT_OTHER) {
+					if (rstEvnt->chanId.chanNmbSlotMap.pres == PRSNT_NODEF) {
+						chan_no = rstEvnt->chanId.chanNmbSlotMap.val[0];
+					}
+				}
+			}
+			if (!chan_no) {
+				ftdm_log(FTDM_LOG_CRIT, "Failed to determine channel from RESTART\n");
+				return;
+			}
+			break;
+		case IN_CL_SNGINT: /* Single interface */
+		case IN_CL_ALLINT: /* All interfaces */
+			/* In case restart class indicates all interfaces, we will duplicate
+					this event on each span associated to this d-channel in sngisdn_rcv_rst_cfm,
+					so treat it as a single interface anyway */
+			break;
+		default:
+			ftdm_log(FTDM_LOG_CRIT, "Invalid restart indicator class:%d\n", rstEvnt->rstInd.rstClass.val);
+			return;
+	}
+
+	if (chan_no) { /* For a single channel */
+		if (chan_no > ftdm_span_get_chan_count(signal_data->ftdm_span)) {
+			ftdm_log(FTDM_LOG_CRIT, "Received RESTART IND on invalid channel:%d\n", chan_no);
+		} else {
+			ftdm_iterator_t *chaniter = NULL;
+			ftdm_iterator_t *curr = NULL;
+			
+			chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL);
+			for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {			
+				ftdm_channel_t *ftdmchan = (ftdm_channel_t*)ftdm_iterator_current(curr);
+				if (ftdmchan->physical_chan_id == chan_no) {
+					sngisdn_force_down(ftdmchan);
+				}
+			}
+			ftdm_iterator_free(chaniter);
+		}
+	} else { /* for all channels */
+		ftdm_iterator_t *chaniter = NULL;
+		ftdm_iterator_t *curr = NULL;
+
+		chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL);
+		for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {
+			sngisdn_force_down((ftdm_channel_t*)ftdm_iterator_current(curr));
+		}
+		ftdm_iterator_free(chaniter);
+	}
+
+	
 	ISDN_FUNC_TRACE_EXIT(__FUNCTION__);
 	return;
 }
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
index d455fc06d0..f44f1032a3 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
@@ -757,7 +757,7 @@ void print_hex_dump(char* str, uint32_t *str_len, uint8_t* data, uint32_t index_
 {
 	uint32_t k;
 	*str_len += sprintf(&str[*str_len], "  [ ");
-	for(k=index_start; k <= index_end; k++) {
+	for(k=index_start; k < index_end; k++) {
 		if (k && !(k%32)) {
 			*str_len += sprintf(&str[*str_len], "\n    ");
 		}
@@ -917,7 +917,7 @@ static ftdm_status_t sngisdn_map_call(sngisdn_span_data_t *signal_data, sngisdn_
 		case PROT_Q931_MSGTYPE_USER_INFO:
 		case PROT_Q931_MSGTYPE_DISCONNECT:
 		case PROT_Q931_MSGTYPE_RELEASE:
-		case PROT_Q931_MSGTYPE_RELEASE_ACK:
+		case PROT_Q931_MSGTYPE_RESTART_ACK:
 		case PROT_Q931_MSGTYPE_RELEASE_COMPLETE:
 		case PROT_Q931_MSGTYPE_FACILITY:
 		case PROT_Q931_MSGTYPE_NOTIFY:
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h
index db33cf8083..ca6683d4e3 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h
@@ -211,7 +211,7 @@ struct code2str dcodQ931CallRefLoTable[] = {
 #define PROT_Q931_MSGTYPE_DISCONNECT		69
 #define PROT_Q931_MSGTYPE_RESTART			70
 #define PROT_Q931_MSGTYPE_RELEASE			77
-#define PROT_Q931_MSGTYPE_RELEASE_ACK		78
+#define PROT_Q931_MSGTYPE_RESTART_ACK		78
 #define PROT_Q931_MSGTYPE_RELEASE_COMPLETE	90
 #define PROT_Q931_MSGTYPE_SEGMENT			96
 #define PROT_Q931_MSGTYPE_FACILITY			98
@@ -240,7 +240,7 @@ struct code2str dcodQ931MsgTypeTable[] = {
 	{PROT_Q931_MSGTYPE_DISCONNECT, "DISCONNECT"},
 	{PROT_Q931_MSGTYPE_RESTART, "RESTART"},
 	{PROT_Q931_MSGTYPE_RELEASE, "RELEASE"},
-	{PROT_Q931_MSGTYPE_RELEASE_ACK, "RELEASR ACK"},
+	{PROT_Q931_MSGTYPE_RESTART_ACK, "RESTART ACK"},
 	{PROT_Q931_MSGTYPE_RELEASE_COMPLETE, "RELEASE COMPLETE"},
 	{PROT_Q931_MSGTYPE_SEGMENT, "SEGMENT"},
 	{PROT_Q931_MSGTYPE_FACILITY, "FACILITY"},

From 478e685292f86d0b1a7b0e6bc9110b5b35849357 Mon Sep 17 00:00:00 2001
From: David Yat Sin <dyatsin@sangoma.com>
Date: Mon, 30 May 2011 17:07:59 -0400
Subject: [PATCH 3/6] freetdm - ISDN: Added API command to print Trillium
 memory info

---
 .../ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c  |  3 +++
 .../ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h  |  2 +-
 .../ftmod_sangoma_isdn_stack_hndl.c                | 14 ++++++++++----
 .../ftmod_sangoma_isdn_stack_rcv.c                 |  2 +-
 .../ftmod_sangoma_isdn_support.c                   |  3 +--
 5 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
index 94e816e0d0..103cb869a5 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
@@ -1297,6 +1297,9 @@ static FIO_API_FUNCTION(ftdm_sangoma_isdn_api)
 		status = sngisdn_check_free_ids();
 		goto done;
 	}
+	if (!strcasecmp(argv[0], "check_mem")) {
+		sngisdn_get_memory_info();
+	}
 done:
 	switch (status) {
 		case FTDM_SUCCESS:
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
index ac69fa8d66..7930a827f3 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
@@ -394,7 +394,7 @@ void sngisdn_trace_interpreted_q931(sngisdn_span_data_t *signal_data, ftdm_trace
 void sngisdn_trace_raw_q921(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t dir, uint8_t *data, uint32_t data_len);
 void sngisdn_trace_raw_q931(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t dir, uint8_t *data, uint32_t data_len);
 
-void get_memory_info(void);
+void sngisdn_get_memory_info(void);
 
 ftdm_status_t sng_isdn_activate_trace(ftdm_span_t *span, sngisdn_tracetype_t trace_opt);
 ftdm_status_t sngisdn_check_free_ids(void);
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
index e9d0fdc58a..242963ec9c 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
@@ -1134,10 +1134,9 @@ static void sngisdn_process_restart_confirm(ftdm_channel_t *ftdmchan)
 static ftdm_status_t sngisdn_force_down(ftdm_channel_t *ftdmchan)
 {
 	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
+	ftdm_status_t status = FTDM_SUCCESS;
 	
 	ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Forcing channel to DOWN state (%s)\n", ftdm_channel_state2str(ftdmchan->state));
-	
-	ftdm_status_t status = FTDM_SUCCESS;
 	switch (ftdmchan->state) {
 		case FTDM_CHANNEL_STATE_DOWN:
 			/* Do nothing */
@@ -1257,11 +1256,18 @@ void sngisdn_process_rst_ind (sngisdn_event_data_t *sngisdn_event)
 	int16_t dChan = sngisdn_event->dChan;
 	uint8_t ces = sngisdn_event->ces;
 	uint8_t evntType = sngisdn_event->evntType;
+	Rst *rstEvnt = NULL;
+	sngisdn_span_data_t     *signal_data = NULL;
 
 	ISDN_FUNC_TRACE_ENTER(__FUNCTION__);
 
-	Rst *rstEvnt = &sngisdn_event->event.rstEvnt;
-	sngisdn_span_data_t	*signal_data = g_sngisdn_data.dchans[dChan].spans[1];
+	rstEvnt = &sngisdn_event->event.rstEvnt;
+
+	/* TODO: readjust this when NFAS is implemented as signal_data will not always be the first
+	 * span for that d-channel */
+
+	signal_data = g_sngisdn_data.dchans[dChan].spans[1];
+
 	if (!signal_data) {
 		ftdm_log(FTDM_LOG_CRIT, "Received RESTART IND on unconfigured span (suId:%d)\n", suId);
 		return;
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
index 67de893f5d..d1b202e77d 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
@@ -708,7 +708,7 @@ void sngisdn_rcv_q921_ind(BdMngmt *status)
 void sngisdn_rcv_q931_ind(InMngmt *status)
 {	
 	if (status->t.usta.alarm.cause == 287) {
-		get_memory_info();
+		sngisdn_get_memory_info();
 		return;
 	}
 
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
index 3ba776928d..62ff830b20 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
@@ -47,7 +47,6 @@ static uint8_t get_trillium_val(ftdm2trillium_t *vals, uint8_t ftdm_val, uint8_t
 static uint8_t get_ftdm_val(ftdm2trillium_t *vals, uint8_t trillium_val, uint8_t default_val);
 
 extern ftdm_sngisdn_data_t	g_sngisdn_data;
-void get_memory_info(void);
 
 ftdm2trillium_t npi_codes[] = {
 	{FTDM_NPI_UNKNOWN,	IN_NP_UNK},
@@ -1085,7 +1084,7 @@ ftdm_status_t sngisdn_check_free_ids(void)
 	return FTDM_SUCCESS;
 }
 
-void get_memory_info(void)
+void sngisdn_get_memory_info(void)
 {
 	U32 availmen = 0;
 	SRegInfoShow(S_REG, &availmen);

From b92ad06b98ecca96a2f609accc4b9132fa46c4a6 Mon Sep 17 00:00:00 2001
From: David Yat Sin <dyatsin@sangoma.com>
Date: Wed, 1 Jun 2011 11:54:45 -0400
Subject: [PATCH 4/6] Disabled calling SRegInfoShow on Windows

---
 .../ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c      | 2 ++
 .../ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c  | 7 +++++++
 2 files changed, 9 insertions(+)

diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
index d1b202e77d..879b1491a9 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
@@ -707,10 +707,12 @@ void sngisdn_rcv_q921_ind(BdMngmt *status)
 }
 void sngisdn_rcv_q931_ind(InMngmt *status)
 {	
+#ifndef WIN32
 	if (status->t.usta.alarm.cause == 287) {
 		sngisdn_get_memory_info();
 		return;
 	}
+#endif
 
 	switch (status->t.usta.alarm.event) {
 		case LCM_EVENT_UP:
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
index 62ff830b20..a148130b74 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
@@ -1086,8 +1086,15 @@ ftdm_status_t sngisdn_check_free_ids(void)
 
 void sngisdn_get_memory_info(void)
 {
+#ifdef WIN32
+	/* SRegInfoShow is not formally supported by Trillium with Windows */
+	ftdm_log(FTDM_LOG_WARNING, "SRegInfoShow not supported on Windows\n");
+#else	
+	/* SRegInfoShow is not formally supported by Trillium in Linux either, but
+	 * it seems like its working fine so far */
 	U32 availmen = 0;
 	SRegInfoShow(S_REG, &availmen);
+#endif	
 	return;
 }
 

From 3fa2fce3f3e3b66151259874d9885fbaeca94d97 Mon Sep 17 00:00:00 2001
From: Moises Silva <moy@sangoma.com>
Date: Wed, 1 Jun 2011 15:24:09 -0400
Subject: [PATCH 5/6] freetdm: added fail-on-error global configuration to
 refuse          to load the module if there is any error

Conflicts:

	libs/freetdm/mod_freetdm/mod_freetdm.c
---
 libs/freetdm/conf/freetdm.conf.xml     |   5 +
 libs/freetdm/mod_freetdm/mod_freetdm.c | 194 +++++++------------------
 2 files changed, 60 insertions(+), 139 deletions(-)

diff --git a/libs/freetdm/conf/freetdm.conf.xml b/libs/freetdm/conf/freetdm.conf.xml
index 7d5de5a189..29232d927b 100644
--- a/libs/freetdm/conf/freetdm.conf.xml
+++ b/libs/freetdm/conf/freetdm.conf.xml
@@ -18,6 +18,11 @@ with the signaling protocols that you can run on top of your I/O interfaces.
 		-->
 		<!--<param name="enable-analog-option" value="call-swap"/>-->
 		<!--<param name="enable-analog-option" value="3-way"/>-->
+		<!--
+		Refuse to load the module if there is configuration errors
+		Defaults to 'no'
+		-->
+		<!--<param name="fail-on-error" value="no"/>-->
 	</settings>
 
 	<!-- Sample analog configuration (The analog_spans tag is for ftmod_analog) -->
diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index 618f2501ff..a3cefce403 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -87,7 +87,9 @@ static struct {
 	analog_option_t analog_options;
 	switch_hash_t *ss7_configs;
 	int sip_headers;
-	int crash_on_assert;
+	uint8_t crash_on_assert;
+	uint8_t fail_on_error;
+	uint8_t config_error;
 } globals;
 
 /* private data attached to each fs session */
@@ -2427,6 +2429,10 @@ static uint32_t enable_analog_option(const char *str, uint32_t current_options)
 	
 }
 
+#define CONFIG_ERROR(...) do { \
+		ftdm_log(FTDM_LOG_ERROR, __VA_ARGS__); \
+		globals.config_error = 1; \
+	} while(0)
 /* create ftdm_conf_node_t tree based on a fixed pattern XML configuration list 
  * last 2 args are for limited aka dumb recursivity
  * */
@@ -2619,7 +2625,7 @@ static int add_profile_parameters(switch_xml_t cfg, const char *profname, ftdm_c
 
 	profnode = switch_xml_child(cfg, "config_profiles");
 	if (!profnode) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot find profile '%s', there is no 'config_profiles' XML section\n", profname);
+		CONFIG_ERROR("cannot find profile '%s', there is no 'config_profiles' XML section\n", profname);
 		return 0;
 	}
 
@@ -2635,7 +2641,7 @@ static int add_profile_parameters(switch_xml_t cfg, const char *profname, ftdm_c
 	}
 
 	if (!profile) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to find profile '%s'\n", profname);
+		CONFIG_ERROR("failed to find profile '%s'\n", profname);
 		return 0;
 	}
 
@@ -2670,7 +2676,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans)
 		unsigned paramindex = 0;
 
 		if (!name && !id) {
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sangoma isdn span missing required attribute 'id' or 'name', skipping ...\n");
+			CONFIG_ERROR("sangoma isdn span missing required attribute 'id' or 'name', skipping ...\n");
 			continue;
 		}
 
@@ -2688,7 +2694,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans)
 		}
 
 		if (zstatus != FTDM_SUCCESS) {
-			ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
+			CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
 			continue;
 		}
 		
@@ -2716,7 +2722,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans)
 			char *val = (char *) switch_xml_attr_soft(param, "value");
 
 			if (ftdm_array_len(spanparameters) - 1 == paramindex) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for ss7 span, ignoring any parameter after %s\n", var);
+				CONFIG_ERROR("Too many parameters for ss7 span, ignoring any parameter after %s\n", var);
 				break;
 			}
 
@@ -2746,10 +2752,10 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans)
 				int calls;
 				int seconds;
 				if (sscanf(val, "%d/%d", &calls, &seconds) != 2) {
-					ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var);
+					CONFIG_ERROR("Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var);
 				} else {
 					if (calls < 1 || seconds < 1) {
-						ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, minimum call limit must be 1 per second\n", var);
+						CONFIG_ERROR("Invalid %s parameter value, minimum call limit must be 1 per second\n", var);
 					} else {
 						SPAN_CONFIG[span_id].limit_calls = calls;
 						SPAN_CONFIG[span_id].limit_seconds = seconds;
@@ -2759,7 +2765,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans)
 				if (!strcasecmp(val, "answer")) {
 					SPAN_CONFIG[span_id].limit_reset_event = FTDM_LIMIT_RESET_ON_ANSWER;
 				} else {
-					ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, only accepted event is 'answer'\n", var);
+					CONFIG_ERROR("Invalid %s parameter value, only accepted event is 'answer'\n", var);
 				}
 			} else {
 				spanparameters[paramindex].var = var;
@@ -2772,7 +2778,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans)
 						  "sangoma_isdn", 
 						  on_clear_channel_signal,
 						  spanparameters) != FTDM_SUCCESS) {
-			ftdm_log(FTDM_LOG_ERROR, "Error configuring Sangoma ISDN FreeTDM span %d\n", span_id);
+			CONFIG_ERROR("Error configuring Sangoma ISDN FreeTDM span %d\n", span_id);
 			continue;
 		}
 		SPAN_CONFIG[span_id].span = span;
@@ -2789,15 +2795,11 @@ static switch_status_t load_config(void)
 	const char *cf = "freetdm.conf";
 	switch_xml_t cfg, xml, settings, param, spans, myspan;
 	ftdm_conf_node_t *ss7confnode = NULL;
-	ftdm_span_t *boost_spans[FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN];
-	ftdm_span_t *boost_span = NULL;
-	unsigned boosti = 0;
 	unsigned int i = 0;
 	ftdm_channel_t *fchan = NULL;
 	ftdm_iterator_t *chaniter = NULL;
 	ftdm_iterator_t *curr = NULL;
 
-	memset(boost_spans, 0, sizeof(boost_spans));
 	memset(&globals, 0, sizeof(globals));
 	switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool);
 	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
@@ -2816,6 +2818,8 @@ static switch_status_t load_config(void)
 				switch_set_string(globals.hold_music, val);
 			} else if (!strcasecmp(var, "crash-on-assert")) {
 				globals.crash_on_assert = switch_true(val);
+			} else if (!strcasecmp(var, "fail-on-error")) {
+				globals.fail_on_error = switch_true(val);
 			} else if (!strcasecmp(var, "sip-headers")) {
 				globals.sip_headers = switch_true(val);
 			} else if (!strcasecmp(var, "enable-analog-option")) {
@@ -2846,11 +2850,11 @@ static switch_status_t load_config(void)
 			uint32_t span_id = 0;
 			unsigned paramindex = 0;
 			if (!name && !id) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ss7 span missing required attribute 'id' or 'name', skipping ...\n");
+				CONFIG_ERROR("ss7 span missing required attribute 'id' or 'name', skipping ...\n");
 				continue;
 			}
 			if (!configname) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ss7 span missing required attribute, skipping ...\n");
+				CONFIG_ERROR("ss7 span missing required attribute, skipping ...\n");
 				continue;
 			}
 			if (name) {
@@ -2867,7 +2871,7 @@ static switch_status_t load_config(void)
 			}
 
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
+				CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
 				continue;
 			}
 			
@@ -2877,7 +2881,7 @@ static switch_status_t load_config(void)
 
 			ss7confnode = get_ss7_config_node(cfg, configname);
 			if (!ss7confnode) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding ss7config '%s' for FreeTDM span id: %s\n", configname, switch_str_nil(id));
+				CONFIG_ERROR("Error finding ss7config '%s' for FreeTDM span id: %s\n", configname, switch_str_nil(id));
 				continue;
 			}
 
@@ -2891,7 +2895,7 @@ static switch_status_t load_config(void)
 				char *val = (char *) switch_xml_attr_soft(param, "value");
 
 				if (ftdm_array_len(spanparameters) - 1 == paramindex) {
-					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for ss7 span, ignoring any parameter after %s\n", var);
+					CONFIG_ERROR("Too many parameters for ss7 span, ignoring any parameter after %s\n", var);
 					break;
 				}
 
@@ -2910,7 +2914,7 @@ static switch_status_t load_config(void)
 						          "sangoma_ss7", 
 						          on_clear_channel_signal,
 							  spanparameters) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error configuring ss7 FreeTDM span %d\n", span_id);
+				CONFIG_ERROR("Error configuring ss7 FreeTDM span %d\n", span_id);
 				continue;
 			}
 			SPAN_CONFIG[span_id].span = span;
@@ -2961,7 +2965,7 @@ static switch_status_t load_config(void)
 			}
 
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
+				CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
 				continue;
 			}
 			
@@ -2995,10 +2999,10 @@ static switch_status_t load_config(void)
 					int calls;
 					int seconds;
 					if (sscanf(val, "%d/%d", &calls, &seconds) != 2) {
-						ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var);
+						CONFIG_ERROR("Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var);
 					} else {
 						if (calls < 1 || seconds < 1) {
-							ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, minimum call limit must be 1 per second\n", var);
+							CONFIG_ERROR("Invalid %s parameter value, minimum call limit must be 1 per second\n", var);
 						} else {
 							SPAN_CONFIG[span_id].limit_calls = calls;
 							SPAN_CONFIG[span_id].limit_seconds = seconds;
@@ -3008,7 +3012,7 @@ static switch_status_t load_config(void)
 					if (!strcasecmp(val, "answer")) {
 						SPAN_CONFIG[span_id].limit_reset_event = FTDM_LIMIT_RESET_ON_ANSWER;
 					} else {
-						ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, only accepted event is 'answer'\n", var);
+						CONFIG_ERROR("Invalid %s parameter value, only accepted event is 'answer'\n", var);
 					}
 				} else if (!strcasecmp(var, "dial-regex")) {
 					dial_regex = val;
@@ -3036,7 +3040,7 @@ static switch_status_t load_config(void)
 			}
 				
 			if (!id && !name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n");
+				CONFIG_ERROR("span missing required param 'id'\n");
 				continue;
 			}
 			
@@ -3066,7 +3070,7 @@ static switch_status_t load_config(void)
 			}
 
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
+				CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
 				continue;
 			}
 			
@@ -3086,7 +3090,7 @@ static switch_status_t load_config(void)
 								   "callwaiting", &callwaiting,
 								   "wait_dialtone_timeout", &dialtone_timeout,
 								   FTDM_TAG_END) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span));
+				CONFIG_ERROR("Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span));
 				continue;
 			}
 
@@ -3162,7 +3166,7 @@ static switch_status_t load_config(void)
 			}
 				
 			if (!id && !name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n");
+				CONFIG_ERROR("span missing required param 'id'\n");
 				continue;
 			}
 
@@ -3194,7 +3198,7 @@ static switch_status_t load_config(void)
 			}
 
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
+				CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
 				continue;
 			}
 			
@@ -3208,7 +3212,7 @@ static switch_status_t load_config(void)
 								   "digit_timeout", &to,
 								   "max_dialstr", &max,
 								   FTDM_TAG_END) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d\n", span_id);
+				CONFIG_ERROR("Error starting FreeTDM span %d\n", span_id);
 				continue;
 			}
 
@@ -3245,7 +3249,7 @@ static switch_status_t load_config(void)
 			uint32_t span_id = 0;
 
 			if (!name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n");
+				CONFIG_ERROR("span missing required attribute 'name'\n");
 				continue;
 			}
 
@@ -3256,7 +3260,7 @@ static switch_status_t load_config(void)
 				char *val = (char *) switch_xml_attr_soft(param, "value");
 
 				if (ftdm_array_len(spanparameters) - 1 == paramindex) {
-					ftdm_log(FTDM_LOG_ERROR, "Too many parameters for pri span '%s', ignoring everything after '%s'\n", name, var);
+					CONFIG_ERROR("Too many parameters for pri span '%s', ignoring everything after '%s'\n", name, var);
 					break;
 				}
 
@@ -3278,13 +3282,13 @@ static switch_status_t load_config(void)
 
 			zstatus = ftdm_span_find_by_name(name, &span);
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name);
+				CONFIG_ERROR("Error finding FreeTDM span %s\n", name);
 				continue;
 			}
 
 			span_id = ftdm_span_get_id(span);
 			if (ftdm_configure_span_signaling(span, "isdn", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name);
+				CONFIG_ERROR("Error configuring FreeTDM span %s\n", name);
 				continue;
 			}
 
@@ -3311,7 +3315,7 @@ static switch_status_t load_config(void)
 			int span_id = 0;
 
 			if (!name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n");
+				CONFIG_ERROR("span missing required attribute 'name'\n");
 				continue;
 			}
 
@@ -3322,7 +3326,7 @@ static switch_status_t load_config(void)
 				char *val = (char *) switch_xml_attr_soft(param, "value");
 
 				if (ftdm_array_len(spanparameters) - 1 == paramindex) {
-					ftdm_log(FTDM_LOG_ERROR, "Too many parameters for pritap span '%s', ignoring everything after '%s'\n", name, var);
+					CONFIG_ERROR("Too many parameters for pritap span '%s', ignoring everything after '%s'\n", name, var);
 					break;
 				}
 
@@ -3339,13 +3343,13 @@ static switch_status_t load_config(void)
 	
 			zstatus = ftdm_span_find_by_name(name, &span);
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name);
+				CONFIG_ERROR("Error finding FreeTDM span %s\n", name);
 				continue;
 			}
 
 			span_id = ftdm_span_get_id(span);
 			if (ftdm_configure_span_signaling(span, "pritap", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name);
+				CONFIG_ERROR("Error configuring FreeTDM span %s\n", name);
 				continue;
 			}
 
@@ -3371,7 +3375,7 @@ static switch_status_t load_config(void)
 			uint32_t span_id = 0;
 
 			if (!name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n");
+				CONFIG_ERROR("span missing required attribute 'name'\n");
 				continue;
 			}
 
@@ -3382,7 +3386,7 @@ static switch_status_t load_config(void)
 				char *val = (char *) switch_xml_attr_soft(param, "value");
 
 				if (ftdm_array_len(spanparameters) - 1 == paramindex) {
-					ftdm_log(FTDM_LOG_ERROR, "Too many parameters for libpri span, ignoring everything after '%s'\n", var);
+					CONFIG_ERROR("Too many parameters for libpri span, ignoring everything after '%s'\n", var);
 					break;
 				}
 
@@ -3404,13 +3408,13 @@ static switch_status_t load_config(void)
 
 			zstatus = ftdm_span_find_by_name(name, &span);
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name);
+				CONFIG_ERROR("Error finding FreeTDM span %s\n", name);
 				continue;
 			}
 
 			span_id = ftdm_span_get_id(span);
 			if (ftdm_configure_span_signaling(span, "libpri", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name);
+				CONFIG_ERROR("Error configuring FreeTDM span %s\n", name);
 				continue;
 			}
 
@@ -3423,86 +3427,6 @@ static switch_status_t load_config(void)
 		}
 	}
 
-	if ((spans = switch_xml_child(cfg, "boost_spans"))) {
-		for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) {
-			char *id = (char *) switch_xml_attr(myspan, "id");
-			char *name = (char *) switch_xml_attr(myspan, "name");
-			char *sigmod = (char *) switch_xml_attr(myspan, "sigmod");
-			ftdm_status_t zstatus = FTDM_FAIL;
-			const char *context = "default";
-			const char *dialplan = "XML";
-			uint32_t span_id = 0;
-			ftdm_span_t *span = NULL;
-			ftdm_conf_parameter_t spanparameters[30];
-			unsigned paramindex = 0;
-			
-			if (!id && !name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "boost span requires an id or name as attribute: <span id=ftid|name=ftname>\n");
-				continue;
-			}
-			memset(spanparameters, 0, sizeof(spanparameters));
-			if (sigmod) {
-				spanparameters[paramindex].var = "sigmod";
-				spanparameters[paramindex].val = sigmod;
-				paramindex++;
-			}
-
-			for (param = switch_xml_child(myspan, "param"); param; param = param->next) {
-				char *var = (char *) switch_xml_attr_soft(param, "name");
-				char *val = (char *) switch_xml_attr_soft(param, "value");
-
-				if (ftdm_array_len(spanparameters) - 1 == paramindex) {
-					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for boost span, ignoring any parameter after %s\n", var);
-					break;
-				}
-
-				if (!strcasecmp(var, "context")) {
-					context = val;
-				} else if (!strcasecmp(var, "dialplan")) {
-					dialplan = val;
-				} else {
-					spanparameters[paramindex].var = var;
-					spanparameters[paramindex].val = val;
-					paramindex++;
-				}
-			}
-
-			if (name) {
-				zstatus = ftdm_span_find_by_name(name, &span);
-			} else {
-				if (switch_is_number(id)) {
-					span_id = atoi(id);
-					zstatus = ftdm_span_find(span_id, &span);
-				}
-
-				if (zstatus != FTDM_SUCCESS) {
-					zstatus = ftdm_span_find_by_name(id, &span);
-				}
-			}
-
-			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name));
-				continue;
-			}
-			
-			if (!span_id) {
-				span_id = ftdm_span_get_id(span);
-			}
-
-			if (ftdm_configure_span_signaling(span, "sangoma_boost", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d error: %s\n", span_id, ftdm_span_get_last_error(span));
-				continue;
-			}
-
-			SPAN_CONFIG[span_id].span = span;
-			switch_copy_string(SPAN_CONFIG[span_id].context, context, sizeof(SPAN_CONFIG[span_id].context));
-			switch_copy_string(SPAN_CONFIG[span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span_id].dialplan));
-
-			switch_copy_string(SPAN_CONFIG[span_id].type, "Sangoma (boost)", sizeof(SPAN_CONFIG[span_id].type));
-			boost_spans[boosti++] = span;
-		}
-	}
-
 	if ((spans = switch_xml_child(cfg, "r2_spans"))) {
 		for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) {
 			char *name = (char *) switch_xml_attr(myspan, "name");
@@ -3521,7 +3445,7 @@ static switch_status_t load_config(void)
 			unsigned paramindex = 0;
 
 			if (!name) {
-				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "'name' attribute required for R2 spans!\n");
+				CONFIG_ERROR("'name' attribute required for R2 spans!\n");
 				continue;
 			}
 
@@ -3556,14 +3480,13 @@ static switch_status_t load_config(void)
 
 			zstatus = ftdm_span_find_by_name(name, &span);
 			if (zstatus != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM R2 Span '%s'\n", name);
+				CONFIG_ERROR("Error finding FreeTDM R2 Span '%s'\n", name);
 				continue;
 			}
 			span_id = ftdm_span_get_id(span);
 
 			if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, spanparameters) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM R2 span %s, error: %s\n", 
-				name, ftdm_span_get_last_error(span));
+				CONFIG_ERROR("Error configuring FreeTDM R2 span %s, error: %s\n", name, ftdm_span_get_last_error(span));
 				continue;
 			}
 
@@ -3581,24 +3504,12 @@ static switch_status_t load_config(void)
 			switch_copy_string(SPAN_CONFIG[span_id].type, "R2", sizeof(SPAN_CONFIG[span_id].type));
 
 			if (ftdm_span_start(span) == FTDM_FAIL) {
-				ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM R2 span %s, error: %s\n", name, ftdm_span_get_last_error(span));
+				CONFIG_ERROR("Error starting FreeTDM R2 span %s, error: %s\n", name, ftdm_span_get_last_error(span));
 				continue;
 			}
 		}
 	}
 
-	/* start all boost spans now that we're done configuring. Unfortunately at this point boost modules have the limitation
-	 * of needing all spans to be configured before starting them */
-	for (i=0 ; i < boosti; i++) {
-		boost_span = boost_spans[i];
-		ftdm_log(FTDM_LOG_DEBUG, "Starting boost span %d\n", ftdm_span_get_id(boost_span));
-		if (ftdm_span_start(boost_span) == FTDM_FAIL) {
-			ftdm_log(FTDM_LOG_ERROR, "Error starting boost FreeTDM span %d, error: %s\n",
-					ftdm_span_get_id(boost_span), ftdm_span_get_last_error(boost_span));
-			continue;
-		}
-	}
-
 	if (globals.crash_on_assert) {
 		ftdm_log(FTDM_LOG_WARNING, "Crash on assert enabled\n");
 		ftdm_global_set_crash_policy(FTDM_CRASH_ON_ASSERT);
@@ -3606,6 +3517,11 @@ static switch_status_t load_config(void)
 
 	switch_xml_free(xml);
 
+	if (globals.fail_on_error && globals.config_error) {
+		ftdm_log(FTDM_LOG_ERROR, "Refusing to load module with errors\n");
+		return SWITCH_STATUS_TERM;
+	}
+
 	return SWITCH_STATUS_SUCCESS;
 }
 

From bd7672242c31e84c677b4439c0a5663033591edf Mon Sep 17 00:00:00 2001
From: David Yat Sin <dyatsin@sangoma.com>
Date: Thu, 26 May 2011 11:38:24 -0400
Subject: [PATCH 6/6] Support for AT&T *8 Transfer (VRU only)

---
 libs/freetdm/Makefile.am                      |   1 +
 libs/freetdm/docs/async.txt                   |   1 +
 libs/freetdm/mod_freetdm/mod_freetdm.c        |  68 ++++-
 libs/freetdm/src/ftdm_io.c                    | 236 ++++++++++-----
 .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.c   | 177 +++++++----
 .../ftmod_sangoma_isdn/ftmod_sangoma_isdn.h   |  48 ++-
 .../ftmod_sangoma_isdn_cfg.c                  |  11 +-
 .../ftmod_sangoma_isdn_stack_hndl.c           |   7 +-
 .../ftmod_sangoma_isdn_stack_out.c            |  40 +--
 .../ftmod_sangoma_isdn_support.c              |  48 ++-
 .../ftmod_sangoma_isdn_transfer.c             | 281 ++++++++++++++++++
 .../src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c   |  31 +-
 libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c    |  18 +-
 libs/freetdm/src/include/freetdm.h            |  49 ++-
 libs/freetdm/src/include/private/ftdm_core.h  |  10 +
 libs/freetdm/src/include/private/ftdm_state.h |   3 +-
 libs/freetdm/src/include/private/ftdm_types.h |  13 +-
 17 files changed, 845 insertions(+), 197 deletions(-)
 create mode 100644 libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c

diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am
index 6bd486e49b..b0398ef90c 100644
--- a/libs/freetdm/Makefile.am
+++ b/libs/freetdm/Makefile.am
@@ -235,6 +235,7 @@ ftmod_sangoma_isdn_la_SOURCES = \
 	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c \
 	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cntrl.c \
 	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c \
+	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c \
 	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c \
 	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_cntrl.c \
 	$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_cfg.c \
diff --git a/libs/freetdm/docs/async.txt b/libs/freetdm/docs/async.txt
index 04069ebebd..c5d706ecba 100644
--- a/libs/freetdm/docs/async.txt
+++ b/libs/freetdm/docs/async.txt
@@ -2,6 +2,7 @@ APIs that result in an event when the API returns FTDM_SUCCESS
 
 ftdm_channel_call_answer()
 ftdm_channel_call_indicate()
+ftdm_channel_call_transfer()
 	FTDM_SIGEVENT_INDICATION_COMPLETED
 	*note that FTDM_SIGEVENT_INDICATION_COMPLETED has associated data to indicate the result of the indication
 	*note this event is only delivered on non-blocking channels
diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index a3cefce403..731893a2f6 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -67,7 +67,8 @@ typedef enum {
 	TFLAG_CODEC = (1 << 2),
 	TFLAG_BREAK = (1 << 3),
 	TFLAG_HOLD = (1 << 4),
-	TFLAG_DEAD = (1 << 5)
+	TFLAG_DEAD = (1 << 5),
+	TFLAG_TRANSFER = (1 << 6),
 } TFLAGS;
 
 static struct {
@@ -882,10 +883,30 @@ static switch_status_t channel_receive_message_b(switch_core_session_t *session,
 			ftdm_channel_call_answer(tech_pvt->ftdmchan);
 		}
 		break;
+	case SWITCH_MESSAGE_INDICATE_REDIRECT:
+	case SWITCH_MESSAGE_INDICATE_DEFLECT:
+		{
+			ftdm_usrmsg_t usrmsg;
+			const char *val = NULL;
+
+			memset(&usrmsg, 0, sizeof(usrmsg));
+
+			if ((val = switch_channel_get_variable(channel, "freetdm_transfer_data"))) {
+				ftdm_usrmsg_add_var(&usrmsg, "transfer_data", val);
+			}
+
+			switch_set_flag(tech_pvt, TFLAG_TRANSFER);
+			if (ftdm_channel_call_transfer_ex(tech_pvt->ftdmchan, msg->string_arg, &usrmsg) != FTDM_SUCCESS) {
+				switch_clear_flag(tech_pvt, TFLAG_TRANSFER);
+			}
+			while (switch_test_flag(tech_pvt, TFLAG_TRANSFER)) {
+				switch_yield(100000);
+			}
+		}
 	default:
 		break;
 	}
-	
+
 	return SWITCH_STATUS_SUCCESS;
 }
 
@@ -1749,11 +1770,13 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session
 
 static FIO_SIGNAL_CB_FUNCTION(on_common_signal)
 {
-	switch_event_t *event = NULL;
-	ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE;
 	uint32_t chanid, spanid;
+	switch_event_t *event = NULL;	
+	ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE;
+
 	chanid = ftdm_channel_get_id(sigmsg->channel);
 	spanid = ftdm_channel_get_span_id(sigmsg->channel);
+
 	switch (sigmsg->event_id) {
 
 	case FTDM_SIGEVENT_ALARM_CLEAR:
@@ -1788,14 +1811,44 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal)
 			}
 			return FTDM_SUCCESS;
 		}
+	case FTDM_SIGEVENT_TRANSFER_COMPLETED:
+		{
+			switch_core_session_t *session = NULL;
+			switch_channel_t *channel = NULL;
+			private_t *tech_pvt = NULL;
 
-	case FTDM_SIGEVENT_RELEASED: 
+			if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) {
+				channel = switch_core_session_get_channel(session);
+				tech_pvt = switch_core_session_get_private(session);
+
+				switch_clear_flag_locked(tech_pvt, TFLAG_TRANSFER);
+				switch_channel_set_variable(channel, "freetdm_transfer_response", ftdm_transfer_response2str(sigmsg->ev_data.transfer_completed.response));
+				switch_core_session_rwunlock(session);
+			}
+			return FTDM_SUCCESS;
+		}
+		break;
+	case FTDM_SIGEVENT_RELEASED:
 	case FTDM_SIGEVENT_INDICATION_COMPLETED:
 	case FTDM_SIGEVENT_DIALING:
-		{ 
+		{
 			/* Swallow these events */
 			return FTDM_BREAK;
-		} 
+		}
+		break;
+	case FTDM_SIGEVENT_STOP:
+	case FTDM_SIGEVENT_RESTART:
+		{
+			switch_core_session_t *session = NULL;
+			private_t *tech_pvt = NULL;
+			while((session = ftdm_channel_get_session(sigmsg->channel, 0))) {
+				tech_pvt = switch_core_session_get_private(session);
+
+				switch_clear_flag_locked(tech_pvt, TFLAG_TRANSFER);
+				switch_core_session_rwunlock(session);
+				return FTDM_SUCCESS;
+			}
+		}
 		break;
 	default:
 		return FTDM_SUCCESS;
@@ -2350,6 +2403,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal)
 		break;
 	case FTDM_SIGEVENT_PROCEED:
 	case FTDM_SIGEVENT_FACILITY:
+	case FTDM_SIGEVENT_TRANSFER_COMPLETED:
 		/* FS does not have handlers for these messages, so ignore them for now */
 		break;
 	default:
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index b57292c5b0..93729f0a50 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -58,6 +58,8 @@ struct tm *localtime_r(const time_t *clock, struct tm *result);
 #define FTDM_READ_TRACE_INDEX 0
 #define FTDM_WRITE_TRACE_INDEX 1
 #define MAX_CALLIDS 6000
+#define FTDM_HALF_DTMF_PAUSE 500
+#define FTDM_FULL_DTMF_PAUSE 1000
 
 ftdm_time_t time_last_throttle_log = 0;
 ftdm_time_t time_current_throttle_log = 0;
@@ -306,7 +308,10 @@ FTDM_ENUM_NAMES(CALLING_PARTY_CATEGORY_NAMES, CALLING_PARTY_CATEGORY_STRINGS)
 FTDM_STR2ENUM(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t, CALLING_PARTY_CATEGORY_NAMES, FTDM_CPC_INVALID)
 
 FTDM_ENUM_NAMES(INDICATION_NAMES, INDICATION_STRINGS)
-FTDM_STR2ENUM(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID)
+FTDM_STR2ENUM(ftdm_str2ftdm_channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID)
+
+FTDM_ENUM_NAMES(TRANSFER_RESPONSE_NAMES, TRANSFER_RESPONSE_STRINGS)
+FTDM_STR2ENUM(ftdm_str2ftdm_transfer_response, ftdm_transfer_response2str, ftdm_transfer_response_t, TRANSFER_RESPONSE_NAMES, FTDM_TRANSFER_RESPONSE_INVALID)
 
 static ftdm_status_t ftdm_group_add_channels(ftdm_span_t* span, int currindex, const char* name);
 
@@ -1191,6 +1196,10 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read_event(ftdm_channel_t *ftdmchan, ftdm
 		goto done;
 	}
 
+	if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT)) {
+		ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT);
+	}
+
 	status = span->fio->channel_next_event(ftdmchan, event);
 	if (status != FTDM_SUCCESS) {
 		goto done;
@@ -1952,6 +1961,7 @@ FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, f
 	if (!citer) {
 		return FTDM_ENOMEM;
 	}
+
 	for (curr = citer ; curr; curr = ftdm_iterator_next(curr)) {
 		fchan = ftdm_iterator_current(curr);
 		if (enabled) {
@@ -2076,12 +2086,12 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char
 FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status)
 {
 	ftdm_sigmsg_t msg;
-	
+
 	if (!ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) {
 		return;
 	}
 
-	ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n", 
+	ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n",
 			ftdm_channel_indication2str(indication), ftdm_channel_state2str(fchan->state), status);
 	ftdm_clear_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING);
 	memset(&msg, 0, sizeof(msg));
@@ -2155,11 +2165,36 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
 	return status;
 }
 
+FT_DECLARE(ftdm_status_t) _ftdm_channel_call_transfer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, const char* arg, ftdm_usrmsg_t *usrmsg)
+{
+	ftdm_status_t status;
+	ftdm_usrmsg_t *msg = NULL;
+	ftdm_bool_t free_msg = FTDM_FALSE;
+
+	if (!usrmsg) {
+		msg = ftdm_calloc(1, sizeof(*msg));
+		ftdm_assert_return(msg, FTDM_FAIL, "Failed to allocate usr msg");
+		memset(msg, 0, sizeof(*msg));
+		free_msg = FTDM_TRUE;
+	} else {
+		msg = usrmsg;
+	}
+
+	ftdm_usrmsg_add_var(msg, "transfer_arg", arg);
+	/* we leave the locking up to ftdm_channel_call_indicate, DO NOT lock here since ftdm_channel_call_indicate expects
+	* the lock recursivity to be 1 */
+	status = _ftdm_channel_call_indicate(file, func, line, ftdmchan, FTDM_CHANNEL_INDICATE_TRANSFER, msg);
+	if (free_msg == FTDM_TRUE) {
+		ftdm_safe_free(msg);
+	}
+	return status;
+}
+
 /* lock must be acquired by the caller! */
 static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char *func, int line, ftdm_channel_t *chan, ftdm_usrmsg_t *usrmsg)
 {
 	ftdm_status_t status = FTDM_SUCCESS;
-	
+
 	if (chan->state != FTDM_CHANNEL_STATE_DOWN) {
 		if (chan->state == FTDM_CHANNEL_STATE_HANGUP) {
 			/* make user's life easier, and just ignore double hangup requests */
@@ -2351,6 +2386,14 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch
 	case FTDM_CHANNEL_INDICATE_ANSWER:
 		status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan, usrmsg);
 		break;
+	case FTDM_CHANNEL_INDICATE_TRANSFER:
+		if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_TRANSFER)) {
+			ftdm_log_chan_ex_msg(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Transfer not supported\n");
+			status = FTDM_EINVAL;
+			goto done;
+		}
+		status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_TRANSFER, 1, usrmsg);
+		break;
 	default:
 		/* See if signalling module can provide this indication */
 		status = ftdm_channel_sig_indicate(ftdmchan, indication, usrmsg);
@@ -2720,10 +2763,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_close(ftdm_channel_t **ftdmchan)
 	return status;
 }
 
-
 static ftdm_status_t ftdmchan_activate_dtmf_buffer(ftdm_channel_t *ftdmchan)
 {
-
 	if (!ftdmchan->dtmf_buffer) {
 		if (ftdm_buffer_create(&ftdmchan->dtmf_buffer, 1024, 3192, 0) != FTDM_SUCCESS) {
 			ftdm_log(FTDM_LOG_ERROR, "Failed to allocate DTMF Buffer!\n");
@@ -2752,10 +2793,29 @@ static ftdm_status_t ftdmchan_activate_dtmf_buffer(ftdm_channel_t *ftdmchan)
 	return FTDM_SUCCESS;
 }
 
+/*
+ * ftdmchan_activate_dtmf_buffer to initialize ftdmchan->dtmf_buffer should be called prior to
+ * calling ftdm_insert_dtmf_pause
+ */
+static ftdm_status_t ftdm_insert_dtmf_pause(ftdm_channel_t *ftdmchan, ftdm_size_t pausems)
+{
+	void *data = NULL;
+	unsigned int datalen = pausems * sizeof(uint16_t);
+
+	data = ftdm_malloc(datalen);
+	ftdm_assert(data, "Failed to allocate memory\n");
+
+	memset(data, FTDM_SILENCE_VALUE(ftdmchan), datalen);
+
+	ftdm_buffer_write(ftdmchan->dtmf_buffer, data, datalen);
+	ftdm_safe_free(data);
+	return FTDM_SUCCESS;
+}
+
 FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_command_t command, void *obj)
 {
 	ftdm_status_t status = FTDM_FAIL;
-	
+
 	ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
 	ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n");
 
@@ -3519,13 +3579,18 @@ skipdebug:
 
 	status = ftdm_buffer_write(ftdmchan->digit_buffer, dtmf, wr) ? FTDM_SUCCESS : FTDM_FAIL;
 	ftdm_mutex_unlock(ftdmchan->mutex);
-	
+
 	return status;
 }
 
-static FIO_WRITE_FUNCTION(ftdm_raw_write)
+FIO_WRITE_FUNCTION(ftdm_raw_write)
 {
 	int dlen = (int) *datalen;
+
+	if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE)) {
+		ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE);
+	}
+
 	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED)) {
 		ftdmchan->txdrops++;
 		if (ftdmchan->txdrops <= 10) {
@@ -3545,11 +3610,16 @@ static FIO_WRITE_FUNCTION(ftdm_raw_write)
 	return ftdmchan->fio->write(ftdmchan, data, datalen);
 }
 
-static FIO_READ_FUNCTION(ftdm_raw_read)
+FIO_READ_FUNCTION(ftdm_raw_read)
 {
-	ftdm_status_t  status = ftdmchan->fio->read(ftdmchan, data, datalen);
+	ftdm_status_t  status;
+	
+	if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) {
+		ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ);
+	}
+	status = ftdmchan->fio->read(ftdmchan, data, datalen);
 
-	if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN) 
+	if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN)
 	   && (ftdmchan->native_codec == FTDM_CODEC_ALAW || ftdmchan->native_codec == FTDM_CODEC_ULAW)) {
 		ftdm_size_t i = 0;
 		unsigned char *rdata = data;
@@ -3616,30 +3686,31 @@ static ftdm_status_t handle_tone_generation(ftdm_channel_t *ftdmchan)
 
 		if (ftdm_buffer_read(ftdmchan->gen_dtmf_buffer, digits, dblen) && !ftdm_strlen_zero_buf(digits)) {
 			ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Generating DTMF [%s]\n", digits);
-		
+
 			cur = digits;
 
-			if (*cur == 'F') {
-				ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL);
-				cur++;
-			}
-
 			for (; *cur; cur++) {
-				if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) {
-					ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2);
-					x++;
+				if (*cur == 'F') {
+					ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL);
+				} else if (*cur == 'w') {
+					ftdm_insert_dtmf_pause(ftdmchan, FTDM_HALF_DTMF_PAUSE);
+				} else if (*cur == 'W') {
+					ftdm_insert_dtmf_pause(ftdmchan, FTDM_FULL_DTMF_PAUSE);
 				} else {
-					ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Problem adding DTMF sequence [%s]\n", digits);
-					return FTDM_FAIL;
+					if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) {
+						ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2);
+						x++;
+					} else {
+						ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Problem adding DTMF sequence [%s]\n", digits);
+						return FTDM_FAIL;
+					}
+				}
+				if (x) {
+					ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4;
 				}
-			}
-
-			if (x) {
-				ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4;
 			}
 		}
 	}
-	
 
 	if (!ftdmchan->buffer_delay || --ftdmchan->buffer_delay == 0) {
 		/* time to pick a buffer, either the dtmf or fsk buffer */
@@ -3699,6 +3770,7 @@ static ftdm_status_t handle_tone_generation(ftdm_channel_t *ftdmchan)
 
 }
 
+
 FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor)
 {
     int16_t x;
@@ -3720,49 +3792,12 @@ FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint
     }
 }
 
-FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen)
+FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen)
 {
 	ftdm_status_t status = FTDM_FAIL;
 	fio_codec_t codec_func = NULL;
 	ftdm_size_t max = *datalen;
 
-	ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n");
-	ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n");
-
-	ftdm_channel_lock(ftdmchan);
-	
-	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
-		snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open");
-		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel that is not open\n");
-		status = FTDM_FAIL;
-		goto done;
-	}
-
-	if (!ftdmchan->fio->read) {
-		snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented");
-		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "read method not implemented\n");
-		status = FTDM_FAIL;
-		goto done;
-	}
-
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
-		ftdmchan->rxdrops++;
-		if (ftdmchan->rxdrops <= 10) {
-			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel with rx disabled\n");
-		}
-		if (ftdmchan->rxdrops == 10) {
-			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "too many rx drops, not logging anymore\n");
-		}
-		status = FTDM_FAIL;
-		goto done;
-	}
-
-	status = ftdm_raw_read(ftdmchan, data, datalen);
-	if (status != FTDM_SUCCESS) {
-		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read filed\n");
-		goto done;
-	}
-
 	handle_tone_generation(ftdmchan);
 
 	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DIGITAL_MEDIA)) {
@@ -3789,8 +3824,10 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data
 		}
 	}
 
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) || 
+	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) ||
+		ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) ||
 		ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT)) {
+
 		uint8_t sln_buf[1024] = {0};
 		int16_t *sln;
 		ftdm_size_t slen = 0;
@@ -3894,8 +3931,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data
 				}
 			}
 		}
-	
-		
+
 		if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) && !ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
 			teletone_dtmf_detect(&ftdmchan->dtmf_detect, sln, (int)slen);
 			teletone_dtmf_get(&ftdmchan->dtmf_detect, digit_str, sizeof(digit_str));
@@ -3904,7 +3940,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data
 				if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) {
 					ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++;
 				} else {
-					ftdm_channel_queue_dtmf(ftdmchan, digit_str);
+					if (!ftdmchan->span->sig_dtmf || (ftdmchan->span->sig_dtmf(ftdmchan, (const char*)digit_str) != FTDM_BREAK)) {
+						ftdm_channel_queue_dtmf(ftdmchan, digit_str);
+					}
 
 					if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF)) {
 						ftdmchan->skip_read_frames = 20;
@@ -3915,20 +3953,19 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data
 	}
 
 	if (ftdmchan->skip_read_frames > 0 || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MUTE)) {
-		
+
 		ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
 		if (ftdmchan->pre_buffer && ftdm_buffer_inuse(ftdmchan->pre_buffer)) {
 			ftdm_buffer_zero(ftdmchan->pre_buffer);
 		}
 		ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
 
-
 		memset(data, FTDM_SILENCE_VALUE(ftdmchan), *datalen);
 
 		if (ftdmchan->skip_read_frames > 0) {
 			ftdmchan->skip_read_frames--;
 		}
-	}  else	{
+	} else {
 		ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
 		if (ftdmchan->pre_buffer_size && ftdmchan->pre_buffer) {
 			ftdm_buffer_write(ftdmchan->pre_buffer, data, *datalen);
@@ -3942,9 +3979,55 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data
 	}
 
 done:
+	return FTDM_SUCCESS;
+}
 
+
+FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen)
+{
+
+	ftdm_status_t status = FTDM_FAIL;
+
+	ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n");
+	ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n");
+
+	ftdm_channel_lock(ftdmchan);
+
+	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
+		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel that is not open\n");
+		status = FTDM_FAIL;
+		goto done;
+	}
+
+	if (!ftdmchan->fio->read) {		
+		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "read method not implemented\n");
+		status = FTDM_FAIL;
+		goto done;
+	}
+
+	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
+		ftdmchan->rxdrops++;
+		if (ftdmchan->rxdrops <= 10) {
+			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel with rx disabled\n");
+		}
+		if (ftdmchan->rxdrops == 10) {
+			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "too many rx drops, not logging anymore\n");
+		}
+		status = FTDM_FAIL;
+		goto done;
+	}
+	status = ftdm_raw_read(ftdmchan, data, datalen);
+	if (status != FTDM_SUCCESS) {
+		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read filed\n");
+		goto done;
+	}
+
+	status = ftdm_channel_process_media(ftdmchan, data, datalen);
+	if (status != FTDM_SUCCESS) {
+		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to process media\n");
+	}
+done:
 	ftdm_channel_unlock(ftdmchan);
-
 	return status;
 }
 
@@ -3972,14 +4055,12 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *dat
 
 	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
 		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot write in channel not open\n");
-		snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open");
 		status = FTDM_FAIL;
 		goto done;
 	}
 
 	if (!ftdmchan->fio->write) {
 		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "write method not implemented\n");
-		snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented");
 		status = FTDM_FAIL;
 		goto done;
 	}
@@ -4003,8 +4084,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *dat
 			status = codec_func(data, max, datalen);
 		} else {
 			ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Do not know how to handle transcoding from %d to %d\n", 
-					ftdmchan->effective_codec, ftdmchan->native_codec);
-			snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!");
+					ftdmchan->effective_codec, ftdmchan->native_codec);			
 			status = FTDM_FAIL;
 			goto done;
 		}
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
index 0cc8654f56..2a0fa83706 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c
@@ -44,6 +44,7 @@
 static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj);
 static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span);
 static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span);
+static ftdm_status_t ftdm_sangoma_isdn_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf);
 
 ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event);
 static void ftdm_sangoma_isdn_poll_events(ftdm_span_t *span);
@@ -53,10 +54,13 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev
 static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan);
 static void ftdm_sangoma_isdn_dchan_set_queue_size(ftdm_channel_t *ftdmchan);
 
-static ftdm_io_interface_t	    	g_sngisdn_io_interface;
+static ftdm_io_interface_t			g_sngisdn_io_interface;
 static sng_isdn_event_interface_t	g_sngisdn_event_interface;
 
-ftdm_sngisdn_data_t 			g_sngisdn_data;
+ftdm_sngisdn_data_t					g_sngisdn_data;
+
+FTDM_ENUM_NAMES(SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_TYPE_STRINGS)
+FTDM_STR2ENUM(ftdm_str2sngisdn_transfer_type, sngisdn_transfer_type2str, sngisdn_transfer_type_t, SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_INVALID)
 
 ftdm_state_map_t sangoma_isdn_state_map = {
 	{
@@ -126,8 +130,7 @@ ftdm_state_map_t sangoma_isdn_state_map = {
 		ZSD_INBOUND,
 		ZSM_UNACCEPTABLE,
 		{FTDM_CHANNEL_STATE_PROCEED, FTDM_END},
-		{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
-		 FTDM_CHANNEL_STATE_UP, FTDM_END}
+		{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_TRANSFER, FTDM_END}
 	},
 	{
 		ZSD_INBOUND,
@@ -151,7 +154,14 @@ ftdm_state_map_t sangoma_isdn_state_map = {
 		ZSD_INBOUND,
 		ZSM_UNACCEPTABLE,
 		{FTDM_CHANNEL_STATE_UP, FTDM_END},
-		{FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END},
+		{FTDM_CHANNEL_STATE_TRANSFER, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END},
+	},
+
+	{
+		ZSD_INBOUND,
+		ZSM_UNACCEPTABLE,
+		{FTDM_CHANNEL_STATE_TRANSFER, FTDM_END},
+		{FTDM_CHANNEL_STATE_PROCEED, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_TERMINATING,FTDM_END},
 	},
 	{
 		ZSD_INBOUND,
@@ -352,49 +362,96 @@ static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan)
 	return;
 }
 
-static void *ftdm_sangoma_isdn_dchan_run(ftdm_thread_t *me, void *obj)
+static void *ftdm_sangoma_isdn_io_run(ftdm_thread_t *me, void *obj)
 {
 	uint8_t data[1000];
+	unsigned i;
 	ftdm_status_t status = FTDM_SUCCESS;
 	ftdm_wait_flag_t wflags = FTDM_READ;
 	ftdm_span_t *span = (ftdm_span_t*) obj;
-	ftdm_channel_t *dchan = ((sngisdn_span_data_t*)span->signal_data)->dchan;
 	ftdm_size_t len = 0;
-	
-	ftdm_channel_set_feature(dchan, FTDM_CHANNEL_FEATURE_IO_STATS);
-	ftdm_sangoma_isdn_dchan_set_queue_size(dchan);
+	ftdm_channel_t *ftdmchan = NULL;
+	unsigned waitms = 10000;
+	ftdm_iterator_t *chaniter = NULL;
+	ftdm_iterator_t *citer = NULL;
+	short *poll_events = ftdm_malloc(sizeof(short) * span->chan_count);
+
+	/* Initialize the d-channel */
+	ftdm_assert(((sngisdn_span_data_t*)span->signal_data)->dchan, "Span does not have a dchannel");
+	ftdm_channel_set_feature(((sngisdn_span_data_t*)span->signal_data)->dchan, FTDM_CHANNEL_FEATURE_IO_STATS);
+	ftdm_sangoma_isdn_dchan_set_queue_size(((sngisdn_span_data_t*)span->signal_data)->dchan);
+	ftdm_channel_open_chan(((sngisdn_span_data_t*)span->signal_data)->dchan);
+
+	chaniter = ftdm_span_get_chan_iterator(span, NULL);
+	if (!chaniter) {
+		ftdm_log(FTDM_LOG_CRIT, "Failed to allocate channel iterator for span %s!\n", span->name);
+		goto done;
+	}
 
-	ftdm_assert(dchan, "Span does not have a dchannel");
-	ftdm_channel_open_chan(dchan);
-	
 	while (ftdm_running() && !(ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD))) {
+		len = 1000;
+		waitms = 1000;
 		wflags = FTDM_READ;
-		status = ftdm_channel_wait(dchan, &wflags, 10000);
-		switch(status) {
+		memset(poll_events, 0, sizeof(short)*span->chan_count);
+
+		for (i = 0, citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer), i++) {
+			ftdmchan = ftdm_iterator_current(citer);
+
+			if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
+				if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
+					poll_events[i] |= FTDM_READ;
+					waitms = 20;
+				}
+			} else {
+				/* We always read the d-channel */
+				poll_events[i] |= FTDM_READ;
+			}
+		}
+
+		status = ftdm_span_poll_event(span, waitms, poll_events);
+		switch (status) {
 			case FTDM_FAIL:
-				ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Failed to wait for d-channel\n");
+				ftdm_log(FTDM_LOG_CRIT, "Failed to poll span for IO\n");
 				break;
 			case FTDM_TIMEOUT:
 				break;
 			case FTDM_SUCCESS:
-				if ((wflags & FTDM_READ)) {
+				for (citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer)) {
 					len = 1000;
-					status = ftdm_channel_read(dchan, data, &len);
-					if (status == FTDM_SUCCESS) {
-						sngisdn_snd_data(dchan, data, len);
+					ftdmchan = ftdm_iterator_current(citer);
+					if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
+						if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
+							if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) {
+								status = ftdm_raw_read(ftdmchan, data, &len);
+								if (status != FTDM_SUCCESS) {
+									ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read failed\n");
+									continue;
+								}
+
+								status = ftdm_channel_process_media(ftdmchan, data, &len);
+								if (status != FTDM_SUCCESS) {
+									ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to process media\n");
+									continue;
+								}
+							}
+						}
 					} else {
-						ftdm_log_chan_msg(dchan, FTDM_LOG_WARNING, "Failed to read from channel \n");
+						if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) {
+							status = ftdm_channel_read(ftdmchan, data, &len);
+							if (status == FTDM_SUCCESS) {
+								sngisdn_snd_data(ftdmchan, data, len);
+							}
+						}
 					}
-#ifndef WIN32 /* It is valid on WIN32 for poll to return without errors, but no flags set */
-				} else {
-					ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Failed to poll for d-channel\n");
-#endif
 				}
 				break;
 			default:
-				ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Unhandled IO event\n");
+				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Unhandled IO event\n");
 		}
-	}	
+	}
+done:
+	ftdm_iterator_free(chaniter);
+	ftdm_safe_free(poll_events);
 	return NULL;
 }
 
@@ -600,10 +657,10 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev
 /* this function is called with the channel already locked by the core */
 static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
 {
-	ftdm_sigmsg_t		sigev;
-	ftdm_channel_state_t initial_state;
-	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
-	uint8_t 		state_change = 0;
+	ftdm_sigmsg_t			sigev;
+	ftdm_channel_state_t	initial_state;
+	sngisdn_chan_data_t		*sngisdn_info = ftdmchan->call_data;
+	uint8_t					state_change = 0;
 
 	memset(&sigev, 0, sizeof(sigev));
 
@@ -632,18 +689,15 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm
 		break;
 	case FTDM_CHANNEL_STATE_GET_CALLERID:
 		{
-			if (!sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) {
-				/* By default, we do not send a progress indicator in the proceed */
-				ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
-				
-				sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED);
-				sngisdn_snd_proceed(ftdmchan, prog_ind);
-			}
-			/* Wait in this state until we get FACILITY msg */			
+			/* By default, we do not send a progress indicator in the proceed */
+			ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
+			sngisdn_snd_proceed(ftdmchan, prog_ind);
+
+			/* Wait in this state until we get FACILITY msg */
 		}
 		break;
 	case FTDM_CHANNEL_STATE_RING: /* incoming call request */
-		{			
+		{
 			ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending incoming call from %s to %s to FTDM core\n", ftdmchan->caller_data.ani.digits, ftdmchan->caller_data.dnis.digits);
 
 			/* we have enough information to inform FTDM of the call*/
@@ -678,13 +732,8 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm
 					ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
 				}
 			} else {
-				if (!sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) {
-					/* By default, we do not send a progress indicator in the proceed */
-					ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
-					sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED);
-					
-					sngisdn_snd_proceed(ftdmchan, prog_ind);
-				}
+				ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
+				sngisdn_snd_proceed(ftdmchan, prog_ind);
 			}
 		}
 		break;
@@ -846,7 +895,6 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm
 				ftdm_channel_close(&close_chan);
 			}
 			if (glare) {
-
 				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Glare detected, processing saved call\n");
 				/* We are calling sngisdn_rcv_con_ind with ftdmchan->mutex being locked,
 					so no other threads will be able to touch this channel. The next time we will
@@ -856,11 +904,17 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm
 			}
 		}
 		break;
+	case FTDM_CHANNEL_STATE_TRANSFER:
+		{
+			/* sngisdn_transfer function will always result in a state change */
+			sngisdn_transfer(ftdmchan);
+			state_change++;
+		}
+		break;
 	case FTDM_CHANNEL_STATE_RESTART:
 		{
 			/* IMPLEMENT ME */
 		}
-
 		break;
 	case FTDM_CHANNEL_STATE_SUSPENDED:
 		{
@@ -984,6 +1038,21 @@ static FIO_SPAN_SET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_set_span_sig_status)
 	return FTDM_NOTIMPL;
 }
 
+static ftdm_status_t ftdm_sangoma_isdn_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf)
+{
+	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+	switch(sngisdn_info->transfer_data.type) {
+		case SNGISDN_TRANSFER_ATT_COURTESY_VRU:
+		case SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA:
+			return sngisdn_att_transfer_process_dtmf(ftdmchan, dtmf);
+		default:
+			/* We do not care about DTMF events, do nothing */
+			break;
+	}
+
+	return FTDM_SUCCESS;
+}
+
 static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span)
 {
 	sngisdn_span_data_t *signal_data = span->signal_data;
@@ -992,6 +1061,7 @@ static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span)
 		ftdm_log(FTDM_LOG_CRIT, "Failed to start span %s\n", span->name);
 		return FTDM_FAIL;
 	}
+
 	/* clear the monitor thread stop flag */
 	ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD);
 	ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
@@ -1015,7 +1085,7 @@ static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span)
 	}
 
 	/*start the dchan monitor thread*/
-	if (ftdm_thread_create_detached(ftdm_sangoma_isdn_dchan_run, span) != FTDM_SUCCESS) {
+	if (ftdm_thread_create_detached(ftdm_sangoma_isdn_io_run, span) != FTDM_SUCCESS) {
 		ftdm_log(FTDM_LOG_CRIT,"Failed to start Sangoma ISDN d-channel Monitor Thread!\n");
 		return FTDM_FAIL;
 	}
@@ -1106,6 +1176,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config)
 	span->indicate = ftdm_sangoma_isdn_indicate;
 	span->channel_request = NULL;
 	span->signal_cb	= sig_cb;
+	span->sig_dtmf = ftdm_sangoma_isdn_dtmf;
 	span->get_channel_sig_status = ftdm_sangoma_isdn_get_chan_sig_status;
 	span->set_channel_sig_status = ftdm_sangoma_isdn_set_chan_sig_status;
 	span->get_span_sig_status = ftdm_sangoma_isdn_get_span_sig_status;
@@ -1117,10 +1188,9 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config)
 	ftdm_set_flag(span, FTDM_SPAN_USE_PROCEED_STATE);
 	ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES);
 	ftdm_set_flag(span, FTDM_SPAN_NON_STOPPABLE);
+	ftdm_set_flag(span, FTDM_SPAN_USE_TRANSFER);
 
-	if (span->trunk_type == FTDM_TRUNK_BRI_PTMP ||
-		span->trunk_type == FTDM_TRUNK_BRI) {
-		
+	if (FTDM_SPAN_IS_BRI(span)) {
 		sngisdn_set_span_avail_rate(span, SNGISDN_AVAIL_PWR_SAVING);
 	}
 
@@ -1321,6 +1391,7 @@ done:
 	return status;
 }
 
+
 static FIO_IO_LOAD_FUNCTION(ftdm_sangoma_isdn_io_init)
 {
 	memset(&g_sngisdn_io_interface, 0, sizeof(g_sngisdn_io_interface));
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
index 7930a827f3..68e1371b79 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
@@ -82,6 +82,8 @@ typedef enum {
 	FLAG_MEDIA_READY		= (1 << 11),
 	/* Set when we already sent a Channel ID IE */
 	FLAG_SENT_CHAN_ID		= (1 << 12),
+	/* Set when we already sent a Connect */
+	FLAG_SENT_CONNECT		= (1 << 13),
 } sngisdn_flag_t;
 
 
@@ -152,10 +154,11 @@ typedef struct ftdm_sngisdn_prog_ind {
 } ftdm_sngisdn_progind_t;
 
 /* Only timers that can be cancelled are listed here */
-#define SNGISDN_NUM_TIMERS 1
+#define SNGISDN_NUM_TIMERS 2
 /* Increase NUM_TIMERS as number of ftdm_sngisdn_timer_t increases */
 typedef enum {
 	SNGISDN_TIMER_FACILITY = 0,
+	SNGISDN_TIMER_ATT_TRANSFER,
 } ftdm_sngisdn_timer_t;
 
 typedef struct sngisdn_glare_data {
@@ -165,8 +168,35 @@ typedef struct sngisdn_glare_data {
 	int16_t		dChan;
     ConEvnt		setup;
 	uint8_t		ces;
-}sngisdn_glare_data_t;
+} sngisdn_glare_data_t;
 
+typedef enum {
+	SNGISDN_TRANSFER_NONE = 0, /* Default value, no transfer being done */
+	SNGISDN_TRANSFER_ATT_COURTESY_VRU,
+	SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA,
+	SNGISDN_TRANSFER_INVALID,
+} sngisdn_transfer_type_t;
+#define SNGISDN_TRANSFER_TYPE_STRINGS "NONE", "ATT_COURTESY_VRU", "ATT_COURTERY_VRU_DATA", "INVALID"
+FTDM_STR2ENUM_P(ftdm_str2sngisdn_transfer_type, sngisdn_transfer_type2str, sngisdn_transfer_type_t)
+
+/* From section 4.2 of TR50075, max length of data is 100 when single UUI is sent */
+#define COURTESY_TRANSFER_MAX_DATA_SIZE 100
+
+typedef struct _att_courtesy_vru
+{
+	char dtmf_digits [20];
+	char data[COURTESY_TRANSFER_MAX_DATA_SIZE];
+} att_courtesy_vru_t;
+
+typedef struct _sngisdn_transfer_data
+{
+	sngisdn_transfer_type_t type; /* Specifies which type of transfer is being used */
+	ftdm_transfer_response_t response;
+	union
+	{
+		att_courtesy_vru_t att_courtesy_vru;
+	} tdata;
+} sngisdn_transfer_data_t;
 
 /* Channel specific data */
 typedef struct sngisdn_chan_data {
@@ -180,7 +210,8 @@ typedef struct sngisdn_chan_data {
 
 	uint8_t                 globalFlg;
 	sngisdn_glare_data_t	glare;
-	ftdm_timer_id_t 		timers[SNGISDN_NUM_TIMERS];
+	ftdm_timer_id_t			timers[SNGISDN_NUM_TIMERS];
+	sngisdn_transfer_data_t transfer_data;
 
 	/* variables saved here will be sent to the user application
 	on next SIGEVENT_XXX */
@@ -211,8 +242,10 @@ typedef struct sngisdn_span_data {
 	uint8_t			facility_ie_decode;
 	uint8_t			facility;
 	int8_t			facility_timeout;
+	uint8_t			att_remove_dtmf;
+	int32_t			transfer_timeout;
 	uint8_t			num_local_numbers;
-	uint8_t 		ignore_cause_value;
+	uint8_t			ignore_cause_value;
 	uint8_t			trace_q931; /* TODO: combine with trace_flags */
 	uint8_t			trace_q921; /* TODO: combine with trace_flags */
 	uint8_t			raw_trace_q931; /* TODO: combine with trace_flags */
@@ -432,18 +465,23 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId);
 ftdm_status_t set_restart_ind_ie(ftdm_channel_t *ftdmchan, RstInd *rstInd);
 ftdm_status_t set_facility_ie(ftdm_channel_t *ftdmchan, FacilityStr *facilityStr);
 ftdm_status_t set_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t *data_len);
+ftdm_status_t set_user_to_user_ie(ftdm_channel_t *ftdmchan, UsrUsr *usrUsr);
+ftdm_status_t set_cause_ie(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn);
 
 
 ftdm_status_t sngisdn_add_var(sngisdn_chan_data_t *sngisdn_info, const char* var, const char* val);
 ftdm_status_t sngisdn_add_raw_data(sngisdn_chan_data_t *sngisdn_info, uint8_t* data, ftdm_size_t data_len);
 ftdm_status_t sngisdn_clear_data(sngisdn_chan_data_t *sngisdn_info);
 void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t event_id);
-				 
+
 uint8_t sngisdn_get_infoTranCap_from_user(ftdm_bearer_cap_t bearer_capability);
 uint8_t sngisdn_get_usrInfoLyr1Prot_from_user(ftdm_user_layer1_prot_t layer1_prot);
 ftdm_bearer_cap_t sngisdn_get_infoTranCap_from_stack(uint8_t bearer_capability);
 ftdm_user_layer1_prot_t sngisdn_get_usrInfoLyr1Prot_from_stack(uint8_t layer1_prot);
 
+ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan);
+ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf);
+
 static __inline__ uint32_t sngisdn_test_flag(sngisdn_chan_data_t *sngisdn_info, sngisdn_flag_t flag)
 {
 	return (uint32_t) sngisdn_info->flags & flag;
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c
index 49b07fff33..ec688eb2ba 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c
@@ -285,7 +285,9 @@ ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_
 	signal_data->timer_t3 = 8;
 	signal_data->restart_opt = SNGISDN_OPT_DEFAULT;
 	signal_data->link_id = span->span_id;
-	
+	signal_data->transfer_timeout = 20000;
+	signal_data->att_remove_dtmf = 1;
+
 	span->default_caller_data.dnis.plan = FTDM_NPI_INVALID;
 	span->default_caller_data.dnis.type = FTDM_TON_INVALID;
 	span->default_caller_data.cid_num.plan = FTDM_NPI_INVALID;
@@ -365,6 +367,13 @@ ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_
 			if (signal_data->facility_timeout < 0) {
 				signal_data->facility_timeout = 0;
 			}
+		} else if (!strcasecmp(var, "transfer-timeout")) {
+			signal_data->transfer_timeout = atoi(val);
+			if (signal_data->transfer_timeout < 0) {
+				signal_data->transfer_timeout = 0;
+			}
+		} else if (!strcasecmp(var, "att-remove-dtmf")) {
+			parse_yesno(var, val, &signal_data->att_remove_dtmf);
 		} else if (!strcasecmp(var, "facility-ie-decode")) {
 			parse_yesno(var, val, &signal_data->facility_ie_decode);
 		} else if (!strcasecmp(var, "ignore-cause-value")) {
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
index 242963ec9c..0cb2b1401d 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
@@ -318,8 +318,9 @@ void sngisdn_process_con_cfm (sngisdn_event_data_t *sngisdn_event)
 		}
 	} else {
 		switch(ftdmchan->state) {
+			case FTDM_CHANNEL_STATE_TRANSFER:
 			case FTDM_CHANNEL_STATE_UP:
-				/* This is the only valid state we should get a CONNECT ACK on */
+				/* These are the only valid states we should get a CONNECT ACK on */
 				/* do nothing */
 				break;
 			case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
@@ -1265,6 +1266,10 @@ void sngisdn_process_rst_ind (sngisdn_event_data_t *sngisdn_event)
 
 	/* TODO: readjust this when NFAS is implemented as signal_data will not always be the first
 	 * span for that d-channel */
+	if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) {
+		ftdm_log(FTDM_LOG_DEBUG, "Received RESTART IND, but Restart Indicator IE not present\n");
+		return;
+	}
 
 	signal_data = g_sngisdn_data.dchans[dChan].spans[1];
 
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c
index 674e642719..9a1e66fe51 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c
@@ -146,13 +146,18 @@ void sngisdn_snd_proceed(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_i
 	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) {
+	if (sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) {
+		return;
+	}
+	sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED);
+
+	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;
 	}
-	
+
 	memset(&cnStEvnt, 0, sizeof(cnStEvnt));
 
 	/* Indicate channel ID only in first response */
@@ -161,7 +166,7 @@ void sngisdn_snd_proceed(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_i
 	}
 	set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind);
 	set_facility_ie(ftdmchan, &cnStEvnt.facilityStr);
-	
+
 	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending PROCEED (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces);
 
 	if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, MI_CALLPROC, signal_data->dchan_id, sngisdn_info->ces)) {
@@ -230,12 +235,17 @@ void sngisdn_snd_alert(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_ind
 
 void sngisdn_snd_connect(ftdm_channel_t *ftdmchan)
 {
-	CnStEvnt cnStEvnt;	
+	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_info->suInstId || !sngisdn_info->spInstId) {
+	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);
@@ -353,22 +363,12 @@ void sngisdn_snd_disconnect(ftdm_channel_t *ftdmchan)
 		ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
 		return;
 	}
-	
-	memset(&discEvnt, 0, sizeof(discEvnt));
-	
-	/* Fill discEvnt here */
-	/* TODO move this to set_cause_ie function */
-  	discEvnt.causeDgn[0].eh.pres = PRSNT_NODEF;
-	discEvnt.causeDgn[0].location.pres = PRSNT_NODEF;
-	discEvnt.causeDgn[0].location.val = IN_LOC_PRIVNETLU;
-	discEvnt.causeDgn[0].codeStand3.pres = PRSNT_NODEF;
-	discEvnt.causeDgn[0].codeStand3.val = IN_CSTD_CCITT;
-	discEvnt.causeDgn[0].causeVal.pres = PRSNT_NODEF;
-	discEvnt.causeDgn[0].causeVal.val = ftdmchan->caller_data.hangup_cause;
-	discEvnt.causeDgn[0].recommend.pres = NOTPRSNT;
-	discEvnt.causeDgn[0].dgnVal.pres = NOTPRSNT;
 
-	set_facility_ie(ftdmchan, &discEvnt.facilityStr);	
+	memset(&discEvnt, 0, sizeof(discEvnt));
+
+	set_cause_ie(ftdmchan, &discEvnt.causeDgn[0]);
+	set_facility_ie(ftdmchan, &discEvnt.facilityStr);
+	set_user_to_user_ie(ftdmchan, &discEvnt.usrUsr);
 
 	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
 	if (sng_isdn_disc_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &discEvnt)) {
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
index a148130b74..30ee953c40 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c
@@ -100,11 +100,12 @@ void clear_call_data(sngisdn_chan_data_t *sngisdn_info)
 	g_sngisdn_data.ccs[cc_id].active_spInstIds[sngisdn_info->spInstId]=NULL;
 	g_sngisdn_data.ccs[cc_id].active_suInstIds[sngisdn_info->suInstId]=NULL;
 	ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].mutex);
-	
+
 	sngisdn_info->suInstId = 0;
 	sngisdn_info->spInstId = 0;
 	sngisdn_info->globalFlg = 0;
 	sngisdn_info->flags = 0;
+	sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE;
 	return;
 }
 
@@ -845,6 +846,41 @@ ftdm_status_t set_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd, ftdm_s
 	return FTDM_SUCCESS;
 }
 
+ftdm_status_t set_user_to_user_ie(ftdm_channel_t *ftdmchan, UsrUsr *usrUsr)
+{
+	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+
+	if (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
+		usrUsr->eh.pres = PRSNT_NODEF;
+
+		usrUsr->protocolDisc.pres = PRSNT_NODEF;
+		usrUsr->protocolDisc.val = 0x08;
+		usrUsr->usrInfo.pres = PRSNT_NODEF;
+		usrUsr->usrInfo.len = strlen(sngisdn_info->transfer_data.tdata.att_courtesy_vru.data);
+		memcpy(usrUsr->usrInfo.val, sngisdn_info->transfer_data.tdata.att_courtesy_vru.data, usrUsr->usrInfo.len);
+		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending AT&T Transfer data len:%d\n", usrUsr->usrInfo.len);
+
+		return FTDM_SUCCESS;
+	}
+
+	return FTDM_SUCCESS;
+}
+
+ftdm_status_t set_cause_ie(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn)
+{
+
+	causeDgn->eh.pres = PRSNT_NODEF;
+	causeDgn->location.pres = PRSNT_NODEF;
+	causeDgn->location.val = IN_LOC_PRIVNETLU;
+	causeDgn->codeStand3.pres = PRSNT_NODEF;
+	causeDgn->codeStand3.val = IN_CSTD_CCITT;
+	causeDgn->causeVal.pres = PRSNT_NODEF;
+	causeDgn->causeVal.val = ftdmchan->caller_data.hangup_cause;
+	causeDgn->recommend.pres = NOTPRSNT;
+	causeDgn->dgnVal.pres = NOTPRSNT;
+	return FTDM_SUCCESS;
+}
+
 ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId)
 {
 	sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
@@ -853,7 +889,7 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId)
 	}
 
 	ftdm_set_flag(sngisdn_info, FLAG_SENT_CHAN_ID);
-	
+
 	chanId->eh.pres = PRSNT_NODEF;
 	chanId->prefExc.pres = PRSNT_NODEF;
 	chanId->prefExc.val = IN_PE_EXCLSVE;
@@ -862,8 +898,7 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId)
 	chanId->intIdentPres.pres = PRSNT_NODEF;
 	chanId->intIdentPres.val = IN_IIP_IMPLICIT;
 
-	if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI ||
-		   ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP) {
+	if (FTDM_SPAN_IS_BRI(ftdmchan->span)) {
 
 		/* BRI only params */
 		chanId->intType.pres = PRSNT_NODEF;
@@ -1294,10 +1329,13 @@ void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t
 		
 		sigev.raw.data = sngisdn_info->raw_data;
 		sigev.raw.len = sngisdn_info->raw_data_len;
-		
+
 		sngisdn_info->raw_data = NULL;
 		sngisdn_info->raw_data_len = 0;
 	}
+	if (event_id == FTDM_SIGEVENT_TRANSFER_COMPLETED) {
+		sigev.ev_data.transfer_completed.response = sngisdn_info->transfer_data.response;
+	}
 	ftdm_span_send_signal(ftdmchan->span, &sigev);
 }
 
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c
new file mode 100644
index 0000000000..f00087a061
--- /dev/null
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2011, Sangoma Technologies
+ * David Yat Sin <davidy@sangoma.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ftmod_sangoma_isdn.h"
+
+#define TRANSFER_FUNC(name) ftdm_status_t (name)(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* target)
+
+#define SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF		"**1"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED	"**5"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_OK				"**6"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM		"**7"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND	"**8"
+
+
+void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response);
+void att_courtesy_transfer_timeout(void* p_sngisdn_info);
+static ftdm_status_t att_courtesy_vru(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* target);
+
+typedef struct transfer_interfaces {
+	const char *name;
+	sngisdn_transfer_type_t type;
+	TRANSFER_FUNC(*func);
+}transfer_interface_t;
+
+static transfer_interface_t transfer_interfaces[] = {
+	/* AT&T TR-50075 Courtesy Transfer - VRU -- No Data (Section 4.3) */
+	{ "ATT_COURTESY_TRANSFER_V", SNGISDN_TRANSFER_ATT_COURTESY_VRU, att_courtesy_vru},
+	/* AT&T TR-50075 Courtesy Transfer - VRU --Data (Section 4.4) */
+	{ "ATT_COURTESY_TRANSFER_V_DATA", SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA, att_courtesy_vru},
+};
+
+void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response)
+{
+	ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
+	ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED);
+	ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DTMF_DETECT, NULL);
+
+	sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE;
+	sngisdn_info->transfer_data.response = response;
+
+	ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Transfer Complete:%s\n", ftdm_transfer_response2str(sngisdn_info->transfer_data.response));
+	sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_TRANSFER_COMPLETED);
+
+	ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_RX_BUFFERS, NULL);
+	return;
+}
+
+void att_courtesy_transfer_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 (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_NONE) {
+		/* Call was already cleared */
+		ftdm_mutex_unlock(ftdmchan->mutex);
+		return;
+	}
+
+	ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "AT&T Courtesy Transfer timeout (%d)\n", signal_data->transfer_timeout);
+	att_courtesy_transfer_complete(sngisdn_info, FTDM_TRANSFER_RESPONSE_TIMEOUT);
+	ftdm_mutex_unlock(ftdmchan->mutex);
+	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:%d 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_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;
+}
+
+
+ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan)
+{
+	const char* args;
+	char *p;
+	char *type = NULL;
+	char *target = NULL;
+	ftdm_status_t status = FTDM_FAIL;
+	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+	unsigned i;
+
+	args = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_arg");
+	if (ftdm_strlen_zero(args)) {
+		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because call_transfer_arg variable is not set\n");
+		goto done;
+	}
+
+	type = ftdm_strdup(args);
+	if ((p = strchr(type, '/'))) {
+		target = ftdm_strdup(p+1);
+		*p = '\0';
+	}
+
+	if (ftdm_strlen_zero(type) || ftdm_strlen_zero(target)) {
+		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid parameters for transfer %s, expected <type>/<target>\n", args);
+		goto done;
+	}
+
+	if (sngisdn_info->transfer_data.type != SNGISDN_TRANSFER_NONE) {
+		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because an existing transfer transfer is pending (%s)\n", sngisdn_transfer_type2str(sngisdn_info->transfer_data.type));
+		goto done;
+	}
+
+	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer requested type:%s target:%s\n", type, target);
+	for (i = 0; i < ftdm_array_len(transfer_interfaces); i++ ) {
+		if (!strcasecmp(transfer_interfaces[i].name, type)) {
+			/* Depending on the transfer type, the transfer function may change the
+			 * channel state to UP, or last_state, but the transfer function will always result in
+			 * an immediate state change if FTDM_SUCCESS is returned */
+
+			status = transfer_interfaces[i].func(ftdmchan, transfer_interfaces[i].type, target);
+			goto done;
+		}
+	}
+
+	ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid transfer type:%s\n", type);
+
+done:
+	if (status != FTDM_SUCCESS) {
+		ftdm_set_state(ftdmchan, ftdmchan->last_state);
+	}
+
+	ftdm_safe_free(type);
+	ftdm_safe_free(target);
+	return status;
+}
+
+ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf)
+{
+	ftdm_status_t status = FTDM_SUCCESS;
+	sngisdn_chan_data_t  *sngisdn_info = ftdmchan->call_data;
+	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
+	char *dtmf_digits = sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits;
+	ftdm_size_t dtmf_digits_len = strlen(dtmf_digits);
+
+	dtmf_digits_len += sprintf(&dtmf_digits[dtmf_digits_len], "%s", dtmf);
+	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer response digits:%s\n", dtmf_digits);
+	if (dtmf_digits_len == 3) {
+		if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF)) {
+			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_CP_DROP_OFF;
+		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED)) {
+			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED;
+		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_OK)) {
+			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_OK;
+		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM)) {
+			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_NUM;
+		} else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND)) {
+			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_COMMAND;
+		} else {
+			sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID;
+		}
+		if (signal_data->transfer_timeout) {
+			ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_TIMER_ATT_TRANSFER]);
+		}
+
+		if (sngisdn_info->transfer_data.response == FTDM_TRANSFER_RESPONSE_OK &&
+			sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
+			sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC);
+			ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING;
+			sngisdn_snd_disconnect(ftdmchan);
+		}
+		/* Network side will send disconnect in case of NO-DATA Transfer */
+		att_courtesy_transfer_complete(sngisdn_info, sngisdn_info->transfer_data.response);
+	}
+
+	if (signal_data->att_remove_dtmf) {
+		/* If we return FTDM_BREAK, dtmf event is not queue'ed to user */
+		status = FTDM_BREAK;
+	}
+	return status;
+}
diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
index 2bf9f6bc2e..6c945e1512 100644
--- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
+++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
@@ -1193,12 +1193,26 @@ FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event)
 #else
 		if (pfds[i-1].revents & POLLPRI) {
 #endif
-			ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT);
+			ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT);
 			ftdmchan->last_event_time = ftdm_current_time_in_ms();
 			k++;
 		}
+#ifdef LIBSANGOMA_VERSION
+		if (outflags[i-1] & POLLIN) {
+#else
+		if (pfds[i-1].revents & POLLIN) {
+#endif
+			ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ);
+		}
+#ifdef LIBSANGOMA_VERSION
+		if (outflags[i-1] & POLLOUT) {
+#else
+		if (pfds[i-1].revents & POLLOUT) {
+#endif
+			ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE);
+		}
 	}
-	/* when k is 0 it might be that an async wanpipe device signal was delivered */	
+	/* when k is 0 it might be that an async wanpipe device signal was delivered */
 	return FTDM_SUCCESS;
 }
 
@@ -1445,14 +1459,9 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
 	wanpipe_tdm_api_t tdm_api;
 	ftdm_span_t *span = ftdmchan->span;
 
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
-		ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
-	}
-
 	memset(&tdm_api, 0, sizeof(tdm_api));
 	status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api);
 	if (status != FTDM_SUCCESS) {
-		snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
 		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to read event from channel: %s\n", strerror(errno));
 		return FTDM_FAIL;
 	}
@@ -1488,7 +1497,7 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
 	for(i = 1; i <= span->chan_count; i++) {
 		/* as a hack for wink/flash detection, wanpipe_poll_event overrides the timeout parameter
 		 * to force the user to call this function each 5ms or so to detect the timeout of our wink/flash */
-		if (span->channels[i]->last_event_time && !ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) {
+		if (span->channels[i]->last_event_time && !ftdm_test_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT)) {
 			ftdm_time_t diff = ftdm_current_time_in_ms() - span->channels[i]->last_event_time;
 			/* XX printf("%u %u %u\n", diff, (unsigned)ftdm_current_time_in_ms(), (unsigned)span->channels[i]->last_event_time); */
 			if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_WINK)) {
@@ -1520,13 +1529,13 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
 					goto event;
 				}
 			}
-		} 
-		if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) {
+		}
+		if (ftdm_test_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT)) {
 			ftdm_status_t status;
 			wanpipe_tdm_api_t tdm_api;
 			ftdm_channel_t *ftdmchan = span->channels[i];
 			memset(&tdm_api, 0, sizeof(tdm_api));
-			ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT);
+			ftdm_clear_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT);
 
 			err = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api);
 			if (err != FTDM_SUCCESS) {
diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c
index 9c01b41299..fa9f76cd3f 100644
--- a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c
+++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c
@@ -976,13 +976,19 @@ FIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event)
 		snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
 		return FTDM_FAIL;
 	}
-	
+
 	for(i = 1; i <= span->chan_count; i++) {
 		if (pfds[i-1].revents & POLLPRI) {
-			ftdm_set_flag(span->channels[i], FTDM_CHANNEL_EVENT);
+			ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT);
 			span->channels[i]->last_event_time = ftdm_current_time_in_ms();
 			k++;
 		}
+		if (pfds[i-1].revents & POLLIN) {
+			ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_READ);
+		}
+		if (pfds[i-1].revents & POLLOUT) {
+			ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_WRITE);
+		}
 	}
 
 	if (!k) {
@@ -1106,8 +1112,8 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(zt_channel_next_event)
 	zt_event_t zt_event_id = 0;
 	ftdm_span_t *span = ftdmchan->span;
 
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
-		ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
+	if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT)) {
+		ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT);
 	}
 
 	if (ioctl(ftdmchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
@@ -1143,8 +1149,8 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event)
 
 	for(i = 1; i <= span->chan_count; i++) {
 		ftdm_channel_t *fchan = span->channels[i];
-		if (ftdm_test_flag(fchan, FTDM_CHANNEL_EVENT)) {
-			ftdm_clear_flag(fchan, FTDM_CHANNEL_EVENT);
+		if (ftdm_test_io_flag(fchan, FTDM_CHANNEL_IO_EVENT)) {
+			ftdm_clear_io_flag(fchan, FTDM_CHANNEL_IO_EVENT);
 			if (ioctl(fchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
 				snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
 				return FTDM_FAIL;
diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h
index 253f256ffa..b792ed4ede 100755
--- a/libs/freetdm/src/include/freetdm.h
+++ b/libs/freetdm/src/include/freetdm.h
@@ -30,9 +30,10 @@
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * Contributors: 
+ * Contributors:
  *
  * Moises Silva <moy@sangoma.com>
+ * David Yat Sin <dyatsin@sangoma.com>
  *
  */
 
@@ -317,6 +318,19 @@ typedef enum {
 #define CALLING_PARTY_CATEGORY_STRINGS "unknown", "operator", "operator-french", "operator-english", "operator-german", "operator-russian", "operator-spanish", "ordinary", "priority", "data-call", "test-call", "payphone", "invalid"
 FTDM_STR2ENUM_P(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t)
 
+/*! Network responses to transfer requests */
+typedef enum {
+	FTDM_TRANSFER_RESPONSE_OK,					/* Call is being transferred */
+	FTDM_TRANSFER_RESPONSE_CP_DROP_OFF,			/* Calling Party drop off */
+	FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED,		/* Cannot redirect, limits exceeded */
+	FTDM_TRANSFER_RESPONSE_INVALID_NUM,			/* Network did not receive or recognize dialed number */
+	FTDM_TRANSFER_RESPONSE_INVALID_COMMAND,		/* Network received an invalid command */
+	FTDM_TRANSFER_RESPONSE_TIMEOUT,				/* We did not receive a response from Network */
+	FTDM_TRANSFER_RESPONSE_INVALID,
+} ftdm_transfer_response_t;
+#define TRANSFER_RESPONSE_STRINGS "transfer-ok", "cp-drop-off", "limits-exceeded", "invalid-num", "invalid-command", "timeout", "invalid"
+FTDM_STR2ENUM_P(ftdm_str2ftdm_transfer_response, ftdm_transfer_response2str, ftdm_transfer_response_t)
+
 /*! \brief Digit limit used in DNIS/ANI */
 #define FTDM_DIGITS_LIMIT 25
 
@@ -436,13 +450,14 @@ typedef enum {
 	FTDM_SIGEVENT_TRACE, /*!<Interpreted trace event */
 	FTDM_SIGEVENT_TRACE_RAW, /*!<Raw trace event */
 	FTDM_SIGEVENT_INDICATION_COMPLETED, /*!< Last requested indication was completed */
-	FTDM_SIGEVENT_DIALING, /* Outgoing call just started */
+	FTDM_SIGEVENT_DIALING, /*!< Outgoing call just started */
+	FTDM_SIGEVENT_TRANSFER_COMPLETED, /*!< Transfer request is completed */
 	FTDM_SIGEVENT_INVALID, /*!<Invalid */
 } ftdm_signal_event_t;
 #define SIGNAL_STRINGS "START", "STOP", "RELEASED", "UP", "FLASH", "PROCEED", "RINGING", "PROGRESS", \
 		"PROGRESS_MEDIA", "ALARM_TRAP", "ALARM_CLEAR", \
 		"COLLECTED_DIGIT", "ADD_CALL", "RESTART", "SIGSTATUS_CHANGED", "FACILITY", \
-		"TRACE", "TRACE_RAW", "INDICATION_COMPLETED", "DIALING", "INVALID"
+		"TRACE", "TRACE_RAW", "INDICATION_COMPLETED", "DIALING", "TRANSFER_COMPLETED", "INVALID"
 /*! \brief Move from string to ftdm_signal_event_t and viceversa */
 FTDM_STR2ENUM_P(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t)
 
@@ -545,12 +560,13 @@ typedef enum {
 	/* Using this indication is equivalent to call ftdm_channel_call_answer API */
 	FTDM_CHANNEL_INDICATE_ANSWER,
 	FTDM_CHANNEL_INDICATE_FACILITY,
+	FTDM_CHANNEL_INDICATE_TRANSFER,
 	FTDM_CHANNEL_INDICATE_INVALID,
 } ftdm_channel_indication_t;
-#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "FACILITY", "INVALID"
+#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "FACILITY", "TRANSFER", "INVALID"
 
 /*! \brief Move from string to ftdm_channel_indication_t and viceversa */
-FTDM_STR2ENUM_P(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t)
+FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t)
 
 typedef struct {
 	/* The indication that was completed */
@@ -559,6 +575,10 @@ typedef struct {
 	ftdm_status_t status;
 } ftdm_event_indication_completed_t;
 
+typedef struct {
+	ftdm_transfer_response_t response;
+} ftdm_event_transfer_completed_t;
+
 typedef void * ftdm_variable_container_t;
 
 typedef struct {
@@ -580,6 +600,7 @@ struct ftdm_sigmsg {
 		ftdm_event_trace_t trace;	/*!< valid if event_id is FTDM_SIGEVENT_TRACE or FTDM_SIGEVENT_TRACE_RAW */
 		ftdm_event_collected_t collected; /*!< valid if event_id is FTDM_SIGEVENT_COLLECTED_DIGIT */
 		ftdm_event_indication_completed_t indication_completed; /*!< valid if the event_id is FTDM_SIGEVENT_INDICATION_COMPLETED */
+		ftdm_event_transfer_completed_t transfer_completed;
 	} ev_data;
 	ftdm_raw_data_t raw;
 };
@@ -913,7 +934,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
 #define ftdm_channel_call_place(ftdmchan) _ftdm_channel_call_place(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL)
 #define ftdm_channel_call_place_ex(ftdmchan, usrmsg) _ftdm_channel_call_place_ex(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg))
 
-/*! \brief Place an outgoing call recording the source code point where it was called (see ftdm_channel_call_place for an easy to use macro) 
+/*! \brief Place an outgoing call recording the source code point where it was called (see ftdm_channel_call_place for an easy to use macro)
  *  \deprecated This function is deprecated since leaves the door open to glare issues, use ftdm_call_place instead
  */
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg);
@@ -972,6 +993,20 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char
 /*! \brief Hangup the call with cause recording the source code point where it was called (see ftdm_channel_call_hangup_with_cause for an easy to use macro) */
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t, ftdm_usrmsg_t *usrmsg);
 
+/*! \brief Transfer call. This can also be accomplished by ftdm_channel_call_indicate with FTDM_CHANNEL_INDICATE_TRANSFER, in both
+ *         cases you will get a FTDM_SIGEVENT_INDICATION_COMPLETED when the indication is sent (or an error occurs).
+ *         Just as with ftdm_channel_call_indicate you won't receive FTDM_SIGEVENT_INDICATION_COMPLETED when this function
+ *         returns anything else than FTDM_SUCCESS
+ *  \note Although this API may result in FTDM_SIGEVENT_INDICATION_COMPLETED event being delivered,
+ *        there is no guarantee of whether the event will arrive after or before your execution thread returns
+ *        from ftdm_channel_call_transfer
+ */
+#define ftdm_channel_call_transfer(ftdmchan, arg) _ftdm_channel_call_transfer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (arg), NULL)
+#define ftdm_channel_call_transfer_ex(ftdmchan, arg, usrmsg) _ftdm_channel_call_transfer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (arg), (usrmsg))
+
+/*! \brief Answer call recording the source code point where the it was called (see ftdm_channel_call_tranasfer for an easy to use macro) */
+FT_DECLARE(ftdm_status_t) _ftdm_channel_call_transfer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, const char* arg, ftdm_usrmsg_t *usrmsg);
+
 /*! \brief Reset the channel */
 #define ftdm_channel_reset(ftdmchan) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL)
 #define ftdm_channel_reset_ex(ftdmchan, usrmsg) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), usrmsg)
@@ -1242,7 +1277,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_add_to_group(const char* name, ftdm_chann
 /*! \brief Remove the channel from a hunt group */
 FT_DECLARE(ftdm_status_t) ftdm_channel_remove_from_group(ftdm_group_t* group, ftdm_channel_t* ftdmchan);
 
-/*! 
+/*!
  * \brief Retrieves an event from the span
  *
  * \note
diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h
index c2de07b3f1..96083430fc 100644
--- a/libs/freetdm/src/include/private/ftdm_core.h
+++ b/libs/freetdm/src/include/private/ftdm_core.h
@@ -155,6 +155,10 @@ extern "C" {
 #define ftdm_clear_alarm_flag(obj, flag) (obj)->alarm_flags &= ~(flag)
 #define ftdm_test_alarm_flag(obj, flag) ((obj)->alarm_flags & flag)
 
+#define ftdm_set_io_flag(obj, flag) (obj)->io_flags |= (flag)
+#define ftdm_clear_io_flag(obj, flag) (obj)->io_flags &= ~(flag)
+#define ftdm_test_io_flag(obj, flag) ((obj)->io_flags & flag)
+
 /*!
   \brief Set a flag on an arbitrary object
   \command obj the object to set the flags on
@@ -399,6 +403,7 @@ struct ftdm_channel {
 	uint64_t flags;
 	uint32_t pflags;
 	uint32_t sflags;
+	uint8_t	 io_flags;
 	ftdm_alarm_flag_t alarm_flags;
 	ftdm_channel_feature_t features;
 	ftdm_codec_t effective_codec;
@@ -503,6 +508,7 @@ struct ftdm_span {
 	ftdm_span_stop_t stop;
 	ftdm_channel_sig_read_t sig_read;
 	ftdm_channel_sig_write_t sig_write;
+	ftdm_channel_sig_dtmf_t sig_dtmf;
 	ftdm_channel_state_processor_t state_processor; /*!< This guy is called whenever state processing is required */
 	void *io_data; /*!< Private I/O data per span. Do not touch unless you are an I/O module */
 	char *type;
@@ -591,6 +597,10 @@ FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indi
 
 FT_DECLARE(ftdm_iterator_t *) ftdm_get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter);
 
+FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen);
+
+FIO_WRITE_FUNCTION(ftdm_raw_write);
+FIO_READ_FUNCTION(ftdm_raw_read);
 
 /*! 
  * \brief Retrieves an event from the span
diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h
index 191f8aef82..e11a18d590 100644
--- a/libs/freetdm/src/include/private/ftdm_state.h
+++ b/libs/freetdm/src/include/private/ftdm_state.h
@@ -67,6 +67,7 @@ typedef enum {
 	FTDM_CHANNEL_STATE_PROGRESS,
 	FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
 	FTDM_CHANNEL_STATE_UP,
+	FTDM_CHANNEL_STATE_TRANSFER,
 	FTDM_CHANNEL_STATE_IDLE,
 	FTDM_CHANNEL_STATE_TERMINATING,
 	FTDM_CHANNEL_STATE_CANCEL,
@@ -78,7 +79,7 @@ typedef enum {
 } ftdm_channel_state_t;
 #define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \
 		"RING", "RINGING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \
-		"RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", \
+		"RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "TRANSFER", "IDLE", "TERMINATING", "CANCEL", \
 		"HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID"
 FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t)
 
diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h
index e4d2b6900d..2ed9fd04c5 100755
--- a/libs/freetdm/src/include/private/ftdm_types.h
+++ b/libs/freetdm/src/include/private/ftdm_types.h
@@ -189,6 +189,8 @@ typedef enum {
 	/* If this flag is set, then this span cannot be stopped individually, it can only be stopped
 		on freetdm unload */
 	FTDM_SPAN_NON_STOPPABLE = (1 << 13),
+	/* If this flag is set, then this span supports TRANSFER state */
+	FTDM_SPAN_USE_TRANSFER = (1 << 14),
 } ftdm_span_flag_t;
 
 /*! \brief Channel supported features */
@@ -206,6 +208,13 @@ typedef enum {
 	FTDM_CHANNEL_FEATURE_MF_GENERATE = (1<<10), /*!< Channel can generate R2 MF tones (read-only) */
 } ftdm_channel_feature_t;
 
+/*! \brief Channel IO pending flags */
+typedef enum {
+	FTDM_CHANNEL_IO_EVENT = (1 << 0),
+	FTDM_CHANNEL_IO_READ = (1 << 1),
+	FTDM_CHANNEL_IO_WRITE = (1 << 2),
+} ftdm_channel_io_flags_t;
+
 /*!< Channel flags. This used to be an enum but we reached the 32bit limit for enums, is safer this way */
 #define FTDM_CHANNEL_CONFIGURED    (1ULL << 0)
 #define FTDM_CHANNEL_READY         (1ULL << 1)
@@ -214,7 +223,6 @@ typedef enum {
 #define FTDM_CHANNEL_SUPRESS_DTMF  (1ULL << 4)
 #define FTDM_CHANNEL_TRANSCODE     (1ULL << 5)
 #define FTDM_CHANNEL_BUFFER        (1ULL << 6)
-#define FTDM_CHANNEL_EVENT         (1ULL << 7)
 #define FTDM_CHANNEL_INTHREAD      (1ULL << 8)
 #define FTDM_CHANNEL_WINK          (1ULL << 9)
 #define FTDM_CHANNEL_FLASH         (1ULL << 10)
@@ -331,10 +339,11 @@ typedef ftdm_status_t (*ftdm_span_start_t)(ftdm_span_t *span);
 typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span);
 typedef ftdm_status_t (*ftdm_channel_sig_read_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
 typedef ftdm_status_t (*ftdm_channel_sig_write_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
+typedef ftdm_status_t (*ftdm_channel_sig_dtmf_t)(ftdm_channel_t *ftdmchan, const char *dtmf);
 
 typedef enum {
 	FTDM_ITERATOR_VARS = 1,
-	FTDM_ITERATOR_CHANS, 
+	FTDM_ITERATOR_CHANS,
 } ftdm_iterator_type_t;
 
 struct ftdm_iterator {