diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am
index 5e804b7505..2ab5c29e18 100644
--- a/libs/freetdm/Makefile.am
+++ b/libs/freetdm/Makefile.am
@@ -73,6 +73,7 @@ libfreetdm_la_SOURCES = \
 	$(SRC)/hashtable.c \
 	$(SRC)/hashtable_itr.c \
 	$(SRC)/ftdm_io.c \
+	$(SRC)/ftdm_state.c \
 	$(SRC)/ftdm_queue.c \
 	$(SRC)/ftdm_sched.c \
 	$(SRC)/ftdm_call_utils.c \
diff --git a/libs/freetdm/conf/m3ua.conf b/libs/freetdm/conf/m3ua.conf
deleted file mode 100644
index e3eeed3a4a..0000000000
--- a/libs/freetdm/conf/m3ua.conf
+++ /dev/null
@@ -1,38 +0,0 @@
-;M3UA SS7 Links Config
-;
-;ss7box-m3ua_mode => true
-;local_sctp_ip => localhost
-;local sctp_port => 30000
-;remote_sctp_ip => localhost
-;remote_sctp_port => 30001
-;opc => 0-0-0
-;dpc => 0-0-0
-
-
-; AP Specific Stuff.  This will likely move later.
-
-; CNAM Gateways
-cnam1_dpc => 0-0-0
-cnam1_ssn => 253
-cnam2_dpc => 0-0-0
-cnam2_ssn => 253
-cnam3_dpc => 0-0-0
-cnam3_ssn => 253
-
-;LNP Gateways
-lnp1_dpc => 0-0-0
-lnp1_ssn => 253
-lnp2_dpc => 0-0-0
-lnp2_ssn => 253
-lnp3_dpc => 0-0-0
-lnp3_ssn => 253
-
-;LNP Gateways
-sms8001_dpc => 0-0-0
-sms8001_ssn => 253
-sms8002_dpc => 0-0-0
-sms8002_ssn => 253
-sms8003_dpc => 0-0-0
-sms8003_ssn => 253
-
-
diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac
index e26f10b0b2..a070e994a3 100644
--- a/libs/freetdm/configure.ac
+++ b/libs/freetdm/configure.ac
@@ -160,7 +160,7 @@ AC_ARG_WITH([pritap],
 	[AS_HELP_STRING([--with-pritap], [Install ftmod_pritap])],
 	[case "${withval}" in
 	 no)	enable_pritap="no" ;;
-	 *)	enable_pritab="yes" ;;
+	 *)	enable_pritap="yes" ;;
 	 esac],
 	[enable_pritap="no"]
 )
diff --git a/libs/freetdm/docs/locking.txt b/libs/freetdm/docs/locking.txt
new file mode 100644
index 0000000000..851d045b41
--- /dev/null
+++ b/libs/freetdm/docs/locking.txt
@@ -0,0 +1,125 @@
+Last Updated: Fri 30 Dec, 2010
+
+== Background ==
+
+FreeTDM is a threaded library. As such, locking considerations must be taken when using it and when writing code inside the library.
+
+At the moment locks are not exposed to users. This means API users cannot acquire a lock on a channel or span structure. There is no
+need for users to lock channels or spans since all their interactions with those structures should be done thru the FreeTDM API which
+can (and in most cases must) internally lock on their behalf.
+
+Internally, locking can be done either by the core or the signaling modules. To better understand the locking considerations we must
+understand first the threading model of FreeTDM.
+
+== Threading Model ==
+
+At startup, when the user calls ftdm_global_init(), just one timing thread is created to dispatch internal timers. If you write
+a signaling module or any other code using the scheduling API, you can choose to run your schedule in this timing thread or in
+a thread of your choice. This is the only thread launched at initialization.
+
+If the application decides to use ftdm_global_configuration(), which reads freetdm.conf to create the spans and channels data
+structures, then possibly another thread will be launched for CPU usage monitoring (only if enabled in the configuration cpu_monitor=yes
+This thread sole purpose is to check the CPU and raise an alarm if reaches a configurable threshold, the alarm then is checked to avoid
+placing or receiving further calls.
+
+At this point FreeTDM has initialized and configured its channels input output configuration.
+
+The user is then supposed to configure the signaling via ftdm_configure_span_signaling() and then start the signaling work
+using ftdm_span_start(). This will typically launch at least 1 thread per span. Some signaling modules (actually just the analog one)
+launches another thread per channel when receiving a call. The rest of the signaling modules currently launch only one thread per
+span and the signaling for all channels within the span is handled in that thread. We call that thread 'the signaling thread'.
+
+At this point the user can start placing calls using the FreeTDM call API ftdm_channel_call_place(). Any of the possible threads in 
+which the user calls the FreeTDM API is called 'the user thread', depending on the application thread model (the application using FreeTDM)
+this user thread may be different each time or the same all the time, we cannot make any assumptions. In the case of FreeSWITCH, the most
+common user of FreeTDM, the user thread is most of the cases a thread for each new call leg.
+
+At this point we have identified 4 types of threads.
+
+1. The timing thread (the core thread that triggers timers).
+   Its responsibility is simply check for timers that were scheduled and trigger them when the time comes. This means that if you decide
+   to use the scheduling API in freerun mode (where you use the core timer thread) you callbacks will be executed in this global thread
+   and you MUST not block at all since there might be other events waiting.
+
+2. The CPU thread (we don't really care about this one as it does not interact with channels or spans).
+
+3. The signaling thread.
+   There is one thread of this per span. This thread takes care of reading signaling specific messages from the network (ISDN network, etc) and
+   changing the channel call state accordingly and processing state changes caused by user API calls (like ftdm_channel_call_hangup for example).
+
+4. The user thread.
+   This is a thread in which the user decides to execute FreeTDM APIs, in some cases it might even be the same than the signaling thread (because
+   most SIGEVENT notifications are delivered by the signaling thread, however we are advicing users to not use FreeTDM unsafe APIs from the
+   thread where they receive SIGEVENT notifications as some APIs may block for a few milliseconds, effectively blocking the whole signaling thread
+   that is servicing a span.
+
+== Application Locking ==
+
+Users of the FreeTDM API will typically have locking of their own for their own application-specific data structures (in the case of FreeSWITCH, the
+session lock for example). Other application-specific locks may be involved.
+
+== DeadLocks ==
+
+As soon as we think of application locks, and we mix them with the FreeTDM internal locks, the possibility of deadlocks arise.
+
+A typical deadlock scenario when 2 locks are involved is:
+
+- User Thread -                                                                    - Signaling Thread -
+1. Application locks applock.                                                     1. A network message is received for a channel.
+
+2. Aplication invokes a FreeTDM call API (ie: ftdm_channel_call_hangup()).        2. The involved channel is locked.
+
+3. The FreeTDM API attempts to acquire the channel lock and stalls because        3. The message processing results in a notification
+   the signaling thread just acquired it.                                            to be delivered to the user via the callback function
+										     provided for that purpose. The callback is then called.
+
+4. The thread is now deadlocked because the signaling thread will never           4. The application callback attempts to acquire its application
+   release the channel lock.                                                         lock but deadlocks because the user thread already has it.
+
+To avoid this signaling modules should not deliver signals to the user while holding the channel lock. An easy way to avoid this is
+to not deliver signals while processing a state change, but rather defer them until the channel lock is released. Most new signaling modules
+accomplish this by setting the span flag FTDM_SPAN_USE_SIGNALS_QUEUE, this flag tells the core to enqueue signals (ie FTDM_SIGEVENT_START) 
+when ftdm_span_send_signal() is called and not deliver them until ftdm_span_trigger_signals() is called, which is done by the signaling module
+in its signaling thread when no channel lock is being held.
+
+== State changes while locking ==
+
+Only 2 types of threads should be performing state changes.
+
+User threads.
+The user thread is a random thread that was crated by the API user. We do not know what threading model users of FreeTDM will follow
+and therefore cannot make assumptions about it. The user should be free to call FreeTDM APIs from any thread, except threads that
+are under our control, like the signaling threads. Although it may work in most situations, is discouraged for users to try
+to use FreeTDM APIs from the signaling thread, that is, the thread where the signaling callback provided during configuration
+is called (the callback where FTDM_SIGEVENT_XXX signals are delivered).
+
+A user thread may request state changes implicitly through calls to FreeTDM API's. The idea of state changes is internal to freetdm
+and should not be exposed to users of the API (except for debugging purposes, like the ftdm_channel_get_state, ftdm_channel_get_state_str etc)
+
+This is an example of the API's that implicitly request a state change.
+
+ftdm_channel_call_answer()
+
+Signaling modules should guarantee that upon releasing a lock on a channel, any state changes will be already processed and
+not deferred to other threads, otherwise that leads to a situation where a state change requested by the signaling module is pending
+to be serviced by another signaling module thread but a user thread wins the channel lock and attempts to perform a state change which will
+fail because another state change is pending (and user threads are not meant to process signaling states).
+
+ONLY one signaling thread per channel should try to perform state changes and processing of the states, 
+otherwise complexity arises and is not worth it!
+
+At some point before we stablished this policies we could have 3 different threads doing state changes.
+
+1. A user random thread could implcitly try to change the state in response to a call API.
+2. The ftmod signaling thread could try to change the state in response to other state changes.
+3. The lower level signaling stack threads could try to change the state in response to stack events.
+
+As a result, lower level signaling stack thread could set a state and then let the signaling thread to
+process it, but when unlocking the channel, the user thread may win the lock over the signaling thread and
+may try to set a state change of its own and fail (due to the unprocessed state change)!
+
+The rule is, the signaling module should never unlock a channel with states pending to process this way the user, 
+when acquiring a channel lock (inside ftdm_channel_call_answer for example) it will always find a consistent state 
+for the channel and not in the middle of state processing.
+
+
diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index d9c26db4fe..7bbdef6dae 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -421,16 +421,18 @@ static switch_status_t channel_on_routing(switch_core_session_t *session)
 	private_t *tech_pvt = NULL;
 
 	channel = switch_core_session_get_channel(session);
-	assert(channel != NULL);
+	switch_assert(channel != NULL);
 
 	tech_pvt = switch_core_session_get_private(session);
-	assert(tech_pvt != NULL);
+	switch_assert(tech_pvt != NULL);
 
-	assert(tech_pvt->ftdmchan != NULL);
+	switch_assert(tech_pvt->ftdmchan != NULL);
 
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel));
 
-	ftdm_channel_call_indicate(tech_pvt->ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED);
+	if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
+		ftdm_channel_call_indicate(tech_pvt->ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED);
+	}
 	return SWITCH_STATUS_SUCCESS;
 }
 
@@ -441,10 +443,10 @@ static switch_status_t channel_on_execute(switch_core_session_t *session)
 	private_t *tech_pvt = NULL;
 
 	channel = switch_core_session_get_channel(session);
-	assert(channel != NULL);
+	switch_assert(channel != NULL);
 
 	tech_pvt = switch_core_session_get_private(session);
-	assert(tech_pvt != NULL);
+	switch_assert(tech_pvt != NULL);
 
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
 
@@ -1640,6 +1642,14 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal)
 			}
 			return FTDM_SUCCESS;
 		}
+
+	case FTDM_SIGEVENT_RELEASED: 
+	case FTDM_SIGEVENT_INDICATION_COMPLETED:
+		{ 
+			/* Swallow these events */
+			return FTDM_BREAK;
+		} 
+		break;
 	default:
 		return FTDM_SUCCESS;
 		break;
@@ -1730,7 +1740,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
 			}
 		}
 		break;
-	case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
 	case FTDM_SIGEVENT_SIGSTATUS_CHANGED: { /* twiddle */ } break;
 	
 	default:
@@ -1786,7 +1795,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
 			}
 		}
 		break;
-	case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
 	
     case FTDM_SIGEVENT_STOP:
 		{
@@ -2013,8 +2021,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
 		}
 		break;
 		
-		case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
-		
 		/* on DNIS received from the R2 forward side, return status == FTDM_BREAK to stop requesting DNIS */
 		case FTDM_SIGEVENT_COLLECTED_DIGIT: 
 		{
@@ -2094,6 +2100,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
 		break;
 
 		case FTDM_SIGEVENT_PROCEED:{} break;
+		case FTDM_SIGEVENT_INDICATION_COMPLETED:{} break;
 
 		default:
 		{
@@ -2132,8 +2139,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal)
 		}
 		break;
 
-	case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
-		
     case FTDM_SIGEVENT_STOP:
     case FTDM_SIGEVENT_RESTART:
 		{
diff --git a/libs/freetdm/msvc/freetdm.2008.vcproj b/libs/freetdm/msvc/freetdm.2008.vcproj
index c72891e525..0539ff3f42 100644
--- a/libs/freetdm/msvc/freetdm.2008.vcproj
+++ b/libs/freetdm/msvc/freetdm.2008.vcproj
@@ -94,78 +94,6 @@
 				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
-			IntermediateDirectory="$(ConfigurationName)"
-			ConfigurationType="2"
-			CharacterSet="2"
-			WholeProgramOptimization="1"
-			BuildLogFile="$(IntDir)\BuildLog-freetdm.htm"
-			>
-			<Tool
-				Name="VCPreBuildEventTool"
-			/>
-			<Tool
-				Name="VCCustomBuildTool"
-			/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"
-			/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"
-			/>
-			<Tool
-				Name="VCMIDLTool"
-			/>
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../src/include;../src/include/private;../src/isdn/include"
-				PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS"
-				RuntimeLibrary="2"
-				DisableLanguageExtensions="false"
-				RuntimeTypeInfo="false"
-				UsePrecompiledHeader="0"
-				WarningLevel="4"
-				WarnAsError="true"
-				Detect64BitPortabilityProblems="false"
-				DebugInformationFormat="3"
-				CompileAs="1"
-			/>
-			<Tool
-				Name="VCManagedResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCResourceCompilerTool"
-			/>
-			<Tool
-				Name="VCPreLinkEventTool"
-			/>
-			<Tool
-				Name="VCLinkerTool"
-			/>
-			<Tool
-				Name="VCALinkTool"
-			/>
-			<Tool
-				Name="VCManifestTool"
-			/>
-			<Tool
-				Name="VCXDCMakeTool"
-			/>
-			<Tool
-				Name="VCBscMakeTool"
-			/>
-			<Tool
-				Name="VCFxCopTool"
-			/>
-			<Tool
-				Name="VCAppVerifierTool"
-			/>
-			<Tool
-				Name="VCPostBuildEventTool"
-			/>
-		</Configuration>
 		<Configuration
 			Name="Debug|x64"
 			OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
@@ -243,6 +171,78 @@
 				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			BuildLogFile="$(IntDir)\BuildLog-freetdm.htm"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="../src/include;../src/include/private;../src/isdn/include"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS"
+				RuntimeLibrary="2"
+				DisableLanguageExtensions="false"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="4"
+				WarnAsError="true"
+				Detect64BitPortabilityProblems="false"
+				DebugInformationFormat="3"
+				CompileAs="1"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
 		<Configuration
 			Name="Release|x64"
 			OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
@@ -366,6 +366,10 @@
 				RelativePath="..\src\include\private\ftdm_sched.h"
 				>
 			</File>
+			<File
+				RelativePath="..\src\include\private\ftdm_state.h"
+				>
+			</File>
 			<File
 				RelativePath="..\src\include\ftdm_threadmutex.h"
 				>
@@ -452,6 +456,10 @@
 				RelativePath="..\src\ftdm_sched.c"
 				>
 			</File>
+			<File
+				RelativePath="..\src\ftdm_state.c"
+				>
+			</File>
 			<File
 				RelativePath="..\src\ftdm_threadmutex.c"
 				>
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index 9c38755754..efd52542a6 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -268,9 +268,6 @@ FTDM_STR2ENUM(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_
 FTDM_ENUM_NAMES(SIGNAL_NAMES, SIGNAL_STRINGS)
 FTDM_STR2ENUM(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t, SIGNAL_NAMES, FTDM_SIGEVENT_INVALID)
 
-FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS)
-FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID)
-
 FTDM_ENUM_NAMES(MDMF_TYPE_NAMES, MDMF_STRINGS)
 FTDM_STR2ENUM(ftdm_str2ftdm_mdmf_type, ftdm_mdmf_type2str, ftdm_mdmf_type_t, MDMF_TYPE_NAMES, MDMF_INVALID)
 
@@ -304,6 +301,9 @@ FTDM_STR2ENUM(ftdm_str2ftdm_usr_layer1_prot, ftdm_user_layer1_prot2str, ftdm_use
 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)
+
 static ftdm_status_t ftdm_group_add_channels(ftdm_span_t* span, int currindex, const char* name);
 
 static const char *cut_path(const char *in)
@@ -615,6 +615,9 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan)
 
 		ftdm_mutex_destroy(&ftdmchan->mutex);
 		ftdm_mutex_destroy(&ftdmchan->pre_buffer_mutex);
+		if (ftdmchan->state_completed_interrupt) {
+			ftdm_interrupt_destroy(&ftdmchan->state_completed_interrupt);
+		}
 	}
 	
 	return FTDM_SUCCESS;
@@ -1022,6 +1025,8 @@ FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t
 		}
 
 		ftdm_set_flag(new_chan, FTDM_CHANNEL_CONFIGURED | FTDM_CHANNEL_READY);		
+		new_chan->state = FTDM_CHANNEL_STATE_DOWN;
+		new_chan->state_status = FTDM_STATE_STATUS_COMPLETED;
 		*chan = new_chan;
 		return FTDM_SUCCESS;
 	}
@@ -1340,237 +1345,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_add_token(ftdm_channel_t *ftdmchan, char
 }
 
 
-FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan)
-{
-	ftdm_channel_state_t state = ftdmchan->state;
-
-	if (state == FTDM_CHANNEL_STATE_PROGRESS) {
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
-	} else if (state == FTDM_CHANNEL_STATE_UP) {
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);	
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED);	
-	} else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);	
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);	
-	}
-
-	return FTDM_SUCCESS;
-}
-
-static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map)
-{
-	int x = 0, ok = 0;
-	ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND;
-
-	for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) {
-		int i = 0, proceed = 0;
-		if (!state_map->nodes[x].type) {
-			break;
-		}
-
-		if (state_map->nodes[x].direction != direction) {
-			continue;
-		}
-		
-		if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) {
-			proceed = 1;
-		} else {
-			for(i = 0; i < FTDM_MAP_MAX; i++) {
-				if (state_map->nodes[x].check_states[i] == ftdmchan->state) {
-					proceed = 1;
-					break;
-				}
-			}
-		}
-
-		if (!proceed) {
-			continue;
-		}
-		
-		for(i = 0; i < FTDM_MAP_MAX; i++) {
-			ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE);
-			if (state_map->nodes[x].states[i] == FTDM_END) {
-				break;
-			}
-			if (state_map->nodes[x].states[i] == state) {
-				ok = !ok;
-				goto end;
-			}
-		}
-	}
- end:
-	
-	return ok;
-}
-
-/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
-#define DEFAULT_WAIT_TIME 1000
-FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
-{
-	int ok = 1;
-	int waitms = DEFAULT_WAIT_TIME;	
-
-	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
-		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
-				ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
-		return FTDM_FAIL;
-	}
-
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the previous state change has not been processed yet\n",
-				ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
-		return FTDM_FAIL;
-	}
-
-	if (ftdmchan->state == state) {
-		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
-		return FTDM_FAIL;
-	}
-
-	if (ftdmchan->span->state_map) {
-		ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
-		goto end;
-	}
-
-	/* basic core state validation (by-passed if the signaling module provides a state_map) */
-	switch(ftdmchan->state) {
-	case FTDM_CHANNEL_STATE_HANGUP:
-	case FTDM_CHANNEL_STATE_TERMINATING:
-		{
-			ok = 0;
-			switch(state) {
-			case FTDM_CHANNEL_STATE_DOWN:
-			case FTDM_CHANNEL_STATE_BUSY:
-			case FTDM_CHANNEL_STATE_RESTART:
-				ok = 1;
-				break;
-			default:
-				break;
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_UP:
-		{
-			ok = 1;
-			switch(state) {
-			case FTDM_CHANNEL_STATE_PROGRESS:
-			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
-			case FTDM_CHANNEL_STATE_RING:
-				ok = 0;
-				break;
-			default:
-				break;
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_DOWN:
-		{
-			ok = 0;
-			
-			switch(state) {
-			case FTDM_CHANNEL_STATE_DIALTONE:
-			case FTDM_CHANNEL_STATE_COLLECT:
-			case FTDM_CHANNEL_STATE_DIALING:
-			case FTDM_CHANNEL_STATE_RING:
-			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
-			case FTDM_CHANNEL_STATE_PROGRESS:				
-			case FTDM_CHANNEL_STATE_IDLE:				
-			case FTDM_CHANNEL_STATE_GET_CALLERID:
-			case FTDM_CHANNEL_STATE_GENRING:
-				ok = 1;
-				break;
-			default:
-				break;
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_BUSY:
-		{
-			switch(state) {
-			case FTDM_CHANNEL_STATE_UP:
-				ok = 0;
-				break;
-			default:
-				break;
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_RING:
-		{
-			switch(state) {
-			case FTDM_CHANNEL_STATE_UP:
-				ok = 1;
-				break;
-			default:
-				break;
-			}
-		}
-		break;
-	default:
-		break;
-	}
-
-end:
-
-	if (ok) {
-		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
-		ftdmchan->last_state = ftdmchan->state; 
-		ftdmchan->state = state;
-		ftdmchan->history[ftdmchan->hindex].file = file;
-		ftdmchan->history[ftdmchan->hindex].func = func;
-		ftdmchan->history[ftdmchan->hindex].line = line;
-		ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
-		ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
-		ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
-		ftdmchan->hindex++;
-		if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
-			ftdmchan->hindex = 0;
-		}
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
-
-		ftdm_mutex_lock(ftdmchan->span->mutex);
-		ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
-		if (ftdmchan->span->pendingchans) {
-			ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
-		}
-		ftdm_mutex_unlock(ftdmchan->span->mutex);
-	} else {
-		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
-		goto done;
-	}
-
-	/* there is an inherent race here between set and check of the change flag but we do not care because
-	 * the flag should never last raised for more than a few ms for any state change */
-	while (waitrq && waitms > 0) {
-		/* give a chance to the signaling stack to process it */
-		ftdm_mutex_unlock(ftdmchan->mutex);
-
-		ftdm_sleep(10);
-		waitms -= 10;
-
-		ftdm_mutex_lock(ftdmchan->mutex);
-		
-		/* if the flag is no longer set, the state change was processed (or is being processed) */
-		if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-			break;
-		}
-
-		/* if the state is no longer what we set, the state change was 
-		 * obviously processed (and the current state change flag is for other state change) */
-		if (ftdmchan->state != state) {
-			break;
-		}
-	}
-
-	if (waitms <= 0) {
-		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n",
-				ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
-	}
-done:
-	return ok ? FTDM_SUCCESS : FTDM_FAIL;
-}
-
 FT_DECLARE(uint32_t) ftdm_group_get_id(const ftdm_group_t *group)
 {
 	return group->group_id;
@@ -1930,17 +1704,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direc
 	return status;
 }
 
-
-FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan)
-{
-	if (ftdmchan->init_state != FTDM_CHANNEL_STATE_DOWN) {
-		ftdm_set_state(ftdmchan, ftdmchan->init_state);
-		ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
-	}
-
-	return FTDM_SUCCESS;
-}
-
 FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan)
 {
 	ftdm_status_t status = FTDM_FAIL;
@@ -2115,6 +1878,28 @@ FT_DECLARE(void) ftdm_span_set_trunk_type(ftdm_span_t *span, ftdm_trunk_type_t t
 	span->trunk_type = type;
 }
 
+FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, ftdm_bool_t enabled)
+{
+	ftdm_channel_t *fchan = NULL;
+	ftdm_iterator_t *citer = NULL;
+	ftdm_iterator_t *curr = NULL;
+
+	citer = ftdm_span_get_chan_iterator(span, NULL);
+	if (!citer) {
+		return FTDM_ENOMEM;
+	}
+	for (curr = citer ; curr; curr = ftdm_iterator_next(curr)) {
+		fchan = ftdm_iterator_current(curr);
+		if (enabled) {
+			ftdm_clear_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK);
+		} else {
+			ftdm_set_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK);
+		}
+	}
+	ftdm_iterator_free(citer);
+	return FTDM_SUCCESS;
+}
+
 FT_DECLARE(ftdm_trunk_type_t) ftdm_span_get_trunk_type(const ftdm_span_t *span)
 {
 	return span->trunk_type;
@@ -2201,19 +1986,107 @@ FT_DECLARE(ftdm_bool_t) ftdm_channel_call_check_done(const ftdm_channel_t *ftdmc
 
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
 {
+	ftdm_status_t status;
 	ftdm_channel_lock(ftdmchan);
+
 	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_HOLD);
-	ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0);
+	status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0);
 	ftdm_channel_unlock(ftdmchan);
-	return FTDM_SUCCESS;
+
+	return status;
 }
 
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
 {
+	ftdm_status_t status;
+
 	ftdm_channel_lock(ftdmchan);
-	ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0);
+
+	status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0);
+
 	ftdm_channel_unlock(ftdmchan);
-	return FTDM_SUCCESS;
+
+	return status;
+}
+
+FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status)
+{
+	ftdm_sigmsg_t msg;
+	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));
+	msg.channel = fchan;
+	msg.event_id = FTDM_SIGEVENT_INDICATION_COMPLETED;
+	msg.ev_data.indication_completed.indication = indication;
+	msg.ev_data.indication_completed.status = status;
+	ftdm_span_send_signal(fchan->span, &msg);
+}
+
+/*! \brief Answer call without locking the channel. The caller must have locked first
+ *  \note This function was added because ftdm_channel_call_indicate needs to answer the call
+ *        when its already locking the channel, ftdm_channel_set_state cannot be called with the same
+ *        lock locked once or more (recursive lock) and wait for the result  */
+static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
+{
+	ftdm_status_t status = FTDM_SUCCESS;
+
+	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
+		status = FTDM_EINVAL;
+		goto done;
+	}
+
+	if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
+		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n");
+		status = FTDM_ECANCELED;
+		goto done;
+	}
+
+	if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
+		/* We will fail RFC's if we not skip states, but some modules apart from ftmod_sangoma_isdn 
+		* expect the call to always to go PROGRESS and PROGRESS MEDIA state before going to UP, so
+		* use FTDM_SPAN_USE_SKIP_STATES for now while we update the sig modules */
+
+		if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
+			status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
+			if (status != FTDM_SUCCESS) {
+				status = FTDM_ECANCELED;
+				goto done;
+			}
+		}
+
+		/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
+		if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
+			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
+			status = FTDM_ECANCELED;
+			goto done;
+		}
+
+		if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
+			status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
+			if (status != FTDM_SUCCESS) {
+				status = FTDM_ECANCELED;
+				goto done;
+			}
+		}
+
+		/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
+		if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
+			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to UP\n");
+			status = FTDM_ECANCELED;
+			goto done;
+		}
+	}
+
+	status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1);
+	if (status != FTDM_SUCCESS) {
+		status = FTDM_ECANCELED;
+		goto done;
+	}
+
+done:
+
+	return status;
 }
 
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
@@ -2222,55 +2095,17 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
 
 	ftdm_channel_lock(ftdmchan);
 
-	if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
-		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n");
-		goto done;
-	}
-
-	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED);
-	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
-	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
-
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-		goto done;
-	}
-
-	if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
-		/* We will fail RFC's if we not skip states, but some modules apart from ftmod_sangoma_isdn 
-		* expect the call to always to go PROGRESS and PROGRESS MEDIA state before going to UP, so
-		* use FTDM_SPAN_USE_SKIP_STATESfor now while we update the sig modules */
-
-		if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
-			ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
-		}
-
-		/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
-		if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
-			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
-			goto done;
-		}
-
-		if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
-			ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
-		}
-
-		/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
-		if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
-			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to UP\n");
-			goto done;
-		}
-	}
-	ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1);
-
-done:
+	status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan);
 
 	ftdm_channel_unlock(ftdmchan);
 	return status;
 }
 
 /* lock must be acquired by the caller! */
-static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const char *func, int line)
+static ftdm_status_t _ftdm_channel_call_hangup_nl(ftdm_channel_t *chan, const char *file, const char *func, int line)
 {
+	ftdm_status_t status = FTDM_SUCCESS;
+	
 	ftdm_set_flag(chan, FTDM_CHANNEL_USER_HANGUP);
 
 	ftdm_set_echocancel_call_end(chan);
@@ -2283,7 +2118,7 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c
 		if (chan->hangup_timer) {
 			ftdm_sched_cancel_timer(globals.timingsched, chan->hangup_timer);
 		}
-		ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
+		status = ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
 	} else {
 		/* the signaling stack did not touch the state, 
 		 * core is responsible from clearing flags and stuff, however, because ftmod_analog
@@ -2296,28 +2131,34 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c
 			ftdm_channel_close(&chan);
 		}
 	}
-	return FTDM_SUCCESS;
+	return status;
 }
 
 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 cause)
 {
+	ftdm_status_t status = FTDM_SUCCESS;
 	ftdm_channel_lock(ftdmchan);
 
 	ftdmchan->caller_data.hangup_cause = cause;
 	
-	call_hangup(ftdmchan, file, func, line);
+	status = _ftdm_channel_call_hangup_nl(ftdmchan, file, func, line);
 
 	ftdm_channel_unlock(ftdmchan);
-	return FTDM_SUCCESS;
+	return status;
 }
 
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
 {
+	ftdm_status_t status = FTDM_SUCCESS;
+
 	ftdm_channel_lock(ftdmchan);
+	
 	ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING;
-	call_hangup(ftdmchan, file, func, line);
+
+	status = _ftdm_channel_call_hangup_nl(ftdmchan, file, func, line);
+
 	ftdm_channel_unlock(ftdmchan);
-	return FTDM_SUCCESS;
+	return status;
 }
 
 FT_DECLARE(const char *) ftdm_channel_get_last_error(const ftdm_channel_t *ftdmchan)
@@ -2335,42 +2176,6 @@ FT_DECLARE(ftdm_caller_data_t *) ftdm_channel_get_caller_data(ftdm_channel_t *ft
 	return &ftdmchan->caller_data;
 }
 
-FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan)
-{
-	int state;
-	ftdm_channel_lock(ftdmchan);
-	state = ftdmchan->state;
-	ftdm_channel_unlock(ftdmchan);
-	return state;
-}
-
-FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan)
-{
-	const char *state;
-	ftdm_channel_lock(ftdmchan);
-	state = ftdm_channel_state2str(ftdmchan->state);
-	ftdm_channel_unlock(ftdmchan);
-	return state;
-}
-
-FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan)
-{
-	int last_state;
-	ftdm_channel_lock(ftdmchan);
-	last_state = ftdmchan->last_state;
-	ftdm_channel_unlock(ftdmchan);
-	return last_state;
-}
-
-FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan)
-{
-	const char *state;
-	ftdm_channel_lock(ftdmchan);
-	state = ftdm_channel_state2str(ftdmchan->last_state);
-	ftdm_channel_unlock(ftdmchan);
-	return state;
-}
-
 FT_DECLARE(ftdm_channel_t *) ftdm_span_get_channel(const ftdm_span_t *span, uint32_t chanid)
 {
 	ftdm_channel_t *chan;
@@ -2402,13 +2207,48 @@ FT_DECLARE(uint32_t) ftdm_channel_get_ph_span_id(const ftdm_channel_t *ftdmchan)
 	return id;
 }
 
+/*
+ * Every user requested indication *MUST* be acknowledged with the proper status (ftdm_status_t)
+ * However, if the indication fails before we notify the signaling stack, we don't need to ack
+ * but if we already notified the signaling stack about the indication, the signaling stack is
+ * responsible for the acknowledge. Bottom line is, whenever this function returns FTDM_SUCCESS
+ * someone *MUST* acknowledge the indication, either the signaling stack, this function or the core
+ * at some later point
+ * */
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication)
 {
 	ftdm_status_t status = FTDM_SUCCESS;
+
+	ftdm_assert_return(ftdmchan, FTDM_FAIL, "Null channel\n");
+
+	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Indicating %s in state %s\n",
+			ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state));
+
 	ftdm_channel_lock(ftdmchan);
 
+	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_IND_ACK_PENDING)) {
+		ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in channel with indication %s still pending in state %s\n",
+				ftdm_channel_indication2str(indication), 
+				ftdm_channel_indication2str(ftdmchan->indication),
+				ftdm_channel_state2str(ftdmchan->state));
+		status = FTDM_EBUSY;
+		goto done;
+	}
+
+	ftdmchan->indication = indication;
+	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_IND_ACK_PENDING);
+
+	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
+		ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in outgoing channel in state %s\n",
+				ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state));
+		status = FTDM_EINVAL;
+		goto done;
+	}
+
 	if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
-		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
+		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Ignoring indication %s because the call is in %s state\n",
+				ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state));
+		status = FTDM_ECANCELED;
 		goto done;
 	}
 
@@ -2416,55 +2256,52 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch
 	/* FIXME: ring and busy cannot be used with all signaling stacks 
 	 * (particularly isdn stacks I think, we should emulate or just move to hangup with busy cause) */
 	case FTDM_CHANNEL_INDICATE_RINGING:
-		ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1);
+		status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1);
 		break;
 	case FTDM_CHANNEL_INDICATE_BUSY:
-		ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1);
+		status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1);
 		break;
 	case FTDM_CHANNEL_INDICATE_PROCEED:
-		if (ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) {
-			if (ftdmchan->state == FTDM_CHANNEL_STATE_RING) {
-				ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1);
-			}
+		if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) {
+			ftdm_ack_indication(ftdmchan, indication, status);
+			goto done;
 		}
+		status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1);
 		break;
 	case FTDM_CHANNEL_INDICATE_PROGRESS:
-		if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-			ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
-		} else {
-			ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
-		}
+		status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
 		break;
 	case FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA:
-		if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-			ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
-			ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
-		} else {
-			if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
-				if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
-					ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
-				}
-
-				/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
-				if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
-					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
+		if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
+			if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
+				status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
+				if (status != FTDM_SUCCESS) {
 					goto done;
 				}
 			}
 
-			ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
+			/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
+			if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
+				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring progress media because the call is terminating\n");
+				goto done;
+			}
 		}
+		status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
+		break;
+	case FTDM_CHANNEL_INDICATE_ANSWER:
+		/* _ftdm_channel_call_answer takes care of the indication ack */
+		status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan);
 		break;
 	default:
 		ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Do not know how to indicate %d\n", indication);
-		status = FTDM_FAIL;
+		status = FTDM_EINVAL;
 		break;
 	}
 
 done:
 	ftdm_channel_unlock(ftdmchan);
 
-	return FTDM_SUCCESS;
+	return status;
 }
 
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_send_msg(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_sigmsg_t *sigmsg)
@@ -2636,6 +2473,7 @@ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan)
 
 	ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
 	ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
+	ftdmchan->state_status = FTDM_STATE_STATUS_COMPLETED;
 
 	ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DEBUG_DTMF, NULL);
 	ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_INPUT_DUMP, NULL);
@@ -4397,7 +4235,7 @@ static void print_core_usage(ftdm_stream_handle_t *stream)
 	stream->write_function(stream, 
 	"--------------------------------------------------------------------------------\n"
 	"ftdm core state [!]<state_name> - List all channels in or not in the given state\n"
-	"ftdm core flag <flag-int-value> - List all channels with the fiven flag value set\n"
+	"ftdm core flag <flag-int-value> - List all channels with the given flag value set\n"
 	"ftdm core calls - List all known calls to the FreeTDM core\n"
 	"--------------------------------------------------------------------------------\n");
 }
@@ -5195,7 +5033,7 @@ FT_DECLARE(ftdm_status_t) ftdm_configure_span_signaling(ftdm_span_t *span, const
 	ftdm_assert_return(parameters != NULL, FTDM_FAIL, "No parameters");
 
 	if (!span->chan_count) {
-		ftdm_log(FTDM_LOG_WARNING, "Cannot configure signaling on span with no channels\n");
+		ftdm_log(FTDM_LOG_WARNING, "Cannot configure signaling on span %s with no channels\n", span->name);
 		return FTDM_FAIL;
 	}
 
@@ -5455,7 +5293,7 @@ static void execute_safety_hangup(void *data)
 	fchan->hangup_timer = 0;
 	if (fchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
 		ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Forcing hangup since the user did not confirmed our hangup after %dms\n", FORCE_HANGUP_TIMER);
-		call_hangup(fchan, __FILE__, __FUNCTION__, __LINE__);
+		_ftdm_channel_call_hangup_nl(fchan, __FILE__, __FUNCTION__, __LINE__);
 	} else {
 		ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Not performing safety hangup, channel state is %s\n", ftdm_channel_state2str(fchan->state));
 	}
@@ -6069,63 +5907,6 @@ FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen)
 	return new;
 }
 
