diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
index 32a7e09b83..033d24b620 100644
--- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
+++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
@@ -38,6 +38,8 @@
 static ftdm_status_t ftdm_libpri_start(ftdm_span_t *span);
 static ftdm_io_interface_t ftdm_libpri_interface;
 
+static int on_timeout_t302(struct lpwrap_pri *spri, struct lpwrap_timer *timer);
+
 
 static void _ftdm_channel_set_state_force(ftdm_channel_t *chan, const ftdm_channel_state_t state)
 {
@@ -902,8 +904,10 @@ static ftdm_state_map_t isdn_state_map = {
  */
 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;
+	ftdm_span_t *span = ftdm_channel_get_span(chan);
+	ftdm_libpri_data_t *isdn_data = span->signal_data;
+	ftdm_libpri_b_chan_t *chan_priv = chan->call_data;
+	q931_call *call = chan_priv->call;
 	ftdm_status_t status;
 	ftdm_sigmsg_t sig;
 
@@ -920,21 +924,26 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 	switch (ftdm_channel_get_state(chan)) {
 	case FTDM_CHANNEL_STATE_DOWN:
 		{
-			ftdm_channel_t *chtmp = chan;
+			if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_B) {
+				ftdm_channel_t *chtmp = chan;
 
-			if (call) {
-				pri_destroycall(isdn_data->spri.pri, call);
-				chan->call_data = NULL;
-			}
+				if (call) {
+					pri_destroycall(isdn_data->spri.pri, call);
+					chan_priv->call = NULL;
+				}
 
-			if (ftdm_channel_close(&chtmp) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_WARNING, "-- Failed to close channel %d:%d\n",
-					ftdm_channel_get_span_id(chan),
-					ftdm_channel_get_id(chan));
-			} else {
-				ftdm_log(FTDM_LOG_DEBUG, "-- Closed channel %d:%d\n",
-					ftdm_channel_get_span_id(chan),
-					ftdm_channel_get_id(chan));
+				/* Stop T302 */
+				lpwrap_stop_timer(&isdn_data->spri, &chan_priv->t302);
+
+				if (ftdm_channel_close(&chtmp) != FTDM_SUCCESS) {
+					ftdm_log(FTDM_LOG_WARNING, "-- Failed to close channel %d:%d\n",
+						ftdm_channel_get_span_id(chan),
+						ftdm_channel_get_id(chan));
+				} else {
+					ftdm_log(FTDM_LOG_DEBUG, "-- Closed channel %d:%d\n",
+						ftdm_channel_get_span_id(chan),
+						ftdm_channel_get_id(chan));
+				}
 			}
 		}
 		break;
@@ -943,7 +952,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 		{
 			if (ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) {
 				sig.event_id = FTDM_SIGEVENT_PROGRESS;
-				if ((status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig) != FTDM_SUCCESS)) {
+				if ((status = ftdm_span_send_signal(span, &sig) != FTDM_SUCCESS)) {
 					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
 				}
 			} else if (call) {
@@ -958,7 +967,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 		{
 			if (ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) {
 				sig.event_id = FTDM_SIGEVENT_RINGING;
-				if ((status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig) != FTDM_SUCCESS)) {
+				if ((status = ftdm_span_send_signal(span, &sig) != FTDM_SUCCESS)) {
 					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
 				}
 			} else if (call) {
@@ -974,7 +983,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 		{
 			if (ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) {
 				sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
-				if ((status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig) != FTDM_SUCCESS)) {
+				if ((status = ftdm_span_send_signal(span, &sig) != FTDM_SUCCESS)) {
 					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
 				}
 			} else if (call) {
@@ -994,7 +1003,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 			if (ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) {
 				/* PROCEED from other end, notify user */
 				sig.event_id = FTDM_SIGEVENT_PROCEED;
-				if ((status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig) != FTDM_SUCCESS)) {
+				if ((status = ftdm_span_send_signal(span, &sig) != FTDM_SUCCESS)) {
 					ftdm_log(FTDM_LOG_ERROR, "Failed to send PROCEED sigevent on Channel %d:%d\n",
 						ftdm_channel_get_span_id(chan),
 						ftdm_channel_get_id(chan));
@@ -1024,6 +1033,11 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 					caller_data->hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER;
 					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
 				}
+				else {
+					/* Start T302 */
+					lpwrap_start_timer(&isdn_data->spri, &chan_priv->t302,
+						isdn_data->overlap_timeout_ms, &on_timeout_t302);
+				}
 			} else {
 				ftdm_log_chan_msg(chan, FTDM_LOG_ERROR, "Overlap receiving on outbound call?\n");
 				ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RESTART);
@@ -1042,7 +1056,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 					pri_proceeding(isdn_data->spri.pri, call, ftdm_channel_get_id(chan), 0);
 //					pri_acknowledge(isdn_data->spri.pri, call, ftdm_channel_get_id(chan), 0);
 					sig.event_id = FTDM_SIGEVENT_START;
-					if ((status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig) != FTDM_SUCCESS)) {
+					if ((status = ftdm_span_send_signal(span, &sig) != FTDM_SUCCESS)) {
 						ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
 					}
 				} else {
@@ -1054,10 +1068,20 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 
 	case FTDM_CHANNEL_STATE_RESTART:
 		{
-			chan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_UNSPECIFIED;
-			sig.event_id = FTDM_SIGEVENT_RESTART;
-			status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig);
-			ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_DOWN);
+			if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_B) {
+				chan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_UNSPECIFIED;
+				sig.event_id = FTDM_SIGEVENT_RESTART;
+				status = ftdm_span_send_signal(span, &sig);
+
+				if (!(chan_priv->flags & FTDM_LIBPRI_B_REMOTE_RESTART)) {
+					/* Locally triggered restart, send RESTART to remote, wait for ACK */
+					pri_reset(isdn_data->spri.pri, ftdm_channel_get_id(chan));
+				} else {
+					/* Remote restart complete, clear flag */
+					chan_priv->flags &= ~FTDM_LIBPRI_B_REMOTE_RESTART;
+					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_DOWN);
+				}
+			}
 		}
 		break;
 
@@ -1065,7 +1089,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 		{
 			if (ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) {
 				sig.event_id = FTDM_SIGEVENT_UP;
-				if ((status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig) != FTDM_SUCCESS)) {
+				if ((status = ftdm_span_send_signal(span, &sig) != FTDM_SUCCESS)) {
 					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP);
 				}
 			} else if (call) {
@@ -1109,7 +1133,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 				ton = isdn_data->ton;
 			}
 
-			chan->call_data = call;
+			chan_priv->call = call;
 
 			sr = pri_sr_new();
 			if (!sr) {
@@ -1155,7 +1179,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 
 				pri_hangup(isdn_data->spri.pri, call, caller_data->hangup_cause);
 //				pri_destroycall(isdn_data->spri.pri, call);
-//				chan->call_data = NULL;
+//				chan_priv->call = NULL;
 			}
 			ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
 		}
@@ -1165,7 +1189,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 		{
 //			if (call) {
 //				pri_destroycall(isdn_data->spri.pri, call);
-//				chan->call_data = NULL;
+//				chan_priv->call = NULL;
 //			}
 			ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_DOWN);
 		}
@@ -1174,7 +1198,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
 	case FTDM_CHANNEL_STATE_TERMINATING:
 		{
 			sig.event_id = FTDM_SIGEVENT_STOP;
-			status = ftdm_span_send_signal(ftdm_channel_get_span(chan), &sig);
+			status = ftdm_span_send_signal(span, &sig);
 			/* user moves us to HANGUP and from there we go to DOWN */
 		}
 	default:
@@ -1203,18 +1227,48 @@ static __inline__ void check_state(ftdm_span_t *span)
 	}
 }
 
