ftmod_libpri: Major rework of runtime thread, lpwrap event loop, restart b-channel handling and some new features.

Add per b-channel private data (ftdm_libpri_b_data_t), move the q.931 libpri
call pointer into it and use channel->call_data to make it accessible.

Use ftdm_channel_wait() instead of select() to wait for I/O events on
the D-Channel and rewrite the timeout handling, adding support for custom
timers that can be added to the b-channel private data and started/stopped
with lpwrap_timer_start()/_stop().

The (sorted) list of active lpwrap timers is protected by the timer_mutex,
which is initialized in the now unified lpwrap_init_pri() (see below) and
destroyed in the new lpwrap_destroy_pri() function.

Unify lpwrap_init_pri() and lpwrap_init_bri(). Use the span type to select
BRI/PRI and Point-to-Point vs. Multipoint settings.

Use the new custom lpwrap timer API to implement T302 (aka overlap receiving
digit timeout). The default T302 timeout is 3000 miliseconds and can be set
via the "digit_timeout" or "t302" span configuration parameter
(0 = disable timer, valid range = 3000 ... 30000 miliseconds).

The lpwrap_pri_init() changes made it neccessarry to rewrite the span runtime
thread event loop. The d-channel initialization and libpri event handler
registration have been moved out of the event loop. The b-channel restart
handling in the event loop has been left as-is, pending a further clean up in
the future.

Rename on_info() to on_information() and add a new on_keypad_digit() event
handler to convert incoming KEYPAD DIGIT messages to channel DTMF events
(only useful on BRI PTMP NT-mode).

The channel restart handling has been improved to distinguish between locally
triggered and restarts caused by the remote end (= incoming RESTART messages).
Restarts on the D-Channel now use pri_restart(), causing a Q.921 reset on
the span.

Large patch, i'd have loved to split this into smaller pieces and actually
test some of the bits more toroughly (restart handling), but some people
are f*cking annoying, so here it is.

Signed-off-by: Stefan Knoblich <stkn@openisdn.net>
This commit is contained in:
Stefan Knoblich 2012-09-03 19:51:50 +02:00
parent f3cd4f41a0
commit f012b7abf8
4 changed files with 604 additions and 335 deletions

View File

@ -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,13 +924,17 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
switch (ftdm_channel_get_state(chan)) {
case FTDM_CHANNEL_STATE_DOWN:
{
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;
chan_priv->call = NULL;
}
/* 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),
@ -937,13 +945,14 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
ftdm_channel_get_id(chan));
}
}
}
break;
case FTDM_CHANNEL_STATE_PROGRESS:
{
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,18 +1068,28 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan)
case FTDM_CHANNEL_STATE_RESTART:
{
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(ftdm_channel_get_span(chan), &sig);
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;
case FTDM_CHANNEL_STATE_UP:
{
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,61 +2344,38 @@ 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;
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++) {
/*
* 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(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++;
ftdm_log_chan_msg(chan, FTDM_LOG_DEBUG, "Opened D-Channel\n");
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));
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:
/*
* 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);
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");
if (res) {
ftdm_log(FTDM_LOG_CRIT, "Failed to initialize BRI/PRI on span %d\n",
ftdm_span_get_id(span));
goto out;
}
@ -2293,10 +2390,10 @@ static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj)
#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 */);
pri_set_service_message_support(isdn_data->spri.pri, 1);
}
if (res == 0) {
/* 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);
@ -2308,26 +2405,27 @@ static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj)
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_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);
#ifdef HAVE_LIBPRI_AOC
LPWRAP_MAP_PRI_EVENT(isdn_data->spri, LPWRAP_PRI_EVENT_FACILITY, on_facility);
#endif
/* 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 (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;
}
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++;
}
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;
}
@ -2610,6 +2698,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
/* set some default values */
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;

View File

@ -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:

View File

@ -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;
}
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;
}
if (spri->errs >= 2) {
spri->errs = 0;
return -1;
}
/* Default timeout when no scheduled events are pending */
timeout_ms = LPWRAP_MAX_TIMEOUT_MS;
next_ms = 0;
now_ms = ftdm_current_time_in_ms();
FD_ZERO(&rfds);
FD_ZERO(&efds);
#ifdef _MSC_VER
//Windows macro for FD_SET includes a warning C4127: conditional expression is constant
#pragma warning(push)
#pragma warning(disable:4127)
#endif
FD_SET(pri_fd(spri->pri), &rfds);
FD_SET(pri_fd(spri->pri), &efds);
#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) {
/*
* 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))) {
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");
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);
}
}
/*
* 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);
}
}
/* */
if (timeout_ms > 0) {
flags = FTDM_READ | FTDM_EVENTS;
ret = ftdm_channel_wait(spri->dchan, &flags, timeout_ms);
if (spri->flags & LPWRAP_PRI_ABORT)
return FTDM_SUCCESS;
if (ret == FTDM_TIMEOUT) {
now_ms = ftdm_current_time_in_ms();
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));
}
} else if (sel > 0) {
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);
}
}
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;
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;
}
#endif
ftdm_log(FTDM_LOG_CRIT, "Error = %i [%s]\n", ret, strerror(errno));
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

View File

@ -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