-
-static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime)
-{
-	char func[255];
-	char line[255];
-	char states[255];
-	const char *filename = NULL;
-	snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state));
-	snprintf(func, sizeof(func), "[%s]", fchan->history[i].func);
-	filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR);
-	if (!filename) {
-		filename = fchan->history[i].file;
-	} else {
-		filename++;
-	}
-	if (!(*prevtime)) {
-		*prevtime = fchan->history[i].time;
-	}
-	snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line);
-	stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime));
-	*prevtime = fchan->history[i].time;
-}
-
-FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
-{
-	uint8_t i = 0;
-	ftdm_time_t currtime = 0;
-	ftdm_time_t prevtime = 0;
-
-	ftdm_stream_handle_t stream = { 0 };
-	FTDM_STANDARD_STREAM(stream);
-	if (!fchan->history[0].file) {
-		stream.write_function(&stream, "-- No state history --\n");
-		return stream.data;
-	}
-
-	stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", 
-			"-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");
-
-	for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
-		if (!fchan->history[i].file) {
-			break;
-		}
-		write_history_entry(fchan, &stream, i, &prevtime);
-	}
-
-	for (i = 0; i < fchan->hindex; i++) {
-		write_history_entry(fchan, &stream, i, &prevtime);
-	}
-
-	currtime = ftdm_current_time_in_ms();
-
-	stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));
-
-	return stream.data;
-}
-
 static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data)
 {
 	uint32_t current_call_id;
diff --git a/libs/freetdm/src/ftdm_m3ua.c b/libs/freetdm/src/ftdm_m3ua.c
deleted file mode 100644
index 8d3e00213a..0000000000
--- a/libs/freetdm/src/ftdm_m3ua.c
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- *  ftdm_m3ua.c
- *  freetdm
- *
- *  Created by Shane Burrell on 4/3/08.
- *  Copyright 2008 Shane Burrell. All rights reserved.
- *
- * 
- * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic * 
- *
- * 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 "freetdm.h"
-#include "m3ua_client.h"
-#include "ftdm_m3ua.h"
-
-#define MAX_REQ_ID MAX_PENDING_CALLS
-typedef uint16_t m3ua_request_id_t;
-
-typedef enum {
-	BST_FREE,
-	BST_WAITING,
-	BST_READY,
-	BST_FAIL
-} m3ua_request_status_t;
-
-typedef struct {
-	m3ua_request_status_t status;
-	m3uac_event_t event;
-	ftdm_span_t *span;
-	ftdm_channel_t *ftdmchan;
-} m3ua_request_t;
-
-
-struct general_config {
-	uint32_t region;
-};
-typedef struct general_config general_config_t;
-
-
-struct m3ua_channel_profile {
-	char name[80];
-	int cust_span;
-	unsigned char opc[3];
-	unsigned char dpc[3];
-	int local_ip[4];
-	int local_port;
-	int remote_ip[4];
-	int remote_port;
-	int m3ua_mode;
-};
-typedef struct m3ua_channel_profile m3ua_channel_profile_t;
-
-static struct {
-	ftdm_hash_t *profile_hash;
-	general_config_t general_config;
-} globals;
-
-struct m3ua_span_data {
-	uint32_t boardno;
-	uint32_t flags;
-};
-typedef struct m3ua_span_data m3ua_span_data_t;
-
-struct m3ua_chan_data {
-	ftdm_buffer_t *digit_buffer;
-	ftdm_mutex_t *digit_mutex;
-	ftdm_size_t dtmf_len;
-	uint32_t flags;
-	uint32_t hdlc_bytes;
-};
-typedef struct m3ua_chan_data m3ua_chan_data_t;
-
-static ftdm_mutex_t *request_mutex = NULL;
-static ftdm_mutex_t *signal_mutex = NULL;
-
-static uint8_t req_map[MAX_REQ_ID+1] = { 0 };
-
-static void release_request_id(m3ua_request_id_t r)
-{
-	ftdm_mutex_lock(request_mutex);
-	req_map[r] = 0;
-	ftdm_mutex_unlock(request_mutex);
-}
-
-/*static m3ua_request_id_t next_request_id(void)
-{
-	m3ua_request_id_t r = 0;
-	int ok = 0;
-	
-	while(!ok) {
-		ftdm_mutex_lock(request_mutex);
-		for (r = 1; r <= MAX_REQ_ID; r++) {
-			if (!req_map[r]) {
-				ok = 1;
-				req_map[r] = 1;
-				break;
-			}
-		}
-		ftdm_mutex_unlock(request_mutex);
-		if (!ok) {
-			ftdm_sleep(5);
-		}
-	}
-	return r;
-}
-*/
-
-static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
-{
-
-	m3ua_data_t *m3ua_data = ftdmchan->span->signal_data;
-	m3uac_connection_t *mcon = &m3ua_data->mcon;
-	ftdm_sigmsg_t sig;
-	ftdm_status_t status;
-
-	ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
-	
-	memset(&sig, 0, sizeof(sig));
-	sig.chan_id = ftdmchan->chan_id;
-	sig.span_id = ftdmchan->span_id;
-	sig.channel = ftdmchan;
-
-	switch (ftdmchan->state) {
-	case FTDM_CHANNEL_STATE_DOWN:
-		{
-			if (ftdmchan->extra_id) {
-				release_request_id((m3ua_request_id_t)ftdmchan->extra_id);
-				ftdmchan->extra_id = 0;
-			}
-			ftdm_channel_close(&ftdmchan);			
-		}
-		break;
-	case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
-	case FTDM_CHANNEL_STATE_PROGRESS:
-		{
-			if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-				sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
-				if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) {
-					ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
-				}
-			} else {
-				m3uac_exec_command(mcon,
-								   ftdmchan->physical_span_id-1,
-								   ftdmchan->physical_chan_id-1,								   
-								   0,
-								   SIGBOOST_EVENT_CALL_START_ACK,
-								   0);
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_RING:
-		{
-			if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-				sig.event_id = FTDM_SIGEVENT_START;
-				if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) {
-					ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
-				}
-			}
-
-		}
-		break;
-	case FTDM_CHANNEL_STATE_RESTART:
-		{
-			if (ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP && ftdmchan->last_state != FTDM_CHANNEL_STATE_DOWN) {
-				ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
-			} else {
-				ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_UP:
-		{
-			if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-				sig.event_id = FTDM_SIGEVENT_UP;
-				if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) {
-					ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
-				}
-			} else {
-				if (!(ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA))) {
-					m3uac_exec_command(mcon,
-									   ftdmchan->physical_span_id-1,
-									   ftdmchan->physical_chan_id-1,								   
-									   0,
-									   SIGBOOST_EVENT_CALL_START_ACK,
-									   0);
-				}
-				
-				m3uac_exec_command(mcon,
-								   ftdmchan->physical_span_id-1,
-								   ftdmchan->physical_chan_id-1,								   
-								   0,
-								   SIGBOOST_EVENT_CALL_ANSWERED,
-								   0);
-			}
-		}
-		break;
-	case FTDM_CHANNEL_STATE_DIALING:
-		{
-		}
-		break;
-	case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
-		{
-			ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-		}
-		break;
-	case FTDM_CHANNEL_STATE_HANGUP:
-		{
-			if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA)) {
-				m3uac_exec_command(mcon,
-								   ftdmchan->physical_span_id-1,
-								   ftdmchan->physical_chan_id-1,
-								   0,
-								   SIGBOOST_EVENT_CALL_STOPPED,
-								   ftdmchan->caller_data.hangup_cause);
-			} else {
-				m3uac_exec_command(mcon,
-								   ftdmchan->physical_span_id-1,
-								   ftdmchan->physical_chan_id-1,								   
-								   0,
-								   SIGBOOST_EVENT_CALL_START_NACK,
-								   ftdmchan->caller_data.hangup_cause);
-			}			
-		}
-		break;
-	case FTDM_CHANNEL_STATE_CANCEL:
-		{
-			sig.event_id = FTDM_SIGEVENT_STOP;
-			status = m3ua_data->signal_cb(&sig);
-			ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-			m3uac_exec_command(mcon,
-							   ftdmchan->physical_span_id-1,
-							   ftdmchan->physical_chan_id-1,
-							   0,
-							   SIGBOOST_EVENT_CALL_START_NACK_ACK,
-							   0);
-		}
-		break;
-	case FTDM_CHANNEL_STATE_TERMINATING:
-		{
-			sig.event_id = FTDM_SIGEVENT_STOP;
-			status = m3ua_data->signal_cb(&sig);
-			ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-			m3uac_exec_command(mcon,
-							   ftdmchan->physical_span_id-1,
-							   ftdmchan->physical_chan_id-1,
-							   0,
-							   SIGBOOST_EVENT_CALL_STOPPED_ACK,
-							   0);
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-
-static __inline__ void check_state(ftdm_span_t *span)
-{
-    if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
-        uint32_t j;
-        ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
-        for(j = 1; j <= span->chan_count; j++) {
-            if (ftdm_test_flag((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) {
-                ftdm_clear_flag_locked((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE);
-                state_advance(&span->channels[j]);
-                ftdm_channel_complete_state(&span->channels[j]);
-            }
-        }
-    }
-}
-
-
-static int parse_ss7_event(ftdm_span_t *span, m3uac_connection_t *mcon, m3uac_event_t *event)
-{
-	ftdm_mutex_lock(signal_mutex);
-	
-	if (!ftdm_running()) {
-		ftdm_log(FTDM_LOG_WARNING, "System is shutting down.\n");
-		goto end;
-	}
-
-
-	if (ftdm_test_flag(span, FTDM_SPAN_SUSPENDED) && 
-		event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && event->event_id != SIGBOOST_EVENT_HEARTBEAT) {
-
-		ftdm_log(FTDM_LOG_WARNING,
-				"INVALID EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
-				m3uac_event_id_name(event->event_id),
-				event->event_id,
-				event->span+1,
-				event->chan+1,
-				event->release_cause,
-				event->call_setup_id,
-				event->fseqno,
-				(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
-				(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
-				);
-		
-		goto end;
-	}
-
-
-	ftdm_log(FTDM_LOG_DEBUG,
-			"RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
-			   m3uac_event_id_name(event->event_id),
-			   event->event_id,
-			   event->span+1,
-			   event->chan+1,
-			   event->release_cause,
-			   event->call_setup_id,
-			   event->fseqno,
-			   (event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
-			   (event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
-			   );
-
-
-	
-    switch(event->event_id) {
-
-    case SIGBOOST_EVENT_CALL_START:
-		//handle_call_start(span, mcon, event);
-		break;
-    case SIGBOOST_EVENT_CALL_STOPPED:
-		//handle_call_stop(span, mcon, event);
-		break;
-    case SIGBOOST_EVENT_CALL_START_ACK:
-		//handle_call_start_ack(mcon, event);
-		break;
-    case SIGBOOST_EVENT_CALL_START_NACK:
-		//handle_call_start_nack(span, mcon, event);
-		break;
-    case SIGBOOST_EVENT_CALL_ANSWERED:
-		//handle_call_answer(span, mcon, event);
-		break;
-    case SIGBOOST_EVENT_HEARTBEAT:
-		//handle_heartbeat(mcon, event);
-		break;
-    case SIGBOOST_EVENT_CALL_STOPPED_ACK:
-    case SIGBOOST_EVENT_CALL_START_NACK_ACK:
-		//handle_call_done(span, mcon, event);
-		break;
-    case SIGBOOST_EVENT_INSERT_CHECK_LOOP:
-		//handle_call_loop_start(event);
-		break;
-    case SIGBOOST_EVENT_REMOVE_CHECK_LOOP:
-		//handle_call_stop(event);
-		break;
-    case SIGBOOST_EVENT_SYSTEM_RESTART_ACK:
-		//handle_restart_ack(mcon, span, event);
-		break;
-    case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE:
-		//handle_gap_abate(event);
-		break;
-    default:
-		ftdm_log(FTDM_LOG_WARNING, "No handler implemented for [%s]\n", m3uac_event_id_name(event->event_id));
-		break;
-    }
-
- end:
-
-	ftdm_mutex_unlock(signal_mutex);
-
-	return 0;
-}
-
-static FIO_CONFIGURE_FUNCTION(m3ua_configure)
-{
-	m3ua_channel_profile_t *profile = NULL;
-
-	int ok = 1;
-
-	if (!(profile = (m3ua_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) {
-		profile = ftdm_malloc(sizeof(*profile));
-		memset(profile, 0, sizeof(*profile));
-		ftdm_set_string(profile->name, category);
-		hashtable_insert(globals.profile_hash, (void *)profile->name, profile);
-		ftdm_log(FTDM_LOG_INFO, "creating profile [%s]\n", category);
-	}
-
-//	ftdm_set_string(m3ua_data->mcon. cfg.local_ip, local_ip);
-	if (!strcasecmp(var, "local_sctp_port")) {
-		profile->local_port = 30000 ;
-		profile->remote_port = 30000;
-		profile->cust_span++;
-	} 
-	ok = 1;
-	
-
-	if (ok) {
-		ftdm_log(FTDM_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category);
-	} else {
-		ftdm_log(FTDM_LOG_ERROR, "unknown param [%s]\n", var);
-	}
-
-	return FTDM_SUCCESS;
-}
-
-static FIO_CONFIGURE_SPAN_FUNCTION(m3ua_configure_span)
-{
-
-	return FTDM_FAIL;
-}
-
-static FIO_OPEN_FUNCTION(m3ua_open) 
-{
-	
-	return FTDM_FAIL;
-}
-
-static FIO_CLOSE_FUNCTION(m3ua_close)
-{
-	
-	return FTDM_FAIL;
-}
-
-/*static FIO_SET_INTERVAL_FUNCTION(m3ua_set_interval)
-{
-	
-	return 0;
-}*/
-
-static FIO_WAIT_FUNCTION(m3ua_wait)
-{
-	
-	return FTDM_FAIL;
-}
-
-static FIO_READ_FUNCTION(m3ua_read)
-{
-	
-	return FTDM_FAIL;
-}
-
-static FIO_WRITE_FUNCTION(m3ua_write)
-{
-	
-	return FTDM_FAIL;
-}
-
-static FIO_COMMAND_FUNCTION(m3ua_command)
-{
-	return FTDM_FAIL;
-}
-
-static FIO_SPAN_POLL_EVENT_FUNCTION(m3ua_poll_event)
-{
-	return FTDM_FAIL;
-}
-
-static FIO_SPAN_NEXT_EVENT_FUNCTION(m3ua_next_event)
-{
-	return FTDM_FAIL;
-}
-
-
-static FIO_SPAN_DESTROY_FUNCTION(m3ua_span_destroy)
-{
-	m3ua_span_data_t *span_data = (m3ua_span_data_t *) span->io_data;
-	
-	if (span_data) {
-		ftdm_safe_free(span_data);
-	}
-	
-	return FTDM_SUCCESS;
-}
-static FIO_CHANNEL_DESTROY_FUNCTION(m3ua_channel_destroy)
-{
-	m3ua_chan_data_t *chan_data = (m3ua_chan_data_t *) ftdmchan->io_data;
-	m3ua_span_data_t *span_data = (m3ua_span_data_t *) ftdmchan->span->io_data;
-	
-	if (!chan_data) {
-		return FTDM_FAIL;
-	}
-
-	
-		
-
-
-
-	ftdm_mutex_destroy(&chan_data->digit_mutex);
-	ftdm_buffer_destroy(&chan_data->digit_buffer);
-
-
-	ftdm_safe_free(chan_data);
-	
-	if (span_data) {
-		ftdm_safe_free(span_data);
-	}
-	
-			
-	return FTDM_SUCCESS;
-}
-
-
-
-static FIO_GET_ALARMS_FUNCTION(m3ua_get_alarms)
-{
-	return FTDM_FAIL;
-}
-
-static ftdm_io_interface_t m3ua_interface;
-
-ftdm_status_t m3ua_init(ftdm_io_interface_t **zint)
-{
-	assert(zint != NULL);
-	memset(&m3ua_interface, 0, sizeof(m3ua_interface));
-
-	m3ua_interface.name = "m3ua";
-	m3ua_interface.configure =  m3ua_configure;
-	m3ua_interface.configure_span =  m3ua_configure_span;
-	m3ua_interface.open = m3ua_open;
-	m3ua_interface.close = m3ua_close;
-	m3ua_interface.wait = m3ua_wait;
-	m3ua_interface.read = m3ua_read;
-	m3ua_interface.write = m3ua_write;
-	m3ua_interface.command = m3ua_command;
-	m3ua_interface.poll_event = m3ua_poll_event;
-	m3ua_interface.next_event = m3ua_next_event;
-	m3ua_interface.channel_destroy = m3ua_channel_destroy;
-	m3ua_interface.span_destroy = m3ua_span_destroy;
-	m3ua_interface.get_alarms = m3ua_get_alarms;
-	*zint = &m3ua_interface;
-
-	return FTDM_FAIL;
-}
-
-ftdm_status_t m3ua_destroy(void)
-{
-	return FTDM_FAIL;
-}
-
-
-static void *m3ua_run(ftdm_thread_t *me, void *obj)
-{
-    ftdm_span_t *span = (ftdm_span_t *) obj;
-    m3ua_data_t *m3ua_data = span->signal_data;
-	m3uac_connection_t *mcon, *pcon;
-	uint32_t ms = 10, too_long = 60000;
-		
-
-	m3ua_data->pcon = m3ua_data->mcon;
-
-	if (m3uac_connection_open(&m3ua_data->mcon,
-							  m3ua_data->mcon.cfg.local_ip,
-							  m3ua_data->mcon.cfg.local_port,
-							  m3ua_data->mcon.cfg.remote_ip,
-							  m3ua_data->mcon.cfg.remote_port) < 0) {
-		ftdm_log(FTDM_LOG_DEBUG, "Error: Opening MCON Socket [%d] %s\n", m3ua_data->mcon.socket, strerror(errno));
-		goto end;
-    }
-
-	if (m3uac_connection_open(&m3ua_data->pcon,
-							  m3ua_data->pcon.cfg.local_ip,
-							  ++m3ua_data->pcon.cfg.local_port,
-							  m3ua_data->pcon.cfg.remote_ip,
-							  m3ua_data->pcon.cfg.remote_port) < 0) {
-		ftdm_log(FTDM_LOG_DEBUG, "Error: Opening PCON Socket [%d] %s\n", m3ua_data->pcon.socket, strerror(errno));
-		goto end;
-    }
-	
-	mcon = &m3ua_data->mcon;
-	pcon = &m3ua_data->pcon;
-
-	top:
-
-	//init_outgoing_array();		
-
-	m3uac_exec_command(mcon,
-					   0,
-					   0,
-					   -1,
-					   SIGBOOST_EVENT_SYSTEM_RESTART,
-					   0);
-	
-	while (ftdm_test_flag(m3ua_data, FTDM_M3UA_RUNNING)) {
-		fd_set rfds, efds;
-		struct timeval tv = { 0, ms * 1000 };
-		int max, activity, i = 0;
-		m3uac_event_t *event = NULL;
-		
-		if (!ftdm_running()) {
-			m3uac_exec_command(mcon,
-							   0,
-							   0,
-							   -1,
-							   SIGBOOST_EVENT_SYSTEM_RESTART,
-							   0);
-			break;
-		}
-
-		FD_ZERO(&rfds);
-		FD_ZERO(&efds);
-		FD_SET(mcon->socket, &rfds);
-		FD_SET(mcon->socket, &efds);
-		FD_SET(pcon->socket, &rfds);
-		FD_SET(pcon->socket, &efds);
-
-		max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1;
-		
-		if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) {
-			goto error;
-		}
-		
-		if (activity) {
-			if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) {
-				goto error;
-			}
-
-			if (FD_ISSET(pcon->socket, &rfds)) {
-				if ((event = m3uac_connection_readp(pcon, i))) {
-					parse_ss7_event(span, mcon, event);
-				} else goto top;
-			}
-
-			if (FD_ISSET(mcon->socket, &rfds)) {
-				if ((event = m3uac_connection_read(mcon, i))) {
-					parse_ss7_event(span, mcon, event);
-				} else goto top;
-			}
-		}
-		
-		check_state(span);
-		mcon->hb_elapsed += ms;
-		
-		if (mcon->hb_elapsed >= too_long && (mcon->up || !ftdm_test_flag(span, FTDM_SPAN_SUSPENDED))) {
-			ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
-			ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED);
-			mcon->up = 0;
-			ftdm_log(FTDM_LOG_CRIT, "Lost Heartbeat!\n");
-		}
-
-	}
-
-	goto end;
-
- error:
-	ftdm_log(FTDM_LOG_CRIT, "Socket Error!\n");
-
- end:
-
-	m3uac_connection_close(&m3ua_data->mcon);
-	m3uac_connection_close(&m3ua_data->pcon);
-
-	ftdm_clear_flag(m3ua_data, FTDM_M3UA_RUNNING);
-
-	ftdm_log(FTDM_LOG_DEBUG, "M3UA thread ended.\n");
-	return NULL;
-}
-ftdm_status_t m3ua_start(ftdm_span_t *span)
-{
-	m3ua_data_t *m3ua_data = span->signal_data;
-	ftdm_set_flag(m3ua_data, FTDM_M3UA_RUNNING);
-	return ftdm_thread_create_detached(m3ua_run, span);
-}
-
-/* For Emacs:
- * Local Variables:
- * mode:c
- * indent-tabs-mode:t
- * tab-width:4
- * c-basic-offset:4
- * End:
-*/
diff --git a/libs/freetdm/src/ftdm_state.c b/libs/freetdm/src/ftdm_state.c
new file mode 100644
index 0000000000..0221fa2a1e
--- /dev/null
+++ b/libs/freetdm/src/ftdm_state.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2010, Sangoma Technologies
+ * Moises Silva <moy@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 "private/ftdm_core.h"
+
+FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS)
+FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID)
+
+FTDM_ENUM_NAMES(CHANNEL_STATE_STATUS_NAMES, CHANNEL_STATE_STATUS_STRINGS)
+FTDM_STR2ENUM(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t, CHANNEL_STATE_STATUS_NAMES, FTDM_STATE_STATUS_INVALID)
+
+/* This function is only needed for boost and we should get rid of it at the next refactoring */
+FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan)
+{
+	ftdm_channel_lock(fchan);
+
+	if (fchan->init_state != FTDM_CHANNEL_STATE_DOWN) {
+		ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, fchan, fchan->init_state, 1);
+		fchan->init_state = FTDM_CHANNEL_STATE_DOWN;
+	}
+
+	ftdm_channel_unlock(fchan);
+	return FTDM_SUCCESS;
+}
+
+FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
+{
+	uint8_t hindex = 0;
+	ftdm_time_t diff = 0;
+	ftdm_channel_state_t state = fchan->state;
+
+	if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) {
+		ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL, 
+				"State change flag set but state is not completed\n");
+		return FTDM_SUCCESS;
+	}
+
+	ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
+
+	if (state == FTDM_CHANNEL_STATE_PROGRESS) {
+		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
+	} else if (state == FTDM_CHANNEL_STATE_UP) {
+		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
+		ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA);	
+		ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED);	
+	} else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
+		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);	
+		ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA);	
+	}
+
+	/* if there is a pending ack for an indication
+	 * MAINTENANCE WARNING: we're assuming an indication performed 
+	 * via state change will involve a single state change
+	 */
+	if (ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) {
+		ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS);
+	}
+
+	hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
+	
+	ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n");
+
+	fchan->history[hindex].end_time = ftdm_current_time_in_ms();
+
+	fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
+
+	diff = fchan->history[hindex].end_time - fchan->history[hindex].time;
+
+	ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %llums\n", 
+			ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
+	
+
+	if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
+		ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
+		ftdm_interrupt_signal(fchan->state_completed_interrupt);
+	}
+
+	return FTDM_SUCCESS;
+}
+
+FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line,
+		ftdm_channel_t *fchan, ftdm_channel_state_t state)
+{
+	if (fchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
+		/* the current state is not completed, setting a new state from a signaling module
+		   when the current state is not completed is equivalent to implicitly acknowledging 
+		   the current state */
+		_ftdm_channel_complete_state(file, func, line, fchan);
+	}
+	return ftdm_channel_set_state(file, func, line, fchan, state, 0);
+}
+
+static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map)
+{
+	int x = 0, ok = 0;
+	ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND;
+
+	for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) {
+		int i = 0, proceed = 0;
+		if (!state_map->nodes[x].type) {
+			break;
+		}
+
+		if (state_map->nodes[x].direction != direction) {
+			continue;
+		}
+		
+		if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) {
+			proceed = 1;
+		} else {
+			for(i = 0; i < FTDM_MAP_MAX; i++) {
+				if (state_map->nodes[x].check_states[i] == ftdmchan->state) {
+					proceed = 1;
+					break;
+				}
+			}
+		}
+
+		if (!proceed) {
+			continue;
+		}
+		
+		for(i = 0; i < FTDM_MAP_MAX; i++) {
+			ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE);
+			if (state_map->nodes[x].states[i] == FTDM_END) {
+				break;
+			}
+			if (state_map->nodes[x].states[i] == state) {
+				ok = !ok;
+				goto end;
+			}
+		}
+	}
+ end:
+	
+	return ok;
+}
+
+/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
+#define DEFAULT_WAIT_TIME 1000
+FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
+{
+	ftdm_status_t status;
+	int ok = 1;
+	int waitms = DEFAULT_WAIT_TIME;	
+
+	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
+		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
+				ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
+		return FTDM_FAIL;
+	}
+
+	if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
+		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, 
+		"Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n",
+		ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state),
+		ftdm_state_status2str(ftdmchan->state_status));
+		return FTDM_FAIL;
+	}
+
+	if (ftdmchan->state == state) {
+		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
+		return FTDM_FAIL;
+	}
+
+	if (!ftdmchan->state_completed_interrupt) {
+		status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET);
+		if (status != FTDM_SUCCESS) {
+			ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, 
+					"Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
+			return status;
+		}
+	}
+
+
+	if (ftdmchan->span->state_map) {
+		ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
+		goto end;
+	}
+
+	/* basic core state validation (by-passed if the signaling module provides a state_map) */
+	switch(ftdmchan->state) {
+	case FTDM_CHANNEL_STATE_HANGUP:
+	case FTDM_CHANNEL_STATE_TERMINATING:
+		{
+			ok = 0;
+			switch(state) {
+			case FTDM_CHANNEL_STATE_DOWN:
+			case FTDM_CHANNEL_STATE_BUSY:
+			case FTDM_CHANNEL_STATE_RESTART:
+				ok = 1;
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	case FTDM_CHANNEL_STATE_UP:
+		{
+			ok = 1;
+			switch(state) {
+			case FTDM_CHANNEL_STATE_PROGRESS:
+			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
+			case FTDM_CHANNEL_STATE_RING:
+				ok = 0;
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	case FTDM_CHANNEL_STATE_DOWN:
+		{
+			ok = 0;
+			
+			switch(state) {
+			case FTDM_CHANNEL_STATE_DIALTONE:
+			case FTDM_CHANNEL_STATE_COLLECT:
+			case FTDM_CHANNEL_STATE_DIALING:
+			case FTDM_CHANNEL_STATE_RING:
+			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
+			case FTDM_CHANNEL_STATE_PROGRESS:				
+			case FTDM_CHANNEL_STATE_IDLE:				
+			case FTDM_CHANNEL_STATE_GET_CALLERID:
+			case FTDM_CHANNEL_STATE_GENRING:
+				ok = 1;
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	case FTDM_CHANNEL_STATE_BUSY:
+		{
+			switch(state) {
+			case FTDM_CHANNEL_STATE_UP:
+				ok = 0;
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	case FTDM_CHANNEL_STATE_RING:
+		{
+			switch(state) {
+			case FTDM_CHANNEL_STATE_UP:
+				ok = 1;
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+end:
+
+	if (!ok) {
+		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
+		goto done;
+	}
+
+	ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
+	ftdmchan->last_state = ftdmchan->state; 
+	ftdmchan->state = state;
+	ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
+	ftdmchan->history[ftdmchan->hindex].file = file;
+	ftdmchan->history[ftdmchan->hindex].func = func;
+	ftdmchan->history[ftdmchan->hindex].line = line;
+	ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
+	ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
+	ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
+	ftdmchan->history[ftdmchan->hindex].end_time = 0;
+	ftdmchan->hindex++;
+	if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
+		ftdmchan->hindex = 0;
+	}
+	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
+
+	ftdm_mutex_lock(ftdmchan->span->mutex);
+	ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
+	if (ftdmchan->span->pendingchans) {
+		ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
+	}
+	ftdm_mutex_unlock(ftdmchan->span->mutex);
+
+	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) {
+		/* the channel should not block waiting for state processing */
+		goto done;
+	}
+
+	if (!waitrq) {
+		/* no waiting was requested */
+		goto done;
+	}
+
+	/* let's wait for the state change to be completed by the signaling stack */
+	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
+
+	ftdm_mutex_unlock(ftdmchan->mutex);
+
+	status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms);
+
+	ftdm_mutex_lock(ftdmchan->mutex);
+
+	if (status != FTDM_SUCCESS) {
+		ftdm_log_chan_ex(ftdmchan, file, func, line, 
+				FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n",
+				ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
+		ok = 0;
+		goto done;
+	}
+done:
+	return ok ? FTDM_SUCCESS : FTDM_FAIL;
+}
+
+FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan)
+{
+	int state;
+	ftdm_channel_lock(ftdmchan);
+	state = ftdmchan->state;
+	ftdm_channel_unlock(ftdmchan);
+	return state;
+}
+
+FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan)
+{
+	const char *state;
+	ftdm_channel_lock(ftdmchan);
+	state = ftdm_channel_state2str(ftdmchan->state);
+	ftdm_channel_unlock(ftdmchan);
+	return state;
+}
+
+FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan)
+{
+	int last_state;
+	ftdm_channel_lock(ftdmchan);
+	last_state = ftdmchan->last_state;
+	ftdm_channel_unlock(ftdmchan);
+	return last_state;
+}
+
+FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan)
+{
+	const char *state;
+	ftdm_channel_lock(ftdmchan);
+	state = ftdm_channel_state2str(ftdmchan->last_state);
+	ftdm_channel_unlock(ftdmchan);
+	return state;
+}
+
+static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime)
+{
+	char func[255];
+	char line[255];
+	char states[255];
+	const char *filename = NULL;
+	snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state));
+	snprintf(func, sizeof(func), "[%s]", fchan->history[i].func);
+	filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR);
+	if (!filename) {
+		filename = fchan->history[i].file;
+	} else {
+		filename++;
+	}
+	if (!(*prevtime)) {
+		*prevtime = fchan->history[i].time;
+	}
+	snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line);
+	stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime));
+	*prevtime = fchan->history[i].time;
+}
+
+FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
+{
+	uint8_t i = 0;
+	ftdm_time_t currtime = 0;
+	ftdm_time_t prevtime = 0;
+
+	ftdm_stream_handle_t stream = { 0 };
+	FTDM_STANDARD_STREAM(stream);
+	if (!fchan->history[0].file) {
+		stream.write_function(&stream, "-- No state history --\n");
+		return stream.data;
+	}
+
+	stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", 
+			"-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");
+
+	for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
+		if (!fchan->history[i].file) {
+			break;
+		}
+		write_history_entry(fchan, &stream, i, &prevtime);
+	}
+
+	for (i = 0; i < fchan->hindex; i++) {
+		write_history_entry(fchan, &stream, i, &prevtime);
+	}
+
+	currtime = ftdm_current_time_in_ms();
+
+	stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));
+
+	return stream.data;
+}
+
+FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan)
+{
+	ftdm_channel_state_t state;
+
+	ftdm_assert_return(fchan->span->state_processor, FTDM_FAIL, "Cannot process states without a state processor!\n");
+	
+	while (fchan->state_status == FTDM_STATE_STATUS_NEW) {
+		state = fchan->state;
+		ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Executing state processor for %s\n", ftdm_channel_state2str(fchan->state));
+		fchan->span->state_processor(fchan);
+		if (state == fchan->state && fchan->state_status == FTDM_STATE_STATUS_NEW) {
+			/* if the state did not change and is still NEW, the state status must go to PROCESSED
+			 * otherwise we don't touch it since is a new state and the old state was
+			 * already completed implicitly by the state_processor() function via some internal
+			 * call to ftdm_set_state() */
+			fchan->state_status = FTDM_STATE_STATUS_PROCESSED;
+		}
+	}
+
+	return FTDM_SUCCESS;
+}
+
+FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
+{
+	uint32_t j;
+	for(j = 1; j <= span->chan_count; j++) {
+		if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */
diff --git a/libs/freetdm/src/ftdm_threadmutex.c b/libs/freetdm/src/ftdm_threadmutex.c
index b1884ec587..6efa27714c 100644
--- a/libs/freetdm/src/ftdm_threadmutex.c
+++ b/libs/freetdm/src/ftdm_threadmutex.c
@@ -56,7 +56,11 @@ struct ftdm_interrupt {
 	/* for generic interruption */
 	HANDLE event;
 #else
-	/* for generic interruption */
+	/* In theory we could be using thread conditions for generic interruption,
+	 * however, Linux does not have a primitive like Windows WaitForMultipleObjects
+	 * to wait for both thread condition and file descriptors, therefore we decided
+	 * to use a dummy pipe for generic interruption/condition logic
+	 * */
 	int readfd;
 	int writefd;
 #endif
@@ -243,6 +247,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex)
 
 FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device)
 {
+	ftdm_status_t status = FTDM_SUCCESS;
 	ftdm_interrupt_t *interrupt = NULL;
 #ifndef WIN32
 	int fds[2];
@@ -253,7 +258,7 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt,
 	interrupt = ftdm_calloc(1, sizeof(*interrupt));
 	if (!interrupt) {
 		ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n");
-		return FTDM_FAIL;
+		return FTDM_ENOMEM;
 	}
 
 	interrupt->device = device;
@@ -261,11 +266,13 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt,
 	interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL);
 	if (!interrupt->event) {
 		ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n");
+		status = FTDM_ENOMEM;
 		goto failed;
 	}
 #else
 	if (pipe(fds)) {
 		ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno));