+
 /**
- * \brief Handler for libpri information event (incoming call?)
+ * \brief Handler for libpri keypad digit event
  * \param spri Pri wrapper structure (libpri, span, dchan)
  * \param event_type Event type (unused)
  * \param pevent Event
  * \return 0
  */
-static int on_info(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent)
+static int on_keypad_digit(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent)
 {
 	ftdm_span_t *span = spri->span;
 	ftdm_channel_t *chan = ftdm_span_get_channel(span, pevent->ring.channel);
+
+	if (!chan) {
+		ftdm_log(FTDM_LOG_ERROR, "-- Keypad event on invalid channel %d:%d\n",
+			ftdm_span_get_id(span), pevent->ring.channel);
+		return 0;
+	}
+
+	ftdm_log_chan(chan, FTDM_LOG_DEBUG, "-- Keypad event received, incoming digits: '%s'\n",
+		pevent->digit.digits);
+
+	/* Enqueue DTMF digits on channel */
+	ftdm_channel_queue_dtmf(chan, pevent->digit.digits);
+	return 0;
+}
+
+
+/**
+ * \brief Handler for libpri information event (overlap receiving)
+ * \param spri Pri wrapper structure (libpri, span, dchan)
+ * \param event_type Event type (unused)
+ * \param pevent Event
+ * \return 0
+ */
+static int on_information(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent)
+{
+	ftdm_span_t *span = spri->span;
+	ftdm_channel_t *chan = ftdm_span_get_channel(span, pevent->ring.channel);
+	ftdm_libpri_b_chan_t *chan_priv = NULL;
 	ftdm_caller_data_t *caller_data = NULL;
+	ftdm_libpri_data_t *isdn_data = span->signal_data;
 
 	if (!chan) {
 		ftdm_log(FTDM_LOG_CRIT, "-- Info on channel %d:%d but it's not in use?\n", ftdm_span_get_id(span), pevent->ring.channel);
@@ -1222,11 +1276,19 @@ static int on_info(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
 	}
 
 	caller_data = ftdm_channel_get_caller_data(chan);
+	chan_priv   = chan->call_data;
 
 	switch (ftdm_channel_get_state(chan)) {
 	case FTDM_CHANNEL_STATE_COLLECT:	/* TE-mode overlap receiving */
-		ftdm_log_chan(chan, FTDM_LOG_DEBUG, "-- Incoming INFORMATION indication, current called number: '%s', number complete: %s\n",
-			pevent->ring.callednum, pevent->ring.complete ? "yes" : "no");
+	case FTDM_CHANNEL_STATE_DIALTONE:	/* NT-mode overlap receiving */
+
+		ftdm_log_chan(chan, FTDM_LOG_DEBUG, "-- Incoming INFORMATION indication, received digits: '%s', number complete: %c, collected digits: '%s'\n",
+			pevent->ring.callednum,
+			pevent->ring.complete ? 'Y' : 'N',
+			caller_data->dnis.digits);
+
+		/* Stop T302 */
+		lpwrap_stop_timer(spri, &chan_priv->t302);
 
 		/* append digits to dnis */
 		if (!ftdm_strlen_zero(pevent->ring.callednum)) {
@@ -1241,7 +1303,7 @@ static int on_info(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
 
 			len = ftdm_min(sizeof(caller_data->dnis.digits) - 1 - offset, digits);	/* max. length without terminator */
 			if (len < digits) {
-				ftdm_log_chan(chan, FTDM_LOG_WARNING, "Length %d of digit string exceeds available space %d of DNIS, truncating!\n",
+				ftdm_log_chan(chan, FTDM_LOG_WARNING, "Digit string of length %d exceeds available space %d of DNIS, truncating!\n",
 					digits, len);
 			}
 			if (len) {
@@ -1250,25 +1312,16 @@ static int on_info(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
 			}
 		}
 		if (pevent->ring.complete) {
-			ftdm_log_chan_msg(chan, FTDM_LOG_DEBUG, "Number complete indicated, moving channel to RING state\n");
+			ftdm_log_chan_msg(chan, FTDM_LOG_DEBUG, "Number complete indication received, moving channel to RING state\n");
 			/* notify switch */
 			ftdm_set_state(chan, FTDM_CHANNEL_STATE_RING);
-		}
-		break;
-	case FTDM_CHANNEL_STATE_DIALTONE:	/* NT-mode overlap receiving */
-		ftdm_log_chan(chan, FTDM_LOG_DEBUG, "-- Incoming INFORMATION indication, current called number: '%s'\n",
-			pevent->ring.callednum);
-
-		/* Need to add proper support for overlap receiving in NT-mode (requires FreeSWITCH + FreeTDM core support) */
-		if (strlen(pevent->ring.callednum) > 3) {
-			ftdm_log(FTDM_LOG_DEBUG, "final number is: %s\n", pevent->ring.callednum);
-			pri_answer(spri->pri, pevent->ring.call, 0, 1);
+		} else {
+			/* Restart T302 */
+			lpwrap_start_timer(spri, &chan_priv->t302, isdn_data->overlap_timeout_ms, &on_timeout_t302);
 		}
 		break;
 	default:
-		ftdm_log_chan(chan, FTDM_LOG_ERROR, "-- INFORMATION indication on channel %d:%d in invalid state '%s'\n",
-			ftdm_channel_get_span_id(chan),
-			ftdm_channel_get_id(chan),
+		ftdm_log_chan(chan, FTDM_LOG_ERROR, "-- INFORMATION indication in invalid state '%s'\n",
 			ftdm_channel_get_state_str(chan));
 	}
 	return 0;
@@ -1650,6 +1703,7 @@ static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
 {
 	ftdm_span_t *span = spri->span;
 	ftdm_libpri_data_t *isdn_data = span->signal_data;
+	ftdm_libpri_b_chan_t *chan_priv = NULL;
 	ftdm_channel_t *chan = NULL;
 	ftdm_caller_data_t *caller_data = NULL;
 	int ret = 0;
@@ -1730,11 +1784,14 @@ static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
 		}
 	}
 
-	if (chan->call_data) {
+	/* Get per-channel private data */
+	chan_priv = chan->call_data;
+
+	if (chan_priv->call) {
 		/* we could drop the incoming call, but most likely the pointer is just a ghost of the past,
 		 * this check is just to detect potentially unreleased pointers */
-		ftdm_log_chan(chan, FTDM_LOG_WARNING, "Channel already has call %p!\n", chan->call_data);
-		chan->call_data = NULL;
+		ftdm_log_chan(chan, FTDM_LOG_WARNING, "Channel already has call %p!\n", chan_priv->call);
+		chan_priv->call = NULL;
 	}
 
 	caller_data = ftdm_channel_get_caller_data(chan);
@@ -1761,7 +1818,7 @@ static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
 
 	// scary to trust this pointer, you'd think they would give you a copy of the call data so you own it......
 	/* hurr, this is valid as along as nobody releases the call */
-	chan->call_data = pevent->ring.call;
+	chan_priv->call = pevent->ring.call;
 
 	/* Open Channel if inband information is available */
 	if ((pevent->ring.progressmask & PRI_PROG_INBAND_AVAILABLE)) {
@@ -1799,6 +1856,21 @@ done:
 	return ret;
 }
 
+
+/**
+ * Timeout handler for T302 (overlap receiving)
+ */
+static int on_timeout_t302(struct lpwrap_pri *spri, struct lpwrap_timer *timer)
+{
+	ftdm_libpri_b_chan_t *chan_priv = ftdm_container_of(timer, ftdm_libpri_b_chan_t, t302);
+	ftdm_channel_t *chan = chan_priv->channel;
+
+	ftdm_log(FTDM_LOG_NOTICE, "-- T302 timed out, going to state RING\n");
+	ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RING);
+	return 0;
+}
+
+
 /**
  * \brief Processes freetdm event
  * \param span Span on which the event was fired
@@ -1826,22 +1898,15 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
 			}
 
 			ftdm_set_flag(event->channel, FTDM_CHANNEL_SUSPENDED);
-
 			ftdm_channel_get_alarms(event->channel, &alarmbits);
-			ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) has alarms! [%s]\n",
-					ftdm_channel_get_span_id(event->channel), ftdm_channel_get_id(event->channel),
-					ftdm_channel_get_ph_span_id(event->channel), ftdm_channel_get_ph_id(event->channel),
-					ftdm_channel_get_last_error(event->channel));
+			ftdm_log_chan_msg(event->channel, FTDM_LOG_WARNING, "channel has alarms!\n");
 		}
 		break;
 	case FTDM_OOB_ALARM_CLEAR:
 		{
-			ftdm_log(FTDM_LOG_WARNING, "channel %d:%d (%d:%d) alarms Cleared!\n",
-					ftdm_channel_get_span_id(event->channel), ftdm_channel_get_id(event->channel),
-					ftdm_channel_get_ph_span_id(event->channel), ftdm_channel_get_ph_id(event->channel));
-
 			ftdm_clear_flag(event->channel, FTDM_CHANNEL_SUSPENDED);
 			ftdm_channel_get_alarms(event->channel, &alarmbits);
+			ftdm_log_chan_msg(event->channel, FTDM_LOG_WARNING, "channel alarms cleared!\n");
 		}
 		break;
 	}
@@ -1892,11 +1957,6 @@ static __inline__ void check_events(ftdm_span_t *span)
 static int check_flags(lpwrap_pri_t *spri)
 {
 	ftdm_span_t *span = spri->span;
-
-	if (!ftdm_running() || ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) {
-		return -1;
-	}
-
 	check_state(span);
 	check_events(span);
 	return 0;
@@ -1911,24 +1971,84 @@ static int check_flags(lpwrap_pri_t *spri)
  */
 static int on_restart(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent)
 {
+	ftdm_channel_t *chan = NULL;
 	ftdm_span_t *span = spri->span;
-	ftdm_channel_t *chan = ftdm_span_get_channel(span, pevent->restart.channel);
-
-	ftdm_log(FTDM_LOG_NOTICE, "-- Restarting %d:%d\n", ftdm_span_get_id(span), pevent->restart.channel);
-	_ftdm_channel_set_state_force(spri->dchan, FTDM_CHANNEL_STATE_UP);
-
-	if (!chan) {
-		return 0;
-	}
+	int i;
 
 	if (pevent->restart.channel < 1) {
-		ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
-	} else {
+		ftdm_log_chan_msg(spri->dchan, FTDM_LOG_NOTICE, "-- Restarting interface\n");
+
+		for (i = 1; i <= ftdm_span_get_chan_count(span); i++) {
+			chan = ftdm_span_get_channel(span, i);
+			if (!chan)
+				continue;
+			if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_B) {
+				ftdm_libpri_b_chan_t *chan_priv = chan->call_data;
+				chan_priv->flags |= FTDM_LIBPRI_B_REMOTE_RESTART;		/* Remote triggered RESTART, set flag */
+				ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RESTART);
+			}
+		}
+	}
+	else if ((chan = ftdm_span_get_channel(span, pevent->restart.channel))) {
+		ftdm_libpri_b_chan_t *chan_priv = chan->call_data;
+
+		ftdm_log_chan_msg(chan, FTDM_LOG_NOTICE, "-- Restarting single channel\n");
+		chan_priv->flags |= FTDM_LIBPRI_B_REMOTE_RESTART;
 		ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RESTART);
 	}
+	else {
+		ftdm_log(FTDM_LOG_ERROR, "Invalid restart indicator / channel id '%d' received\n",
+			pevent->restart.channel);
+	}
+
+	_ftdm_channel_set_state_force(spri->dchan, FTDM_CHANNEL_STATE_UP);
 	return 0;
 }
 
+/**
+ * \brief Handler for libpri restart acknowledge event
+ * \param spri Pri wrapper structure (libpri, span, dchan)
+ * \param event_type Event type (unused)
+ * \param pevent Event
+ * \return 0
+ */
+static int on_restart_ack(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event *pevent)
+{
+	ftdm_channel_t *chan = NULL;
+	ftdm_span_t *span = spri->span;
+	int i;
+
+	if (pevent->restartack.channel < 1) {
+		ftdm_log_chan_msg(spri->dchan, FTDM_LOG_NOTICE, "-- Restart of interface completed\n");
+
+		for (i = 1; i <= ftdm_span_get_chan_count(span); i++) {
+			chan = ftdm_span_get_channel(span, i);
+			if (!chan)
+				continue;
+			if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_B) {
+				ftdm_libpri_b_chan_t *chan_priv = chan->call_data;
+				if (!(chan_priv->flags & FTDM_LIBPRI_B_REMOTE_RESTART)) {
+					ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_DOWN);
+				}
+			}
+		}
+	}
+	else if ((chan = ftdm_span_get_channel(span, pevent->restart.channel))) {
+		ftdm_log_chan_msg(chan, FTDM_LOG_NOTICE, "-- Restart of channel completed\n");
+		ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_DOWN);
+	}
+	else {
+		ftdm_log(FTDM_LOG_ERROR, "Invalid restart indicator / channel id '%d' received\n",
+			pevent->restartack.channel);
+	}
+
+	_ftdm_channel_set_state_force(spri->dchan, FTDM_CHANNEL_STATE_UP);
+	return 0;
+}
+
+
+
+
 /*
  * FACILITY Advice-On-Charge handler
  */
