summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES11
-rw-r--r--UPGRADE.txt5
-rw-r--r--channels/chan_dahdi.c14
-rw-r--r--channels/sig_pri.c966
-rw-r--r--channels/sig_pri.h65
-rw-r--r--configs/chan_dahdi.conf.sample15
6 files changed, 1011 insertions, 65 deletions
diff --git a/CHANGES b/CHANGES
index a99de3aed..1053f09bb 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 7c8eb919c..57c490e1e 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 0b8261c8c..9010e3384 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 7f7cfccd4..9a5080469 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) {
@@ -4038,6 +4033,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
* \brief Post an AMI hold event.
* \since 1.10
*
@@ -4137,6 +4803,94 @@ done_with_private:;
#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
* \brief Handle the retrieve event from libpri.
* \since 1.8
*
@@ -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 c981cfbb8..2832bd5d3 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 cc58fac9d..0c0bed722 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