+		status = FTDM_FAIL;
 		goto failed;
 	}
 	interrupt->readfd = fds[0];
@@ -287,7 +294,7 @@ failed:
 #endif
 		ftdm_safe_free(interrupt);
 	}
-	return FTDM_FAIL;
+	return status;
 }
 
 #define ONE_BILLION 1000000000
diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c
index 981df5dcc9..818f1c5754 100644
--- a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c
+++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c
@@ -585,8 +585,8 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
 
 					if (done) {
 						ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
-						ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
 						ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
+						ftdm_channel_complete_state(ftdmchan);
 						ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0;
 					}
 				}
@@ -628,7 +628,6 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
 				break;
 			}
 		} else {
-			ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
 			ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
 			ftdm_channel_complete_state(ftdmchan);
 			indicate = 0;
@@ -672,11 +671,12 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
 					    !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) &&
 					    ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) {
 						ftdm_polarity_t polarity = FTDM_POLARITY_REVERSE;
-						if (ftdmchan->polarity != FTDM_POLARITY_FORWARD) {
-							ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Polarity is already reversed on answer??\n");
-						} else {
+						if (ftdmchan->polarity == FTDM_POLARITY_FORWARD) {
 							ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on answer\n");
 							ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity);
+						} else {
+							/* the polarity may be already reversed if this is the second time we 
+							 * answer (ie, due to 2 calls being on the same line) */
 						}
 					}
 
@@ -1036,8 +1036,8 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
 			if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) {
 				if (event->channel->state == FTDM_CHANNEL_STATE_HANGUP && 
 				    ftdm_test_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE)) { 
-					ftdm_clear_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE);
 					/* we do not need to process HANGUP since the device also hangup already */
+					ftdm_channel_complete_state(event->channel);
 				}
 				ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_DOWN);
 			}
@@ -1052,8 +1052,8 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
 		{
 			if (event->channel->state == FTDM_CHANNEL_STATE_CALLWAITING) {
 				ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_UP);
-				ftdm_clear_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE);
 				ftdm_clear_flag(event->channel->span, FTDM_SPAN_STATE_CHANGE);
+				ftdm_channel_complete_state(event->channel);
 				event->channel->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0;
 			} 
 
diff --git a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c
index 843ac484a5..31c2421b9b 100644
--- a/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c
+++ b/libs/freetdm/src/ftmod/ftmod_analog_em/ftmod_analog_em.c
@@ -355,7 +355,6 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
 				break;
 			}
 		} else {
-			ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
 			ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
 			ftdm_channel_complete_state(ftdmchan);
 			indicate = 0;
diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
index 1d3c76d2e2..8be6d579e1 100644
--- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
+++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
@@ -497,7 +497,7 @@ static ftdm_state_map_t isdn_state_map = {
  * \param ftdmchan Channel to handle
  * \note This function MUST be called with the channel locked
  */
-static __inline__ void state_advance(ftdm_channel_t *chan)
+static ftdm_status_t state_advance(ftdm_channel_t *chan)
 {
 	ftdm_libpri_data_t *isdn_data = chan->span->signal_data;
 	q931_call *call = (q931_call *)chan->call_data;
@@ -511,6 +511,8 @@ static __inline__ void state_advance(ftdm_channel_t *chan)
 	sig.chan_id = ftdm_channel_get_id(chan);
 	sig.span_id = ftdm_channel_get_span_id(chan);
 	sig.channel = chan;
+	
+	ftdm_channel_complete_state(chan);
 
 	switch (ftdm_channel_get_state(chan)) {
 	case FTDM_CHANNEL_STATE_DOWN:
@@ -625,7 +627,7 @@ static __inline__ void state_advance(ftdm_channel_t *chan)
 					ftdm_channel_get_span_id(chan), ftdm_channel_get_id(chan));
 				/* TODO: set hangup cause? */
 				ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RESTART);
-				return;
+				return FTDM_SUCCESS;
 			}
 
 			ton = caller_data->dnis.type;
@@ -708,6 +710,7 @@ static __inline__ void state_advance(ftdm_channel_t *chan)
 	default:
 		break;
 	}
+	return FTDM_SUCCESS;
 }
 
 /**
@@ -723,13 +726,8 @@ static __inline__ void check_state(ftdm_span_t *span)
 
 		for (j = 1; j <= ftdm_span_get_chan_count(span); j++) {
 			ftdm_channel_t *chan = ftdm_span_get_channel(span, j);
-
 			ftdm_channel_lock(chan);
-			while (ftdm_test_flag(chan, FTDM_CHANNEL_STATE_CHANGE)) {
-				ftdm_clear_flag(chan, FTDM_CHANNEL_STATE_CHANGE);
-				state_advance(chan);
-				ftdm_channel_complete_state(chan);
-			}
+			ftdm_channel_advance_states(chan);
 			ftdm_channel_unlock(chan);
 		}
 	}
@@ -1910,6 +1908,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 	span->outgoing_call = isdn_outgoing_call;
 
 	span->state_map = &isdn_state_map;
+	span->state_processor = state_advance;
 
 	span->get_channel_sig_status = isdn_get_channel_sig_status;
 	span->get_span_sig_status = isdn_get_span_sig_status;
diff --git a/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c b/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c
index 27fbe2139f..48a2f012eb 100644
--- a/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c
+++ b/libs/freetdm/src/ftmod/ftmod_pritap/ftmod_pritap.c
@@ -265,7 +265,7 @@ static ftdm_state_map_t pritap_state_map = {
 	}
 };
 
-static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
+static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
 {
 	ftdm_status_t status;
 	ftdm_sigmsg_t sig;
@@ -278,6 +278,8 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
 	sig.span_id = ftdmchan->span_id;
 	sig.channel = ftdmchan;
 
+        ftdm_channel_complete_state(ftdmchan);
+
 	switch (ftdmchan->state) {
 	case FTDM_CHANNEL_STATE_DOWN:
 		{			
@@ -321,24 +323,20 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
 		break;
 	}
 
-	return;
+	return FTDM_SUCCESS;
 }
 
 static __inline__ void pritap_check_state(ftdm_span_t *span)
 {
-    if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
-        uint32_t j;
-        ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
-        for(j = 1; j <= span->chan_count; j++) {
-            if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) {
-		ftdm_mutex_lock(span->channels[j]->mutex);
-                ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE);
-                state_advance(span->channels[j]);
-                ftdm_channel_complete_state(span->channels[j]);
-		ftdm_mutex_unlock(span->channels[j]->mutex);
-            }
-        }
-    }
+	if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
+		uint32_t j;
+		ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
+		for(j = 1; j <= span->chan_count; j++) {
+			ftdm_mutex_lock(span->channels[j]->mutex);
+			ftdm_channel_advance_states(span->channels[j]);
+			ftdm_mutex_unlock(span->channels[j]->mutex);
+		}
+	}
 }
 
 static int pri_io_read(struct pri *pri, void *buf, int buflen)
@@ -896,6 +894,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_pritap_configure_span)
 	span->get_span_sig_status = pritap_get_span_sig_status;
 	
 	span->state_map = &pritap_state_map;
+	span->state_processor = state_advance;
 
 	return FTDM_SUCCESS;
 }
diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
index 8792b519d0..f1ef61ce9f 100644
--- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
+++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
@@ -73,7 +73,6 @@ typedef struct ftdm_r2_call_t {
 	int disconnect_rcvd:1;
 	int ftdm_call_started:1;
 	int protocol_error:1;
-	ftdm_channel_state_t chanstate;
 	ftdm_size_t dnis_index;
 	ftdm_size_t ani_index;
 	char logname[255];
@@ -168,8 +167,7 @@ static ftdm_hash_t *g_mod_data_hash;
 /* IO interface for the command API */
 static ftdm_io_interface_t g_ftdm_r2_interface;
 
-static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan);
-static void ftdm_r2_state_advance_all(ftdm_channel_t *ftdmchan);
+static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan);
 
 /* whether R2 call accept process is pending */
 #define IS_ACCEPTING_PENDING(ftdmchan) \
@@ -349,7 +347,6 @@ static void ft_r2_clean_call(ftdm_r2_call_t *call)
 	call->disconnect_rcvd = 0;
 	call->ftdm_call_started = 0;
 	call->protocol_error = 0;
-	call->chanstate = FTDM_CHANNEL_STATE_DOWN;
 	call->dnis_index = 0;
 	call->ani_index = 0;
 	call->name[0] = 0;
@@ -479,7 +476,6 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call)
 	}
 
 	R2CALL(ftdmchan)->ftdm_call_started = 1;
-	R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
 	ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING);
 
 	ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS);
@@ -624,7 +620,7 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
 			ftdm_sched_cancel_timer(r2data->sched, r2call->protocol_error_recovery_timer);
 			ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Cancelled protocol error recovery timer\n");
 			ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-			ftdm_r2_state_advance_all(ftdmchan);
+			ftdm_channel_advance_states(ftdmchan);
 		}
 	}
 
@@ -658,7 +654,6 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
 		ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_OUTPUT_DUMP, &r2data->mf_dump_size);
 	}
 
-	R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
 	ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
 	ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS);
 	ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_TX_BUFFERS, NULL);
@@ -708,12 +703,10 @@ static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, cons
 static void clear_accept_pending(ftdm_channel_t *fchan)
 {
 	if (IS_ACCEPTING_PENDING(fchan)) {
-		ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
 		ftdm_channel_complete_state(fchan);
 	} else if (ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) {
 		ftdm_log_chan(fchan, FTDM_LOG_CRIT, "State change flag set in state %s, last state = %s\n", 
 				ftdm_channel_state2str(fchan->state), ftdm_channel_state2str(fchan->last_state));
-		ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
 		ftdm_channel_complete_state(fchan);
 	}
 }
@@ -821,7 +814,7 @@ static void ftdm_r2_on_call_end(openr2_chan_t *r2chan)
 	ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
 
 	/* in some circumstances openr2 can call on_call_init right after this, so let's advance the state right here */
-	ftdm_r2_state_advance_all(ftdmchan);
+	ftdm_channel_advance_states(ftdmchan);
 }
 
 static void ftdm_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
@@ -853,7 +846,7 @@ static void ftdm_r2_recover_from_protocol_error(void *data)
 		goto done;
 	}
 	ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-	ftdm_r2_state_advance_all(ftdmchan);
+	ftdm_channel_advance_states(ftdmchan);
 done:
 	ftdm_channel_unlock(ftdmchan);
 }
@@ -1617,10 +1610,14 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
 	span->set_channel_sig_status = ftdm_r2_set_channel_sig_status;
 
 	span->state_map = &r2_state_map;
+	span->state_processor = ftdm_r2_state_advance;
 
 	/* use signals queue */
 	ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE);
 
+	/* we can skip states (going straight from RING to UP) */
+	ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES);
+
 	/* setup the scheduler */
 	snprintf(schedname, sizeof(schedname), "ftmod_r2_%s", span->name);
 	ftdm_assert(ftdm_sched_create(&r2data->sched, schedname) == FTDM_SUCCESS, "Failed to create schedule!\n");
@@ -1643,10 +1640,10 @@ fail:
 }
 
 /* the channel must be locked when calling this function */