@@ -2224,110 +2344,88 @@ static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj)
 	ftdm_span_t *span = (ftdm_span_t *) obj;
 	ftdm_libpri_data_t *isdn_data = span->signal_data;
 	int down = 0;
-	int got_d = 0;
 	int res = 0;
+	int i;
 
 	ftdm_set_flag(span, FTDM_SPAN_IN_THREAD);
+	isdn_data->dchan = NULL;
 
+	/*
+	 * Open D-Channel
+	 */
+	for (i = 1; i <= ftdm_span_get_chan_count(span); i++) {
+		ftdm_channel_t *chan = ftdm_span_get_channel(span, i);
+
+		if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_DQ921) {
+			if (ftdm_channel_open(ftdm_span_get_id(span), i, &isdn_data->dchan) == FTDM_SUCCESS) {
+				ftdm_log_chan_msg(chan, FTDM_LOG_DEBUG, "Opened D-Channel\n");
+				break;
+			} else {
+				ftdm_log_chan_msg(chan, FTDM_LOG_CRIT, "Failed to open D-Channel\n");
+				goto out;
+			}
+		}
+	}
+
+	/*
+	 * Initialize BRI/PRI context
+	 */
+	res = lpwrap_init_pri(&isdn_data->spri, span, isdn_data->dchan,
+		isdn_data->dialect, isdn_data->mode, isdn_data->debug_mask);
+
+	if (res) {
+		ftdm_log(FTDM_LOG_CRIT, "Failed to initialize BRI/PRI on span %d\n",
+			ftdm_span_get_id(span));
+		goto out;
+	}
+
+#ifdef HAVE_LIBPRI_AOC
+	/*
+	 * Only enable facility on trunk if really required,
+	 * this may help avoid problems on troublesome lines.
+	 */
+	if (isdn_data->opts & FTMOD_LIBPRI_OPT_FACILITY_AOC) {
+		pri_facility_enable(isdn_data->spri.pri);
+	}
+#endif
+	/* Support the different switch of service status */
+	if (isdn_data->service_message_support) {
+		pri_set_service_message_support(isdn_data->spri.pri, 1);
+	}
+
+	/* Callbacks for libpri events */
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANY, on_anything);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RING, on_ring);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RINGING, on_ringing);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_PROCEEDING, on_proceeding);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_PROGRESS, on_progress);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANSWER, on_answer);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_UP, on_dchan_up);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_DOWN, on_dchan_down);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP_REQ, on_hangup);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP_ACK, on_hangup);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP, on_hangup);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_INFO_RECEIVED, on_information);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_KEYPAD_DIGIT, on_keypad_digit);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RESTART, on_restart);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RESTART_ACK, on_restart_ack);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_IO_FAIL, on_io_fail);
+	LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_FACILITY, on_facility);
+
+	/* Callback invoked on each iteration of the lpwrap_run_pri() event loop */
+	isdn_data->spri.on_loop = check_flags;
+
+	/*
+	 * Event loop
+	 */
 	while (ftdm_running() && !ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) {
-		if (!got_d) {
-			int i, x;
-
-			for (i = 1, x = 0; i <= ftdm_span_get_chan_count(span); i++) {
-				ftdm_channel_t *chan = ftdm_span_get_channel(span, i);
-
-				if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_DQ921) {
-					if (ftdm_channel_open(ftdm_span_get_id(span), i, &isdn_data->dchan) == FTDM_SUCCESS) {
-						ftdm_log(FTDM_LOG_DEBUG, "opening D-Channel #%d %d:%d\n", x,
-							ftdm_channel_get_span_id(isdn_data->dchan), ftdm_channel_get_id(isdn_data->dchan));
-						got_d = 1;
-						x++;
-						break;
-					} else {
-					    ftdm_log(FTDM_LOG_ERROR, "failed to open D-Channel #%d %d:%d\n", x,
-						ftdm_channel_get_span_id(chan), ftdm_channel_get_id(chan));
-					}
-				}
-			}
-		}
-		if (!got_d || !isdn_data->dchan) {
-			ftdm_log(FTDM_LOG_ERROR, "Failed to get a D-Channel in span %d\n", ftdm_span_get_id(span));
-			break;
+		if (down) {
+			ftdm_log(FTDM_LOG_INFO, "PRI back up on span %d\n", ftdm_span_get_id(span));
+			ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
+			down = 0;
 		}
 
-		/* Initialize libpri trunk */
-		switch (ftdm_span_get_trunk_type(span)) {
-		case FTDM_TRUNK_E1:
-		case FTDM_TRUNK_T1:
-		case FTDM_TRUNK_J1:
-			res = lpwrap_init_pri(&isdn_data->spri, span, isdn_data->dchan,
-					isdn_data->dialect, isdn_data->mode, isdn_data->debug_mask);
-			break;
-		case FTDM_TRUNK_BRI:
-			res = lpwrap_init_bri(&isdn_data->spri, span, isdn_data->dchan,
-					isdn_data->dialect, isdn_data->mode, 1, isdn_data->debug_mask);
-#ifndef HAVE_LIBPRI_BRI
-			goto out;
-#endif
-			break;
-		case FTDM_TRUNK_BRI_PTMP:
-			res = lpwrap_init_bri(&isdn_data->spri, span, isdn_data->dchan,
-					isdn_data->dialect, isdn_data->mode, 0, isdn_data->debug_mask);
-#ifndef HAVE_LIBPRI_BRI
-			goto out;
-#endif
-			break;
-		default:
-			snprintf(span->last_error, sizeof(span->last_error), "Invalid trunk type");
-			goto out;
-		}
-
-#ifdef HAVE_LIBPRI_AOC
-		/*
-		 * Only enable facility on trunk if really required,
-		 * this may help avoid problems on troublesome lines.
-		 */
-		if (isdn_data->opts & FTMOD_LIBPRI_OPT_FACILITY_AOC) {
-			pri_facility_enable(isdn_data->spri.pri);
-		}
-#endif
-		/* Support the different switch of service status */
-		if (isdn_data->service_message_support) {
-			pri_set_service_message_support(isdn_data->spri.pri, 1 /* True */);
-		}
-
-		if (res == 0) {
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANY, on_anything);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RING, on_ring);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RINGING, on_ringing);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_PROCEEDING, on_proceeding);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_PROGRESS, on_progress);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_ANSWER, on_answer);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_UP, on_dchan_up);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_DCHAN_DOWN, on_dchan_down);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP_REQ, on_hangup);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP_ACK, on_hangup);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_HANGUP, on_hangup);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_INFO_RECEIVED, on_info);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_RESTART, on_restart);
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_IO_FAIL, on_io_fail);
-#ifdef HAVE_LIBPRI_AOC
-			LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_FACILITY, on_facility);
-#endif
-			if (down) {
-				ftdm_log(FTDM_LOG_INFO, "PRI back up on span %d\n", ftdm_span_get_id(span));
-				ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
-				down = 0;
-			}
-
-			isdn_data->spri.on_loop = check_flags;
-
-			lpwrap_run_pri(&isdn_data->spri);
-		} else {
-			ftdm_log(FTDM_LOG_CRIT, "PRI init failed!\n");
-			snprintf(span->last_error, sizeof(span->last_error), "PRI init failed!");
-			break;
-		}
+		lpwrap_run_pri(&isdn_data->spri);
 
 		if (!ftdm_running() || ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) {
 			break;
@@ -2353,8 +2451,7 @@ out:
 	/* close d-channel, if set */
 	if (isdn_data->dchan) {
 		if (ftdm_channel_close(&isdn_data->dchan) != FTDM_SUCCESS) {
-			ftdm_log(FTDM_LOG_ERROR, "Failed to close D-Channel %d:%d\n",
-				ftdm_channel_get_span_id(isdn_data->dchan), ftdm_channel_get_id(isdn_data->dchan));
+			ftdm_log_chan_msg(isdn_data->dchan, FTDM_LOG_ERROR, "Failed to close D-Channel\n");
 		}
 	}
 
@@ -2363,6 +2460,7 @@ out:
 	ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
 	ftdm_clear_flag(isdn_data, FTMOD_LIBPRI_RUNNING);
 
+	lpwrap_destroy_pri(&isdn_data->spri);
 	return NULL;
 }
 
