summaryrefslogtreecommitdiff
path: root/channels
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2011-01-04 16:38:28 +0000
committerRichard Mudgett <rmudgett@digium.com>2011-01-04 16:38:28 +0000
commit90177fe708cf2068b6b29f99f708760eb2859066 (patch)
treebca0002a7ada0b454ea6b331a9ed928b2cad99eb /channels
parent9be73e35de3ff7455063db96326c351b65dc52fa (diff)
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
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_dahdi.c14
-rw-r--r--channels/sig_pri.c966
-rw-r--r--channels/sig_pri.h65
3 files changed, 987 insertions, 58 deletions
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 */