-static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
+static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
 {
 	ftdm_sigmsg_t sigev;
-	int ret;
+	ftdm_status_t ret;
 	ftdm_r2_call_t *r2call = R2CALL(ftdmchan);
 	openr2_chan_t *r2chan = r2call->r2chan;
 	ftdm_r2_data_t *r2data = ftdmchan->span->signal_data;
@@ -1656,183 +1653,173 @@ static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
 	sigev.span_id = ftdmchan->span_id;
 	sigev.channel = ftdmchan;
 
-	ret = 0;
+	ret = FTDM_SUCCESS;
 
-	/* because we do not always acknowledge the state change (clearing the FTDM_CHANNEL_STATE_CHANGE flag) due to the accept
-	 * procedure described below, we need the chanstate member to NOT process some states twice, so is valid entering this 
-	 * function with the FTDM_CHANNEL_STATE_CHANGE flag set but with a state that was already processed and is just waiting
-	 * to complete (the processing is media-bound)
-	 * */
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE) 
-			&& (r2call->chanstate != ftdmchan->state)) {
+	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
 
-		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
-		r2call->chanstate = ftdmchan->state;
-
-		if (IS_ACCEPTING_PENDING(ftdmchan)) {
-			/* 
-			   Moving to PROGRESS, PROGRESS_MEDIA or UP means that we must accept the call first, and accepting
-			   the call in R2 means sending a tone, then waiting for the acknowledge from the other end,
-			   since all of that requires sending and detecting tones, it takes a few milliseconds (I'd say around 100)
-			   which means during that time the user should not try to perform any operations like answer, hangup or anything
-			   else, therefore we DO NOT clear the FTDM_CHANNEL_STATE_CHANGE flag here, we rely on ftdm_io.c to block
-			   the user thread until we're done with the accept (see on_call_accepted callback) and then we clear the state change flag,
-			   otherwise we have a race condition between freetdm calling openr2_chan_answer_call and openr2 accepting the call first, 
-			   if freetdm calls openr2_chan_answer_call before the accept cycle completes, openr2 will fail to answer the call */
-			ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "State ack for state %s will have to wait a bit\n", ftdm_channel_state2str(ftdmchan->state));
-		} else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){
-			ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
-			ftdm_channel_complete_state(ftdmchan);
-		}
-
-		switch (ftdmchan->state) {
-
-			/* starting an incoming call */
-			case FTDM_CHANNEL_STATE_COLLECT: 
-				{
-					uint32_t interval = 0;
-					ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
-					ftdm_assert(interval != 0, "Invalid interval!");
-					ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting processing of incoming call with interval %d\n", interval);
-					openr2_chan_enable_read(r2chan);
-				}
-				break;
-
-				/* starting an outgoing call */
-			case FTDM_CHANNEL_STATE_DIALING:
-				{
-					uint32_t interval = 0;
-					ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
-					ftdm_assert(interval != 0, "Invalid interval!");
-					ftdm_log_chan(ftdmchan, 
-						FTDM_LOG_DEBUG, "Starting processing of outgoing call in channel with interval %d\n", interval);
-					openr2_chan_enable_read(r2chan);
-				}
-				break;
-
-				/* incoming call was offered */
-			case FTDM_CHANNEL_STATE_RING:
-
-				/* notify the user about the new call */
-				sigev.event_id = FTDM_SIGEVENT_START;
-
-				ftdm_span_send_signal(ftdmchan->span, &sigev);
-				r2call->ftdm_call_started = 1; 
-
-				break;
-
-				/* the call is making progress */
-			case FTDM_CHANNEL_STATE_PROGRESS:
-			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
-				{
-					if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-						if (!r2call->accepted) {
-							ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Accepting call\n");
-							ft_r2_accept_call(ftdmchan);
-						} 
-					} else {
-						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying progress\n");
-						sigev.event_id = FTDM_SIGEVENT_PROCEED;
-						ftdm_span_send_signal(ftdmchan->span, &sigev);
-
-						sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
-						ftdm_span_send_signal(ftdmchan->span, &sigev);
-					}
-				}
-				break;
-
-				/* the call was answered */
-			case FTDM_CHANNEL_STATE_UP:
-				{
-					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call was answered\n");
-					if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-						if (!r2call->accepted) {
-							ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call has not been accepted, need to accept first\n");
-							// the answering will be done in the on_call_accepted handler
-							ft_r2_accept_call(ftdmchan);
-							r2call->answer_pending = 1;
-						} else {
-							ft_r2_answer_call(ftdmchan);
-						}
-					} else {
-						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying of call answered\n");
-						sigev.event_id = FTDM_SIGEVENT_UP;
-						ftdm_span_send_signal(ftdmchan->span, &sigev);
-					}
-				}
-				break;
-
-				/* just got hangup */
-			case FTDM_CHANNEL_STATE_HANGUP:
-				{
-					if (!r2call->disconnect_rcvd) {
-						openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
-						ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
-						/* this will disconnect the call, but need to wait for the call end before moving to DOWN */
-						openr2_chan_disconnect_call(r2chan, disconnect_cause);
-					} else if (!r2call->protocol_error) {
-						/* just ack the hangup, on_call_end will be called by openr2 right after */
-						openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
-					} else {
-						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Clearing call due to protocol error\n");
-						/* do not set to down yet, give some time for recovery */
-						ftdm_sched_timer(r2data->sched, "protocolerr_recover", 100, 
-								ftdm_r2_recover_from_protocol_error, r2chan, &r2call->protocol_error_recovery_timer);
-					}
-				}
-				break;
-
-			case FTDM_CHANNEL_STATE_TERMINATING:
-				{
-					/* if the call has not been started yet we must go to HANGUP right here */ 
-					if (!r2call->ftdm_call_started) {
-						ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
-					} else {
-						openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
-						ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
-						/* notify the user of the call terminating and we wait for the user to move us to hangup */
-						sigev.event_id = FTDM_SIGEVENT_STOP;
-						ftdm_span_send_signal(ftdmchan->span, &sigev);
-					}
-				}
-				break;
-
-				/* finished call for good */
-			case FTDM_CHANNEL_STATE_DOWN: 
-				{
-					if (ftdmchan->last_state != FTDM_CHANNEL_STATE_RESET) {
-						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n");
-					}
-					ret = 1;
-				}
-				break;
-
-				/* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */
-			case FTDM_CHANNEL_STATE_RINGING: 
-				{
-					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n");
-				}
-				break;
-
-				/* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
-			case FTDM_CHANNEL_STATE_RESET:
-				{
-					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
-					openr2_chan_set_idle(r2chan);
-					ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
-				}
-				break;
-
-			default:
-				{
-					ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
-				}
-				break;
-
-		}
+	if (IS_ACCEPTING_PENDING(ftdmchan)) {
+		/* 
+		   Moving to PROGRESS, PROGRESS_MEDIA or UP means that we must accept the call first, and accepting
+		   the call in R2 means sending a tone, then waiting for the acknowledge from the other end,
+		   since all of that requires sending and detecting tones, it takes a few milliseconds (I'd say around 100)
+		   which means during that time the user should not try to perform any operations like answer, hangup or anything
+		   else, therefore we DO NOT clear the FTDM_CHANNEL_STATE_CHANGE flag here, we rely on ftdm_io.c to block
+		   the user thread until we're done with the accept (see on_call_accepted callback) and then we clear the state change flag,
+		   otherwise we have a race condition between freetdm calling openr2_chan_answer_call and openr2 accepting the call first, 
+		   if freetdm calls openr2_chan_answer_call before the accept cycle completes, openr2 will fail to answer the call */
+		ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "State ack for state %s will have to wait a bit\n", ftdm_channel_state2str(ftdmchan->state));
+	} else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){
+		ftdm_channel_complete_state(ftdmchan);
 	}
 
-	if (ret) {
+	switch (ftdmchan->state) {
+
+		/* starting an incoming call */
+		case FTDM_CHANNEL_STATE_COLLECT: 
+			{
+				uint32_t interval = 0;
+				ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
+				ftdm_assert(interval != 0, "Invalid interval!");
+				ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting processing of incoming call with interval %d\n", interval);
+				openr2_chan_enable_read(r2chan);
+			}
+			break;
+
+			/* starting an outgoing call */
+		case FTDM_CHANNEL_STATE_DIALING:
+			{
+				uint32_t interval = 0;
+				ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
+				ftdm_assert(interval != 0, "Invalid interval!");
+				ftdm_log_chan(ftdmchan, 
+					FTDM_LOG_DEBUG, "Starting processing of outgoing call in channel with interval %d\n", interval);
+				openr2_chan_enable_read(r2chan);
+			}
+			break;
+
+			/* incoming call was offered */
+		case FTDM_CHANNEL_STATE_RING:
+
+			/* notify the user about the new call */
+			sigev.event_id = FTDM_SIGEVENT_START;
+
+			ftdm_span_send_signal(ftdmchan->span, &sigev);
+			r2call->ftdm_call_started = 1; 
+
+			break;
+
+			/* the call is making progress */
+		case FTDM_CHANNEL_STATE_PROGRESS:
+		case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
+			{
+				if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
+					if (!r2call->accepted) {
+						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Accepting call\n");
+						ft_r2_accept_call(ftdmchan);
+					} 
+				} else {
+					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying progress\n");
+					sigev.event_id = FTDM_SIGEVENT_PROCEED;
+					ftdm_span_send_signal(ftdmchan->span, &sigev);
+
+					sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
+					ftdm_span_send_signal(ftdmchan->span, &sigev);
+				}
+			}
+			break;
+
+			/* the call was answered */
+		case FTDM_CHANNEL_STATE_UP:
+			{
+				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call was answered\n");
+				if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
+					if (!r2call->accepted) {
+						ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call has not been accepted, need to accept first\n");
+						// the answering will be done in the on_call_accepted handler
+						ft_r2_accept_call(ftdmchan);
+						r2call->answer_pending = 1;
+					} else {
+						ft_r2_answer_call(ftdmchan);
+					}
+				} else {
+					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying of call answered\n");
+					sigev.event_id = FTDM_SIGEVENT_UP;
+					ftdm_span_send_signal(ftdmchan->span, &sigev);
+				}
+			}
+			break;
+
+			/* just got hangup */
+		case FTDM_CHANNEL_STATE_HANGUP:
+			{
+				if (!r2call->disconnect_rcvd) {
+					openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
+					ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
+					/* this will disconnect the call, but need to wait for the call end before moving to DOWN */
+					openr2_chan_disconnect_call(r2chan, disconnect_cause);
+				} else if (!r2call->protocol_error) {
+					/* just ack the hangup, on_call_end will be called by openr2 right after */
+					openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
+				} else {
+					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Clearing call due to protocol error\n");
+					/* do not set to down yet, give some time for recovery */
+					ftdm_sched_timer(r2data->sched, "protocolerr_recover", 100, 
+							ftdm_r2_recover_from_protocol_error, r2chan, &r2call->protocol_error_recovery_timer);
+				}
+			}
+			break;
+
+		case FTDM_CHANNEL_STATE_TERMINATING:
+			{
+				/* if the call has not been started yet we must go to HANGUP right here */ 
+				if (!r2call->ftdm_call_started) {
+					ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
+				} else {
+					openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
+					ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
+					/* notify the user of the call terminating and we wait for the user to move us to hangup */
+					sigev.event_id = FTDM_SIGEVENT_STOP;
+					ftdm_span_send_signal(ftdmchan->span, &sigev);
+				}
+			}
+			break;
+
+			/* finished call for good */
+		case FTDM_CHANNEL_STATE_DOWN: 
+			{
+				if (ftdmchan->last_state != FTDM_CHANNEL_STATE_RESET) {
+					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n");
+				} else {
+					ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Reset Complete\n");
+				}
+				ret = FTDM_BREAK;
+			}
+			break;
+
+			/* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */
+		case FTDM_CHANNEL_STATE_RINGING: 
+			{
+				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n");
+			}
+			break;
+
+			/* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
+		case FTDM_CHANNEL_STATE_RESET:
+			{
+				ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
+				openr2_chan_set_idle(r2chan);
+				ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
+			}
+			break;
+
+		default:
+			{
+				ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
+			}
+			break;
+	}
+
+	if (ret == FTDM_BREAK) {
 		ftdm_channel_t *closed_chan;
 		closed_chan = ftdmchan;
 		ftdm_channel_close(&closed_chan);
@@ -1841,20 +1828,6 @@ static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
 	return ret;
 }
 
-/* the channel must be locked when calling this function */
-static void ftdm_r2_state_advance_all(ftdm_channel_t *ftdmchan)
-{
-	/* because we do not always acknowledge the state change (clearing the FTDM_CHANNEL_STATE_CHANGE flag) due to the accept
-	 * procedure described below, we need the chanstate member to NOT process some states twice, so is valid entering this 
-	 * function with the FTDM_CHANNEL_STATE_CHANGE flag set but with a state that was already processed and is just waiting
-	 * to complete (the processing is media-bound)
-	 * */
-	while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)
-		&& (R2CALL(ftdmchan)->chanstate != ftdmchan->state)) {
-		ftdm_r2_state_advance(ftdmchan);
-	}
-}
-
 static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
 {
 	openr2_chan_t *r2chan = NULL;
@@ -1983,12 +1956,12 @@ static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
 			ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED);
 			ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED);
 
-			ftdm_r2_state_advance_all(ftdmchan);
+			ftdm_channel_advance_states(ftdmchan);
 
 			r2chan = call->r2chan;
 			openr2_chan_process_signaling(r2chan);
 
-			ftdm_r2_state_advance_all(ftdmchan);
+			ftdm_channel_advance_states(ftdmchan);
 
 			if (!call->accepted) {
 				/* if the call is not accepted we do not want users reading */
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c
index 97e242dacb..d0bc14c8d8 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_boost/ftmod_sangoma_boost.c
@@ -951,7 +951,6 @@ static void handle_call_answer(ftdm_span_t *span, sangomabc_connection_t *mcon,
 	}
 }
 
-static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan);
 static __inline__ void stop_loop(ftdm_channel_t *ftdmchan);
 
 /**
@@ -1002,7 +1001,7 @@ tryagain:
 			} else if (ftdmchan->state == FTDM_CHANNEL_STATE_IN_LOOP && retry) {
 				retry = 0;
 				stop_loop(ftdmchan);
-				advance_chan_states(ftdmchan);
+				ftdm_channel_advance_states(ftdmchan);
 				goto tryagain;
 			} else {
 				ftdm_log(FTDM_LOG_ERROR, "s%dc%d: rejecting incoming call in channel state %s\n", 
@@ -1267,7 +1266,7 @@ static ftdm_channel_t* event_process_states(ftdm_span_t *span, sangomabc_short_e
     }
 
     ftdm_mutex_lock(ftdmchan->mutex);
-    advance_chan_states(ftdmchan);
+    ftdm_channel_advance_states(ftdmchan);
     return ftdmchan;
 }
 
@@ -1354,11 +1353,11 @@ static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon,
     }
 
     if(ftdmchan != NULL) {
-        advance_chan_states(ftdmchan);
+    	ftdm_channel_advance_states(ftdmchan);
         ftdm_mutex_unlock(ftdmchan->mutex);
     }
 
-	return 0;
+    return 0;
 
 }
 
@@ -1366,7 +1365,7 @@ static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon,
  * \brief Handler for channel state change
  * \param ftdmchan Channel to handle
  */
-static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
+static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
 {
 	ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data;
 	sangomabc_connection_t *mcon = &sangoma_boost_data->mcon;
@@ -1374,12 +1373,6 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
 	ftdm_status_t status;
 
 
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-		ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
-	} else {
-		return FTDM_SUCCESS;
-	}
-
 	ftdm_assert_return(ftdmchan->last_state != ftdmchan->state, FTDM_FAIL, "Channel state already processed\n");
 
 	ftdm_log(FTDM_LOG_DEBUG, "%d:%d PROCESSING STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
@@ -1389,6 +1382,8 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
 	sig.span_id = ftdmchan->span_id;
 	sig.channel = ftdmchan;
 
+	ftdm_channel_complete_state(ftdmchan);
+
 	switch (ftdmchan->state) {
 	case FTDM_CHANNEL_STATE_DOWN:
 		{
@@ -1640,24 +1635,15 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
 	default:
 		break;
 	}
-	ftdm_channel_complete_state(ftdmchan);
 	return FTDM_SUCCESS;
 }
 
-static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan)
-{
-	while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-		state_advance(ftdmchan);
-	}
-}
-
 /**
  * \brief Initialises outgoing requests array
  */
 static __inline__ void init_outgoing_array(void)
 {
 	memset(&OUTBOUND_REQUESTS, 0, sizeof(OUTBOUND_REQUESTS));
-
 }
 
 /**
@@ -1685,7 +1671,7 @@ static __inline__ void check_state(ftdm_span_t *span)
 					if (susp && span->channels[j]->state != FTDM_CHANNEL_STATE_DOWN) {
 						ftdm_set_state(span->channels[j], FTDM_CHANNEL_STATE_RESTART);
 					}
-					state_advance(span->channels[j]);
+					ftdm_channel_advance_states(span->channels[j]);
 					ftdm_mutex_unlock(span->channels[j]->mutex);
 				}
 			}
@@ -1695,7 +1681,7 @@ static __inline__ void check_state(ftdm_span_t *span)
 				 * but without taking the chan out of the queue, so check th
 				 * flag before advancing the state */
 				ftdm_mutex_lock(ftdmchan->mutex);
-				state_advance(ftdmchan);
+				ftdm_channel_advance_states(ftdmchan);
 				ftdm_mutex_unlock(ftdmchan->mutex);
 			}
 		}
@@ -2687,6 +2673,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_boost_configure_span)
 	span->get_span_sig_status = sangoma_boost_get_span_sig_status;
 	span->set_span_sig_status = sangoma_boost_set_span_sig_status;
 	span->state_map = &boost_state_map;
+	span->state_processor = state_advance;
 	sangoma_boost_data->mcon.debuglevel = FTDM_LOG_LEVEL_DEBUG;
 	sangoma_boost_data->pcon.debuglevel = FTDM_LOG_LEVEL_DEBUG;
 	ftdm_clear_flag(span, FTDM_SPAN_SUGGEST_CHAN_ID);
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 bb29b4f4a5..259bf18b81 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
@@ -46,10 +46,9 @@ static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span);
 static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span);
 
 ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event);
-static void ftdm_sangoma_isdn_advance_chan_states(ftdm_channel_t *ftdmchan);
 static void ftdm_sangoma_isdn_poll_events(ftdm_span_t *span);
 static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event);
-static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan);
+static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan);
 static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event);
 static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan);
 static void ftdm_sangoma_isdn_dchan_set_queue_size(ftdm_channel_t *ftdmchan);
@@ -270,13 +269,6 @@ ftdm_state_map_t sangoma_isdn_state_map = {
 	}
 };
 
-static __inline__ void ftdm_sangoma_isdn_advance_chan_states(ftdm_channel_t *ftdmchan)
-{
-	while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-		ftdm_sangoma_isdn_process_state_change(ftdmchan);
-	}
-}
-
 static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event)
 {
 	sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) span->signal_data;
@@ -457,7 +449,7 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj)
 				while ((ftdmchan = ftdm_queue_dequeue(span->pendingchans))) {
 					/* double check that this channel has a state change pending */
 					ftdm_channel_lock(ftdmchan);
-					ftdm_sangoma_isdn_advance_chan_states(ftdmchan);
+					ftdm_channel_advance_states(ftdmchan);
 					ftdm_channel_unlock(ftdmchan);
 				}
 
@@ -470,11 +462,11 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj)
 				/* twiddle */
 				break;
 			case FTDM_FAIL:
-				ftdm_log(FTDM_LOG_ERROR,"%s:ftdm_interrupt_wait returned error!\n", span->name);
+				ftdm_log(FTDM_LOG_ERROR, "%s: ftdm_interrupt_wait returned error!\n", span->name);
 				break;
 
 			default:
-				ftdm_log(FTDM_LOG_ERROR,"%s:ftdm_interrupt_wait returned with unknown code\n", span->name);
+				ftdm_log(FTDM_LOG_ERROR, "%s: ftdm_interrupt_wait returned with unknown code\n", span->name);
 				break;
 		}
 
@@ -536,7 +528,7 @@ ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisd
 			break;
 	}
  	ftdm_channel_lock(ftdmchan);
-	ftdm_sangoma_isdn_advance_chan_states(ftdmchan);
+	ftdm_channel_advance_states(ftdmchan);
 	return ftdmchan;
 }
 
@@ -600,13 +592,14 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev
 			sngisdn_process_rst_ind(sngisdn_event);
 			break;
 	}
-	if(ftdmchan != NULL) {
-		ftdm_sangoma_isdn_advance_chan_states(ftdmchan);
+	if (ftdmchan != NULL) {
+		ftdm_channel_advance_states(ftdmchan);
 		ftdm_channel_unlock(ftdmchan);
 	}
 }
 
-static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
+/* 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;
@@ -618,13 +611,12 @@ static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
 	sigev.span_id = ftdmchan->span_id;
 	sigev.channel = ftdmchan;
 
-	/*first lock the channel*/
-	ftdm_channel_lock(ftdmchan);
-	/*clear the state change flag...since we might be setting a new state*/
-	ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
+	/* Acknowledge the state change */
+	ftdm_channel_complete_state(ftdmchan);
+
 #ifdef FTDM_DEBUG_CHAN_MEMORY
 	if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) {
-		ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ)==0, "Failed to mprotect");
+		ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ) == 0, "Failed to mprotect");
 	}
 #endif
 	
@@ -879,11 +871,10 @@ static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
 	}
 #ifdef FTDM_DEBUG_CHAN_MEMORY
 	if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) {
-		ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ|PROT_WRITE)==0, "Failed to mprotect");
+		ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ|PROT_WRITE) == 0, "Failed to mprotect");
 	}
 #endif
-	ftdm_channel_unlock(ftdmchan);
-	return;
+	return FTDM_SUCCESS;
 }
 
 static FIO_CHANNEL_SEND_MSG_FUNCTION(ftdm_sangoma_isdn_send_msg)
@@ -1098,6 +1089,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config)
 	span->get_span_sig_status = ftdm_sangoma_isdn_get_span_sig_status;
 	span->set_span_sig_status = ftdm_sangoma_isdn_set_span_sig_status;
 	span->state_map	= &sangoma_isdn_state_map;
+	span->state_processor = ftdm_sangoma_isdn_process_state_change;
 	ftdm_set_flag(span, FTDM_SPAN_USE_CHAN_QUEUE);
 	ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE);
 	ftdm_set_flag(span, FTDM_SPAN_USE_PROCEED_STATE);
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c
index 05a325b913..9016771671 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.c
@@ -46,7 +46,6 @@ ftdm_sngss7_data_t g_ftdm_sngss7_data;
 
 /* PROTOTYPES *****************************************************************/
 static void *ftdm_sangoma_ss7_run (ftdm_thread_t * me, void *obj);
-void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan);
 static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_event);
 
 static ftdm_status_t ftdm_sangoma_ss7_stop (ftdm_span_t * span);