@@ -2381,11 +2479,14 @@ static ftdm_status_t ftdm_libpri_stop(ftdm_span_t *span)
 		return FTDM_FAIL;
 	}
 
-	ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
+	ftdm_log(FTDM_LOG_INFO, "Stopping span [s%d][%s]\n",
+		ftdm_span_get_id(span), ftdm_span_get_name(span));
 
+	ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
 	check_state(span);
 
 	ftdm_set_flag(span, FTDM_SPAN_STOP_THREAD);
+	lpwrap_stop_pri(&isdn_data->spri);
 
 	while (ftdm_test_flag(span, FTDM_SPAN_IN_THREAD)) {
 		ftdm_sleep(100);
@@ -2411,6 +2512,9 @@ static ftdm_status_t ftdm_libpri_start(ftdm_span_t *span)
 		return FTDM_FAIL;
 	}
 
+	ftdm_log(FTDM_LOG_INFO, "Starting span [s%d][%s]\n",
+		ftdm_span_get_id(span), ftdm_span_get_name(span));
+
 	ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD);
 	ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
 
@@ -2552,7 +2656,6 @@ static uint32_t parse_opts(const char *in)
 static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 {
 	ftdm_libpri_data_t *isdn_data = NULL;
-	//ftdm_channel_t *dchan = NULL;
 	uint32_t bchan_count = 0;
 	uint32_t dchan_count = 0;
 	uint32_t i;
@@ -2569,23 +2672,10 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 		case FTDM_CHAN_TYPE_DQ921:
 			if (dchan_count > 1) {
 				ftdm_log(FTDM_LOG_ERROR, "Span has more than 2 D-Channels!\n");
-				snprintf(span->last_error, sizeof(span->last_error), "Span has more than 2 D-Channels!");
 				return FTDM_FAIL;
-			} else {
-#if 0
-				if (ftdm_channel_open(ftdm_span_get_id(span), i, &dchan) == FTDM_SUCCESS) {
-					ftdm_log(FTDM_LOG_DEBUG, "opening D-Channel %d:%d\n", ftdm_channel_get_span_id(dchan), ftdm_channel_get_id(dchan));
-					_ftdm_channel_set_state_force(dchan, FTDM_CHANNEL_STATE_UP);
-				} else {
-					ftdm_log(FTDM_LOG_ERROR, "Failed to open D-Channel %d:%d\n", ftdm_channel_get_span_id(chan), ftdm_channel_getid(chan));
-					snprintf(span->last_error, sizeof(span->last_error), "Failed to open D-Channel %d:%d\n", ftdm_channel_get_span_id(chan), ftdm_channel_getid(chan));
-					return FTDM_FAIL;
-				}
-#endif
-				dchan_count++;
 			}
+			dchan_count++;
 			break;
-
 		case FTDM_CHAN_TYPE_B:
 			bchan_count++;
 			break;
@@ -2595,12 +2685,10 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 	}
 	if (!dchan_count) {
 		ftdm_log(FTDM_LOG_ERROR, "Span has no D-Channel!\n");
-		snprintf(span->last_error, sizeof(span->last_error), "Span has no D-Channel!");
 		return FTDM_FAIL;
 	}
 	if (!bchan_count) {
 		ftdm_log(FTDM_LOG_ERROR, "Span has no B-Channels!\n");
-		snprintf(span->last_error, sizeof(span->last_error), "Span has no B-Channels!");
 		return FTDM_FAIL;
 	}
 
