From 03dc3b7b8d734e5a916c0dd48c408e78e6f66f55 Mon Sep 17 00:00:00 2001
From: Arnaldo Pereira <arnaldo@sangoma.com>
Date: Wed, 24 Nov 2010 21:25:24 -0200
Subject: [PATCH] freetdm: ftdm_channel_read_event() to retrieve events from a
 channel, removed from ftmod_r2: ftdm_r2_sig_read(), FTDM_R2_PROCESSING flag
 and locks for ftdmchan

---
 libs/freetdm/src/ftdm_io.c                    |  45 +++++
 libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c    | 145 ++++++--------
 .../src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c   | 184 +++++++++++++++++-
 libs/freetdm/src/include/freetdm.h            |   4 +
 libs/freetdm/src/include/private/ftdm_core.h  |  17 ++
 5 files changed, 311 insertions(+), 84 deletions(-)

diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index 64233a97ac..0065d779f6 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -988,6 +988,51 @@ FT_DECLARE(ftdm_status_t) ftdm_span_next_event(ftdm_span_t *span, ftdm_event_t *
 	return status;
 }
 
+FT_DECLARE(ftdm_status_t) ftdm_channel_read_event(ftdm_channel_t *ftdmchan, ftdm_event_t **event)
+{
+	ftdm_status_t status = FTDM_FAIL;
+	ftdm_sigmsg_t sigmsg;
+	ftdm_span_t *span = ftdmchan->span;
+	ftdm_assert_return(span->fio != NULL, FTDM_FAIL, "No I/O module attached to this span!\n");
+
+	if (!span->fio->channel_next_event) {
+		ftdm_log(FTDM_LOG_ERROR, "channel_next_event method not implemented in module %s!", span->fio->name);
+		return FTDM_NOTIMPL;
+	}
+
+	status = span->fio->channel_next_event(ftdmchan, event);
+	if (status != FTDM_SUCCESS) {
+		return status;
+	}
+
+	/* before returning the event to the user we do some core operations with certain OOB events */
+	memset(&sigmsg, 0, sizeof(sigmsg));
+	sigmsg.span_id = span->span_id;
+	sigmsg.chan_id = (*event)->channel->chan_id;
+	sigmsg.channel = (*event)->channel;
+	switch ((*event)->enum_id) {
+	case FTDM_OOB_ALARM_CLEAR:
+		{
+			sigmsg.event_id = FTDM_SIGEVENT_ALARM_CLEAR;
+			ftdm_clear_flag_locked((*event)->channel, FTDM_CHANNEL_IN_ALARM);
+			ftdm_span_send_signal(span, &sigmsg);
+		}
+		break;
+	case FTDM_OOB_ALARM_TRAP:
+		{
+			sigmsg.event_id = FTDM_SIGEVENT_ALARM_TRAP;
+			ftdm_set_flag_locked((*event)->channel, FTDM_CHANNEL_IN_ALARM);
+			ftdm_span_send_signal(span, &sigmsg);
+		}
+		break;
+	default:
+		/* NOOP */
+		break;
+	}
+
+	return status;
+}
+
 static ftdm_status_t ftdmchan_fsk_write_sample(int16_t *buf, ftdm_size_t buflen, void *user_data)
 {
 	ftdm_channel_t *ftdmchan = (ftdm_channel_t *) user_data;
diff --git a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
index 30de78857a..7c5f4a46af 100644
--- a/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
+++ b/libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
@@ -57,8 +57,7 @@ typedef enum {
 } ftdm_r2_flag_t;
 
 typedef enum {
-	FTDM_R2_PROCESSING = (1 << 0),
-	FTDM_R2_WAITING_ACK = (1 << 1),
+	FTDM_R2_WAITING_ACK = (1 << 0),
 } ftdm_r2_call_flag_t;
 
 /* private call information stored in ftdmchan->call_data void* ptr */
@@ -398,44 +397,26 @@ static ftdm_status_t ftdm_r2_stop(ftdm_span_t *span)
 	return FTDM_SUCCESS;
 }
 
