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)
{
*/
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;
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;
{
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) {
{
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) {
{
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) {
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));
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);
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 {
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;
{
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) {
ton = isdn_data->ton;
}
- chan->call_data = call;
+ chan_priv->call = call;
sr = pri_sr_new();
if (!sr) {
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);
}
{
// 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);
}
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:
}
}
+
/**
- * \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);
}
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)) {
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) {
}
}
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;
{
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;
}
}
- 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);
// 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)) {
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
}
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;
}
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;
*/
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);
+ int i;
- 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 (pevent->restart.channel < 1) {
+ ftdm_log_chan_msg(spri->dchan, FTDM_LOG_NOTICE, "-- Restarting interface\n");
- if (!chan) {
- return 0;
+ 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;
- if (pevent->restart.channel < 1) {
- ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
- } else {
+ 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
*/
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;
- 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));
- }
- }
+ /*
+ * 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;
}
}
- 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;
- }
+ }
- /* 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;
- }
+ /*
+ * 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 /* 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);
+ /*
+ * 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
- 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;
+ /* 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;
- 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;
+ /*
+ * Event loop
+ */
+ while (ftdm_running() && !ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) {
+ 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;
}
+ lpwrap_run_pri(&isdn_data->spri);
+
if (!ftdm_running() || ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD)) {
break;
}
/* 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");
}
}
ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
ftdm_clear_flag(isdn_data, FTMOD_LIBPRI_RUNNING);
+ lpwrap_destroy_pri(&isdn_data->spri);
return NULL;
}
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);
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);
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;
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;
}
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;
}
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) {
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:
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;
}
*/
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;
}
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;
}
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");
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;
}
}
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;
#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"},
{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)
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;
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 (ftdm_mutex_create(&spri->timer_mutex) != FTDM_SUCCESS) {
+ ftdm_log(FTDM_LOG_ERROR, "Failed to create timer list mutex\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;
+ 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
+ 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_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");
+ ftdm_log(FTDM_LOG_CRIT, "Unable to create BRI/PRI\n");
+ ftdm_mutex_destroy(&spri->timer_mutex);
}
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)
+
+#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)
{
- int ret = -1;
+ struct lpwrap_timer **prev, *cur;
-#ifdef HAVE_LIBPRI_BRI
- memset(spri, 0, sizeof(struct lpwrap_pri));
- spri->dchan = dchan;
- spri->span = span;
+ if (!spri || !timer || timer->timeout)
+ return -1;
- if (!spri->dchan) {
- ftdm_log(FTDM_LOG_ERROR, "No D-Channel available, unable to create BRI\n");
- return ret;
+ 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;
}
- 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;
+ ftdm_mutex_unlock(spri->timer_mutex);
+ return 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);
+int lpwrap_stop_timer(struct lpwrap_pri *spri, struct lpwrap_timer *timer)
+{
+ struct lpwrap_timer **prev, *cur;
- ret = 0;
- } else {
- ftdm_log(FTDM_LOG_ERROR, "Unable to create BRI\n");
+ 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;
+ }
}
-#else
- ftdm_log(FTDM_LOG_ERROR, "Installed libpri version (%s) has no BRI support\n",
- pri_get_version());
-#endif
- return ret;
+
+ 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;
+}
-int lpwrap_one_loop(struct lpwrap_pri *spri)
+static int lpwrap_run_expired(struct lpwrap_pri *spri, ftdm_time_t now_ms)
{
- fd_set rfds, efds;
- struct timeval now = {0,0}, *next = NULL;
- pri_event *event = NULL;
- event_handler handler;
- int sel;
+ struct lpwrap_timer *expired_list = NULL;
+ struct lpwrap_timer **prev, *cur;
- if (spri->on_loop) {
- if ((sel = spri->on_loop(spri)) < 0) {
- return sel;
+ 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;
+ }
- if (spri->errs >= 2) {
- spri->errs = 0;
- return -1;
+ 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;
+ ftdm_wait_flag_t flags;
+ ftdm_time_t now_ms, next_ms, timeout_ms, tmp_ms;
+ int ret;
+
+ if (spri->on_loop) {
+ if ((ret = spri->on_loop(spri)) < 0)
+ return FTDM_FAIL;
}
- FD_ZERO(&rfds);
- FD_ZERO(&efds);
+ /* 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);
+ }
+ }
-#ifdef _MSC_VER
- //Windows macro for FD_SET includes a warning C4127: conditional expression is constant
-#pragma warning(push)
-#pragma warning(disable:4127)
-#endif
+ /*
+ * 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_SET(pri_fd(spri->pri), &rfds);
- FD_SET(pri_fd(spri->pri), &efds);
+ /* */
+ if (timeout_ms > 0) {
+ flags = FTDM_READ | FTDM_EVENTS;
+ ret = ftdm_channel_wait(spri->dchan, &flags, timeout_ms);
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
+ if (spri->flags & LPWRAP_PRI_ABORT)
+ return FTDM_SUCCESS;
- now.tv_sec = 0;
- now.tv_usec = 100000;
+ if (ret == FTDM_TIMEOUT) {
+ now_ms = ftdm_current_time_in_ms();
- 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];
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