@@ -2609,7 +2697,8 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 	memset(isdn_data, 0, sizeof(*isdn_data));
 
 	/* set some default values */
-	isdn_data->ton  = PRI_UNKNOWN;
+	isdn_data->ton = PRI_UNKNOWN;
+	isdn_data->overlap_timeout_ms = OVERLAP_TIMEOUT_MS_DEFAULT;
 
 	/* Use span's trunk_mode as a reference for the default libpri mode */
 	if (ftdm_span_get_trunk_mode(span) == FTDM_TRUNK_MODE_NET) {
@@ -2623,7 +2712,6 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 	case FTDM_TRUNK_BRI_PTMP:
 #ifndef HAVE_LIBPRI_BRI
 		ftdm_log(FTDM_LOG_ERROR, "Unsupported trunk type: '%s', libpri too old\n", ftdm_span_get_trunk_type_str(span));
-		snprintf(span->last_error, sizeof(span->last_error), "Unsupported trunk type [%s], libpri too old", ftdm_span_get_trunk_type_str(span));
 		goto error;
 #endif
 	case FTDM_TRUNK_E1:
@@ -2639,7 +2727,6 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 		break;
 	default:
 		ftdm_log(FTDM_LOG_ERROR, "Invalid trunk type: '%s'\n", ftdm_span_get_trunk_type_str(span));
-		snprintf(span->last_error, sizeof(span->last_error), "Invalid trunk type [%s]", ftdm_span_get_trunk_type_str(span));
 		goto error;
 	}
 
@@ -2648,7 +2735,6 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 	 */
 	if (msn_filter_init(isdn_data) != FTDM_SUCCESS) {
 		ftdm_log(FTDM_LOG_ERROR, "Failed to init MSN filter\n");
-		snprintf(span->last_error, sizeof(span->last_error), "Failed to init MSN filter");
 		goto error;
 	}
 
@@ -2663,7 +2749,6 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 
 		if (ftdm_strlen_zero(val)) {
 			ftdm_log(FTDM_LOG_ERROR, "Parameter '%s' has no value\n", var);
-			snprintf(span->last_error, sizeof(span->last_error), "Parameter [%s] has no value", var);
 			goto error;
 		}
 
@@ -2691,6 +2776,17 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 				isdn_data->overlap = FTMOD_LIBPRI_OVERLAP_NONE;
 			}
 		}
+		else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "t302")) {
+			int tmp = atoi(val);
+			if (!tmp) {
+				isdn_data->overlap_timeout_ms = 0; /* disabled */
+			}
+			else if ((isdn_data->overlap_timeout_ms = ftdm_clamp(tmp, OVERLAP_TIMEOUT_MS_MIN, OVERLAP_TIMEOUT_MS_MAX)) != tmp) {
+				ftdm_log(FTDM_LOG_WARNING, "'%s' value '%d' outside of range [%d:%d], using '%d' ms instead\n",
+					var, tmp, OVERLAP_TIMEOUT_MS_MIN, OVERLAP_TIMEOUT_MS_MAX,
+					isdn_data->overlap_timeout_ms);
+			}
+		}
 		else if (!strcasecmp(var, "debug")) {
 			if (parse_debug(val, &isdn_data->debug_mask) == -1) {
 				ftdm_log(FTDM_LOG_ERROR, "Invalid debug flag, ignoring parameter\n");
@@ -2705,13 +2801,11 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 		else if (!strcasecmp(var, "local-number") || !strcasecmp(var, "msn")) {
 			if (msn_filter_add(isdn_data, val) != FTDM_SUCCESS) {
 				ftdm_log(FTDM_LOG_ERROR, "Invalid MSN/DDI(s) '%s' specified\n", val);
-				snprintf(span->last_error, sizeof(span->last_error), "Invalid MSN/DDI(s) '%s' specified!", val);
 				goto error;
 			}
 		}
 		else {
 			ftdm_log(FTDM_LOG_ERROR, "Unknown parameter '%s', aborting configuration\n", var);
-			snprintf(span->last_error, sizeof(span->last_error), "Unknown parameter [%s]", var);
 			goto error;
 		}
 	}
@@ -2748,8 +2842,28 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
 		ftdm_set_flag(span, FTDM_SPAN_SUGGEST_CHAN_ID);
 	}
 
+	/* Allocate per-channel private data */
+	for (i = 1; i <= ftdm_span_get_chan_count(span); i++) {
+		ftdm_channel_t *chan = ftdm_span_get_channel(span, i);
+
+		if (!chan)
+			continue;
+
+		if (ftdm_channel_get_type(chan) == FTDM_CHAN_TYPE_B) {
+			ftdm_libpri_b_chan_t *priv = NULL;
+
+			priv = calloc(1, sizeof(*priv));
+			if (!priv) {
+				ftdm_log_chan_msg(chan, FTDM_LOG_CRIT, "Failed to allocate per-channel private data\n");
+				goto error;
+			}
+			priv->channel   = chan;
+			chan->call_data = priv;
+		}
+	}
 	return FTDM_SUCCESS;
 error:
+	/* TODO: free per-channel private data */
 	msn_filter_destroy(isdn_data);
 	ftdm_safe_free(isdn_data);
 	return FTDM_FAIL;
diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h
index 260df0fe35..fcaa1bc457 100644
--- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h
+++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h
@@ -35,6 +35,10 @@
 #include "freetdm.h"
 #include "lpwrap_pri.h"
 
+#define OVERLAP_TIMEOUT_MS_DEFAULT	5000	/* 5 sec */
+#define OVERLAP_TIMEOUT_MS_MIN		3000	/* 3 sec */
+#define OVERLAP_TIMEOUT_MS_MAX		30000	/* 30 sec */
+
 typedef enum {
         SERVICE_CHANGE_STATUS_INSERVICE = 0,
         SERVICE_CHANGE_STATUS_MAINTENANCE,
@@ -71,6 +75,7 @@ struct ftdm_libpri_data {
 	int mode;
 	int dialect;
 	int overlap;		/*!< Overlap dial flags */
+	int overlap_timeout_ms;	/*!< Overlap dial timeout */
 	unsigned int layer1;
 	unsigned int ton;
 	unsigned int service_message_support;
@@ -84,6 +89,27 @@ struct ftdm_libpri_data {
 
 typedef struct ftdm_libpri_data ftdm_libpri_data_t;
 
+
+/*
+ * b-channel flags
+ */
+enum {
+	FTDM_LIBPRI_B_NONE = 0,
+	FTDM_LIBPRI_B_REMOTE_RESTART = (1 << 0),	/*!< Remote triggered channel restart */
+};
+
+/**
+ * Per-b-channel private data
+ */
+struct ftdm_libpri_b_chan {
+	struct lpwrap_timer t302;	/*!< T302 overlap receive timer */
+	ftdm_channel_t *channel;	/*!< back-pointer to b-channel */
+	q931_call *call;		/*!< libpri opaque call handle */
+	uint32_t flags;			/*!< channel flags */
+};
+
+typedef struct ftdm_libpri_b_chan ftdm_libpri_b_chan_t;
+
 #endif
 
 /* For Emacs:
diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c
index facbc27033..7cbcc7cd41 100644
--- a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c
+++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c
@@ -36,51 +36,7 @@
 #include "private/ftdm_core.h"
 #include "lpwrap_pri.h"
 
-#ifndef HAVE_GETTIMEOFDAY
-#ifdef WIN32
-#include <mmsystem.h>
-
-static __inline int gettimeofday(struct timeval *tp, void *nothing)
-{
-#ifdef WITHOUT_MM_LIB
-	SYSTEMTIME st;
-	time_t tt;
-	struct tm tmtm;
-	/* mktime converts local to UTC */
-	GetLocalTime (&st);
-	tmtm.tm_sec = st.wSecond;
-	tmtm.tm_min = st.wMinute;
-	tmtm.tm_hour = st.wHour;
-	tmtm.tm_mday = st.wDay;
-	tmtm.tm_mon = st.wMonth - 1;
-	tmtm.tm_year = st.wYear - 1900;  tmtm.tm_isdst = -1;
-	tt = mktime (&tmtm);
-	tp->tv_sec = tt;
-	tp->tv_usec = st.wMilliseconds * 1000;
-#else
-	/**
-	 ** The earlier time calculations using GetLocalTime
-	 ** had a time resolution of 10ms.The timeGetTime, part
-	 ** of multimedia apis offer a better time resolution
-	 ** of 1ms.Need to link against winmm.lib for this
-	 **/
-	unsigned long Ticks = 0;
-	unsigned long Sec =0;
-	unsigned long Usec = 0;
-	Ticks = timeGetTime();
-
-	Sec = Ticks/1000;
-	Usec = (Ticks - (Sec*1000))*1000;
-	tp->tv_sec = Sec;
-	tp->tv_usec = Usec;
-#endif /* WITHOUT_MM_LIB */
-	(void)nothing;
-	return 0;
-}
-#endif /* WIN32 */
-#endif /* HAVE_GETTIMEOFDAY */
-
-static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[] = {
+static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[LPWRAP_PRI_EVENT_MAX] = {
 	{0, LPWRAP_PRI_EVENT_ANY, "ANY"},
 	{1, LPWRAP_PRI_EVENT_DCHAN_UP, "DCHAN_UP"},
 	{2, LPWRAP_PRI_EVENT_DCHAN_DOWN, "DCHAN_DOWN"},
@@ -103,8 +59,6 @@ static struct lpwrap_pri_event_list LPWRAP_PRI_EVENT_LIST[] = {
 	{19, LPWRAP_PRI_EVENT_IO_FAIL, "IO_FAIL"},
 };
 
-#define LINE "--------------------------------------------------------------------------------"
-
 const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id)
 {
 	if (event_id < 0 || event_id >= LPWRAP_PRI_EVENT_MAX)
@@ -170,6 +124,10 @@ static int __pri_lpwrap_write(struct pri *pri, void *buf, int buflen)
 	return (int)buflen;
 }
 
+
+/*
+ * Unified init function for BRI + PRI libpri spans
+ */
 int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int debug)
 {
 	int ret = -1;
@@ -179,115 +137,255 @@ int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *
 	spri->span  = span;
 
 	if (!spri->dchan) {
-		ftdm_log(FTDM_LOG_ERROR, "No D-Channel available, unable to create PRI\n");
+		ftdm_log(FTDM_LOG_ERROR, "No D-Channel available, unable to create BRI/PRI\n");
 		return ret;
 	}
 
-	if ((spri->pri = pri_new_cb(spri->dchan->sockfd, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri))) {
-		unsigned char buf[4] = { 0 };
-		size_t buflen = sizeof(buf), len = 0;
-
-		pri_set_debug(spri->pri, debug);
-#ifdef HAVE_LIBPRI_AOC
-		pri_aoc_events_enable(spri->pri, 1);
-#endif
-		ftdm_channel_write(spri->dchan, buf, buflen, &len);
-
-		ret = 0;
-	} else {
-		ftdm_log(FTDM_LOG_ERROR, "Unable to create PRI\n");
+	if (ftdm_mutex_create(&spri->timer_mutex) != FTDM_SUCCESS) {
+		ftdm_log(FTDM_LOG_ERROR, "Failed to create timer list mutex\n");
+		return ret;
 	}
-	return ret;
-}
-
-int lpwrap_init_bri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int ptp, int debug)
-{
-	int ret = -1;
 
+	switch (ftdm_span_get_trunk_type(span)) {
+	case FTDM_TRUNK_E1:
+	case FTDM_TRUNK_J1:
+	case FTDM_TRUNK_T1:
+		spri->pri = pri_new_cb(spri->dchan->sockfd, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri);
+		break;
 #ifdef HAVE_LIBPRI_BRI
-	memset(spri, 0, sizeof(struct lpwrap_pri));
-	spri->dchan = dchan;
-	spri->span  = span;
-
-	if (!spri->dchan) {
-		ftdm_log(FTDM_LOG_ERROR, "No D-Channel available, unable to create BRI\n");
+	case FTDM_TRUNK_BRI:
+		spri->pri = pri_new_bri_cb(spri->dchan->sockfd, 1, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri);
+		break;
+	case FTDM_TRUNK_BRI_PTMP:
+		spri->pri = pri_new_bri_cb(spri->dchan->sockfd, 0, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri);
+		break;
+#endif
+	default:
+		ftdm_log(FTDM_LOG_CRIT, "Invalid/unsupported trunk type '%s'\n",
+			ftdm_span_get_trunk_type_str(span));
+		ftdm_mutex_destroy(&spri->timer_mutex);
 		return ret;
 	}
 
-	if ((spri->pri = pri_new_bri_cb(spri->dchan->sockfd, ptp, node, swtype, __pri_lpwrap_read, __pri_lpwrap_write, spri))) {
-		unsigned char buf[4] = { 0 };
-		size_t buflen = sizeof(buf), len = 0;
-
+	if (spri->pri) {
 		pri_set_debug(spri->pri, debug);
 #ifdef HAVE_LIBPRI_AOC
 		pri_aoc_events_enable(spri->pri, 1);
 #endif
-		ftdm_channel_write(spri->dchan, buf, buflen, &len);
-
 		ret = 0;
 	} else {
-		ftdm_log(FTDM_LOG_ERROR, "Unable to create BRI\n");
+		ftdm_log(FTDM_LOG_CRIT, "Unable to create BRI/PRI\n");
+		ftdm_mutex_destroy(&spri->timer_mutex);
 	}
-#else
-	ftdm_log(FTDM_LOG_ERROR, "Installed libpri version (%s) has no BRI support\n",
-			pri_get_version());
-#endif
 	return ret;
 }
 
 
-int lpwrap_one_loop(struct lpwrap_pri *spri)
+#define timeval_to_ms(x) \
+	(((ftdm_time_t)(x)->tv_sec * 1000) + (ftdm_time_t)((x)->tv_usec / 1000))
+
+int lpwrap_start_timer(struct lpwrap_pri *spri, struct lpwrap_timer *timer, const uint32_t timeout_ms, timeout_handler callback)
 {
-	fd_set rfds, efds;
-	struct timeval now = {0,0}, *next = NULL;
+	struct lpwrap_timer **prev, *cur;
+
+	if (!spri || !timer || timer->timeout)
+		return -1;
+
+	ftdm_log_chan(spri->dchan, FTDM_LOG_DEBUG, "-- Starting timer %p with timeout %u ms\n",
+		timer, timeout_ms);
+
+	timer->timeout  = ftdm_current_time_in_ms() + timeout_ms;
+	timer->callback = callback;
+	timer->next     = NULL;
+
+	ftdm_mutex_lock(spri->timer_mutex);
+
+	for (prev = &spri->timer_list, cur = spri->timer_list; cur; prev = &(*prev)->next, cur = cur->next) {
+		if (cur->timeout < timer->timeout) {
+			*prev = timer;
+			timer->next = cur;
+			break;
+		}
+	}
+	if (!cur) {
+		*prev = timer;
+	}
+
+	ftdm_mutex_unlock(spri->timer_mutex);
+	return 0;
+}
+
+int lpwrap_stop_timer(struct lpwrap_pri *spri, struct lpwrap_timer *timer)
+{
+	struct lpwrap_timer **prev, *cur;
+
+	if (!spri || !timer)
+		return -1;
+
+	if (!timer->timeout)
+		return 0;
+
+	ftdm_log_chan(spri->dchan, FTDM_LOG_DEBUG, "-- Stopping timer %p\n", timer);
+
+	ftdm_mutex_lock(spri->timer_mutex);
+
+	for (prev = &spri->timer_list, cur = spri->timer_list; cur; prev = &(*prev)->next, cur = cur->next) {
+		if (cur == timer) {
+			*prev = cur->next;
+			break;
+		}
+	}
+
+	ftdm_mutex_unlock(spri->timer_mutex);
+
+	timer->next     = NULL;
+	timer->timeout  = 0;
+	timer->callback = NULL;
+	return 0;
+}
+
+static struct lpwrap_timer *lpwrap_timer_next(struct lpwrap_pri *spri)
+{
+	return spri ? spri->timer_list : NULL;
+}
+
+static int lpwrap_run_expired(struct lpwrap_pri *spri, ftdm_time_t now_ms)
+{
+	struct lpwrap_timer *expired_list = NULL;
+	struct lpwrap_timer **prev, *cur;
+
+	if (!spri || !spri->timer_list)
+		return 0;
+
+	ftdm_mutex_lock(spri->timer_mutex);
+
+	/* Move all timers to expired list */
+	expired_list = spri->timer_list;
+
+	for (prev = &expired_list, cur = expired_list; cur; prev = &(*prev)->next, cur = cur->next) {
+		if (cur->timeout > now_ms) {
+			*prev = NULL;
+			break;
+		}
+	}
+	/* Move non-expired timer to front of timer_list (or clear list if there are none) */
+	spri->timer_list = cur;
+
+	ftdm_mutex_unlock(spri->timer_mutex);
+
+	/* fire callbacks */
+	while ((cur = expired_list)) {
+		expired_list = cur->next;
+		if (cur->callback)
+			cur->callback(spri, cur);
+		/* stop timer */
+		cur->next     = NULL;
+		cur->timeout  = 0;
+		cur->callback = NULL;
+	}
+
+	return 0;
+}
+
+
+#define LPWRAP_MAX_TIMEOUT_MS	100
+#define LPWRAP_MAX_ERRORS	2
+
+int lpwrap_run_pri_once(struct lpwrap_pri *spri)
+{
+	struct timeval *next = NULL;
+	struct lpwrap_timer *timer = NULL;
 	pri_event *event = NULL;
-	event_handler handler;
-	int sel;
+	ftdm_wait_flag_t flags;
+	ftdm_time_t now_ms, next_ms, timeout_ms, tmp_ms;
+	int ret;
 
 	if (spri->on_loop) {
-		if ((sel = spri->on_loop(spri)) < 0) {
-			return sel;
+		if ((ret = spri->on_loop(spri)) < 0)
+			return FTDM_FAIL;
+	}
+
+	/* Default timeout when no scheduled events are pending */
+	timeout_ms = LPWRAP_MAX_TIMEOUT_MS;
+	next_ms    = 0;
+	now_ms     = ftdm_current_time_in_ms();
+
+	/*
+	 * Get the next scheduled timer from libpri to set the maximum timeout,
+	 * but limit it to MAX_TIMEOUT_MS (100ms).
+	 */
+	if ((next = pri_schedule_next(spri->pri))) {
+		next_ms = timeval_to_ms(next);
+		if (now_ms >= next_ms) {
+			/* Already late, handle timeout */
+			timeout_ms = 0;
+		} else {
+			/* Calculate new timeout and limit it to MAX_TIMEOUT_MS miliseconds */
+			tmp_ms     = ftdm_min(next_ms - now_ms, LPWRAP_MAX_TIMEOUT_MS);
+			timeout_ms = ftdm_min(timeout_ms, tmp_ms);
 		}
 	}
 
-	if (spri->errs >= 2) {
-		spri->errs = 0;
-		return -1;
+	/*
+	 * Next lpwrap_timer timeout
+	 */
+	if ((timer = lpwrap_timer_next(spri))) {
+		if (now_ms >= timer->timeout) {
+			/* Already late, handle timeout */
+			timeout_ms = 0;
+		} else {
+			/* Calculate new timeout and limit it to MAX_TIMEOUT_MS miliseconds */
+			tmp_ms     = ftdm_min(timer->timeout - now_ms, LPWRAP_MAX_TIMEOUT_MS);
+			timeout_ms = ftdm_min(timeout_ms, tmp_ms);
+		}
 	}
 
-	FD_ZERO(&rfds);
-	FD_ZERO(&efds);
+	/* */
+	if (timeout_ms > 0) {
+		flags = FTDM_READ | FTDM_EVENTS;
+		ret = ftdm_channel_wait(spri->dchan, &flags, timeout_ms);
 
-#ifdef _MSC_VER
-	//Windows macro for FD_SET includes a warning C4127: conditional expression is constant
-#pragma warning(push)
-#pragma warning(disable:4127)
-#endif
+		if (spri->flags & LPWRAP_PRI_ABORT)
+			return FTDM_SUCCESS;
 
-	FD_SET(pri_fd(spri->pri), &rfds);
-	FD_SET(pri_fd(spri->pri), &efds);
+		if (ret == FTDM_TIMEOUT) {
+			now_ms = ftdm_current_time_in_ms();
 
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-	now.tv_sec = 0;
-	now.tv_usec = 100000;
-
-	sel = select(pri_fd(spri->pri) + 1, &rfds, NULL, &efds, &now);
-	if (!sel) {
-		if ((next = pri_schedule_next(spri->pri))) {
-			gettimeofday(&now, NULL);
-			if (now.tv_sec >= next->tv_sec && (now.tv_usec >= next->tv_usec || next->tv_usec <= 100000)) {
-				//ftdm_log(FTDM_LOG_DEBUG, "Check event\n");
+			if (next) {
+				if (next_ms < now_ms) {
+					ftdm_log_chan(spri->dchan, FTDM_LOG_DEBUG, "pri timer %d ms late\n",
+						(int)(now_ms - next_ms));
+				}
 				event = pri_schedule_run(spri->pri);
 			}
+			if (timer) {
+				if (timer->timeout < now_ms) {
+					ftdm_log_chan(spri->dchan, FTDM_LOG_DEBUG, "lpwrap timer %d ms late\n",
+						(int)(now_ms - timer->timeout));
+				}
+				lpwrap_run_expired(spri, now_ms);
+			}
+		} else if (flags & (FTDM_READ | FTDM_EVENTS)) {
+			event = pri_check_event(spri->pri);
+		}
+	} else {
+		/*
+		 * Scheduled event has already expired, handle it immediately
+		 */
+		if (next) {
+			event = pri_schedule_run(spri->pri);
+		}
+		if (timer) {
+			lpwrap_run_expired(spri, now_ms);
 		}
-	} else if (sel > 0) {
-		event = pri_check_event(spri->pri);
 	}
 
+	if (spri->flags & LPWRAP_PRI_ABORT)
+		return FTDM_SUCCESS;
+
 	if (event) {
+		event_handler handler;
+
 		/* 0 is catchall event handler */
 		if (event->e < 0 || event->e >= LPWRAP_PRI_EVENT_MAX) {
 			handler = spri->eventmap[0];
@@ -303,28 +401,47 @@ int lpwrap_one_loop(struct lpwrap_pri *spri)
 			ftdm_log(FTDM_LOG_CRIT, "No event handler found for event %d.\n", event->e);
 		}
 	}
-	return sel;
+
+	return FTDM_SUCCESS;
 }
 
 int lpwrap_run_pri(struct lpwrap_pri *spri)
 {
 	int ret = 0;
 
-	for (;;) {
-		if ((ret = lpwrap_one_loop(spri)) < 0) {
-#ifndef WIN32 //This needs to be adressed fror WIN32 still
-			if (errno == EINTR){
-				/* Igonore an interrupted system call */
-				continue;
-			}
-#endif
-			ftdm_log(FTDM_LOG_CRIT, "Error = %i [%s]\n", ret, strerror(errno));
+	while (!(spri->flags & LPWRAP_PRI_ABORT)) {
+		ret = lpwrap_run_pri_once(spri);
+		if (ret) {
+			ftdm_log(FTDM_LOG_ERROR, "Error = %d, [%s]\n",
+				ret, strerror(errno));
+			spri->errs++;
+		} else {
+			spri->errs = 0;
+		}
+		if (!ftdm_running())
+			break;
+		if (spri->errs >= LPWRAP_MAX_ERRORS) {
+			ftdm_log(FTDM_LOG_CRIT, "Too many errors on span, restarting\n");
+			spri->errs = 0;
 			break;
 		}
 	}
 	return ret;
 }
 
+int lpwrap_stop_pri(struct lpwrap_pri *spri)
+{
+	spri->flags |= LPWRAP_PRI_ABORT;
+	return FTDM_SUCCESS;
+}
+
+int lpwrap_destroy_pri(struct lpwrap_pri *spri)
+{
+	if (spri->timer_mutex)
+		ftdm_mutex_destroy(&spri->timer_mutex);
+	return FTDM_SUCCESS;
+}
+
 /* For Emacs:
  * Local Variables:
  * mode:c
diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h
index 052f3b080d..89708da270 100644
--- a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h
+++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.h
@@ -92,10 +92,14 @@ typedef enum {
 } lpwrap_pri_switch_t;
 
 typedef enum {
-	LPWRAP_PRI_READY = (1 << 0)
+	LPWRAP_PRI_READY = (1 << 0),
+	LPWRAP_PRI_ABORT = (1 << 1)
 } lpwrap_pri_flag_t;
 
 struct lpwrap_pri;
+struct lpwrap_timer;
+
+typedef int (*timeout_handler)(struct lpwrap_pri *, struct lpwrap_timer *);
 typedef int (*event_handler)(struct lpwrap_pri *, lpwrap_pri_event_t, pri_event *);
 typedef int (*loop_handler)(struct lpwrap_pri *);
 
@@ -108,6 +112,8 @@ struct lpwrap_pri {
 	event_handler eventmap[LPWRAP_PRI_EVENT_MAX];
 	loop_handler on_loop;
 	int errs;
+	struct lpwrap_timer *timer_list;
+	ftdm_mutex_t *timer_mutex;
 };
 
 typedef struct lpwrap_pri lpwrap_pri_t;
@@ -118,15 +124,21 @@ struct lpwrap_pri_event_list {
 	const char *name;
 };
 
+struct lpwrap_timer {
+	struct lpwrap_timer *next;
+	ftdm_time_t timeout;
+	timeout_handler callback;
+};
 
+int lpwrap_start_timer(struct lpwrap_pri *spri, struct lpwrap_timer *timer, const uint32_t timeout_ms, timeout_handler callback);
+int lpwrap_stop_timer(struct lpwrap_pri *spri, struct lpwrap_timer *timer);
 
 #define LPWRAP_MAP_PRI_EVENT(spri, event, func) spri.eventmap[event] = func;
-
 const char *lpwrap_pri_event_str(lpwrap_pri_event_t event_id);
-int lpwrap_one_loop(struct lpwrap_pri *spri);
-int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int debug);
-int lpwrap_init_bri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int ptp, int debug);
-int lpwrap_run_pri(struct lpwrap_pri *spri);
-#define lpwrap_run_bri(x)	lpwrap_run_pri(x)
 
+int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t *dchan, int swtype, int node, int debug);
+int lpwrap_destroy_pri(struct lpwrap_pri *spri);
+int lpwrap_run_pri_once(struct lpwrap_pri *spri);
+int lpwrap_run_pri(struct lpwrap_pri *spri);
+int lpwrap_stop_pri(struct lpwrap_pri *spri);
 #endif