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