-static ftdm_status_t ftdm_r2_sig_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size)
-{
-	openr2_chan_t *r2chan = R2CALL(ftdmchan)->r2chan;
-	if (!openr2_chan_get_read_enabled(r2chan)) {
-		ftdm_mutex_lock(ftdmchan->mutex);
-		//openr2_chan_process_input(r2chan, data, size);
-		ftdm_mutex_unlock(ftdmchan->mutex);
-	}
-	return FTDM_SUCCESS;
-}
-
 /* always called from the monitor thread */
 static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
 {
-	//ftdm_status_t status;
 	ftdm_r2_call_t *r2call;
 	ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
-	//ftdm_r2_data_t *r2data = ftdmchan->span->signal_data;
 
 	ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Received request to start call\n");
 
-	ftdm_mutex_lock(ftdmchan->mutex);
-
 	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) {
 		ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Cannot start call when channel is in use (state = %s)\n", ftdm_channel_state2str(ftdmchan->state));
-		ftdm_mutex_unlock(ftdmchan->mutex);
 		return;
 	}
 
 	if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN) {
 		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot handle request to start call in state %s\n", ftdm_channel_state2str(ftdmchan->state));
-		ftdm_mutex_unlock(ftdmchan->mutex);
 		return;
 	}
 
 	if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) {
 		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to open channel during incoming call! [%s]\n", ftdmchan->last_error);
-		ftdm_mutex_unlock(ftdmchan->mutex);
 		return;
 	}
 
@@ -448,7 +429,6 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
 	/* clean the call data structure but keep the R2 processing flag on! */
 	ft_r2_clean_call(ftdmchan->call_data);
 	r2call = R2CALL(ftdmchan);
-	ftdm_set_flag(r2call, FTDM_R2_PROCESSING);
 
 	if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) {
 		R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
@@ -457,7 +437,6 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
 	}
 
 	ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
-	ftdm_mutex_unlock(ftdmchan->mutex);
 }
 
 /* only called for incoming calls when the ANI, DNIS etc is complete and the user has to decide either to accept or reject the call */
@@ -574,11 +553,8 @@ static void ftdm_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_err
 	ftdm_r2_data_t *r2data;
 	ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
 
-	ftdm_mutex_lock(ftdmchan->mutex);
-
 	if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) {
 		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Got protocol error when we're already down!\n");
-		ftdm_mutex_unlock(ftdmchan->mutex);
 	}
 
 	ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Protocol error\n");
@@ -587,7 +563,6 @@ static void ftdm_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_err
 
 	if (!R2CALL(ftdmchan)->ftdm_started) {
 		ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
-		ftdm_mutex_unlock(ftdmchan->mutex);
 		return;
 	}
 
@@ -602,8 +577,6 @@ static void ftdm_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_err
 	r2data = ftdmchan->span->signal_data;
 
 	ftdm_span_send_signal(ftdmchan->span, &sigev);
-
-	ftdm_mutex_unlock(ftdmchan->mutex);
 }
 
 static void ftdm_r2_on_line_blocked(openr2_chan_t *r2chan)
@@ -867,9 +840,37 @@ static int ftdm_r2_io_setup(openr2_chan_t *r2chan)
 
 static int ftdm_r2_io_get_oob_event(openr2_chan_t *r2chan, openr2_oob_event_t *event)
 {
-	*event = 0;
-	ftdm_log(FTDM_LOG_ERROR, "I should not be called (I/O get oob event)!!\n");
-	return 0;
+    ftdm_status_t status;
+    ftdm_event_t *fevent = NULL;
+    ftdm_channel_t *ftdmchan = openr2_chan_get_fd(r2chan);
+
+    *event = OR2_OOB_EVENT_NONE;
+    status = ftdm_channel_read_event(ftdmchan, &fevent);
+    if (status != FTDM_SUCCESS) {
+		//ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "failed to retrieve freetdm event!\n");
+        return -1;
+    }
+	if (fevent->e_type != FTDM_EVENT_OOB)
+		return 0;
+	switch (fevent->enum_id) {
+		case FTDM_OOB_CAS_BITS_CHANGE:
+			{
+				*event = OR2_OOB_EVENT_CAS_CHANGE;
+			}
+			break;
+		case FTDM_OOB_ALARM_TRAP:
+			{
+				*event = OR2_OOB_EVENT_ALARM_ON;
+			}
+			break;
+		case FTDM_OOB_ALARM_CLEAR:
+			{
+				*event = OR2_OOB_EVENT_ALARM_OFF;
+			}
+			break;
+	}
+    ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "returning event: %s\n", ftdm_signal_event2str(*event));
+    return 0;
 }
 
 static openr2_io_interface_t ftdm_r2_io_iface = {
@@ -1137,7 +1138,7 @@ static FIO_SIG_CONFIGURE_FUNCTION(ftdm_r2_configure_span)
 
 	span->start = ftdm_r2_start;
 	span->stop = ftdm_r2_stop;
-	span->sig_read = ftdm_r2_sig_read;
+	span->sig_read = NULL;
 
 	span->signal_cb = sig_cb;
 	span->signal_type = FTDM_SIGTYPE_R2;
@@ -1350,6 +1351,7 @@ static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
 	int ms;
 	struct timeval start, end;
     short *poll_events = ftdm_malloc(sizeof(short)*span->chan_count);
+	ftdm_event_t *event = NULL;
 
 #ifdef __linux__
 	r2data->monitor_thread_id = syscall(SYS_gettid);	
@@ -1401,63 +1403,44 @@ static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
 			continue;
 		}
 
