From 90177fe708cf2068b6b29f99f708760eb2859066 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Tue, 4 Jan 2011 16:38:28 +0000 Subject: [PATCH] Optional HOLD/RETRIEVE signaling for PTMP TE when the bridge goes on and off hold. Added the moh_signaling option to specify what to do when the channel's bridged peer puts the ISDN channel on and off of hold. Implemented as a FSM to control libpri ISDN signaling when the bridged peer places the channel on and off of hold with the AST_CONTROL_HOLD and AST_CONTROL_UNHOLD control frames. JIRA SWP-2687 JIRA ABE-2691 Review: https://reviewboard.asterisk.org/r/1063/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@300212 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 11 +- UPGRADE.txt | 5 +- channels/chan_dahdi.c | 14 + channels/sig_pri.c | 966 +++++++++++++++++++++++++++++++-- channels/sig_pri.h | 65 +++ configs/chan_dahdi.conf.sample | 15 +- 6 files changed, 1011 insertions(+), 65 deletions(-) diff --git a/CHANGES b/CHANGES index a99de3aed4..1053f09bb1 100644 --- a/CHANGES +++ b/CHANGES @@ -15,23 +15,30 @@ Asterisk Manager Interface -------------------------- * PeerStatus now includes Address and Port. + * Added Hold events for when the remote party puts the call on and off hold + for chan_dahdi ISDN channels. Asterisk HTTP Server -------------------------- * The HTTP Server can bind to IPv6 addresses. CLI Changes ------------ +-------------------------- * New 'gtalk show settings' command showing the current settings loaded from gtalk.conf. * The 'logger reload' command now supports an optional argument, specifying an alternate configuration file to use. CDR ---- +-------------------------- * The filter option in cdr_adaptive_odbc now supports negating the argument, thus allowing records which do NOT match the specified filter. +libpri channel driver (chan_dahdi) DAHDI changes +-------------------------- + * Added moh_signaling option to specify what to do when the channel's bridged + peer puts the ISDN channel on hold. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ---------------- ------------------------------------------------------------------------------ diff --git a/UPGRADE.txt b/UPGRADE.txt index 7c8eb919c0..57c490e1e2 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -7,7 +7,7 @@ === versions listed below. These changes may require that === you modify your configuration files, dialplan or (in === some cases) source code if you have your own Asterisk -=== modules or patches. These files also includes advance +=== modules or patches. These files also include advance === notice of any functionality that has been marked as === 'deprecated' and may be removed in a future release, === along with the suggested replacement functionality. @@ -30,6 +30,9 @@ Gtalk: - The default value for 'context' and 'parkinglots' in gtalk.conf has been changed to 'default', previously they were empty. +chan_dahdi: + - The mohinterpret=passthrough setting is deprecated in favor of + moh_signaling=notify. =========================================================== =========================================================== diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 0b8261c8c6..9010e3384e 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -12283,6 +12283,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, ast_copy_string(pris[span].pri.localprefix, conf->pri.pri.localprefix, sizeof(pris[span].pri.localprefix)); ast_copy_string(pris[span].pri.privateprefix, conf->pri.pri.privateprefix, sizeof(pris[span].pri.privateprefix)); ast_copy_string(pris[span].pri.unknownprefix, conf->pri.pri.unknownprefix, sizeof(pris[span].pri.unknownprefix)); + pris[span].pri.moh_signaling = conf->pri.pri.moh_signaling; pris[span].pri.resetinterval = conf->pri.pri.resetinterval; for (x = 0; x < PRI_MAX_TIMERS; x++) { @@ -17091,6 +17092,19 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct } else if (!strcasecmp(v->name, "hold_disconnect_transfer")) { confp->pri.pri.hold_disconnect_transfer = ast_true(v->value); #endif /* defined(HAVE_PRI_CALL_HOLD) */ + } else if (!strcasecmp(v->name, "moh_signaling") + || !strcasecmp(v->name, "moh_signalling")) { + if (!strcasecmp(v->value, "moh")) { + confp->pri.pri.moh_signaling = SIG_PRI_MOH_SIGNALING_MOH; + } else if (!strcasecmp(v->value, "notify")) { + confp->pri.pri.moh_signaling = SIG_PRI_MOH_SIGNALING_NOTIFY; +#if defined(HAVE_PRI_CALL_HOLD) + } else if (!strcasecmp(v->value, "hold")) { + confp->pri.pri.moh_signaling = SIG_PRI_MOH_SIGNALING_HOLD; +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + } else { + confp->pri.pri.moh_signaling = SIG_PRI_MOH_SIGNALING_MOH; + } #if defined(HAVE_PRI_CCSS) } else if (!strcasecmp(v->name, "cc_ptmp_recall_mode")) { if (!strcasecmp(v->value, "global")) { diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 7f7cfccd4e..9a50804694 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -1078,6 +1078,36 @@ static void pri_queue_control(struct sig_pri_span *pri, int chanpos, int subclas pri_queue_frame(pri, chanpos, &f); } +/*! + * \internal + * \brief Find the channel associated with the libpri call. + * \since 1.10 + * + * \param pri sig_pri span controller to find interface. + * \param call LibPRI opaque call pointer to find. + * + * \note Assumes the pri->lock is already obtained. + * + * \retval array-index into private pointer array on success. + * \retval -1 on error. + */ +static int pri_find_principle_by_call(struct sig_pri_span *pri, q931_call *call) +{ + int idx; + + if (!call) { + /* Cannot find a call without a call. */ + return -1; + } + for (idx = 0; idx < pri->numchans; ++idx) { + if (pri->pvts[idx] && pri->pvts[idx]->call == call) { + /* Found the principle */ + return idx; + } + } + return -1; +} + static int pri_find_principle(struct sig_pri_span *pri, int channel, q931_call *call) { int x; @@ -1092,19 +1122,8 @@ static int pri_find_principle(struct sig_pri_span *pri, int channel, q931_call * prioffset = PRI_CHANNEL(channel); if (!prioffset || (channel & PRI_HELD_CALL)) { - if (!call) { - /* Cannot find a call waiting call or held call without a call. */ - return -1; - } - principle = -1; - for (x = 0; x < pri->numchans; ++x) { - if (pri->pvts[x] - && pri->pvts[x]->call == call) { - principle = x; - break; - } - } - return principle; + /* Find the call waiting call or held call. */ + return pri_find_principle_by_call(pri, call); } span = PRI_SPAN(channel); @@ -1222,6 +1241,10 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal #if defined(HAVE_PRI_SETUP_KEYPAD) strcpy(new_chan->keypad_digits, old_chan->keypad_digits); #endif /* defined(HAVE_PRI_SETUP_KEYPAD) */ + strcpy(new_chan->moh_suggested, old_chan->moh_suggested); + new_chan->moh_state = old_chan->moh_state; + old_chan->moh_state = SIG_PRI_MOH_STATE_IDLE; + #if defined(HAVE_PRI_AOC_EVENTS) new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id; new_chan->aoc_e = old_chan->aoc_e; @@ -1432,34 +1455,6 @@ static int pri_find_empty_nobch(struct sig_pri_span *pri) } #endif /* defined(HAVE_PRI_CALL_HOLD) */ -#if defined(HAVE_PRI_CALL_HOLD) -/*! - * \internal - * \brief Find the channel associated with the libpri call. - * \since 1.8 - * - * \param pri sig_pri span controller to find interface. - * \param call LibPRI opaque call pointer to find. - * - * \note Assumes the pri->lock is already obtained. - * - * \retval array-index into private pointer array on success. - * \retval -1 on error. - */ -static int pri_find_pri_call(struct sig_pri_span *pri, q931_call *call) -{ - int idx; - - for (idx = 0; idx < pri->numchans; ++idx) { - if (pri->pvts[idx] && pri->pvts[idx]->call == call) { - /* Found the channel */ - return idx; - } - } - return -1; -} -#endif /* defined(HAVE_PRI_CALL_HOLD) */ - static void *do_idle_thread(void *v_pvt) { struct sig_pri_chan *pvt = v_pvt; @@ -1972,8 +1967,8 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_ c2.held = call_2_held; call_2 = &c2; - call_1->chanpos = pri_find_pri_call(pri, call_1->pri); - call_2->chanpos = pri_find_pri_call(pri, call_2->pri); + call_1->chanpos = pri_find_principle_by_call(pri, call_1->pri); + call_2->chanpos = pri_find_principle_by_call(pri, call_2->pri); if (call_1->chanpos < 0 || call_2->chanpos < 0) { /* Calls not found in span control. */ if (rsp_callback) { @@ -4035,6 +4030,677 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev } } +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief Kill the call. + * \since 1.10 + * + * \param pri Span controller to find interface. + * \param call LibPRI opaque call pointer to find. + * \param cause Reason call was killed. + * + * \note Assumes the pvt->pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_kill_call(struct sig_pri_span *pri, q931_call *call, int cause) +{ + int chanpos; + + chanpos = pri_find_principle_by_call(pri, call); + if (chanpos < 0) { + pri_hangup(pri->pri, call, cause); + return; + } + sig_pri_lock_private(pri->pvts[chanpos]); + if (!pri->pvts[chanpos]->owner) { + pri_hangup(pri->pri, call, cause); + pri->pvts[chanpos]->call = NULL; + sig_pri_unlock_private(pri->pvts[chanpos]); + return; + } + pri->pvts[chanpos]->owner->hangupcause = cause; + pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP); + sig_pri_unlock_private(pri->pvts[chanpos]); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +/*! + * \internal + * \brief Convert the MOH state to string. + * \since 1.10 + * + * \param state MOH state to process. + * + * \return String version of MOH state. + */ +static const char *sig_pri_moh_state_str(enum sig_pri_moh_state state) +{ + const char *str; + + str = "Unknown"; + switch (state) { + case SIG_PRI_MOH_STATE_IDLE: + str = "SIG_PRI_MOH_STATE_IDLE"; + break; + case SIG_PRI_MOH_STATE_NOTIFY: + str = "SIG_PRI_MOH_STATE_NOTIFY"; + break; + case SIG_PRI_MOH_STATE_MOH: + str = "SIG_PRI_MOH_STATE_MOH"; + break; +#if defined(HAVE_PRI_CALL_HOLD) + case SIG_PRI_MOH_STATE_HOLD_REQ: + str = "SIG_PRI_MOH_STATE_HOLD_REQ"; + break; + case SIG_PRI_MOH_STATE_PEND_UNHOLD: + str = "SIG_PRI_MOH_STATE_PEND_UNHOLD"; + break; + case SIG_PRI_MOH_STATE_HOLD: + str = "SIG_PRI_MOH_STATE_HOLD"; + break; + case SIG_PRI_MOH_STATE_RETRIEVE_REQ: + str = "SIG_PRI_MOH_STATE_RETRIEVE_REQ"; + break; + case SIG_PRI_MOH_STATE_PEND_HOLD: + str = "SIG_PRI_MOH_STATE_PEND_HOLD"; + break; + case SIG_PRI_MOH_STATE_RETRIEVE_FAIL: + str = "SIG_PRI_MOH_STATE_RETRIEVE_FAIL"; + break; +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + case SIG_PRI_MOH_STATE_NUM: + /* Not a real state. */ + break; + } + return str; +} + +/*! + * \internal + * \brief Convert the MOH event to string. + * \since 1.10 + * + * \param event MOH event to process. + * + * \return String version of MOH event. + */ +static const char *sig_pri_moh_event_str(enum sig_pri_moh_event event) +{ + const char *str; + + str = "Unknown"; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + str = "SIG_PRI_MOH_EVENT_RESET"; + break; + case SIG_PRI_MOH_EVENT_HOLD: + str = "SIG_PRI_MOH_EVENT_HOLD"; + break; + case SIG_PRI_MOH_EVENT_UNHOLD: + str = "SIG_PRI_MOH_EVENT_UNHOLD"; + break; +#if defined(HAVE_PRI_CALL_HOLD) + case SIG_PRI_MOH_EVENT_HOLD_ACK: + str = "SIG_PRI_MOH_EVENT_HOLD_ACK"; + break; + case SIG_PRI_MOH_EVENT_HOLD_REJ: + str = "SIG_PRI_MOH_EVENT_HOLD_REJ"; + break; + case SIG_PRI_MOH_EVENT_RETRIEVE_ACK: + str = "SIG_PRI_MOH_EVENT_RETRIEVE_ACK"; + break; + case SIG_PRI_MOH_EVENT_RETRIEVE_REJ: + str = "SIG_PRI_MOH_EVENT_RETRIEVE_REJ"; + break; + case SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK: + str = "SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK"; + break; +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + case SIG_PRI_MOH_EVENT_NUM: + /* Not a real event. */ + break; + } + return str; +} + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief Retrieve a call that was placed on hold by the HOLD message. + * \since 1.10 + * + * \param pvt Channel private control structure. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_retrieve_call(struct sig_pri_chan *pvt) +{ + int chanpos; + int channel; + + if (pvt->pri->nodetype == PRI_NETWORK) { + /* Find an available channel to propose */ + chanpos = pri_find_empty_chan(pvt->pri, 1); + if (chanpos < 0) { + /* No channels available. */ + return SIG_PRI_MOH_STATE_RETRIEVE_FAIL; + } + channel = PVT_TO_CHANNEL(pvt->pri->pvts[chanpos]); + + /* + * We cannot occupy or reserve this channel at this time because + * the retrieve may fail or we could have a RETRIEVE collision. + */ + } else { + /* Let the network pick the channel. */ + channel = 0; + } + + if (pri_retrieve(pvt->pri->pri, pvt->call, channel)) { + return SIG_PRI_MOH_STATE_RETRIEVE_FAIL; + } + return SIG_PRI_MOH_STATE_RETRIEVE_REQ; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +/*! + * \internal + * \brief MOH FSM state idle. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_idle(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_HOLD: + if (!strcasecmp(pvt->mohinterpret, "passthrough")) { + /* + * This config setting is deprecated. + * The old way did not send MOH just in case the notification was ignored. + */ + pri_notify(pvt->pri->pri, pvt->call, pvt->prioffset, PRI_NOTIFY_REMOTE_HOLD); + next_state = SIG_PRI_MOH_STATE_NOTIFY; + break; + } + + switch (pvt->pri->moh_signaling) { + default: + case SIG_PRI_MOH_SIGNALING_MOH: + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + next_state = SIG_PRI_MOH_STATE_MOH; + break; + case SIG_PRI_MOH_SIGNALING_NOTIFY: + /* Send MOH anyway in case the far end does not interpret the notification. */ + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + + pri_notify(pvt->pri->pri, pvt->call, pvt->prioffset, PRI_NOTIFY_REMOTE_HOLD); + next_state = SIG_PRI_MOH_STATE_NOTIFY; + break; +#if defined(HAVE_PRI_CALL_HOLD) + case SIG_PRI_MOH_SIGNALING_HOLD: + if (pri_hold(pvt->pri->pri, pvt->call)) { + /* Fall back to MOH instead */ + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + next_state = SIG_PRI_MOH_STATE_MOH; + } else { + next_state = SIG_PRI_MOH_STATE_HOLD_REQ; + } + break; +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + } + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} + +/*! + * \internal + * \brief MOH FSM state notify remote party. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_notify(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_UNHOLD: + pri_notify(pvt->pri->pri, pvt->call, pvt->prioffset, PRI_NOTIFY_REMOTE_RETRIEVAL); + /* Fall through */ + case SIG_PRI_MOH_EVENT_RESET: + ast_moh_stop(chan); + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} + +/*! + * \internal + * \brief MOH FSM state generate moh. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_moh(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + case SIG_PRI_MOH_EVENT_UNHOLD: + ast_moh_stop(chan); + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief MOH FSM state hold requested. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_hold_req(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_UNHOLD: + next_state = SIG_PRI_MOH_STATE_PEND_UNHOLD; + break; + case SIG_PRI_MOH_EVENT_HOLD_REJ: + /* Fall back to MOH */ + if (chan) { + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + } + next_state = SIG_PRI_MOH_STATE_MOH; + break; + case SIG_PRI_MOH_EVENT_HOLD_ACK: + next_state = SIG_PRI_MOH_STATE_HOLD; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief MOH FSM state hold requested with pending unhold. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_pend_unhold(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_HOLD: + next_state = SIG_PRI_MOH_STATE_HOLD_REQ; + break; + case SIG_PRI_MOH_EVENT_HOLD_REJ: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_HOLD_ACK: + next_state = sig_pri_moh_retrieve_call(pvt); + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief MOH FSM state hold. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_hold(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_UNHOLD: + next_state = sig_pri_moh_retrieve_call(pvt); + break; + case SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK: + /* Fall back to MOH */ + if (chan) { + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + } + next_state = SIG_PRI_MOH_STATE_MOH; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief MOH FSM state retrieve requested. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_retrieve_req(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_HOLD: + next_state = SIG_PRI_MOH_STATE_PEND_HOLD; + break; + case SIG_PRI_MOH_EVENT_RETRIEVE_ACK: + case SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_RETRIEVE_REJ: + next_state = SIG_PRI_MOH_STATE_RETRIEVE_FAIL; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief MOH FSM state retrieve requested with pending hold. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_pend_hold(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_UNHOLD: + next_state = SIG_PRI_MOH_STATE_RETRIEVE_REQ; + break; + case SIG_PRI_MOH_EVENT_RETRIEVE_ACK: + case SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK: + /* + * Successfully came off of hold. Now we can reinterpret the + * MOH signaling option to handle the pending HOLD request. + */ + switch (pvt->pri->moh_signaling) { + default: + case SIG_PRI_MOH_SIGNALING_MOH: + if (chan) { + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + } + next_state = SIG_PRI_MOH_STATE_MOH; + break; + case SIG_PRI_MOH_SIGNALING_NOTIFY: + /* Send MOH anyway in case the far end does not interpret the notification. */ + if (chan) { + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + } + + pri_notify(pvt->pri->pri, pvt->call, pvt->prioffset, PRI_NOTIFY_REMOTE_HOLD); + next_state = SIG_PRI_MOH_STATE_NOTIFY; + break; + case SIG_PRI_MOH_SIGNALING_HOLD: + if (pri_hold(pvt->pri->pri, pvt->call)) { + /* Fall back to MOH instead */ + if (chan) { + ast_moh_start(chan, pvt->moh_suggested, pvt->mohinterpret); + } + next_state = SIG_PRI_MOH_STATE_MOH; + } else { + next_state = SIG_PRI_MOH_STATE_HOLD_REQ; + } + break; + } + break; + case SIG_PRI_MOH_EVENT_RETRIEVE_REJ: + /* + * We cannot reinterpret the MOH signaling option because we + * failed to come off of hold. + */ + next_state = SIG_PRI_MOH_STATE_HOLD; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief MOH FSM state retrieve failed. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +static enum sig_pri_moh_state sig_pri_moh_fsm_retrieve_fail(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state next_state; + + next_state = pvt->moh_state; + switch (event) { + case SIG_PRI_MOH_EVENT_RESET: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + case SIG_PRI_MOH_EVENT_HOLD: + next_state = SIG_PRI_MOH_STATE_HOLD; + break; + case SIG_PRI_MOH_EVENT_UNHOLD: + next_state = sig_pri_moh_retrieve_call(pvt); + break; + case SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK: + next_state = SIG_PRI_MOH_STATE_IDLE; + break; + default: + break; + } + pvt->moh_state = next_state; + return next_state; +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +/*! + * \internal + * \brief MOH FSM state function type. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Next MOH state + */ +typedef enum sig_pri_moh_state (*sig_pri_moh_fsm_state)(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event); + +/*! MOH FSM state table. */ +static const sig_pri_moh_fsm_state sig_pri_moh_fsm[SIG_PRI_MOH_STATE_NUM] = { +/* *INDENT-OFF* */ + [SIG_PRI_MOH_STATE_IDLE] = sig_pri_moh_fsm_idle, + [SIG_PRI_MOH_STATE_NOTIFY] = sig_pri_moh_fsm_notify, + [SIG_PRI_MOH_STATE_MOH] = sig_pri_moh_fsm_moh, +#if defined(HAVE_PRI_CALL_HOLD) + [SIG_PRI_MOH_STATE_HOLD_REQ] = sig_pri_moh_fsm_hold_req, + [SIG_PRI_MOH_STATE_PEND_UNHOLD] = sig_pri_moh_fsm_pend_unhold, + [SIG_PRI_MOH_STATE_HOLD] = sig_pri_moh_fsm_hold, + [SIG_PRI_MOH_STATE_RETRIEVE_REQ] = sig_pri_moh_fsm_retrieve_req, + [SIG_PRI_MOH_STATE_PEND_HOLD] = sig_pri_moh_fsm_pend_hold, + [SIG_PRI_MOH_STATE_RETRIEVE_FAIL] = sig_pri_moh_fsm_retrieve_fail, +#endif /* defined(HAVE_PRI_CALL_HOLD) */ +/* *INDENT-ON* */ +}; + +/*! + * \internal + * \brief Send an event to the MOH FSM. + * \since 1.10 + * + * \param chan Channel to post event to (Usually pvt->owner) + * \param pvt Channel private control structure. + * \param event MOH event to process. + * + * \note Assumes the pvt->pri->lock is already obtained. + * \note Assumes the sig_pri_lock_private(pvt) is already obtained. + * + * \return Nothing + */ +static void sig_pri_moh_fsm_event(struct ast_channel *chan, struct sig_pri_chan *pvt, enum sig_pri_moh_event event) +{ + enum sig_pri_moh_state orig_state; + enum sig_pri_moh_state next_state; + const char *chan_name; + + if (chan) { + chan_name = ast_strdupa(chan->name); + } else { + chan_name = "Unknown"; + } + orig_state = pvt->moh_state; + ast_debug(2, "Channel '%s' MOH-Event: %s in state %s\n", chan_name, + sig_pri_moh_event_str(event), sig_pri_moh_state_str(orig_state)); + if (orig_state < SIG_PRI_MOH_STATE_IDLE || SIG_PRI_MOH_STATE_NUM <= orig_state + || !sig_pri_moh_fsm[orig_state]) { + /* Programming error: State not implemented. */ + ast_log(LOG_ERROR, "MOH state not implemented: %s(%d)\n", + sig_pri_moh_state_str(orig_state), orig_state); + return; + } + /* Execute the state. */ + next_state = sig_pri_moh_fsm[orig_state](chan, pvt, event); + ast_debug(2, "Channel '%s' MOH-Next-State: %s\n", chan_name, + (orig_state == next_state) ? "$" : sig_pri_moh_state_str(next_state)); +} + #if defined(HAVE_PRI_CALL_HOLD) /*! * \internal @@ -4134,6 +4800,94 @@ done_with_private:; } #endif /* defined(HAVE_PRI_CALL_HOLD) */ +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief Handle the hold acknowledge event from libpri. + * \since 1.10 + * + * \param pri sig_pri PRI control structure. + * \param ev Hold acknowledge event received. + * + * \note Assumes the pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_handle_hold_ack(struct sig_pri_span *pri, pri_event *ev) +{ + int chanpos; + + /* + * We were successfully put on hold by the remote party + * so we just need to switch to a no_b_channel channel. + */ + chanpos = pri_find_empty_nobch(pri); + if (chanpos < 0) { + /* Very bad news. No hold channel available. */ + ast_log(LOG_ERROR, + "Span %d: No hold channel available for held call that is on %d/%d\n", + pri->span, PRI_SPAN(ev->hold_ack.channel), PRI_CHANNEL(ev->hold_ack.channel)); + sig_pri_kill_call(pri, ev->hold_ack.call, PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED); + return; + } + chanpos = pri_fixup_principle(pri, chanpos, ev->hold_ack.call); + if (chanpos < 0) { + /* Should never happen. */ + sig_pri_kill_call(pri, ev->hold_ack.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE); + return; + } + + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, ev->e, ev->hold_ack.channel, + ev->hold_ack.subcmds, ev->hold_ack.call); + sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], + SIG_PRI_MOH_EVENT_HOLD_ACK); + sig_pri_unlock_private(pri->pvts[chanpos]); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief Handle the hold reject event from libpri. + * \since 1.10 + * + * \param pri sig_pri PRI control structure. + * \param ev Hold reject event received. + * + * \note Assumes the pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_handle_hold_rej(struct sig_pri_span *pri, pri_event *ev) +{ + int chanpos; + + chanpos = pri_find_principle(pri, ev->hold_rej.channel, ev->hold_rej.call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Span %d: Could not find principle for HOLD_REJECT\n", + pri->span); + return; + } + chanpos = pri_fixup_principle(pri, chanpos, ev->hold_rej.call); + if (chanpos < 0) { + /* Should never happen. */ + sig_pri_kill_call(pri, ev->hold_rej.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE); + return; + } + + ast_debug(1, "Span %d: HOLD_REJECT cause: %d(%s)\n", pri->span, + ev->hold_rej.cause, pri_cause2str(ev->hold_rej.cause)); + + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, ev->e, ev->hold_rej.channel, + ev->hold_rej.subcmds, ev->hold_rej.call); + sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], + SIG_PRI_MOH_EVENT_HOLD_REJ); + sig_pri_unlock_private(pri->pvts[chanpos]); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + #if defined(HAVE_PRI_CALL_HOLD) /*! * \internal @@ -4194,10 +4948,93 @@ static void sig_pri_handle_retrieve(struct sig_pri_span *pri, pri_event *ev) sig_pri_ami_hold_event(pri->pvts[chanpos]->owner, 0); ast_channel_unlock(pri->pvts[chanpos]->owner); } - sig_pri_unlock_private(pri->pvts[chanpos]); - sig_pri_span_devstate_changed(pri); pri_retrieve_ack(pri->pri, ev->retrieve.call, PVT_TO_CHANNEL(pri->pvts[chanpos])); + sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], + SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK); + sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief Handle the retrieve acknowledge event from libpri. + * \since 1.10 + * + * \param pri sig_pri PRI control structure. + * \param ev Retrieve acknowledge event received. + * + * \note Assumes the pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_handle_retrieve_ack(struct sig_pri_span *pri, pri_event *ev) +{ + int chanpos; + + chanpos = pri_find_principle(pri, ev->retrieve_ack.channel, ev->retrieve_ack.call); + if (chanpos < 0) { + ast_log(LOG_WARNING, + "Span %d: Could not find principle for RETRIEVE_ACKNOWLEDGE\n", pri->span); + return; + } + chanpos = pri_fixup_principle(pri, chanpos, ev->retrieve_ack.call); + if (chanpos < 0) { + /* Very bad news. The channel is already in use. */ + sig_pri_kill_call(pri, ev->retrieve_ack.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); + return; + } + + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, ev->e, ev->retrieve_ack.channel, + ev->retrieve_ack.subcmds, ev->retrieve_ack.call); + sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], + SIG_PRI_MOH_EVENT_RETRIEVE_ACK); + sig_pri_unlock_private(pri->pvts[chanpos]); +} +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + +#if defined(HAVE_PRI_CALL_HOLD) +/*! + * \internal + * \brief Handle the retrieve reject event from libpri. + * \since 1.10 + * + * \param pri sig_pri PRI control structure. + * \param ev Retrieve reject event received. + * + * \note Assumes the pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_handle_retrieve_rej(struct sig_pri_span *pri, pri_event *ev) +{ + int chanpos; + + chanpos = pri_find_principle(pri, ev->retrieve_rej.channel, ev->retrieve_rej.call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Span %d: Could not find principle for RETRIEVE_REJECT\n", + pri->span); + return; + } + chanpos = pri_fixup_principle(pri, chanpos, ev->retrieve_rej.call); + if (chanpos < 0) { + /* Should never happen. */ + sig_pri_kill_call(pri, ev->retrieve_rej.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE); + return; + } + + ast_debug(1, "Span %d: RETRIEVE_REJECT cause: %d(%s)\n", pri->span, + ev->retrieve_rej.cause, pri_cause2str(ev->retrieve_rej.cause)); + + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, ev->e, ev->retrieve_rej.channel, + ev->retrieve_rej.subcmds, ev->retrieve_rej.call); + sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], + SIG_PRI_MOH_EVENT_RETRIEVE_REJ); + sig_pri_unlock_private(pri->pvts[chanpos]); } #endif /* defined(HAVE_PRI_CALL_HOLD) */ @@ -4392,8 +5229,8 @@ static void *pri_dchannel(void *vpri) if (e) { if (pri->debug) { - ast_verbose("Span: %d Processing event: %s\n", - pri->span, pri_event2str(e->e)); + ast_verbose("Span: %d Processing event: %s(%d)\n", + pri->span, pri_event2str(e->e), e->e); } if (e->e != PRI_EVENT_DCHAN_DOWN) { @@ -5775,12 +6612,14 @@ static void *pri_dchannel(void *vpri) #endif /* defined(HAVE_PRI_CALL_HOLD) */ #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_HOLD_ACK: - ast_debug(1, "Event: HOLD_ACK\n"); + /* We should not be getting any CIS calls with this message type. */ + sig_pri_handle_hold_ack(pri, e); break; #endif /* defined(HAVE_PRI_CALL_HOLD) */ #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_HOLD_REJ: - ast_debug(1, "Event: HOLD_REJ\n"); + /* We should not be getting any CIS calls with this message type. */ + sig_pri_handle_hold_rej(pri, e); break; #endif /* defined(HAVE_PRI_CALL_HOLD) */ #if defined(HAVE_PRI_CALL_HOLD) @@ -5791,16 +6630,19 @@ static void *pri_dchannel(void *vpri) #endif /* defined(HAVE_PRI_CALL_HOLD) */ #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_RETRIEVE_ACK: - ast_debug(1, "Event: RETRIEVE_ACK\n"); + /* We should not be getting any CIS calls with this message type. */ + sig_pri_handle_retrieve_ack(pri, e); break; #endif /* defined(HAVE_PRI_CALL_HOLD) */ #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_RETRIEVE_REJ: - ast_debug(1, "Event: RETRIEVE_REJ\n"); + /* We should not be getting any CIS calls with this message type. */ + sig_pri_handle_retrieve_rej(pri, e); break; #endif /* defined(HAVE_PRI_CALL_HOLD) */ default: - ast_debug(1, "Event: %d\n", e->e); + ast_debug(1, "Span: %d Unhandled event: %s(%d)\n", + pri->span, pri_event2str(e->e), e->e); break; } } @@ -5858,6 +6700,7 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) /* Make sure we have a call (or REALLY have a call in the case of a PRI) */ if (!pri_grab(p, p->pri)) { + sig_pri_moh_fsm_event(ast, p, SIG_PRI_MOH_EVENT_RESET); if (p->call) { if (p->alreadyhungup) { ast_log(LOG_DEBUG, "Already hungup... Calling hangup once, and clearing call\n"); @@ -6546,24 +7389,31 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi } break; case AST_CONTROL_HOLD: - if (p->pri && !strcasecmp(p->mohinterpret, "passthrough")) { + ast_copy_string(p->moh_suggested, S_OR(data, ""), sizeof(p->moh_suggested)); + if (p->pri) { if (!pri_grab(p, p->pri)) { - res = pri_notify(p->pri->pri, p->call, p->prioffset, PRI_NOTIFY_REMOTE_HOLD); + sig_pri_moh_fsm_event(chan, p, SIG_PRI_MOH_EVENT_HOLD); pri_rel(p->pri); } else { ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span); } - } else + } else { + /* Something is wrong here. A PRI channel without the pri pointer? */ ast_moh_start(chan, data, p->mohinterpret); + } break; case AST_CONTROL_UNHOLD: - if (p->pri && !strcasecmp(p->mohinterpret, "passthrough")) { + if (p->pri) { if (!pri_grab(p, p->pri)) { - res = pri_notify(p->pri->pri, p->call, p->prioffset, PRI_NOTIFY_REMOTE_RETRIEVAL); + sig_pri_moh_fsm_event(chan, p, SIG_PRI_MOH_EVENT_UNHOLD); pri_rel(p->pri); + } else { + ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span); } - } else + } else { + /* Something is wrong here. A PRI channel without the pri pointer? */ ast_moh_stop(chan); + } break; case AST_CONTROL_SRCUPDATE: res = 0; diff --git a/channels/sig_pri.h b/channels/sig_pri.h index c981cfbb8f..2832bd5d39 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -83,6 +83,67 @@ enum sig_pri_law { SIG_PRI_ALAW }; +enum sig_pri_moh_signaling { + /*! Generate MOH to the remote party. */ + SIG_PRI_MOH_SIGNALING_MOH, + /*! Send hold notification signaling to the remote party. */ + SIG_PRI_MOH_SIGNALING_NOTIFY, +#if defined(HAVE_PRI_CALL_HOLD) + /*! Use HOLD/RETRIEVE signaling to release the B channel while on hold. */ + SIG_PRI_MOH_SIGNALING_HOLD, +#endif /* defined(HAVE_PRI_CALL_HOLD) */ +}; + +enum sig_pri_moh_state { + /*! Bridged peer has not put us on hold. */ + SIG_PRI_MOH_STATE_IDLE, + /*! Bridged peer has put us on hold and we were to notify the remote party. */ + SIG_PRI_MOH_STATE_NOTIFY, + /*! Bridged peer has put us on hold and we were to play MOH or HOLD/RETRIEVE fallback. */ + SIG_PRI_MOH_STATE_MOH, +#if defined(HAVE_PRI_CALL_HOLD) + /*! Requesting to put channel on hold. */ + SIG_PRI_MOH_STATE_HOLD_REQ, + /*! Trying to go on hold when bridged peer requested to unhold. */ + SIG_PRI_MOH_STATE_PEND_UNHOLD, + /*! Channel is held. */ + SIG_PRI_MOH_STATE_HOLD, + /*! Requesting to take channel out of hold. */ + SIG_PRI_MOH_STATE_RETRIEVE_REQ, + /*! Trying to take channel out of hold when bridged peer requested to hold. */ + SIG_PRI_MOH_STATE_PEND_HOLD, + /*! Failed to take the channel out of hold. No B channels were available? */ + SIG_PRI_MOH_STATE_RETRIEVE_FAIL, +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + + /*! Number of MOH states. Must be last in enum. */ + SIG_PRI_MOH_STATE_NUM +}; + +enum sig_pri_moh_event { + /*! Reset the MOH state machine. (Because of hangup.) */ + SIG_PRI_MOH_EVENT_RESET, + /*! Bridged peer placed this channel on hold. */ + SIG_PRI_MOH_EVENT_HOLD, + /*! Bridged peer took this channel off hold. */ + SIG_PRI_MOH_EVENT_UNHOLD, +#if defined(HAVE_PRI_CALL_HOLD) + /*! The hold request was successfully acknowledged. */ + SIG_PRI_MOH_EVENT_HOLD_ACK, + /*! The hold request was rejected. */ + SIG_PRI_MOH_EVENT_HOLD_REJ, + /*! The unhold request was successfully acknowledged. */ + SIG_PRI_MOH_EVENT_RETRIEVE_ACK, + /*! The unhold request was rejected. */ + SIG_PRI_MOH_EVENT_RETRIEVE_REJ, + /*! The remote party took this channel off hold. */ + SIG_PRI_MOH_EVENT_REMOTE_RETRIEVE_ACK, +#endif /* defined(HAVE_PRI_CALL_HOLD) */ + + /*! Number of MOH events. Must be last in enum. */ + SIG_PRI_MOH_EVENT_NUM +}; + struct sig_pri_span; struct sig_pri_callback { @@ -201,6 +262,9 @@ struct sig_pri_chan { /*! \brief Keypad digits that came in with the SETUP message. */ char keypad_digits[AST_MAX_EXTENSION]; #endif /* defined(HAVE_PRI_SETUP_KEYPAD) */ + /*! Music class suggested with AST_CONTROL_HOLD. */ + char moh_suggested[MAX_MUSICCLASS]; + enum sig_pri_moh_state moh_state; #if defined(HAVE_PRI_AOC_EVENTS) struct pri_subcmd_aoc_e aoc_e; @@ -330,6 +394,7 @@ struct sig_pri_span { char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */ char privateprefix[20]; /*!< for private dialplans */ char unknownprefix[20]; /*!< for unknown dialplans */ + enum sig_pri_moh_signaling moh_signaling; long resetinterval; /*!< Interval (in seconds) for resetting unused channels */ #if defined(HAVE_PRI_MWI) /*! \brief Active MWI mailboxes */ diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample index cc58fac9da..0c0bed7226 100644 --- a/configs/chan_dahdi.conf.sample +++ b/configs/chan_dahdi.conf.sample @@ -902,15 +902,22 @@ pickupgroup=1 ; ;faxbuffers=>6,full ; +; This option specifies what to do when the channel's bridged peer puts the +; ISDN channel on hold. Settable per logical ISDN span. +; moh: Generate music-on-hold to the remote party. +; notify: Send hold notification signaling to the remote party. +; For ETSI PTP and ETSI PTMP NT links. +; (The notify setting deprecates the mohinterpret=passthrough setting.) +; hold: Use HOLD/RETRIEVE signaling to release the B channel while on hold. +; For ETSI PTMP TE links. +; +;moh_signaling=moh +; ; This option specifies a preference for which music on hold class this channel ; should listen to when put on hold if the music class has not been set on the ; channel with Set(CHANNEL(musicclass)=whatever) in the dialplan, and the peer ; channel putting this one on hold did not suggest a music class. ; -; If this option is set to "passthrough", then the hold message will always be -; passed through as signalling instead of generating hold music locally. This -; setting is only valid when used on a channel that uses digital signalling. -; ; This option may be set globally or on a per-channel basis. ; ;mohinterpret=default