diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.c b/src/mod/applications/mod_spandsp/mod_spandsp.c
index 8cfe9d1e24..f72708e9a0 100644
--- a/src/mod/applications/mod_spandsp/mod_spandsp.c
+++ b/src/mod/applications/mod_spandsp/mod_spandsp.c
@@ -34,6 +34,8 @@
  *
  */
 
+
+#define MY_EVENT_TDD_SEND_MESSAGE "TDD::SEND_MESSAGE"
 #include "mod_spandsp.h"
 #include <spandsp/version.h>
 #include "mod_spandsp_modem.h"
@@ -51,7 +53,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init);
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_spandsp_shutdown);
 SWITCH_MODULE_DEFINITION(mod_spandsp, mod_spandsp_init, mod_spandsp_shutdown, NULL);
 
-static switch_event_node_t *NODE = NULL;
 
 SWITCH_STANDARD_APP(spanfax_tx_function)
 {
@@ -74,6 +75,47 @@ SWITCH_STANDARD_APP(stop_dtmf_session_function)
 }
 
 
+SWITCH_STANDARD_APP(tdd_encode_function)
+{
+    char *text = (char *) data;
+
+    if (!zstr(text)) {
+        spandsp_tdd_encode_session(session, text);
+    } else {
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing text data\n");
+    }
+}
+
+SWITCH_STANDARD_APP(tdd_send_function)
+{
+    char *text = (char *) data;
+
+    if (!zstr(text)) {
+        spandsp_tdd_send_session(session, text);
+    } else {
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing text data\n");
+    }
+}
+
+SWITCH_STANDARD_APP(stop_tdd_encode_function)
+{
+	spandsp_stop_tdd_encode_session(session);
+}
+
+
+
+
+SWITCH_STANDARD_APP(tdd_decode_function)
+{
+    spandsp_tdd_decode_session(session);
+}
+
+SWITCH_STANDARD_APP(stop_tdd_decode_function)
+{
+	spandsp_stop_tdd_decode_session(session);
+}
+
+
 SWITCH_STANDARD_APP(spandsp_fax_detect_session_function)
 {
     int argc = 0;
@@ -117,6 +159,36 @@ SWITCH_STANDARD_APP(spandsp_stop_fax_detect_session_function)
     spandsp_fax_stop_detect_session(session);
 }
 
+static void tdd_event_handler(switch_event_t *event)
+{
+    const char *uuid = switch_event_get_header(event, "tdd-uuid");
+    const char *message = switch_event_get_body(event);
+    switch_core_session_t *session;
+
+    if (zstr(message)) {
+        message = switch_event_get_header(event, "tdd-message");
+    }
+
+    if (zstr(message)) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No message for tdd handler\n");   
+        return;
+    }
+
+    if (zstr(uuid)) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No uuid for tdd handler\n");   
+        return;
+    }
+
+    if ((session = switch_core_session_locate(uuid))) {
+
+        spandsp_tdd_encode_session(session, message);
+
+        switch_core_session_rwunlock(session);
+    } else {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No session for supplied uuid.\n");   
+    }
+}
+
 static void event_handler(switch_event_t *event)
 {
     load_configuration(1);
@@ -543,6 +615,16 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init)
 	SWITCH_ADD_APP(app_interface, "spandsp_stop_dtmf", "stop inband dtmf", "Stop detecting inband dtmf.", stop_dtmf_session_function, "", SAF_NONE);
 	SWITCH_ADD_APP(app_interface, "spandsp_start_dtmf", "Detect dtmf", "Detect inband dtmf on the session", dtmf_session_function, "", SAF_MEDIA_TAP);
 
+
+	SWITCH_ADD_APP(app_interface, "spandsp_stop_send_tdd", "stop sending tdd", "", stop_tdd_encode_function, "", SAF_NONE);
+	SWITCH_ADD_APP(app_interface, "spandsp_send_tdd", "Send TDD data", "Send TDD data", tdd_encode_function, "", SAF_MEDIA_TAP);
+
+	SWITCH_ADD_APP(app_interface, "spandsp_stop_detect_tdd", "stop sending tdd", "", stop_tdd_decode_function, "", SAF_NONE);
+	SWITCH_ADD_APP(app_interface, "spandsp_detect_tdd", "Detect TDD data", "Detect TDD data", tdd_decode_function, "", SAF_MEDIA_TAP);
+
+
+	SWITCH_ADD_APP(app_interface, "spandsp_send_tdd", "Send TDD data", "Send TDD data", tdd_send_function, "", SAF_NONE);
+
 	SWITCH_ADD_APP(app_interface, "spandsp_start_fax_detect", "start fax detect", "start fax detect", spandsp_fax_detect_session_function, 
 				   "<app>[ <arg>][ <timeout>][ <tone_type>]", SAF_NONE);
 