-		 if (FTDM_SUCCESS == status) {
-			ftdm_event_t *event;
-			while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) {
-				if (event->enum_id == FTDM_OOB_CAS_BITS_CHANGE) {
-					r2call = R2CALL(event->channel);
-					r2chan = r2call->r2chan;
+		/* XXX
+		* when ftdm_span_poll_event() returns FTDM_SUCCESS, means there are events pending on the span.
+		* is it possible to know on which channels those events are pending, without traversing the span?
+		* XXX */
+		for (i = 1; i <= span->chan_count; i++) {
+			r2chan = R2CALL(span->channels[i])->r2chan;
+			r2call = R2CALL(ftdmchan);
+			ftdmchan = openr2_chan_get_client_data(r2chan);
 
-					ftdm_log(FTDM_LOG_DEBUG, "Handling CAS on channel %d.\n", openr2_chan_get_number(r2chan));
-					// we only expect CAS and other OOB events on this thread/loop, once a call is started
-					// the MF events (in-band signaling) are handled in the call thread
-					openr2_chan_process_cas_signaling(r2chan);
-				} else {
-					ftdm_log(FTDM_LOG_DEBUG, "Ignoring event %d on channel %d.\n", event->enum_id, openr2_chan_get_number(r2chan));
-					// XXX TODO: handle alarms here XXX
+			status = ftdm_channel_read_event(ftdmchan, &event);
+			ftdm_mutex_lock(ftdmchan->mutex);
+			if (status == FTDM_SUCCESS) {
+				switch (event->enum_id) {
+				case FTDM_OOB_CAS_BITS_CHANGE:
+					{
+						ftdm_log(FTDM_LOG_DEBUG, "Handling CAS on channel %d.\n", i);
+						openr2_chan_process_cas_signaling(r2chan);
+					}
+					break;
+				case FTDM_OOB_ALARM_TRAP:
+				case FTDM_OOB_ALARM_CLEAR:
+					{
+						ftdm_log(FTDM_LOG_DEBUG, "OOB EVENT: %s\n", ftdm_signal_event2str(event->enum_id));
+						openr2_chan_process_oob_events(r2chan);
+					}
+					break;
 				}
 			}
 
-			/* XXX
-			* when ftdm_span_poll_event() returns FTDM_SUCCESS, means there are events pending on the span.
-			* is it possible to know on which channels those events are pending, without traversing the span?
-			* XXX */
-			for (i = 1; i <= span->chan_count; i++) {
-				r2chan = R2CALL(span->channels[i])->r2chan;
-				ftdmchan = openr2_chan_get_client_data(r2chan);
-				r2call = R2CALL(ftdmchan);
-
-				ftdm_mutex_lock(ftdmchan->mutex);
-				ftdm_set_flag(r2call, FTDM_R2_PROCESSING);
-
-				if (ftdm_r2_state_advance(ftdmchan)) {
-					ftdm_clear_flag(r2call, FTDM_R2_PROCESSING);
-					ftdm_mutex_unlock(ftdmchan->mutex);
-					continue;
-				}
-
-				/* handle timeout events first if any */
-				openr2_chan_run_schedule(r2chan);
-
-				/* process mf tones, if any */
-				if (openr2_chan_get_read_enabled(r2chan)) {
-					openr2_chan_process_mf_signaling(r2chan);
-				}
-
-				if (ftdm_r2_state_advance(ftdmchan)) {
-					ftdm_clear_flag(r2call, FTDM_R2_PROCESSING);
-					ftdm_mutex_unlock(ftdmchan->mutex);
-					continue;
-				}
-
-				ftdm_clear_flag(r2call, FTDM_R2_PROCESSING);
+			if (!ftdm_r2_state_advance(ftdmchan)) {
+				ftdm_mutex_unlock(ftdmchan->mutex);
+			}
+			openr2_chan_process_signaling(r2chan);
+			if (!ftdm_r2_state_advance(ftdmchan)) {
 				ftdm_mutex_unlock(ftdmchan->mutex);
 			}
-		} else if (status != FTDM_TIMEOUT) {
-			ftdm_log(FTDM_LOG_ERROR, "ftdm_span_poll_event returned %d.\n", status);
 		}
 		ftdm_span_trigger_signals(span);
-		ftdm_sleep(20);
 	}
 
 	for (i = 1; i <= span->chan_count; i++) {
diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
index c13455dd21..ae46852300 100644
--- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
+++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
@@ -35,6 +35,7 @@
  * Moises Silva <moy@sangoma.com>
  * David Yat Sin <davidy@sangoma.com>
  * Nenad Corbic <ncorbic@sangoma.com>
+ * Arnaldo Pereira <arnaldo@sangoma.com>
  *
  */
 
@@ -99,7 +100,8 @@ static struct {
 /* a bunch of this stuff should go into the wanpipe_tdm_api_iface.h */
 
 FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event);
-FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event);
+FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event);
+FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event);
 
 /**
  * \brief Poll for event on a wanpipe socket
@@ -1110,13 +1112,188 @@ static FIO_GET_ALARMS_FUNCTION(wanpipe_get_alarms)
 	return FTDM_SUCCESS;
 }
 
+/**
+ * \brief Retrieves an event from a wanpipe channel
+ * \param channel Channel to retrieve event from
+ * \param event FreeTDM event to return
+ * \return Success or failure
+ */
+FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event)
+{
+	ftdm_status_t status;
+	ftdm_oob_event_t event_id;
+	wanpipe_tdm_api_t tdm_api;
+	ftdm_span_t *span = ftdmchan->span;
+
+	if (ftdmchan->last_event_time && !ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
+		uint32_t diff = (uint32_t)(ftdm_current_time_in_ms() - ftdmchan->last_event_time);
+		/* XX printf("%u %u %u\n", diff, (unsigned)ftdm_current_time_in_ms(), (unsigned)ftdmchan->last_event_time); */
+		if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) {
+			if (diff > wp_globals.wink_ms) {
+				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_WINK);
+				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_FLASH);
+				ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
+				event_id = FTDM_OOB_OFFHOOK;
+				goto event;
+			}
+		}
+
+		if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_FLASH)) {
+			if (diff > wp_globals.flash_ms) {
+				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_FLASH);
+				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_WINK);
+				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
+				event_id = FTDM_OOB_ONHOOK;
+
+				if (ftdmchan->type == FTDM_CHAN_TYPE_FXO) {
+					wanpipe_tdm_api_t tdm_api;
+					memset(&tdm_api, 0, sizeof(tdm_api));
+
+					sangoma_tdm_txsig_onhook(ftdmchan->sockfd,&tdm_api);
+				}
+				goto event;
+			}
+		}
+	} 
+
+	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT))
+		return FTDM_FAIL;
+
+	memset(&tdm_api, 0, sizeof(tdm_api));
+	status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api);
+	if (status != FTDM_SUCCESS) {
+		snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
+		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to read event from channel: %s\n", strerror(errno));
+		return FTDM_FAIL;
+	}
+
+	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "read wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);
+	switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) {
+
+	case WP_TDMAPI_EVENT_LINK_STATUS:
+		{
+			switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
+			case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
+				event_id = FTDM_OOB_ALARM_CLEAR;
+				break;
+			default:
+				event_id = FTDM_OOB_ALARM_TRAP;
+				break;
+			};
+		}
+		break;
+
+	case WP_TDMAPI_EVENT_RXHOOK:
+		{
+			if (ftdmchan->type == FTDM_CHAN_TYPE_FXS) {
+				event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_OFFHOOK : FTDM_OOB_ONHOOK;
+				if (event_id == FTDM_OOB_OFFHOOK) {
+					if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_FLASH)) {
+						ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_FLASH);
+						ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_WINK);
+						event_id = FTDM_OOB_FLASH;
+						goto event;
+					} else {
+						ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_WINK);
+					}
+				} else {
+					if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_WINK)) {
+						ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_WINK);
+						ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_FLASH);
+						event_id = FTDM_OOB_WINK;
+						goto event;
+					} else {
+						ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_FLASH);
+					}
+				}					
+				break;
+			} else {
+				wanpipe_tdm_api_t onhook_tdm_api;
+				memset(&onhook_tdm_api, 0, sizeof(onhook_tdm_api));
+				status = sangoma_tdm_txsig_onhook(ftdmchan->sockfd, &onhook_tdm_api);
+				if (status) {
+					snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ONHOOK Failed");
+					return FTDM_FAIL;
+				}
+				event_id = onhook_tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_ONHOOK : FTDM_OOB_NOOP;	
+			}
+		}
+		break;
+	case WP_TDMAPI_EVENT_RING_DETECT:
+		{
+			event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_RING_START : FTDM_OOB_RING_STOP;
+		}
+		break;
+		/*
+		disabled this ones when configuring, we don't need them, do we?
+	case WP_TDMAPI_EVENT_RING_TRIP_DETECT:
+		{
+			event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? FTDM_OOB_ONHOOK : FTDM_OOB_OFFHOOK;
+		}
+		break;
+		*/
+	case WP_TDMAPI_EVENT_RBS:
+		{
+			event_id = FTDM_OOB_CAS_BITS_CHANGE;
+			ftdmchan->rx_cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits);
+		}
+		break;
+	case WP_TDMAPI_EVENT_DTMF:
+		{
+			char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 };
+			event_id = FTDM_OOB_NOOP;
+
+			if (tmp_dtmf[0] == 'f') {
+				ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Ignoring wanpipe DTMF: %c, fax tones will be passed through!\n", tmp_dtmf[0]);
+				break;
+			}
+
+			if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) {
+				ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_MUTE);
+			}
+
+			if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_STOP) {
+				ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_MUTE);
+				if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE)) {
+					ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Queuing wanpipe DTMF: %c\n", tmp_dtmf[0]);
+					ftdm_channel_queue_dtmf(ftdmchan, tmp_dtmf);
+				}
+			} 
+		}
+		break;
+	case WP_TDMAPI_EVENT_ALARM:
+		{
+			ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Got wanpipe alarms %d\n", tdm_api.wp_tdm_cmd.event.wp_api_event_alarm);
+			event_id = FTDM_OOB_ALARM_TRAP;
+		}
+		break;
+	default:
+		{
+			ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Unhandled wanpipe event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);
+			event_id = FTDM_OOB_INVALID;
+		}
+		break;
+	}
+
+event:
+
+	ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
+
+	ftdmchan->last_event_time = 0;
+	span->event_header.e_type = FTDM_EVENT_OOB;
+	span->event_header.enum_id = event_id;
+	span->event_header.channel = ftdmchan;
+	*event = &span->event_header;
+	return FTDM_SUCCESS;
+}
+
 /**
  * \brief Retrieves an event from a wanpipe span
  * \param span Span to retrieve event from
  * \param event FreeTDM event to return
  * \return Success or failure
  */
-FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event)
+FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event)
 {
 	uint32_t i,err;
 	ftdm_oob_event_t event_id;
@@ -1348,7 +1525,8 @@ static FIO_IO_LOAD_FUNCTION(wanpipe_init)
 	wanpipe_interface.read = wanpipe_read;
 	wanpipe_interface.write = wanpipe_write;
 	wanpipe_interface.poll_event = wanpipe_poll_event;
-	wanpipe_interface.next_event = wanpipe_next_event;
+	wanpipe_interface.next_event = wanpipe_span_next_event;
+	wanpipe_interface.channel_next_event = wanpipe_channel_next_event;
 	wanpipe_interface.channel_destroy = wanpipe_channel_destroy;
 	wanpipe_interface.get_alarms = wanpipe_get_alarms;
 	*fio = &wanpipe_interface;
diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h
index 508c6996d5..9ce213c962 100644
--- a/libs/freetdm/src/include/freetdm.h
+++ b/libs/freetdm/src/include/freetdm.h
@@ -465,6 +465,7 @@ struct ftdm_memory_handler {
 #define FIO_SPAN_GET_SIG_STATUS_ARGS (ftdm_span_t *span, ftdm_signaling_status_t *status)
 #define FIO_SPAN_POLL_EVENT_ARGS (ftdm_span_t *span, uint32_t ms, short *poll_events)
 #define FIO_SPAN_NEXT_EVENT_ARGS (ftdm_span_t *span, ftdm_event_t **event)
+#define FIO_CHANNEL_NEXT_EVENT_ARGS (ftdm_channel_t *ftdmchan, ftdm_event_t **event)
 #define FIO_SIGNAL_CB_ARGS (ftdm_sigmsg_t *sigmsg)
 #define FIO_EVENT_CB_ARGS (ftdm_channel_t *ftdmchan, ftdm_event_t *event)
 #define FIO_CONFIGURE_SPAN_ARGS (ftdm_span_t *span, const char *str, ftdm_chan_type_t type, char *name, char *number)
@@ -496,6 +497,7 @@ typedef ftdm_status_t (*fio_span_set_sig_status_t) FIO_SPAN_SET_SIG_STATUS_ARGS;
 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 ;
 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 ;
@@ -528,6 +530,7 @@ typedef ftdm_status_t (*fio_api_t) FIO_API_ARGS ;
 #define FIO_SPAN_GET_SIG_STATUS_FUNCTION(name) ftdm_status_t name FIO_SPAN_GET_SIG_STATUS_ARGS
 #define FIO_SPAN_POLL_EVENT_FUNCTION(name) ftdm_status_t name FIO_SPAN_POLL_EVENT_ARGS
 #define FIO_SPAN_NEXT_EVENT_FUNCTION(name) ftdm_status_t name FIO_SPAN_NEXT_EVENT_ARGS
+#define FIO_CHANNEL_NEXT_EVENT_FUNCTION(name) ftdm_status_t name FIO_CHANNEL_NEXT_EVENT_ARGS
 #define FIO_SIGNAL_CB_FUNCTION(name) ftdm_status_t name FIO_SIGNAL_CB_ARGS
 #define FIO_EVENT_CB_FUNCTION(name) ftdm_status_t name FIO_EVENT_CB_ARGS
 #define FIO_CONFIGURE_SPAN_FUNCTION(name) ftdm_status_t name FIO_CONFIGURE_SPAN_ARGS
@@ -566,6 +569,7 @@ struct ftdm_io_interface {
 	fio_write_t write; /*!< Write data to the channel */
 	fio_span_poll_event_t poll_event; /*!< Poll for events on the whole span */
 	fio_span_next_event_t next_event; /*!< Retrieve an event from the span */
+	fio_channel_next_event_t channel_next_event; /*!< Retrieve an event from channel */
 	fio_api_t api; /*!< Execute a text command */
 };
 
diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h
index 06f899c8c9..7d3ee68176 100644
--- a/libs/freetdm/src/include/private/ftdm_core.h
+++ b/libs/freetdm/src/include/private/ftdm_core.h
@@ -615,6 +615,23 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan);
  */
 FT_DECLARE(ftdm_status_t) ftdm_span_next_event(ftdm_span_t *span, ftdm_event_t **event);
 
+/*! 
+ * \brief Retrieves an event from the span
+ *
+ * \note
+ * 	This function is non-reentrant and not thread-safe. 
+ * 	The event returned may be modified if the function is called again 
+ * 	from a different thread or even the same. It is recommended to
+ * 	handle events from the same span in a single thread.
+ *
+ * \param span The channel to retrieve the event from
+ * \param event Pointer to store the pointer to the event
+ *
+ * \retval FTDM_SUCCESS success (at least one event available)
+ * \retval FTDM_FAIL failure
+ */
+FT_DECLARE(ftdm_status_t) ftdm_channel_read_event(ftdm_channel_t *ftdmchan, ftdm_event_t **event);
+
 /*! 
  * \brief Enqueue a DTMF string into the channel
  *