From 8c58a109994bd7e80af045653a19a8c54bbbe922 Mon Sep 17 00:00:00 2001 From: Matteo Brancaleoni Date: Thu, 8 Nov 2012 12:59:22 +0100 Subject: [PATCH] ftmod_libpri: Rework handling of peer-initiated hangup events Use peerhangup flag variable to track whether a hangup has been initiated by the peer or libpri itself (e.g. Layer 2 timeouts). These changes fix a couple of problems with hangup events not being handled properly in some situations: - Call abort caused by incoming RESTART on a channel in use - T309 timeout after L2 loss - Improved hangup handling in libpri-side on_hangup() event handler and state_advance() (FreeTDM side) Signed-off-by: Stefan Knoblich --- .../src/ftmod/ftmod_libpri/ftmod_libpri.c | 51 +++++++++++++++---- .../src/ftmod/ftmod_libpri/ftmod_libpri.h | 1 + .../src/ftmod/ftmod_libpri/lpwrap_pri.c | 4 ++ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c index 2f1a5ff9ed..0e22b0f350 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -942,7 +942,7 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan) ftdm_channel_t *chtmp = chan; if (call) { - pri_destroycall(isdn_data->spri.pri, call); + /* pri call destroy is done by libpri itself (on release_ack) */ chan_priv->call = NULL; } @@ -953,6 +953,9 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan) lpwrap_stop_timer(&isdn_data->spri, &chan_priv->t316); chan_priv->t316_timeout_cnt = 0; + /* Unset remote hangup */ + chan_priv->peerhangup = 0; + 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), @@ -1206,12 +1209,21 @@ static ftdm_status_t state_advance(ftdm_channel_t *chan) { if (call) { ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan); - pri_hangup(isdn_data->spri.pri, call, caller_data->hangup_cause); -// pri_destroycall(isdn_data->spri.pri, call); -// chan_priv->call = NULL; + + if (chan_priv->peerhangup) { + /* Call is inbound and hangup has been initiated by peer */ + if (!ftdm_test_flag(chan, FTDM_CHANNEL_OUTBOUND)) { + ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + } else if (caller_data->hangup_cause == PRI_CAUSE_NO_USER_RESPONSE) { + /* Can happen when we have a DL link expire or some timer expired */ + ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + } else if (caller_data->hangup_cause == PRI_CAUSE_DESTINATION_OUT_OF_ORDER) { + /* Can happen when we have a DL link expire or some timer expired */ + ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + } + } } - ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); } break; @@ -1368,6 +1380,7 @@ static int on_hangup(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_even { ftdm_span_t *span = spri->span; ftdm_channel_t *chan = ftdm_span_get_channel(span, pevent->hangup.channel); + ftdm_libpri_b_chan_t *chan_priv = chan->call_data; if (!chan) { ftdm_log(FTDM_LOG_CRIT, "-- Hangup on channel %d:%d but it's not in use?\n", ftdm_span_get_id(spri->span), pevent->hangup.channel); @@ -1386,8 +1399,6 @@ static int on_hangup(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_even ftdm_log(FTDM_LOG_DEBUG, "-- Hangup REQ on channel %d:%d\n", ftdm_span_get_id(spri->span), pevent->hangup.channel); - pri_hangup(spri->pri, pevent->hangup.call, pevent->hangup.cause); - chan->caller_data.hangup_cause = pevent->hangup.cause; switch (ftdm_channel_get_state(chan)) { @@ -1400,19 +1411,27 @@ static int on_hangup(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_even } break; - case LPWRAP_PRI_EVENT_HANGUP_ACK: /* */ + case LPWRAP_PRI_EVENT_HANGUP_ACK: /* RELEASE_COMPLETE */ ftdm_log(FTDM_LOG_DEBUG, "-- Hangup ACK on channel %d:%d\n", ftdm_span_get_id(spri->span), pevent->hangup.channel); - pri_hangup(spri->pri, pevent->hangup.call, pevent->hangup.cause); - - ftdm_set_state(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + switch (ftdm_channel_get_state(chan)) { + case FTDM_CHANNEL_STATE_RESTART: + /* ACK caused by DL FAILURE in DISC REQ */ + ftdm_set_state(chan, FTDM_CHANNEL_STATE_DOWN); + break; + default: + ftdm_set_state(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); + break; + } break; case LPWRAP_PRI_EVENT_HANGUP: /* "RELEASE/RELEASE_COMPLETE/other" */ ftdm_log(FTDM_LOG_DEBUG, "-- Hangup on channel %d:%d\n", ftdm_span_get_id(spri->span), pevent->hangup.channel); + chan_priv->peerhangup = 1; + switch (ftdm_channel_get_state(chan)) { case FTDM_CHANNEL_STATE_DIALING: case FTDM_CHANNEL_STATE_RINGING: @@ -1424,9 +1443,19 @@ static int on_hangup(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_even ftdm_set_state(chan, FTDM_CHANNEL_STATE_TERMINATING); break; case FTDM_CHANNEL_STATE_HANGUP: + /* this will send "RELEASE_COMPLETE", eventually */ + pri_hangup(spri->pri, pevent->hangup.call, chan->caller_data.hangup_cause); chan->caller_data.hangup_cause = pevent->hangup.cause; ftdm_set_state(chan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); break; + case FTDM_CHANNEL_STATE_RESTART: + /* + * We got an hungup doing a restart, normally beacause link has been lost during + * a call and the T309 timer has expired. So destroy it :) (DL_RELEASE_IND) + */ + pri_destroycall(spri->pri, pevent->hangup.call); + ftdm_set_state(chan, FTDM_CHANNEL_STATE_DOWN); + break; // case FTDM_CHANNEL_STATE_TERMINATING: // ftdm_set_state(chan, FTDM_CHANNEL_STATE_HANGUP); // break; diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h index 26db8d3155..852f5caaca 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.h @@ -131,6 +131,7 @@ struct ftdm_libpri_b_chan { q931_call *call; /*!< libpri opaque call handle */ uint32_t flags; /*!< channel flags */ uint32_t t316_timeout_cnt; /*!< T316 timeout counter */ + int peerhangup; /*!< hangup requested from libpri (RELEASE/RELEASE_ACK/DL_RELEASE/TIMERS EXPIRY) */ }; typedef struct ftdm_libpri_b_chan ftdm_libpri_b_chan_t; diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c index c9f0576951..44518a4496 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/lpwrap_pri.c @@ -169,6 +169,10 @@ int lpwrap_init_pri(struct lpwrap_pri *spri, ftdm_span_t *span, ftdm_channel_t * if (spri->pri) { pri_set_debug(spri->pri, debug); +#ifdef HAVE_LIBPRI_BRI + /* "follow Q.931 Section 5.3.2 call hangup better" */ + pri_hangup_fix_enable(spri->pri, 1); +#endif #ifdef HAVE_LIBPRI_AOC pri_aoc_events_enable(spri->pri, 1); #endif