@@ -308,9 +307,7 @@ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj)
 				ftdm_mutex_lock(ftdmchan->mutex);
 
 				/* process state changes for this channel until they are all done */
-				while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-					ftdm_sangoma_ss7_process_state_change (ftdmchan);
-				}
+				ftdm_channel_advance_states(ftdmchan);
  
 				/* unlock the channel */
 				ftdm_mutex_unlock (ftdmchan->mutex);				
@@ -403,9 +400,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev
 	ftdm_mutex_lock(ftdmchan->mutex);
 
 	/* while there's a state change present on this channel process it */
-	while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-		ftdm_sangoma_ss7_process_state_change(ftdmchan);
-	}
+	ftdm_channel_advance_states(ftdmchan);
 
 	/* figure out the type of event and send it to the right handler */
 	switch (sngss7_event->event_id) {
@@ -468,9 +463,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev
 	} /* switch (sngss7_event->event_id) */
 
 	/* while there's a state change present on this channel process it */
-	while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
-		ftdm_sangoma_ss7_process_state_change(ftdmchan);
-	}
+	ftdm_channel_advance_states(ftdmchan);
 
 	/* unlock the channel */
 	ftdm_mutex_unlock(ftdmchan->mutex);
@@ -479,7 +472,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev
 }
 
 /******************************************************************************/
-void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan)
+ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan)
 {
 	sngss7_chan_data_t	*sngss7_info = ftdmchan->call_data;
 	sng_isup_inf_t		*isup_intf = NULL; 
@@ -495,7 +488,7 @@ void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan)
 	SS7_DEBUG_CHAN(ftdmchan, "ftmod_sangoma_ss7 processing state %s\n", ftdm_channel_state2str (ftdmchan->state));
 
 	/* clear the state change flag...since we might be setting a new state */
-	ftdm_clear_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
+	ftdm_channel_complete_state(ftdmchan);
 	
 	/*check what state we are supposed to be in */
 	switch (ftdmchan->state) {
@@ -1212,7 +1205,7 @@ suspend_goto_restart:
 	/**************************************************************************/
 	}/*switch (ftdmchan->state) */
 
-	return;
+	return FTDM_SUCCESS;
 }
 
 /******************************************************************************/
@@ -1476,6 +1469,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_ss7_span_config)
 	span->get_channel_sig_status	= ftdm_sangoma_ss7_get_sig_status;
 	span->set_channel_sig_status 	= ftdm_sangoma_ss7_set_sig_status;
 	span->state_map			 		= &sangoma_ss7_state_map;
+	span->state_processor = ftdm_sangoma_ss7_process_state_change;
 	span->signal_data					= ss7_span_info;
 
 	/* set the flag to indicate that this span uses channel state change queues */
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h
index fe4b6f45c4..f28547f9fe 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_ss7/ftmod_sangoma_ss7_main.h
@@ -452,7 +452,7 @@ extern int				cmbLinkSetId;
 
 /* PROTOTYPES *****************************************************************/
 /* in ftmod_sangoma_ss7_main.c */
-void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan);
+ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan);
 
 /* in ftmod_sangoma_ss7_logger.c */
 void handle_sng_log(uint8_t level, char *fmt,...);
diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h
index d27353ddb0..38bc61188b 100644
--- a/libs/freetdm/src/include/freetdm.h
+++ b/libs/freetdm/src/include/freetdm.h
@@ -349,12 +349,13 @@ typedef enum {
 	FTDM_SIGEVENT_FACILITY, /*!< In call facility event */
 	FTDM_SIGEVENT_TRACE, /*!<Interpreted trace event */
 	FTDM_SIGEVENT_TRACE_RAW, /*!<Raw trace event */
+	FTDM_SIGEVENT_INDICATION_COMPLETED, /*!< Last requested indication was 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", "COLLISION", "FACILITY", "TRACE", "TRACE_RAW", "INVALID"
-
+		"COLLECTED_DIGIT", "ADD_CALL", "RESTART", "SIGSTATUS_CHANGED", "COLLISION", "FACILITY", \
+		"TRACE", "TRACE_RAW", "INDICATION_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)
 
@@ -434,6 +435,32 @@ typedef struct {
 	char digits[FTDM_DIGITS_LIMIT];
 } ftdm_event_collected_t;
 
+/*! \brief FreeTDM supported indications.
+ * This is used during incoming calls when you want to request the signaling stack
+ * to notify about indications occurring locally. See ftdm_channel_call_indicate for more info */
+typedef enum {
+	FTDM_CHANNEL_INDICATE_NONE,
+	FTDM_CHANNEL_INDICATE_RINGING,
+	FTDM_CHANNEL_INDICATE_PROCEED,
+	FTDM_CHANNEL_INDICATE_PROGRESS,
+	FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA,
+	FTDM_CHANNEL_INDICATE_BUSY,
+	/* Using this indication is equivalent to call ftdm_channel_call_answer API */
+	FTDM_CHANNEL_INDICATE_ANSWER,
+	FTDM_CHANNEL_INDICATE_INVALID,
+} ftdm_channel_indication_t;
+#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "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)
+
+typedef struct {
+	/* The indication that was completed */
+	ftdm_channel_indication_t indication;
+	/* Completion status of the indication */
+	ftdm_status_t status;
+} ftdm_event_indication_completed_t;
+
 /*! \brief Generic signaling message */
 struct ftdm_sigmsg {
 	ftdm_signal_event_t event_id; /*!< The type of message */
@@ -444,7 +471,8 @@ struct ftdm_sigmsg {
 	union {
 		ftdm_event_sigstatus_t sigstatus; /*!< valid if event_id is FTDM_SIGEVENT_SIGSTATUS_CHANGED */
 		ftdm_event_trace_t logevent;	/*!< valid if event_id is FTDM_SIGEVENT_TRACE or FTDM_SIGEVENT_TRACE_RAW */
-		ftdm_event_collected_t collected; /*!< valif if event_id is FTDM_SIGEVENT_COLLECTED_DIGIT */
+		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 */
 	} ev_data;
 	struct {
 		uint8_t autofree; /*!< Whether the freetdm core will free it after message delivery */
@@ -555,10 +583,10 @@ typedef enum {
 	FTDM_COMMAND_GET_LINK_STATUS,
 	FTDM_COMMAND_ENABLE_LOOP,
 	FTDM_COMMAND_DISABLE_LOOP,
-	FTDM_COMMAND_COUNT,
 	FTDM_COMMAND_SET_RX_QUEUE_SIZE,
 	FTDM_COMMAND_SET_TX_QUEUE_SIZE,
 	FTDM_COMMAND_SET_POLARITY,
+	FTDM_COMMAND_COUNT,
 } ftdm_command_t;
 
 typedef enum {
@@ -624,7 +652,20 @@ typedef ftdm_status_t (*fio_span_get_sig_status_t) FIO_SPAN_GET_SIG_STATUS_ARGS;
 typedef ftdm_status_t (*fio_span_poll_event_t) FIO_SPAN_POLL_EVENT_ARGS ;
 typedef ftdm_status_t (*fio_span_next_event_t) FIO_SPAN_NEXT_EVENT_ARGS ;
 typedef ftdm_status_t (*fio_channel_next_event_t) FIO_CHANNEL_NEXT_EVENT_ARGS ;
+
+/*! \brief Callback for signal delivery (FTDM_SIGEVENT_START and friends) 
+ *  \note This callback is provided by the user during ftdm_configure_span_signaling
+ *
+ *  \note You must NOT do any blocking during this callback since this function is
+ *        most likely called in an internal signaling thread that can potentially be
+ *        shared for all the channels in a span and blocking will delay processing
+ *        (sometimes even audio processing) for other channels
+ *
+ *  \note Although some simple FreeTDM APIs can work (ie: ftdm_span_get_id etc), the
+ *        use of any FreeTDM call API (ie ftdm_channel_call_answer) is discouraged
+ */
 typedef ftdm_status_t (*fio_signal_cb_t) FIO_SIGNAL_CB_ARGS ;
+
 typedef ftdm_status_t (*fio_event_cb_t) FIO_EVENT_CB_ARGS ;
 typedef ftdm_status_t (*fio_configure_span_t) FIO_CONFIGURE_SPAN_ARGS ;
 typedef ftdm_status_t (*fio_configure_t) FIO_CONFIGURE_ARGS ;
@@ -708,17 +749,6 @@ typedef enum {
 	FTDM_CODEC_NONE = (1 << 30)
 } ftdm_codec_t;
 
-/*! \brief FreeTDM supported indications.
- * This is used during incoming calls when you want to request the signaling stack
- * to notify about indications occurring locally */
-typedef enum {
-	FTDM_CHANNEL_INDICATE_RINGING,
-	FTDM_CHANNEL_INDICATE_PROCEED,
-	FTDM_CHANNEL_INDICATE_PROGRESS,
-	FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA,
-	FTDM_CHANNEL_INDICATE_BUSY,
-} ftdm_channel_indication_t;
-
 /*! \brief FreeTDM supported hardware alarms. */
 typedef enum {
 	FTDM_ALARM_NONE    = 0,
@@ -741,7 +771,12 @@ FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *ha
  */
 FT_DECLARE(int) ftdm_channel_get_availability(ftdm_channel_t *ftdmchan);
 
-/*! \brief Answer call */
+/*! \brief Answer call. This can also be accomplished by ftdm_channel_call_indicate with FTDM_CHANNEL_INDICATE_ANSWER, in both
+ *         cases you will get a FTDM_SIGEVENT_INDICATION_COMPLETED when the indication is sent (or an error occurs) 
+ *  \note Although this API will 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_answer 
+ */
 #define ftdm_channel_call_answer(ftdmchan) _ftdm_channel_call_answer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan))
 
 /*! \brief Answer call recording the source code point where the it was called (see ftdm_channel_call_answer for an easy to use macro) */
@@ -753,7 +788,19 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
 /*! \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) */
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan);
 
-/*! \brief Indicate a new condition in an incoming call */
+/*! \brief Indicate a new condition in an incoming call 
+ *
+ *  \note Every indication request will result in FTDM_SIGEVENT_INDICATION_COMPLETED event being delivered with
+ *        the proper status that will inform you if the request was successful or not. The exception is if this
+ *        function returns something different to FTDM_SUCCESS, in which case the request failed right away and no
+ *        further FTDM_SIGEVENT_INDICATION_COMPLETED will be delivered
+ *        Be aware there is no guarantee of whether the completion event will arrive after or before your execution 
+ *        thread returns from ftdm_channel_call_indicate. This means you could get FTDM_SIGEVENT_INDICATION_COMPLETED 
+ *        even before your execution thread returns from the ftdm_channel_call_indicate() API
+ *
+ * \note  You cannot send more than one indication at the time. You must wait for the completed event before 
+ *        calling this function again (unless the return code was different than FTDM_SUCCESS)
+ */
 #define ftdm_channel_call_indicate(ftdmchan, indication) _ftdm_channel_call_indicate(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (indication))
 
 /*! \brief Indicate a new condition in an incoming call recording the source code point where it was called (see ftdm_channel_call_indicate for an easy to use macro) */
@@ -1494,9 +1541,14 @@ FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *c
  */
 FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *channel);
 
-/*! \brief Initialize channel state for an outgoing call */
+/*! \brief Initialize channel state for an outgoing call
+ *  \note This API will eventually be deprecated, is only needed if you use boost signaling 
+ */
 FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan);
 
+/*! \brief Enable/disable blocking mode in the channels for this span */
+FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, ftdm_bool_t enabled);
+
 /*! \brief Initialize the library */
 FT_DECLARE(ftdm_status_t) ftdm_global_init(void);
 
diff --git a/libs/freetdm/src/include/ftdm_declare.h b/libs/freetdm/src/include/ftdm_declare.h
index bfec448253..88a76930f7 100644
--- a/libs/freetdm/src/include/ftdm_declare.h
+++ b/libs/freetdm/src/include/ftdm_declare.h
@@ -183,7 +183,16 @@ typedef enum {
 	FTDM_TIMEOUT, /*!< Operation timed out (ie: polling on a device)*/
 	FTDM_NOTIMPL, /*!< Operation not implemented */
 	FTDM_BREAK, /*!< Request the caller to perform a break (context-dependant, ie: stop getting DNIS/ANI) */
-	FTDM_EINVAL /*!< Invalid argument */
+
+	/*!< Any new return codes should try to mimc unix style error codes, no need to reinvent */
+	/* Remapping some of the codes that were before */
+	FTDM_ENOMEM = FTDM_MEMERR, /*!< Memory error */
+	FTDM_ETIMEDOUT = FTDM_TIMEOUT, /*!< Operation timedout */
+	FTDM_ENOSYS = FTDM_NOTIMPL, /*!< The function is not implemented */
+
+	FTDM_EINVAL, /*!< Invalid argument */
+	FTDM_ECANCELED, /*!< Operation cancelled */
+	FTDM_EBUSY, /*!< Device busy */
 } ftdm_status_t;
 
 /*! \brief FreeTDM bool type. */
diff --git a/libs/freetdm/src/include/ftdm_os.h b/libs/freetdm/src/include/ftdm_os.h
index f3ebee9ea2..a4605c3371 100644
--- a/libs/freetdm/src/include/ftdm_os.h
+++ b/libs/freetdm/src/include/ftdm_os.h
@@ -51,6 +51,9 @@ extern "C" {
 #include <unistd.h>
 #endif
 
+/*! \brief time data type */
+typedef uint64_t ftdm_time_t; 
+
 /*! \brief sleep x amount of milliseconds */
 #ifdef __WINDOWS__
 #define ftdm_sleep(x) Sleep(x)
@@ -114,6 +117,8 @@ FT_DECLARE(char *) ftdm_strdup(const char *str);
 /*! \brief Duplicate string with limit */
 FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen);
 
+/*! \brief Get the current time in milliseconds */
+FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void);
 
 #ifdef __cplusplus
 } /* extern C */
diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h
index 650fce2fcc..a5862e5d15 100644
--- a/libs/freetdm/src/include/private/ftdm_core.h
+++ b/libs/freetdm/src/include/private/ftdm_core.h
@@ -192,17 +192,6 @@ extern "C" {
 
 #define ftdm_clear_sflag_locked(obj, flag) assert(obj->mutex != NULL); ftdm_mutex_lock(obj->mutex); (obj)->sflags &= ~(flag); ftdm_mutex_unlock(obj->mutex);
 
-#define ftdm_set_state(obj, s) ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);									\
-
-#define ftdm_set_state_locked(obj, s) \
-	do { \
-		ftdm_channel_lock(obj); \
-		ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);									\
-		ftdm_channel_unlock(obj); \
-	} while(0);
-
-#define ftdm_set_state_r(obj, s, r) r = ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);
-
 #ifdef _MSC_VER
 /* The while(0) below throws a conditional expression is constant warning */
 #pragma warning(disable:4127) 
@@ -363,15 +352,6 @@ typedef struct {
 	ftdm_mutex_t *mutex;
 } ftdm_dtmf_debug_t;
 
-typedef struct {
-	const char *file;
-	const char *func;
-	int line;
-	ftdm_channel_state_t state;
-	ftdm_channel_state_t last_state;
-	ftdm_time_t time;
-} ftdm_channel_history_entry_t;
-
 typedef enum {
 	FTDM_IOSTATS_ERROR_CRC		= (1 << 0),
 	FTDM_IOSTATS_ERROR_FRAME	= (1 << 1),
@@ -424,9 +404,11 @@ struct ftdm_channel {
 	uint32_t native_interval;
 	uint32_t packet_len;
 	ftdm_channel_state_t state;
+	ftdm_state_status_t state_status;
 	ftdm_channel_state_t last_state;
 	ftdm_channel_state_t init_state;
-	ftdm_channel_history_entry_t history[10];
+	ftdm_channel_indication_t indication;
+	ftdm_state_history_entry_t history[10];
 	uint8_t hindex;
 	ftdm_mutex_t *mutex;
 	teletone_dtmf_detect_state_t dtmf_detect;
@@ -480,6 +462,7 @@ struct ftdm_channel {
 	ftdm_dtmf_debug_t dtmfdbg;
 	ftdm_io_dump_t rxdump;
 	ftdm_io_dump_t txdump;
+	ftdm_interrupt_t *state_completed_interrupt; /*!< Notify when a state change is completed */
 	int32_t txdrops;
 	int32_t rxdrops;
 };
@@ -517,15 +500,15 @@ struct ftdm_span {
 	ftdm_span_stop_t stop;
 	ftdm_channel_sig_read_t sig_read;
 	ftdm_channel_sig_write_t sig_write;
-	/* Private I/O data per span. Do not touch unless you are an I/O module */
-	void *io_data;
+	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;
 	char *dtmf_hangup;
 	size_t dtmf_hangup_len;
 	ftdm_state_map_t *state_map;
 	ftdm_caller_data_t default_caller_data;
-	ftdm_queue_t *pendingchans;
-	ftdm_queue_t *pendingsignals;
+	ftdm_queue_t *pendingchans; /*!< Channels pending of state processing */
+	ftdm_queue_t *pendingsignals; /*!< Signals pending from being delivered to the user */
 	struct ftdm_span *next;
 };
 
@@ -572,11 +555,7 @@ FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_checksum(ftdm_fsk_data_state_t *stat
 FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_sdmf(ftdm_fsk_data_state_t *state, const char *date, char *number);
 FT_DECLARE(ftdm_status_t) ftdm_channel_send_fsk_data(ftdm_channel_t *ftdmchan, ftdm_fsk_data_state_t *fsk_data, float db_level);
 
-FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line,
-		ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait);
-
 FT_DECLARE(ftdm_status_t) ftdm_span_load_tones(ftdm_span_t *span, const char *mapname);
-FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void);
 
 FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan);
 
@@ -589,8 +568,6 @@ FT_DECLARE(void) print_hex_bytes(uint8_t *data, ftdm_size_t dlen, char *buf, ftd
 FT_DECLARE_NONSTD(int) ftdm_hash_equalkeys(void *k1, void *k2);
 FT_DECLARE_NONSTD(uint32_t) ftdm_hash_hashfromstring(void *ky);
 
-FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan);
-
 FT_DECLARE(int) ftdm_load_modules(void);
 
 FT_DECLARE(ftdm_status_t) ftdm_unload_modules(void);
@@ -606,6 +583,7 @@ FT_DECLARE(int) ftdm_vasprintf(char **ret, const char *fmt, va_list ap);
 
 FT_DECLARE(ftdm_status_t) ftdm_span_close_all(void);
 FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan);
+FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_status_t status);
 
 /*! 
  * \brief Retrieves an event from the span
@@ -706,30 +684,6 @@ static __inline__ void ftdm_abort(void)
 #endif
 }
 
-static __inline__ void ftdm_set_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
-{
-	uint32_t j;
-	ftdm_mutex_lock(span->mutex);
-	for(j = 1; j <= span->chan_count; j++) {
-		if (!FTDM_IS_DCHAN(span->channels[j])) {
-			ftdm_set_state_locked((span->channels[j]), state);
-		}
-	}
-	ftdm_mutex_unlock(span->mutex);
-}
-
-static __inline__ int ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
-{
-	uint32_t j;
-	for(j = 1; j <= span->chan_count; j++) {
-		if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) {
-			return 0;
-		}
-	}
-
-	return 1;
-}
-
 static __inline__ int16_t ftdm_saturated_add(int16_t sample1, int16_t sample2)
 {
 	int addres;
diff --git a/libs/freetdm/src/include/private/ftdm_m3ua.h b/libs/freetdm/src/include/private/ftdm_m3ua.h
deleted file mode 100644
index 1bf830853c..0000000000
--- a/libs/freetdm/src/include/private/ftdm_m3ua.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- *  ftdm_m3ua.h
- *  freetdm
- *
- *  Created by Shane Burrell on 4/3/08.
- *  Copyright 2008 Shane Burrell. All rights reserved.
- *
- * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic
- * 
- * 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 "m3ua_client.h"
-#include "freetdm.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-enum	e_sigboost_event_id_values
-{
-	SIGBOOST_EVENT_CALL_START			= 0x80, /*128*/
-	SIGBOOST_EVENT_CALL_START_ACK			= 0x81, /*129*/
-	SIGBOOST_EVENT_CALL_START_NACK			= 0x82, /*130*/
-	SIGBOOST_EVENT_CALL_START_NACK_ACK		= 0x83, /*131*/
-	SIGBOOST_EVENT_CALL_ANSWERED			= 0x84, /*132*/
-	SIGBOOST_EVENT_CALL_STOPPED			= 0x85, /*133*/
-	SIGBOOST_EVENT_CALL_STOPPED_ACK			= 0x86, /*134*/
-	SIGBOOST_EVENT_SYSTEM_RESTART			= 0x87, /*135*/
-	SIGBOOST_EVENT_SYSTEM_RESTART_ACK		= 0x88, /*136*/
-	/* Following IDs are ss7boost to sangoma_mgd only. */
-	SIGBOOST_EVENT_HEARTBEAT			= 0x89, /*137*/
-	SIGBOOST_EVENT_INSERT_CHECK_LOOP		= 0x8a, /*138*/
-	SIGBOOST_EVENT_REMOVE_CHECK_LOOP		= 0x8b, /*139*/
-	SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE		= 0x8c, /*140*/
-};
-enum	e_sigboost_release_cause_values
-{
-	SIGBOOST_RELEASE_CAUSE_UNDEFINED		= 0,
-	SIGBOOST_RELEASE_CAUSE_NORMAL			= 16,
-	SIGBOOST_RELEASE_CAUSE_BUSY			= 17,
-	/* probable elimination */
-	//SIGBOOST_RELEASE_CAUSE_BUSY			= 0x91, /* 145 */
-	//SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST	= 0x92, /* 146 */
-	//SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET		= 0x93, /* 147 */
-	//SIGBOOST_RELEASE_CAUSE_NOANSWER		= 0x94, /* 148 */
-};
-
-enum	e_sigboost_call_setup_ack_nack_cause_values
-{
-	SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY		= 117, /* unused Q.850 value */
-	SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY		= 118, /* unused Q.850 value */
-	SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER		= 28,
-	/* probable elimination */
-	//SIGBOOST_CALL_SETUP_RESERVED			= 0x00,
-	//SIGBOOST_CALL_SETUP_CIRCUIT_RESET		= 0x10,
-	//SIGBOOST_CALL_SETUP_NACK_CKT_START_TIMEOUT	= 0x11,
-	//SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP	= 0x17,
-};
-typedef enum {
-	M3UA_SPAN_SIGNALING_M3UA,
-	M3UA_SPAN_SIGNALING_SS7BOX,
-	
-} M3UA_TSpanSignaling;
-#define M3UA_SPAN_STRINGS "M3UA", "SS7BOX"
-FTDM_STR2ENUM_P(m3ua_str2span, m3ua_span2str, M3UA_TSpanSignaling)
-
-
-
-typedef enum {
-	FTDM_M3UA_RUNNING = (1 << 0)
-} ftdm_m3uat_flag_t;
-
-/*typedef struct m3ua_data {
-	m3uac_connection_t mcon;
-	m3uac_connection_t pcon;
-	fio_signal_cb_t signal_cb;
-	uint32_t flags;
-} m3ua_data_t;
-
-*/
-/*typedef struct mu3a_link {
-	ss7bc_connection_t mcon;
-	ss7bc_connection_t pcon;
-	fio_signal_cb_t signal_cb;
-	uint32_t flags;
-} ftdm_m3ua_data_t;
-*/
-
-ftdm_status_t m3ua_init(ftdm_io_interface_t **zint);
-ftdm_status_t m3ua_destroy(void);
-ftdm_status_t m3ua_start(ftdm_span_t *span);
-
-#ifdef __cplusplus
-}
-#endif
-
-/* For Emacs:
- * Local Variables:
- * mode:c
- * indent-tabs-mode:t
- * tab-width:4
- * c-basic-offset:4
- * End:
- * For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
- */
-
diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h
new file mode 100644
index 0000000000..7de015b72b
--- /dev/null
+++ b/libs/freetdm/src/include/private/ftdm_state.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2010, Sangoma Technologies
+ * Moises Silva <moy@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.
+ */
+
+#ifndef __FTDM_STATE_H__
+#define __FTDM_STATE_H__
+
+/*! \file
+ * \brief State handling definitions
+ * \note Most, if not all of the state handling functions assume you have a lock acquired. Touching the channel
+ *       state is a sensitive matter that requires checks and careful thought and is typically a process that
+ *       is not encapsulated within a single function, therefore the lock must be explicitly acquired by the 
+ *       caller (most of the time, signaling modules), process states, set a new state and process it, and 
+ *       finally unlock the channel. See docs/locking.txt fore more info
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	FTDM_CHANNEL_STATE_DOWN,
+	FTDM_CHANNEL_STATE_HOLD,
+	FTDM_CHANNEL_STATE_SUSPENDED,
+	FTDM_CHANNEL_STATE_DIALTONE,
+	FTDM_CHANNEL_STATE_COLLECT,
+	FTDM_CHANNEL_STATE_RING,
+	FTDM_CHANNEL_STATE_RINGING,
+	FTDM_CHANNEL_STATE_BUSY,
+	FTDM_CHANNEL_STATE_ATTN,
+	FTDM_CHANNEL_STATE_GENRING,
+	FTDM_CHANNEL_STATE_DIALING,
+	FTDM_CHANNEL_STATE_GET_CALLERID,
+	FTDM_CHANNEL_STATE_CALLWAITING,
+	FTDM_CHANNEL_STATE_RESTART,
+	FTDM_CHANNEL_STATE_PROCEED,
+	FTDM_CHANNEL_STATE_PROGRESS,
+	FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
+	FTDM_CHANNEL_STATE_UP,
+	FTDM_CHANNEL_STATE_IDLE,
+	FTDM_CHANNEL_STATE_TERMINATING,
+	FTDM_CHANNEL_STATE_CANCEL,
+	FTDM_CHANNEL_STATE_HANGUP,
+	FTDM_CHANNEL_STATE_HANGUP_COMPLETE,
+	FTDM_CHANNEL_STATE_IN_LOOP,
+	FTDM_CHANNEL_STATE_RESET,
+	FTDM_CHANNEL_STATE_INVALID
+} 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", \
+		"HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID"
+FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t)
+
+typedef struct {
+	const char *file;
+	const char *func;
+	int line;
+	ftdm_channel_state_t state; /*!< Current state (processed or not) */
+	ftdm_channel_state_t last_state; /*!< Previous state */
+	ftdm_time_t time; /*!< Time the state was set */
+	ftdm_time_t end_time; /*!< Time the state processing was completed */
+} ftdm_state_history_entry_t;
+
+typedef ftdm_status_t (*ftdm_channel_state_processor_t)(ftdm_channel_t *fchan);
+
+/*!
+ * \brief Process channel states by invoking the channel state processing routine
+ *        it will keep calling the processing routine while the state status
+ *        is FTDM_STATE_STATUS_NEW, it will not do anything otherwise
+ */
+FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan);
+
+FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *function, int line, ftdm_channel_t *fchan);
+#define ftdm_channel_complete_state(obj) _ftdm_channel_complete_state(__FILE__, __FUNCTION__, __LINE__, obj)
+FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state);
+
+/*!
+ * \brief Status of the current channel state 
+ * \note A given state goes thru several status (yes, states for the state!)
+ * The order is always FTDM_STATE_STATUS_NEW -> FTDM_STATE_STATUS_PROCESSED -> FTDM_STATUS_COMPLETED
+ * However, is possible to go from NEW -> COMPLETED directly when the signaling module explicitly changes 
+ * the state of the channel in the middle of processing the current state by calling the ftdm_set_state() API
+ *
+ * FTDM_STATE_STATUS_NEW - 
+ *   Someone just set the state of the channel, either the signaling module or the user (implicitly through a call API). 
+ *   This is accomplished by calling ftdm_channel_set_state() which changes the 'state' and 'last_state' memebers of 
+ *   the ftdm_channel_t structure.
+ *
+ * FTDM_STATE_STATUS_PROCESSED -
+ *   The signaling module did something based on the new state.
+ *
+ *   This is accomplished via ftdm_channel_advance_states()
+ *
+ *   When ftdm_channel_advance_states(), at the very least, if the channel has its state in FTDM_STATE_STATUS_NEW, it
+ *   will move to FTDM_STATE_STATUS_PROCESSED, depending on what the signaling module does during the processing
+ *   the state may move to FTDM_STATE_STATUS_COMPLETED right after or wait for a signaling specific event to complete it.
+ *   It is also possible that more state transitions occur during the execution of ftdm_channel_advance_states() if one
+ *   state processing/completion leads to another state change, the function will not return until the chain of events
+ *   lead to a state that is not in FTDM_STATE_STATUS_NEW
+ *
+ * FTDM_STATE_STATUS_COMPLETED - 
+ *   The signaling module completed the processing of the state and there is nothing further to be done for this state.
+ *
+ *   This is accomplished either explicitly by the signaling module by calling ftdm_channel_complete_state() or by
+ *   the signaling module implicitly by trying to set the state of the channel to a new state via ftdm_set_state()
+ *
+ *   When working with blocking channels (FTDM_CHANNEL_NONBLOCK flag not set), the user thread is signaled and unblocked 
+ *   so it can continue.
+ *
+ *   When a state moves to this status is also possible for a signal FTDM_SIGEVENT_INDICATION_COMPLETED to be delivered 
+ *   by the core if the state change was associated to an indication requested by the user, 
+ */
+typedef enum {
+	FTDM_STATE_STATUS_NEW,
+	FTDM_STATE_STATUS_PROCESSED,
+	FTDM_STATE_STATUS_COMPLETED,
+	FTDM_STATE_STATUS_INVALID
+} ftdm_state_status_t;
+#define CHANNEL_STATE_STATUS_STRINGS "NEW", "PROCESSED", "COMPLETED", "INVALID"
+FTDM_STR2ENUM_P(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t)
+
+typedef enum {
+	ZSM_NONE,
+	ZSM_UNACCEPTABLE,
+	ZSM_ACCEPTABLE
+} ftdm_state_map_type_t;
+
+typedef enum {
+	ZSD_INBOUND,
+	ZSD_OUTBOUND,
+} ftdm_state_direction_t;
+
+#define FTDM_MAP_NODE_SIZE 512
+#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2
+
+struct ftdm_state_map_node {
+	ftdm_state_direction_t direction;
+	ftdm_state_map_type_t type;
+	ftdm_channel_state_t check_states[FTDM_MAP_MAX];
+	ftdm_channel_state_t states[FTDM_MAP_MAX];
+};
+typedef struct ftdm_state_map_node ftdm_state_map_node_t;
+
+struct ftdm_state_map {
+	ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE];
+};
+typedef struct ftdm_state_map ftdm_state_map_t;
+
+/*!\brief Set the state for a channel (the channel must be locked when calling this function)
+ * \note Signaling modules should use ftdm_set_state macro instead
+ * \note If this function is called with the wait parameter set to a non-zero value, the recursivity
+ *       of the channel lock must be == 1 because the channel will be unlocked/locked when waiting */
+FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line,
+		ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait);
+
+/*!\brief Set the state of a channel immediately and implicitly complete the previous state if needed 
+ * \note FTDM_SIGEVENT_INDICATION_COMPLETED will be sent if the state change 
+ *       is associated to some indication (ie FTDM_CHANNEL_INDICATE_PROCEED)
+ * \note The channel must be locked when calling this function
+ * */
+FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line,
+			ftdm_channel_t *fchan, ftdm_channel_state_t state);
+#define ftdm_set_state(obj, s) _ftdm_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s);									\
+
+/*!\brief This macro is deprecated, signaling modules should always lock the channel themselves anyways since they must
+ * process first the user pending state changes then set a new state before releasing the lock 
+ * this macro is here for backwards compatibility, DO NOT USE IT in new code since it is *always* wrong to set
+ * a state in a signaling module without checking and processing the current state first (and for that you must lock the channel)
+ */
+#define ftdm_set_state_locked(obj, s) \
+	do { \
+		ftdm_channel_lock(obj); \
+		ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);									\
+		ftdm_channel_unlock(obj); \
+	} while(0);
+
+#define ftdm_set_state_r(obj, s, r) r = ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);
+
+#define ftdm_set_state_all(span, state) \
+	do { \
+		uint32_t _j; \
+		ftdm_mutex_lock((span)->mutex); \
+		for(_j = 1; _j <= (span)->chan_count; _j++) { \
+			if (!FTDM_IS_DCHAN(span->channels[_j])) { \
+				ftdm_set_state_locked((span->channels[_j]), state); \
+			} \
+		} \
+		ftdm_mutex_unlock((span)->mutex); \
+	} while (0);
+
+#ifdef __cplusplus
+} 
+#endif
+
+#endif
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */
diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h
index 5b9d05e6c6..2d683e3098 100644
--- a/libs/freetdm/src/include/private/ftdm_types.h
+++ b/libs/freetdm/src/include/private/ftdm_types.h
@@ -69,8 +69,6 @@ extern "C" {
 #define FTDM_END -1
 #define FTDM_ANY_STATE -1
 
-typedef uint64_t ftdm_time_t; 
-
 typedef enum {
 	FTDM_ENDIAN_BIG = 1,
 	FTDM_ENDIAN_LITTLE = -1
@@ -204,40 +202,6 @@ typedef enum {
 	FTDM_CHANNEL_FEATURE_IO_STATS = (1<<9), /*!< Channel supports IO statistics (HDLC channels only) */
 } ftdm_channel_feature_t;
 
-typedef enum {
-	FTDM_CHANNEL_STATE_DOWN,
-	FTDM_CHANNEL_STATE_HOLD,
-	FTDM_CHANNEL_STATE_SUSPENDED,
-	FTDM_CHANNEL_STATE_DIALTONE,
-	FTDM_CHANNEL_STATE_COLLECT,
-	FTDM_CHANNEL_STATE_RING,
-	FTDM_CHANNEL_STATE_RINGING,
-	FTDM_CHANNEL_STATE_BUSY,
-	FTDM_CHANNEL_STATE_ATTN,
-	FTDM_CHANNEL_STATE_GENRING,
-	FTDM_CHANNEL_STATE_DIALING,
-	FTDM_CHANNEL_STATE_GET_CALLERID,
-	FTDM_CHANNEL_STATE_CALLWAITING,
-	FTDM_CHANNEL_STATE_RESTART,
-	FTDM_CHANNEL_STATE_PROCEED,
-	FTDM_CHANNEL_STATE_PROGRESS,
-	FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
-	FTDM_CHANNEL_STATE_UP,
-	FTDM_CHANNEL_STATE_IDLE,
-	FTDM_CHANNEL_STATE_TERMINATING,
-	FTDM_CHANNEL_STATE_CANCEL,
-	FTDM_CHANNEL_STATE_HANGUP,
-	FTDM_CHANNEL_STATE_HANGUP_COMPLETE,
-	FTDM_CHANNEL_STATE_IN_LOOP,
-	FTDM_CHANNEL_STATE_RESET,
-	FTDM_CHANNEL_STATE_INVALID
-} 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", \
-		"HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID"
-FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_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)
@@ -260,9 +224,16 @@ FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channe
 #define FTDM_CHANNEL_OUTBOUND        (1ULL << 18)
 #define FTDM_CHANNEL_SUSPENDED       (1ULL << 19)
 #define FTDM_CHANNEL_3WAY            (1ULL << 20)
+
+/* this 3 flags are really nonsense used by boost module only, as soon
+ * as we deprecate/delete boost module we can get rid of them 
+ * ==================
+ * */
 #define FTDM_CHANNEL_PROGRESS        (1ULL << 21)
 #define FTDM_CHANNEL_MEDIA           (1ULL << 22)
 #define FTDM_CHANNEL_ANSWERED        (1ULL << 23)
+/* ================== */
+
 #define FTDM_CHANNEL_MUTE            (1ULL << 24)
 #define FTDM_CHANNEL_USE_RX_GAIN     (1ULL << 25)
 #define FTDM_CHANNEL_USE_TX_GAIN     (1ULL << 26)
@@ -273,33 +244,14 @@ FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channe
 #define FTDM_CHANNEL_TX_DISABLED     (1ULL << 31)
 /*!< The user knows about a call in this channel */
 #define FTDM_CHANNEL_CALL_STARTED    (1ULL << 32)
+/*!< The user wants non-blocking operations in the channel */
+#define FTDM_CHANNEL_NONBLOCK        (1ULL << 33)
+/*!< There is a pending acknowledge for an indication */
+#define FTDM_CHANNEL_IND_ACK_PENDING (1ULL << 34)
+/*!< There is someone blocking in the channel waiting for state completion */
+#define FTDM_CHANNEL_BLOCKING        (1ULL << 35)
 
-typedef enum {
-	ZSM_NONE,
-	ZSM_UNACCEPTABLE,
-	ZSM_ACCEPTABLE
-} ftdm_state_map_type_t;
-
-typedef enum {
-	ZSD_INBOUND,
-	ZSD_OUTBOUND,
-} ftdm_state_direction_t;
-
-#define FTDM_MAP_NODE_SIZE 512
-#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2
-
-struct ftdm_state_map_node {
-	ftdm_state_direction_t direction;
-	ftdm_state_map_type_t type;
-	ftdm_channel_state_t check_states[FTDM_MAP_MAX];
-	ftdm_channel_state_t states[FTDM_MAP_MAX];
-};
-typedef struct ftdm_state_map_node ftdm_state_map_node_t;
-
-struct ftdm_state_map {
-	ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE];
-};
-typedef struct ftdm_state_map ftdm_state_map_t;
+#include "ftdm_state.h"
 
 typedef enum ftdm_channel_hw_link_status {
 	FTDM_HW_LINK_DISCONNECTED = 0,
diff --git a/libs/freetdm/src/m3ua/mstm3ua.c b/libs/freetdm/src/m3ua/mstm3ua.c
deleted file mode 100644
index 1d8179c58d..0000000000
--- a/libs/freetdm/src/m3ua/mstm3ua.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* WARNING WORK IN PROGRESS
- *  mstm3ua.c
- *  mstss7d port
- *
- *  Created by Shane Burrell on 2/2/08.
- *  Copyright 2008 Shane Burrell. 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 "mstm3ua.h"
-
-
-
-
-
-int build_m3ua_hdr(unsigned char len,unsigned char *bytemsg)
-
-{
-   
-	*bytemsg++ = M_VERSION_REL1;  // 1 Verison
-	//bytemsg[1] = 0x00;  // 2 RESERVED
-	//bytemsg[2] = M_CLASS_XFER;  // 3 Msg Class
-    //SS7 BOX Kludge
-	*bytemsg++ = 0x01;  // 2 RESERVED
-	*bytemsg++ = 0x00;  // 2 RESERVED				
-	
-	*bytemsg++ = M_TYPE_DATA	;  // 4 Msg Type
-
-	*bytemsg++ = len;  // 5 Msg LENGTH  81  32bit field
-	*bytemsg++ = 0x00;  // 6
-	*bytemsg++ = 0x00;  // 7
-	*bytemsg++ = 0x00;  // 8
-	return(0);
-
-};
\ No newline at end of file
diff --git a/libs/freetdm/src/m3ua/mstm3ua.h b/libs/freetdm/src/m3ua/mstm3ua.h
deleted file mode 100644
index 13527dac35..0000000000
--- a/libs/freetdm/src/m3ua/mstm3ua.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- *  mstm3ua.h
- *  mstss7d
- *
- *  Created by Shane Burrell on 3/2/08.
- *  Copyright 2008 Shane Burrell. 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.
- */
-typedef unsigned long m3ua_ulong;
-typedef unsigned short m3ua_ushort;
-typedef unsigned char m3ua_uchar;
-
-typedef unsigned char u8;
-typedef unsigned short u16;	/* Note: multi-byte values are little-endian */
-typedef unsigned long u32;
-
-
-
-
-#define M_TAG_NETWORK_APPEARANCE	1
-#define	M_TAG_PROTOCOL_DATA		3
-#define M_TAG_INFO_STRING		4
-#define M_TAG_AFFECTED_DPC		5
-#define M_TAG_ROUTING_CONTEXT		6
-#define M_TAG_DIAGNOSTIC_INFORMATION	7
-#define M_TAG_HEARTBEAT_DATA		8
-#define M_TAG_UNAVAILABILITY_CAUSE	9
-#define M_TAG_REASON			10
-#define	M_TAG_TRAFFIC_MODE_TYPE		11
-#define M_TAG_ERROR_CODE		12
-#define	M_TAG_STATUS_TYPE		13
-#define M_TAG_CONGESTED_INDICATIONS	14
-
-#define M_VERSION_REL1   1
-
-#define M_CLASS_MGMT	0x00
-#define M_CLASS_XFER	0x01
-#define	M_CLASS_SSNM	0x02
-#define M_CLASS_ASPSM	0x03
-#define M_CLASS_ASPTM	0x04
-#define M_CLASS_RKM		0x09
-
-#define M_TYPE_ERR		(0|M_CLASS_MGMT
-
-#define M_TYPE_NTFY		(1|M_CLASS_XFER)
-#define M_TYPE_DATA		(1|M_CLASS_XFER)
-
-#define M_TYPE_DUNA		(1|M_CLASS_SSNM)
-#define M_TYPE_DAVA		(2|M_CLASS_SSNM)
-#define M_TYPE_DUAD		(3|M_CLASS_SSNM)
-#define M_TYPE_SCON		(4|M_CLASS_SSNM)
-#define M_TYPE_DUPU		(5|M_CLASS_SSNM)
-
-#define	M_TYPE_UP		(1|M_CLASS_ASPSM)
-#define	M_TYPE_DOWN		(2|M_CLASS_ASPSM)
-#define	M_TYPE_BEAT		(3|M_CLASS_ASPSM)
-#define	M_TYPE_UP_ACK		(4|M_CLASS_ASPSM)
-#define	M_TYPE_DOWN_ACK		(5|M_CLASS_ASPSM)
-#define	M_TYPE_BEAT_ACK		(6|M_CLASS_ASPSM)
-
-#define M_TYPE_ACTIVE		(1|M_CLASS_ASPTM)
-#define M_TYPE_INACTIVE		(2|M_CLASS_ASPTM)
-#define M_TYPE_ACTIVE_ACK	(3|M_CLASS_ASPTM)
-#define M_TYPE_INACTIVE_ACK	(4|M_CLASS_ASPTM)
-
-#define M_CLASS_MASK	0xff00
-#define	M_TYPE_MASK	0x00ff
-
diff --git a/libs/freetdm/src/m3ua_client.c b/libs/freetdm/src/m3ua_client.c
deleted file mode 100644
index 7608183896..0000000000
--- a/libs/freetdm/src/m3ua_client.c
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- *  m3ua_client.c
- *  freetdm
- *
- *  Created by Shane Burrell on 4/3/08.
- *  Copyright 2008 Shane Burrell. All rights reserved.
- *
- * 
-  * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic
- * 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.
- */
-
-#if HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#include "freetdm.h"
-#include <m3ua_client.h>
-
-
-#ifndef HAVE_GETHOSTBYNAME_R
-extern int gethostbyname_r (const char *__name,
-							struct hostent *__result_buf,
-							char *__buf, size_t __buflen,
-							struct hostent **__result,
-							int *__h_errnop);
-#endif
-
-struct m3uac_map {
-	uint32_t event_id;
-	const char *name;
-};
-
-static struct m3uac_map m3uac_table[] = {
-	{M3UA_EVENT_CALL_START, "CALL_START"},
-	{M3UA_EVENT_CALL_START_ACK, "CALL_START_ACK"},
-	{M3UA_EVENT_CALL_START_NACK, "CALL_START_NACK"},
-	{M3UA_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"},
-	{M3UA_EVENT_CALL_ANSWERED, "CALL_ANSWERED"},
-	{M3UA_EVENT_CALL_STOPPED, "CALL_STOPPED"},
-	{M3UA_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"},
-	{M3UA_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"},
-	{M3UA_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"},
-	{M3UA_EVENT_HEARTBEAT, "HEARTBEAT"},
-	{M3UA_EVENT_INSERT_CHECK_LOOP, "LOOP START"},
-	{M3UA_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"} 
-}; 
-
-
-
-static int create_conn_socket(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port)
-{
-	int rc;
-	struct hostent *result, *local_result;
-	char buf[512], local_buf[512];
-	int err = 0;
-
-	memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp));
-	memset(&mcon->local_hp, 0, sizeof(mcon->local_hp));
-	mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
- 
-	ftdm_log(FTDM_LOG_DEBUG, "Creating L=%s:%d R=%s:%d\n",
-			local_ip,local_port,ip,port);
-
-	if (mcon->socket >= 0) {
-		int flag;
-
-		flag = 1;
-		gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err);
-		gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &err);
-		if (result && local_result) {
-			mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype;
-			memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length);
-			mcon->remote_addr.sin_port = htons(port);
-
-			mcon->local_addr.sin_family = mcon->local_hp.h_addrtype;
-			memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length);
-			mcon->local_addr.sin_port = htons(local_port);
-
-
-			setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, (char *)&flag, sizeof(int));
-
-			rc=listen(mcon->socket,100);
-			if (rc) {
-			close(mcon->socket);
-			mcon->socket = -1;
-			
-			}
-		}
-	}
-
-	ftdm_mutex_create(&mcon->mutex);
-
-	return mcon->socket;
-}
-
-int m3uac_connection_close(m3uac_connection_t *mcon)
-{
-	if (mcon->socket > -1) {
-		close(mcon->socket);
-	}
-
-	ftdm_mutex_lock(mcon->mutex);
-	ftdm_mutex_unlock(mcon->mutex);
-	ftdm_mutex_destroy(&mcon->mutex);
-	memset(mcon, 0, sizeof(*mcon));
-	mcon->socket = -1;
-
-	return 0;
-}
-
-int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port)
-{
-	create_conn_socket(mcon, local_ip, local_port, ip, port);
-	return mcon->socket;
-}
-
-
-int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause)
-{
-    m3uac_event_t oevent;
-    int retry = 5;
-
-    m3uac_event_init(&oevent, cmd, chan, span);
-    oevent.release_cause = cause;
-
-	if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART) {
-		mcon->rxseq_reset = 1;
-		mcon->txseq = 0;
-		mcon->rxseq = 0;
-		mcon->txwindow = 0;
-	}
-
-    if (id >= 0) {
-        oevent.call_setup_id = id;
-    }
-
-    while (m3uac_connection_write(mcon, &oevent) <= 0) {
-        if (--retry <= 0) {
-            ftdm_log(FTDM_LOG_CRIT, "Failed to tx on M3UA socket: %s\n", strerror(errno));
-            return -1;
-        } else {
-            ftdm_log(FTDM_LOG_WARNING, "Failed to tx on M3UA socket: %s :retry %i\n", strerror(errno), retry);
-			ftdm_sleep(1);
-        }
-    }
-
-    return 0;
-}
-
-
-
-m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration)
-{
-	unsigned int fromlen = sizeof(struct sockaddr_in);
-	int bytes = 0;
-
-	bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, 
-					 (struct sockaddr *) &mcon->local_addr, &fromlen);
-
-	if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) {
-
-		if (mcon->rxseq_reset) {
-			if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) {
-				ftdm_log(FTDM_LOG_DEBUG, "Rx sync ok\n");
-				mcon->rxseq = mcon->event.fseqno;
-				return &mcon->event;
-			}
-			errno=EAGAIN;
-			ftdm_log(FTDM_LOG_DEBUG, "Waiting for rx sync...\n");
-			return NULL;
-		}
-		
-		mcon->txwindow = mcon->txseq - mcon->event.bseqno;
-		mcon->rxseq++;
-
-		if (mcon->rxseq != mcon->event.fseqno) {
-			ftdm_log(FTDM_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno);
-			return NULL;
-		}
-
-		return &mcon->event;
-	} else {
-		if (iteration == 0) {
-			ftdm_log(FTDM_LOG_CRIT, "Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event));
-			return NULL;
-		}
-	}
-
-	return NULL;
-}
-
-m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration)
-{
-	unsigned int fromlen = sizeof(struct sockaddr_in);
-	int bytes = 0;
-
-	bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen);
-
-	if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) {
-		return &mcon->event;
-	} else {
-		if (iteration == 0) {
-			ftdm_log(FTDM_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event));
-			return NULL;
-		}
-	}
-
-	return NULL;
-}
-
-
-int m3uac_connection_write(m3uac_connection_t *mcon, ss7bc_event_t *event)
-{
-	int err;
-
-	if (!event || mcon->socket < 0 || !mcon->mutex) {
-		ftdm_log(FTDM_LOG_DEBUG,  "Critical Error: No Event Device\n");
-		return -EINVAL;
-	}
-
-	if (event->span > 16 || event->chan > 31) {
-		ftdm_log(FTDM_LOG_CRIT, "Critical Error: TX Cmd=%s Invalid Span=%i Chan=%i\n", m3uac_event_id_name(event->event_id), event->span,event->chan);
-		return -1;
-	}
-
-	gettimeofday(&event->tv,NULL);
-	
-	ftdm_mutex_lock(mcon->mutex);
-	event->fseqno = mcon->txseq++;
-	event->bseqno = mcon->rxseq;
-	err = sendto(mcon->socket, event, sizeof(m3uac_event_t), 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr));
-	ftdm_mutex_unlock(mcon->mutex);
-
-	if (err != sizeof(m3uac_event_t)) {
-		err = -1;
-	}
-	
- 	ftdm_log(FTDM_LOG_DEBUG, "TX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
-			m3uac_event_id_name(event->event_id),
-			event->event_id,
-			event->span+1,
-			event->chan+1,
-			event->release_cause,
-			event->call_setup_id,
-			event->fseqno,
-			(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
-			(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
-			);
-
-	return err;
-}
-
-void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id)
-{
-	memset(event, 0, sizeof(m3uac_event_t));
-	event->event_id = M3UA_EVENT_CALL_START;
-
-	if (calling) {
-		strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1);
-		event->calling_number_digits_count = strlen(calling);
-	}
-
-	if (called) {
-		strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1);
-		event->called_number_digits_count = strlen(called);
-	}
-		
-	event->call_setup_id = setup_id;
-	
-}
-
-void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span) 
-{
-	memset(event, 0, sizeof(ss7bc_event_t));
-	event->event_id = event_id;
-	event->chan = chan;
-	event->span = span;
-}
-
-const char *m3uac_event_id_name(uint32_t event_id)
-{
-	unsigned int x;
-	const char *ret = NULL;
-
-	for (x = 0 ; x < sizeof(m3uac_table)/sizeof(struct m3uac_map); x++) {
-		if (m3uac_table[x].event_id == event_id) {
-			ret = m3uac_table[x].name;
-			break;
-		}
-	}
-
-	return ret;
-}
-
-/* For Emacs:
- * Local Variables:
- * mode:c
- * indent-tabs-mode:t
- * tab-width:4
- * c-basic-offset:4
- * End:
- * For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
- */
-
-
diff --git a/libs/freetdm/src/m3ua_client.h b/libs/freetdm/src/m3ua_client.h
deleted file mode 100644
index e451156a41..0000000000
--- a/libs/freetdm/src/m3ua_client.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- *  m3ua_client.h
- *  freetdm
- *
- *  Created by Shane Burrell on 4/3/08.
- *  Copyright 2008 Shane Burrell. All rights reserved.
- *
- * Copyright (c) 2007, Anthony Minessale II, Nenad Corbic
- * 
- * 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 <ctype.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-// Fix this for portability
-#include <sctp.h>
-//#include <netinet/sctp.h>
-#include <arpa/inet.h>
-#include <stdarg.h>
-#include <netdb.h>
-//#include <sigboost.h>
-#include <sys/time.h>
-
-#define MAX_DIALED_DIGITS	31
-#define MAX_CALLING_NAME	31
-
-/* Next two defines are used to create the range of values for call_setup_id
- * in the t_sigboost structure.
- * 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */
-#define CORE_MAX_SPANS 		200
-#define CORE_MAX_CHAN_PER_SPAN 	30
-#define MAX_PENDING_CALLS 	CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN
-/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */
-#define SIZE_RDNIS		80
-
-//#undef MSGWINDOW
-#define MSGWINDOW
-
-
-typedef struct
-{
-	uint32_t	event_id;
-	uint32_t	fseqno;
-#ifdef MSGWINDOW
-	uint32_t	bseqno;
-#endif
-	uint16_t	call_setup_id;
-	uint32_t	trunk_group;
-	uint32_t	span;
-	uint32_t	chan;
-	uint8_t		called_number_digits_count;
-	char		called_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */
-	uint8_t		calling_number_digits_count; /* it's an array */
-	char		calling_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */
-	uint8_t		release_cause;
-	struct timeval  tv;
-	/* ref. Q.931 Table 4-11 and Q.951 Section 3 */
-	uint8_t		calling_number_screening_ind;
-	uint8_t		calling_number_presentation;
-	char		redirection_string [SIZE_RDNIS]; /* it's a null terminated string */
-	
-} t_m3ua;
-
-typedef t_m3ua m3uac_event_t;
-typedef uint32_t m3uac_event_id_t;
-
-
-typedef struct m3uac_ip_cfg
-{
-	char local_ip[25];
-	int local_port;
-	char remote_ip[25];
-	int remote_port;
-}m3uac_ip_cfg_t;
-
-struct m3uac_connection {
-	ftdm_socket_t socket;
-	struct sockaddr_in local_addr;
-	struct sockaddr_in remote_addr;
-	m3uac_event_t event;
-	struct hostent remote_hp;
-	struct hostent local_hp;
-	unsigned int flags;
-	ftdm_mutex_t *mutex;
-	FILE *log;
-	unsigned int txseq;
-	unsigned int rxseq;
-	unsigned int txwindow;
-	unsigned int rxseq_reset;
-	m3uac_ip_cfg_t cfg;
-	uint32_t hb_elapsed;
-	int up;
-};
-
-typedef enum {
-	MSU_FLAG_EVENT = (1 << 0)
-} m3uac_flag_t;
-
-typedef struct m3uac_connection m3uac_connection_t;
-
-static inline void sctp_no_nagle(int socket)
-{
-    //int flag = 1;
-    //setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int));
-}
-
-int m3uac_connection_close(m3uac_connection_t *mcon);
-int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port);
-m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration);
-m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration);
-int m3uac_connection_write(m3uac_connection_t *mcon, m3uac_event_t *event);
-void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span);
-void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id);
-const char *m3uac_event_id_name(uint32_t event_id);
-int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause);
-
-
-
-
-/* For Emacs:
- * Local Variables:
- * mode:c
- * indent-tabs-mode:t
- * tab-width:4
- * c-basic-offset:4
- * End:
- * For VIM:
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
- */
diff --git a/libs/freetdm/src/testm3ua.c b/libs/freetdm/src/testm3ua.c
deleted file mode 100644
index 5848470e7a..0000000000
--- a/libs/freetdm/src/testm3ua.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  testm3ua.c
- *  freetdm
- *
- *  Created by Shane Burrell on 4/8/08.
- *  Copyright 2008 __MyCompanyName__. All rights reserved.
- *
- */
-
-#include "testm3ua.h"
-#include "freetdm.h"
-#include "ftdm_m3ua.h"
-
-static FIO_SIGNAL_CB_FUNCTION(on_signal)
-{
-	return FTDM_FAIL;
-}
-
-int main(int argc, char *argv[])
-{
-	ftdm_span_t *span;
-	//m3ua_data_t *data;
-
-	ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
-
-	if (argc < 5) {
-		printf("more args needed\n");
-		exit(-1);
-	}
-
-	if (ftdm_global_init() != FTDM_SUCCESS) {
-		fprintf(stderr, "Error loading FreeTDM\n");
-		exit(-1);
-	}
-
-	printf("FreeTDM loaded\n");
-
-	if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) {
-		fprintf(stderr, "Error finding FreeTDM span\n");
-		goto done;
-	}
-	
-
-	if (ftdm_m3ua_configure_span(span) == FTDM_SUCCESS) {
-		//data = span->signal_data;
-		ftdm_m3ua_start(span);
-	} else {
-		fprintf(stderr, "Error starting M3UA\n");
-		goto done;
-	}
-
-	//while(ftdm_test_flag(data, FTDM_M3UA_RUNNING)) {
-	//	ftdm_sleep(1 * 1000);
-	//}
-
- done:
-
-	ftdm_global_destroy();
-
-}
diff --git a/libs/freetdm/src/testr2.c b/libs/freetdm/src/testr2.c
index 8ac90c59fd..72d98020bc 100644
--- a/libs/freetdm/src/testr2.c
+++ b/libs/freetdm/src/testr2.c
@@ -2,78 +2,158 @@
 #include <signal.h>
 #include <stdlib.h>
 