@@ -562,13 +644,18 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init)
         SWITCH_ADD_API(api_interface, "start_tone_detect", "Start background tone detection with cadence", start_tone_detect_api, "[name]");
         SWITCH_ADD_API(api_interface, "stop_tone_detect", "Stop background tone detection with cadence", stop_tone_detect_api, "");
 	}
-
-	if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &NODE) != SWITCH_STATUS_SUCCESS)) {
+    
+	if ((switch_event_bind(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL) != SWITCH_STATUS_SUCCESS)) {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our reloadxml handler!\n");
 		/* Not such severe to prevent loading */
 	}
 
 
+	if (switch_event_bind(modname, SWITCH_EVENT_CUSTOM, MY_EVENT_TDD_SEND_MESSAGE, tdd_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
+	}
+
+
 	modem_global_init(module_interface, pool);
 
 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "mod_spandsp loaded, using spandsp library version [%s]\n", SPANDSP_RELEASE_DATETIME_STRING);
@@ -578,7 +665,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_spandsp_init)
 
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_spandsp_shutdown)
 {
-	switch_event_unbind(&NODE);
+	switch_event_unbind_callback(event_handler);
+	switch_event_unbind_callback(tdd_event_handler);
+
 
 	mod_spandsp_fax_shutdown();
 	mod_spandsp_dsp_shutdown();
diff --git a/src/mod/applications/mod_spandsp/mod_spandsp.h b/src/mod/applications/mod_spandsp/mod_spandsp.h
index d4f388dd36..7e1eb49fa8 100644
--- a/src/mod/applications/mod_spandsp/mod_spandsp.h
+++ b/src/mod/applications/mod_spandsp/mod_spandsp.h
@@ -140,3 +140,11 @@ switch_status_t spandsp_fax_stop_detect_session(switch_core_session_t *session);
 void spanfax_log_message(void *user_data, int level, const char *msg);
 switch_status_t load_configuration(switch_bool_t reload);
 void mod_spandsp_indicate_data(switch_core_session_t *session, switch_bool_t self, switch_bool_t on);
+
+switch_status_t spandsp_stop_tdd_encode_session(switch_core_session_t *session);
+switch_status_t spandsp_tdd_encode_session(switch_core_session_t *session, const char *text);
+
+
+switch_status_t spandsp_stop_tdd_decode_session(switch_core_session_t *session);
+switch_status_t spandsp_tdd_decode_session(switch_core_session_t *session);
+switch_status_t spandsp_tdd_send_session(switch_core_session_t *session, const char *text);
diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c
index cc4d4964e2..57ba4547f9 100644
--- a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c
+++ b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c
@@ -35,6 +35,285 @@
 
 #include "mod_spandsp.h"
 
+#define TDD_LEAD 10
+
+typedef struct {
+	switch_core_session_t *session;
+	v18_state_t *tdd_state;
+    int head_lead;
+    int tail_lead;
+} switch_tdd_t;
+
+static switch_bool_t tdd_encode_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+	switch_tdd_t *pvt = (switch_tdd_t *) user_data;
+	switch_frame_t *frame = NULL;
+	switch_bool_t r = SWITCH_TRUE;
+
+	switch (type) {
+	case SWITCH_ABC_TYPE_INIT: {
+		break;
+	}
+	case SWITCH_ABC_TYPE_CLOSE:
+		if (pvt->tdd_state) {
+			v18_free(pvt->tdd_state);
+		}
+		break;
+	case SWITCH_ABC_TYPE_WRITE_REPLACE:
+		if ((frame = switch_core_media_bug_get_write_replace_frame(bug))) {
+			int len;
+
+            if (pvt->tail_lead) {
+                if (!--pvt->tail_lead) {
+                    r = SWITCH_FALSE;
+                }
+                memset(frame->data, 0, frame->datalen);
+
+            } else if (pvt->head_lead) {
+                pvt->head_lead--;
+                memset(frame->data, 0, frame->datalen);
+            } else {
+                len = v18_tx(pvt->tdd_state, frame->data, frame->samples);
+
+                if (!len) {
+                    pvt->tail_lead = TDD_LEAD;
+                }
+            }
+
+			switch_core_media_bug_set_write_replace_frame(bug, frame);
+		}
+		break;
+	case SWITCH_ABC_TYPE_WRITE:
+	default:
+		break;
+	}
+
+	return r;
+}
+
+switch_status_t spandsp_stop_tdd_encode_session(switch_core_session_t *session)
+{
+	switch_media_bug_t *bug;
+	switch_channel_t *channel = switch_core_session_get_channel(session);
+
+	if ((bug = switch_channel_get_private(channel, "tdd_encode"))) {
+		switch_channel_set_private(channel, "tdd_encode", NULL);
+		switch_core_media_bug_remove(session, &bug);
+		return SWITCH_STATUS_SUCCESS;
+	}
+	return SWITCH_STATUS_FALSE;
+}
+
+static void put_text_msg(void *user_data, const uint8_t *msg, int len)
+{
+    switch_tdd_t *pvt = (switch_tdd_t *) user_data;
+    switch_event_t *event, *clone;
+    switch_channel_t *channel = switch_core_session_get_channel(pvt->session);
+    switch_core_session_t *other_session;
+
+
+    switch_channel_add_variable_var_check(channel, "tdd_messages", (char *)msg, SWITCH_FALSE, SWITCH_STACK_PUSH);
+
+    if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
+        
+        switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", "mod_spandsp");
+        switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", "tdd");
+        switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "TDD MESSAGE");
+        switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(pvt->session));
+        switch_event_add_body(event, (char *)msg);
+
+        if (switch_core_session_get_partner(pvt->session, &other_session) == SWITCH_STATUS_SUCCESS) {
+        
+            if (switch_event_dup(&clone, event) == SWITCH_STATUS_SUCCESS) {
+                switch_core_session_receive_event(other_session, &clone);
+            }
+
+            if (switch_event_dup(&clone, event) == SWITCH_STATUS_SUCCESS) {
+                switch_core_session_queue_event(other_session, &clone);
+            }
+
+            switch_core_session_rwunlock(other_session);
+        }
+
+        switch_event_fire(&event);
+
+
+    }
+    
+    switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "%s got TDD Message [%s]\n", switch_channel_get_name(channel), (char *)msg);
+
+}
+
+switch_status_t spandsp_tdd_send_session(switch_core_session_t *session, const char *text)
+{
+    v18_state_t *tdd_state;
+    switch_frame_t *read_frame, write_frame = { 0 };
+    uint8_t write_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
+	switch_codec_implementation_t read_impl = { 0 };
+	switch_codec_t write_codec = { 0 };
+    switch_channel_t *channel = switch_core_session_get_channel(session);
+    switch_status_t status;
+
+	switch_core_session_get_read_impl(session, &read_impl);
+    
+    if (switch_core_codec_init(&write_codec,
+                               "L16",
+                               NULL,
+                               read_impl.actual_samples_per_second,
+                               read_impl.microseconds_per_packet / 1000,
+                               read_impl.number_of_channels,
+                               SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
+                               switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
+        write_frame.data = write_buf;
+        write_frame.buflen = sizeof(write_buf);
+        write_frame.datalen = read_impl.decoded_bytes_per_packet;
+        write_frame.samples = write_frame.datalen / 2;
+        write_frame.codec = &write_codec;
+        switch_core_session_set_read_codec(session, &write_codec);
+    } else {
+        return SWITCH_STATUS_FALSE;
+    }
+
+    tdd_state = v18_init(NULL, TRUE, V18_MODE_5BIT_45, put_text_msg, NULL);
+
+
+	v18_put(tdd_state, text, -1);
+
+    while(switch_channel_ready(channel)) {
+		status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
+
+		if (!SWITCH_READ_ACCEPTABLE(status)) {
+			break;
+		}
+        
+
+        if (!v18_tx(tdd_state, (void *)write_buf, write_frame.samples)) {
+            break;
+        }
+
+        if (switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
+            break;
+        }
+
+    }
+
+    switch_core_codec_destroy(&write_codec);
+    switch_core_session_set_read_codec(session, NULL);
+
+    v18_free(tdd_state);
+
+    return SWITCH_STATUS_SUCCESS;
+}
+
+
+switch_status_t spandsp_tdd_encode_session(switch_core_session_t *session, const char *text)
+{
+	switch_channel_t *channel = switch_core_session_get_channel(session);
+	switch_media_bug_t *bug;
+	switch_status_t status;
+	switch_tdd_t *pvt;
+	//switch_codec_implementation_t read_impl = { 0 };
+
+	//switch_core_session_get_read_impl(session, &read_impl);
+
+	if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) {
+		return SWITCH_STATUS_MEMERR;
+	}
+
+	pvt->session = session;
+	pvt->tdd_state = v18_init(NULL, TRUE, V18_MODE_5BIT_45, put_text_msg, NULL);
+    pvt->head_lead = TDD_LEAD;
+
+	v18_put(pvt->tdd_state, text, -1);
+
+	if ((status = switch_core_media_bug_add(session, "spandsp_tdd_encode", NULL,
+                                            tdd_encode_callback, pvt, 0, SMBF_WRITE_REPLACE | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
+		v18_free(pvt->tdd_state);
+		return status;
+	}
+
+	switch_channel_set_private(channel, "tdd_encode", bug);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+
+///XXX
+static switch_bool_t tdd_decode_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+	switch_tdd_t *pvt = (switch_tdd_t *) user_data;
+	switch_frame_t *frame = NULL;
+	switch_bool_t r = SWITCH_TRUE;
+
+	switch (type) {
+	case SWITCH_ABC_TYPE_INIT: {
+		break;
+	}
+	case SWITCH_ABC_TYPE_CLOSE:
+		if (pvt->tdd_state) {
+			v18_free(pvt->tdd_state);
+		}
+		break;
+	case SWITCH_ABC_TYPE_READ_REPLACE:
+		if ((frame = switch_core_media_bug_get_read_replace_frame(bug))) {
+
+            v18_rx(pvt->tdd_state, frame->data, frame->samples);
+
+			switch_core_media_bug_set_read_replace_frame(bug, frame);
+		}
+		break;
+	case SWITCH_ABC_TYPE_WRITE:
+	default:
+		break;
+	}
+
+	return r;
+}
+
+switch_status_t spandsp_stop_tdd_decode_session(switch_core_session_t *session)
+{
+	switch_media_bug_t *bug;
+	switch_channel_t *channel = switch_core_session_get_channel(session);
+
+	if ((bug = switch_channel_get_private(channel, "tdd_decode"))) {
+		switch_channel_set_private(channel, "tdd_decode", NULL);
+		switch_core_media_bug_remove(session, &bug);
+		return SWITCH_STATUS_SUCCESS;
+	}
+	return SWITCH_STATUS_FALSE;
+}
+
+switch_status_t spandsp_tdd_decode_session(switch_core_session_t *session)
+{
+	switch_channel_t *channel = switch_core_session_get_channel(session);
+	switch_media_bug_t *bug;
+	switch_status_t status;
+	switch_tdd_t *pvt;
+	//switch_codec_implementation_t read_impl = { 0 };
+
+	//switch_core_session_get_read_impl(session, &read_impl);
+
+	if (!(pvt = switch_core_session_alloc(session, sizeof(*pvt)))) {
+		return SWITCH_STATUS_MEMERR;
+	}
+
+	pvt->session = session;
+	pvt->tdd_state = v18_init(NULL, FALSE, V18_MODE_5BIT_45, put_text_msg, pvt);
+
+	if ((status = switch_core_media_bug_add(session, "spandsp_tdd_decode", NULL,
+                                            tdd_decode_callback, pvt, 0, SMBF_READ_REPLACE | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
+		v18_free(pvt->tdd_state);
+		return status;
+	}
+
+	switch_channel_set_private(channel, "tdd_decode", bug);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+///XXX
+
 typedef struct {
 	switch_core_session_t *session;
 	dtmf_rx_state_t *dtmf_detect;