-static int R = 0;
-static ftdm_mutex_t *mutex = NULL;
+static volatile int running = 0;
+static ftdm_mutex_t *the_mutex = NULL;
+static ftdm_channel_t *fchan = NULL;
+static ftdm_channel_indication_t indication = FTDM_CHANNEL_INDICATE_NONE;
 
 static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
 {
 	int chanid = ftdm_channel_get_ph_id(sigmsg->channel);
-    ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), chanid);
-    return FTDM_SUCCESS;
+	ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), chanid);
+	switch (sigmsg->event_id) {
+	case FTDM_SIGEVENT_START:
+		{
+			ftdm_mutex_lock(the_mutex);
+			if (!fchan) {
+				fchan = sigmsg->channel;
+				indication = FTDM_CHANNEL_INDICATE_PROCEED;
+			}
+			ftdm_mutex_unlock(the_mutex);
+		}
+		break;
+	case FTDM_SIGEVENT_INDICATION_COMPLETED:
+		{
+			ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE;
+			if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROCEED) {
+				ftdm_log(FTDM_LOG_DEBUG, "Proceed indication result = %d\n", sigmsg->ev_data.indication_completed.status);
+				ind = FTDM_CHANNEL_INDICATE_PROGRESS;
+			} else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS) {
+				ftdm_log(FTDM_LOG_DEBUG, "Progress indication result = %d\n", sigmsg->ev_data.indication_completed.status);
+				ind = FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA;
+			} else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA) {
+				ftdm_log(FTDM_LOG_DEBUG, "Progress media indication result = %d\n", sigmsg->ev_data.indication_completed.status);
+				ind = FTDM_CHANNEL_INDICATE_ANSWER;
+			} else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_ANSWER) {
+				ftdm_log(FTDM_LOG_DEBUG, "Answer indication result = %d\n", sigmsg->ev_data.indication_completed.status);
+			} else {
+				ftdm_log(FTDM_LOG_DEBUG, "Unexpected indication, result = %d\n", sigmsg->ev_data.indication_completed.status);
+				exit(1);
+			}
+			ftdm_mutex_lock(the_mutex);
+			if (fchan) {
+				indication = ind;
+			}
+			ftdm_mutex_unlock(the_mutex);
+		}
+		break;
+	case FTDM_SIGEVENT_STOP:
+		{
+			ftdm_channel_call_hangup(sigmsg->channel);
+		}
+		break;
+	case FTDM_SIGEVENT_RELEASED:
+		{
+			ftdm_mutex_lock(the_mutex);
+			if (fchan && fchan == sigmsg->channel) {
+				fchan = NULL;
+			}
+			ftdm_mutex_unlock(the_mutex);
+		}
+		break;
+	default:
+		break;
+	}
+	return FTDM_SUCCESS;
 }
 
-static void handle_SIGINT(int sig)
+static void stop_test(int sig)
 {
-	ftdm_mutex_lock(mutex);
-	R = 0;
-	ftdm_mutex_unlock(mutex);
-	return;
+	running = 0;
 }
 
 int main(int argc, char *argv[])
 {
 	ftdm_span_t *span;
-	ftdm_mutex_create(&mutex);
-
-	ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
+	ftdm_conf_parameter_t parameters[20];
+	
+	ftdm_mutex_create(&the_mutex);
 
 	if (argc < 2) {
 		printf("umm no\n");
-		exit(-1);
+		exit(1);
 	}
 
+	ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
+
 	if (ftdm_global_init() != FTDM_SUCCESS) {
 		fprintf(stderr, "Error loading FreeTDM\n");
-		exit(-1);
+		exit(1);
 	}
 
+	ftdm_global_configuration();
+
 	printf("FreeTDM loaded\n");
 
-	if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) {
-		fprintf(stderr, "Error finding FreeTDM span\n");
+	if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) {
+		fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]);
 		goto done;
 	}
 	
+	/* testing non-blocking operation */
+	//ftdm_span_set_blocking_mode(span, FTDM_FALSE);
 
+	parameters[0].var = "variant";
+	parameters[0].val = "br";
 
-	if (ftdm_configure_span(span, "r2", on_r2_signal,
-						   "variant", "mx",
-						   "max_ani", 10,
-						   "max_dnis", 4,
-						   "logging", "all",
-						   FTDM_TAG_END) == FTDM_SUCCESS) {
-						   
+	parameters[1].var = "max_ani";
+	parameters[1].val = "4";
 
+	parameters[2].var = "max_dnis";
+	parameters[2].val = "4";
+
+	parameters[3].var = "logging";
+	parameters[3].val = "all";
+
+	parameters[4].var = NULL;
+	parameters[4].val = NULL;
+
+	if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, parameters) == FTDM_SUCCESS) {
 		ftdm_span_start(span);
 	} else {
 		fprintf(stderr, "Error starting R2 span\n");
 		goto done;
 	}
 
-	signal(SIGINT, handle_SIGINT);
-	ftdm_mutex_lock(mutex);
-	R = 1;
-	ftdm_mutex_unlock(mutex);
-	while(R) {
-		ftdm_sleep(1 * 1000);
+	running = 1;
+	signal(SIGINT, stop_test);
+	while(running) {
+		ftdm_sleep(20);
+		if (fchan && indication != FTDM_CHANNEL_INDICATE_NONE) {
+			ftdm_channel_t *lchan = NULL;
+			ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE;
+			ftdm_time_t start, stop, diff;
+
+			ftdm_mutex_lock(the_mutex);
+			ind = indication;
+			indication = FTDM_CHANNEL_INDICATE_NONE;
+			lchan = fchan;
+			ftdm_mutex_unlock(the_mutex);
+
+			start = ftdm_current_time_in_ms();
+			ftdm_channel_call_indicate(lchan, ind);
+			stop = ftdm_current_time_in_ms();
+			diff = stop - start;
+			ftdm_log(FTDM_LOG_DEBUG, "Setting indication %s took %llums\n", 
+					ftdm_channel_indication2str(ind), diff);
+		}
 	}
 
- done:
+done:
 
 	ftdm_global_destroy();
 
-	return 1;
-
+	return 0;
 }
 
 /* For Emacs: