Make --with-pjproject-bundled the default for Asterisk 15
[asterisk/asterisk.git] / channels / sig_pri.c
index 48c8c33..1b228af 100644 (file)
  */
 //#define ALWAYS_PICK_CHANNEL  1
 
-/*!
- * Define to force a RESTART on a channel that returns a cause
- * code of PRI_CAUSE_REQUESTED_CHAN_UNAVAIL(44).  If the cause
- * is because of a stuck channel on the peer and the channel is
- * always the next channel we pick for an outgoing call then
- * this can help.
- */
-#define FORCE_RESTART_UNAVAIL_CHANS            1
-
 #if defined(HAVE_PRI_CCSS)
 struct sig_pri_cc_agent_prv {
        /*! Asterisk span D channel control structure. */
@@ -729,12 +720,7 @@ static int ast_to_pri_char_set(enum AST_PARTY_CHAR_SET ast_char_set)
  */
 static void sig_pri_set_subaddress(struct ast_party_subaddress *ast_subaddress, const struct pri_party_subaddress *pri_subaddress)
 {
-       char *cnum, *ptr;
-       int x, len;
-
-       if (ast_subaddress->str) {
-               ast_free(ast_subaddress->str);
-       }
+       ast_free(ast_subaddress->str);
        if (pri_subaddress->length <= 0) {
                ast_party_subaddress_init(ast_subaddress);
                return;
@@ -744,8 +730,14 @@ static void sig_pri_set_subaddress(struct ast_party_subaddress *ast_subaddress,
                /* NSAP */
                ast_subaddress->str = ast_strdup((char *) pri_subaddress->data);
        } else {
+               char *cnum;
+               char *ptr;
+               int x;
+               int len;
+
                /* User Specified */
-               if (!(cnum = ast_malloc(2 * pri_subaddress->length + 1))) {
+               cnum = ast_malloc(2 * pri_subaddress->length + 1);
+               if (!cnum) {
                        ast_party_subaddress_init(ast_subaddress);
                        return;
                }
@@ -753,15 +745,15 @@ static void sig_pri_set_subaddress(struct ast_party_subaddress *ast_subaddress,
                ptr = cnum;
                len = pri_subaddress->length - 1; /* -1 account for zero based indexing */
                for (x = 0; x < len; ++x) {
-                       ptr += sprintf(ptr, "%02x", pri_subaddress->data[x]);
+                       ptr += sprintf(ptr, "%02hhx", (unsigned char)pri_subaddress->data[x]);
                }
 
                if (pri_subaddress->odd_even_indicator) {
                        /* ODD */
-                       sprintf(ptr, "%01x", (pri_subaddress->data[len]) >> 4);
+                       sprintf(ptr, "%01hhx", (unsigned char)((pri_subaddress->data[len]) >> 4));
                } else {
                        /* EVEN */
-                       sprintf(ptr, "%02x", pri_subaddress->data[len]);
+                       sprintf(ptr, "%02hhx", (unsigned char)pri_subaddress->data[len]);
                }
                ast_subaddress->str = cnum;
        }
@@ -1027,12 +1019,14 @@ static int sig_pri_play_tone(struct sig_pri_chan *p, enum sig_pri_tone tone)
        }
 }
 
-static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state, int ulaw, int transfercapability, char *exten, const struct ast_channel *requestor)
+static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state,
+       enum sig_pri_law law, int transfercapability, char *exten,
+       const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor)
 {
        struct ast_channel *c;
 
        if (sig_pri_callbacks.new_ast_channel) {
-               c = sig_pri_callbacks.new_ast_channel(p->chan_pvt, state, ulaw, exten, requestor);
+               c = sig_pri_callbacks.new_ast_channel(p->chan_pvt, state, law, exten, assignedids, requestor);
        } else {
                return NULL;
        }
@@ -1040,8 +1034,8 @@ static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int s
                return NULL;
        }
 
-       if (!p->owner)
-               p->owner = c;
+       ast_assert(p->owner == NULL || p->owner == c);
+       p->owner = c;
        p->isidlecall = 0;
        p->alreadyhungup = 0;
        ast_channel_transfercapability_set(c, transfercapability);
@@ -1097,14 +1091,17 @@ static void sig_pri_ami_channel_event(struct sig_pri_chan *p)
        }
 }
 
-struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor, int transfercapability)
+struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law,
+       const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor,
+       int transfercapability)
 {
        struct ast_channel *ast;
 
        ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
 
        sig_pri_set_outgoing(p, 1);
-       ast = sig_pri_new_ast_channel(p, AST_STATE_RESERVED, law, transfercapability, p->exten, requestor);
+       ast = sig_pri_new_ast_channel(p, AST_STATE_RESERVED, law, transfercapability,
+               p->exten, assignedids, requestor);
        if (!ast) {
                sig_pri_set_outgoing(p, 0);
        }
@@ -1370,10 +1367,9 @@ static void sig_pri_queue_unhold(struct sig_pri_span *pri, int chanpos)
 static void pri_queue_control(struct sig_pri_span *pri, int chanpos, int subclass)
 {
        struct ast_frame f = {AST_FRAME_CONTROL, };
-       struct sig_pri_chan *p = pri->pvts[chanpos];
 
        if (sig_pri_callbacks.queue_control) {
-               sig_pri_callbacks.queue_control(p->chan_pvt, subclass);
+               sig_pri_callbacks.queue_control(pri->pvts[chanpos]->chan_pvt, subclass);
        }
 
        f.subclass.integer = subclass;
@@ -1382,6 +1378,31 @@ static void pri_queue_control(struct sig_pri_span *pri, int chanpos, int subclas
 
 /*!
  * \internal
+ * \brief Queue a request to hangup control frame onto the owner channel.
+ *
+ * \param pri PRI span control structure.
+ * \param chanpos Channel position in the span.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_queue_hangup(struct sig_pri_span *pri, int chanpos)
+{
+       if (sig_pri_callbacks.queue_control) {
+               sig_pri_callbacks.queue_control(pri->pvts[chanpos]->chan_pvt, AST_CONTROL_HANGUP);
+       }
+
+       sig_pri_lock_owner(pri, chanpos);
+       if (pri->pvts[chanpos]->owner) {
+               ast_queue_hangup(pri->pvts[chanpos]->owner);
+               ast_channel_unlock(pri->pvts[chanpos]->owner);
+       }
+}
+
+/*!
+ * \internal
  * \brief Queue a PVT_CAUSE_CODE frame onto the owner channel.
  * \since 11
  *
@@ -1446,6 +1467,27 @@ static int pri_find_principle_by_call(struct sig_pri_span *pri, q931_call *call)
 
 /*!
  * \internal
+ * \brief Queue the span for destruction
+ * \since 13.0
+ *
+ * \param pri PRI span control structure.
+ *
+ * Asks the channel driver to queue the span for destruction at a
+ * possibly later time, if (e.g.) locking considerations don't allow
+ * destroying it right now.
+ *
+ * \return Nothing
+ */
+static void pri_destroy_later(struct sig_pri_span *pri)
+{
+       if (!sig_pri_callbacks.destroy_later) {
+               return;
+       }
+       sig_pri_callbacks.destroy_later(pri);
+}
+
+/*!
+ * \internal
  * \brief Kill the call.
  * \since 10.0
  *
@@ -1623,6 +1665,9 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal
 #if defined(HAVE_PRI_CALL_WAITING)
                new_chan->is_call_waiting = old_chan->is_call_waiting;
 #endif /* defined(HAVE_PRI_CALL_WAITING) */
+#if defined(HAVE_PRI_SETUP_ACK_INBAND)
+               new_chan->no_dialed_digits = old_chan->no_dialed_digits;
+#endif /* defined(HAVE_PRI_SETUP_ACK_INBAND) */
 
 #if defined(HAVE_PRI_AOC_EVENTS)
                old_chan->aoc_s_request_invoke_id_valid = 0;
@@ -1638,6 +1683,9 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal
 #if defined(HAVE_PRI_CALL_WAITING)
                old_chan->is_call_waiting = 0;
 #endif /* defined(HAVE_PRI_CALL_WAITING) */
+#if defined(HAVE_PRI_SETUP_ACK_INBAND)
+               old_chan->no_dialed_digits = 0;
+#endif /* defined(HAVE_PRI_SETUP_ACK_INBAND) */
 
                /* More stuff to transfer to the new channel. */
                new_chan->call_level = old_chan->call_level;
@@ -1997,11 +2045,10 @@ static void *do_idle_thread(void *v_pvt)
        int timeout_ms = 30000;
        int ms;
        struct timeval start;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        if ((callid = ast_channel_callid(chan))) {
                ast_callid_threadassoc_add(callid);
-               callid = ast_callid_unref(callid);
        }
 
        ast_verb(3, "Initiating idle call on channel %s\n", ast_channel_name(chan));
@@ -2056,7 +2103,7 @@ static void *pri_ss_thread(void *data)
        int res;
        int len;
        int timeout;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        if (!chan) {
                /* We lost the owner before we could get started. */
@@ -2065,7 +2112,6 @@ static void *pri_ss_thread(void *data)
 
        if ((callid = ast_channel_callid(chan))) {
                ast_callid_threadassoc_add(callid);
-               ast_callid_unref(callid);
        }
 
        /*
@@ -2131,10 +2177,10 @@ static void *pri_ss_thread(void *data)
                /* Start the real PBX */
                ast_channel_exten_set(chan, exten);
                sig_pri_dsp_reset_and_flush_digits(p);
-#if defined(ISSUE_16789)
+#if defined(JIRA_ASTERISK_15594)
                /*
-                * Conditionaled out this code to effectively revert the Mantis
-                * issue 16789 change.  It breaks overlap dialing through
+                * Conditionaled out this code to effectively revert the JIRA
+                * ASTERISK-15594 change.  It breaks overlap dialing through
                 * Asterisk.  There is not enough information available at this
                 * point to know if dialing is complete.  The
                 * ast_exists_extension(), ast_matchmore_extension(), and
@@ -2157,7 +2203,7 @@ static void *pri_ss_thread(void *data)
                        }
                        sig_pri_unlock_private(p);
                }
-#endif /* defined(ISSUE_16789) */
+#endif /* defined(JIRA_ASTERISK_15594) */
 
                sig_pri_set_echocanceller(p, 1);
                ast_channel_lock(chan);
@@ -2421,8 +2467,8 @@ static void party_json_to_ami(struct ast_str **msg, const char *prefix, struct a
        struct ast_json *subaddress = ast_json_object_get(party, "subaddress");
 
        /* Combined party presentation */
-       ast_str_append(msg, 0, "%sPres: %d (%s)\r\n", prefix,
-               (uint32_t)ast_json_integer_get(presentation),
+       ast_str_append(msg, 0, "%sPres: %jd (%s)\r\n", prefix,
+               ast_json_integer_get(presentation),
                ast_json_string_get(presentation_txt));
 
        /* Party number */
@@ -4004,14 +4050,14 @@ static void sig_pri_send_aoce_termination_request(struct sig_pri_span *pri, int
        }
 
        if (!(decoded = ast_aoc_create(AST_AOC_REQUEST, 0, AST_AOC_REQUEST_E))) {
-               ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
+               ast_queue_hangup(pvt->owner);
                goto cleanup_termination_request;
        }
 
        ast_aoc_set_termination_request(decoded);
 
        if (!(encoded = ast_aoc_encode(decoded, &encoded_size, pvt->owner))) {
-               ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
+               ast_queue_hangup(pvt->owner);
                goto cleanup_termination_request;
        }
 
@@ -4020,7 +4066,7 @@ static void sig_pri_send_aoce_termination_request(struct sig_pri_span *pri, int
        whentohangup.tv_sec = ms / 1000;
 
        if (ast_queue_control_data(pvt->owner, AST_CONTROL_AOC, encoded, encoded_size)) {
-               ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
+               ast_queue_hangup(pvt->owner);
                goto cleanup_termination_request;
        }
 
@@ -4264,43 +4310,6 @@ static void sig_pri_handle_cis_subcmds(struct sig_pri_span *pri, int event_id,
        }
 }
 
-#if defined(HAVE_PRI_AOC_EVENTS)
-/*!
- * \internal
- * \brief detect if AOC-S subcmd is present.
- * \since 1.8
- *
- * \param subcmds Subcommands to process if any. (Could be NULL).
- *
- * \note Knowing whether or not an AOC-E subcmd is present on certain
- * PRI hangup events is necessary to determine what method to use to hangup
- * the ast_channel.  If an AOC-E subcmd just came in, then a new AOC-E was queued
- * on the ast_channel.  If a soft hangup is used, the AOC-E msg will never make it
- * across the bridge, but if a AST_CONTROL_HANGUP frame is queued behind it
- * we can ensure the AOC-E frame makes it to it's destination before the hangup
- * frame is read.
- *
- *
- * \retval 0 AOC-E is not present in subcmd list
- * \retval 1 AOC-E is present in subcmd list
- */
-static int detect_aoc_e_subcmd(const struct pri_subcommands *subcmds)
-{
-       int i;
-
-       if (!subcmds) {
-               return 0;
-       }
-       for (i = 0; i < subcmds->counter_subcmd; ++i) {
-               const struct pri_subcommand *subcmd = &subcmds->subcmd[i];
-               if (subcmd->cmd == PRI_SUBCMD_AOC_E) {
-                       return 1;
-               }
-       }
-       return 0;
-}
-#endif /* defined(HAVE_PRI_AOC_EVENTS) */
-
 /*!
  * \internal
  * \brief Handle the call associated PRI subcommand events.
@@ -4368,14 +4377,11 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
 
                                pri->pvts[chanpos]->cid_subaddr[0] = '\0';
 #if defined(HAVE_PRI_SUBADDR)
-                               if (ast_connected.id.subaddress.valid) {
-                                       ast_party_subaddress_set(&ast_channel_caller(owner)->id.subaddress,
-                                               &ast_connected.id.subaddress);
-                                       if (ast_connected.id.subaddress.str) {
-                                               ast_copy_string(pri->pvts[chanpos]->cid_subaddr,
-                                                       ast_connected.id.subaddress.str,
-                                                       sizeof(pri->pvts[chanpos]->cid_subaddr));
-                                       }
+                               if (ast_connected.id.subaddress.str) {
+                                       ast_copy_string(pri->pvts[chanpos]->cid_subaddr,
+                                               ast_connected.id.subaddress.str,
+                                               sizeof(pri->pvts[chanpos]->cid_subaddr));
+                                       caller_id_update = 1;
                                }
 #endif /* defined(HAVE_PRI_SUBADDR) */
                                if (caller_id_update) {
@@ -4389,12 +4395,13 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
                                        ast_caller.id = ast_connected.id;
                                        ast_caller.ani = ast_connected.id;
                                        ast_channel_set_caller_event(owner, &ast_caller, NULL);
-                               }
 
-                               /* Update the connected line information on the other channel */
-                               if (event_id != PRI_EVENT_RING) {
-                                       /* This connected_line update was not from a SETUP message. */
-                                       ast_channel_queue_connected_line_update(owner, &ast_connected, NULL);
+                                       /* Update the connected line information on the other channel */
+                                       if (event_id != PRI_EVENT_RING) {
+                                               /* This connected_line update was not from a SETUP message. */
+                                               ast_channel_queue_connected_line_update(owner, &ast_connected,
+                                                       NULL);
+                                       }
                                }
 
                                ast_party_connected_line_free(&ast_connected);
@@ -4673,7 +4680,7 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
                                        f.frametype = AST_FRAME_TEXT;
                                        f.subclass.integer = 0;
                                        f.offset = 0;
-                                       f.data.ptr = &subcmd->u.display.text;
+                                       f.data.ptr = (void *)&subcmd->u.display.text;
                                        f.datalen = subcmd->u.display.length + 1;
                                        ast_queue_frame(owner, &f);
                                        ast_channel_unlock(owner);
@@ -5324,7 +5331,7 @@ static void sig_pri_moh_fsm_event(struct ast_channel *chan, struct sig_pri_chan
        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",
+               ast_log(LOG_ERROR, "MOH state not implemented: %s(%u)\n",
                        sig_pri_moh_state_str(orig_state), orig_state);
                return;
        }
@@ -5338,13 +5345,12 @@ static void sig_pri_moh_fsm_event(struct ast_channel *chan, struct sig_pri_chan
  * \internal
  * \brief Set callid threadstorage for the pri_dchannel thread when a new call is created
  *
- * \return A new callid which has been bound to threadstorage. The return must be
- *         unreffed and the threadstorage should be unbound when the pri_dchannel
- *         primary loop wraps.
+ * \return A new callid which has been bound to threadstorage. The threadstorage
+ *         should be unbound when the pri_dchannel primary loop wraps.
  */
-static struct ast_callid *func_pri_dchannel_new_callid(void)
+static ast_callid func_pri_dchannel_new_callid(void)
 {
-       struct ast_callid *callid = ast_create_callid();
+       ast_callid callid = ast_create_callid();
 
        if (callid) {
                ast_callid_threadassoc_add(callid);
@@ -5363,20 +5369,18 @@ static struct ast_callid *func_pri_dchannel_new_callid(void)
  * \note Assumes the pri->lock is already obtained.
  * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
  *
- * \return a reference to the callid bound to the channel which has also
- *         been bound to threadstorage if it exists. If this returns non-NULL,
- *         the callid must be unreffed and the threadstorage should be unbound
- *         when the pri_dchannel primary loop wraps.
+ * \return The callid which has also been bound to threadstorage if it exists.
+ *         The threadstorage should be unbound when the pri_dchannel primary loop wraps.
  */
-static struct ast_callid *func_pri_dchannel_chanpos_callid(struct sig_pri_span *pri, int chanpos)
+static ast_callid func_pri_dchannel_chanpos_callid(struct sig_pri_span *pri, int chanpos)
 {
        if (chanpos < 0) {
-               return NULL;
+               return 0;
        }
 
        sig_pri_lock_owner(pri, chanpos);
        if (pri->pvts[chanpos]->owner) {
-               struct ast_callid *callid;
+               ast_callid callid;
                callid = ast_channel_callid(pri->pvts[chanpos]->owner);
                ast_channel_unlock(pri->pvts[chanpos]->owner);
                if (callid) {
@@ -5385,7 +5389,7 @@ static struct ast_callid *func_pri_dchannel_chanpos_callid(struct sig_pri_span *
                }
        }
 
-       return NULL;
+       return 0;
 }
 
 #if defined(HAVE_PRI_CALL_HOLD)
@@ -5408,7 +5412,7 @@ static int sig_pri_handle_hold(struct sig_pri_span *pri, pri_event *ev)
        int chanpos_old;
        int chanpos_new;
        struct ast_channel *owner;
-       struct ast_callid *callid = NULL;
+       ast_callid callid = 0;
 
        chanpos_old = pri_find_principle_by_call(pri, ev->hold.call);
        if (chanpos_old < 0) {
@@ -5468,7 +5472,6 @@ done_with_private:;
        }
 
        if (callid) {
-               ast_callid_unref(callid);
                ast_callid_threadassoc_remove();
        }
 
@@ -5492,7 +5495,7 @@ done_with_private:;
 static void sig_pri_handle_hold_ack(struct sig_pri_span *pri, pri_event *ev)
 {
        int chanpos;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        /*
         * We were successfully put on hold by the remote party
@@ -5524,7 +5527,6 @@ static void sig_pri_handle_hold_ack(struct sig_pri_span *pri, pri_event *ev)
        sig_pri_span_devstate_changed(pri);
 
        if (callid) {
-               ast_callid_unref(callid);
                ast_callid_threadassoc_remove();
        }
 }
@@ -5546,7 +5548,7 @@ static void sig_pri_handle_hold_ack(struct sig_pri_span *pri, pri_event *ev)
 static void sig_pri_handle_hold_rej(struct sig_pri_span *pri, pri_event *ev)
 {
        int chanpos;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        chanpos = pri_find_principle(pri, ev->hold_rej.channel, ev->hold_rej.call);
        if (chanpos < 0) {
@@ -5574,7 +5576,6 @@ static void sig_pri_handle_hold_rej(struct sig_pri_span *pri, pri_event *ev)
        sig_pri_unlock_private(pri->pvts[chanpos]);
 
        if (callid) {
-               ast_callid_unref(callid);
                ast_callid_threadassoc_remove();
        }
 }
@@ -5596,7 +5597,7 @@ static void sig_pri_handle_hold_rej(struct sig_pri_span *pri, pri_event *ev)
 static void sig_pri_handle_retrieve(struct sig_pri_span *pri, pri_event *ev)
 {
        int chanpos;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        if (!(ev->retrieve.channel & PRI_HELD_CALL)) {
                /* The call is not currently held. */
@@ -5649,7 +5650,6 @@ static void sig_pri_handle_retrieve(struct sig_pri_span *pri, pri_event *ev)
        sig_pri_span_devstate_changed(pri);
 
        if (callid) {
-               ast_callid_unref(callid);
                ast_callid_threadassoc_remove();
        }
 }
@@ -5671,7 +5671,7 @@ static void sig_pri_handle_retrieve(struct sig_pri_span *pri, pri_event *ev)
 static void sig_pri_handle_retrieve_ack(struct sig_pri_span *pri, pri_event *ev)
 {
        int chanpos;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        chanpos = pri_find_fixup_principle(pri, ev->retrieve_ack.channel,
                ev->retrieve_ack.call);
@@ -5690,7 +5690,6 @@ static void sig_pri_handle_retrieve_ack(struct sig_pri_span *pri, pri_event *ev)
        sig_pri_span_devstate_changed(pri);
 
        if (callid) {
-               ast_callid_unref(callid);
                ast_callid_threadassoc_remove();
        }
 }
@@ -5712,7 +5711,7 @@ static void sig_pri_handle_retrieve_ack(struct sig_pri_span *pri, pri_event *ev)
 static void sig_pri_handle_retrieve_rej(struct sig_pri_span *pri, pri_event *ev)
 {
        int chanpos;
-       struct ast_callid *callid;
+       ast_callid callid;
 
        chanpos = pri_find_principle(pri, ev->retrieve_rej.channel, ev->retrieve_rej.call);
        if (chanpos < 0) {
@@ -5741,12 +5740,483 @@ static void sig_pri_handle_retrieve_rej(struct sig_pri_span *pri, pri_event *ev)
        sig_pri_unlock_private(pri->pvts[chanpos]);
 
        if (callid) {
-               ast_callid_unref(callid);
                ast_callid_threadassoc_remove();
        }
 }
 #endif /* defined(HAVE_PRI_CALL_HOLD) */
 
+/*!
+ * \internal
+ * \brief Setup channel variables on the owner.
+ *
+ * \param pri PRI span control structure.
+ * \param chanpos Channel position in the span.
+ * \param ev SETUP event received.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
+ *
+ * \return Nothing
+ */
+static void setup_incoming_channel(struct sig_pri_span *pri, int chanpos, pri_event *ev)
+{
+       struct ast_channel *owner;
+       char ani2str[6];
+       char calledtonstr[10];
+
+       sig_pri_lock_owner(pri, chanpos);
+       owner = pri->pvts[chanpos]->owner;
+       if (!owner) {
+               return;
+       }
+
+       ast_channel_stage_snapshot(owner);
+
+#if defined(HAVE_PRI_SUBADDR)
+       if (ev->ring.calling.subaddress.valid) {
+               /* Set Calling Subaddress */
+               sig_pri_set_subaddress(&ast_channel_caller(owner)->id.subaddress,
+                       &ev->ring.calling.subaddress);
+               if (!ev->ring.calling.subaddress.type
+                       && !ast_strlen_zero((char *) ev->ring.calling.subaddress.data)) {
+                       /* NSAP */
+                       pbx_builtin_setvar_helper(owner, "CALLINGSUBADDR",
+                               (char *) ev->ring.calling.subaddress.data);
+               }
+       }
+       if (ev->ring.called_subaddress.valid) {
+               /* Set Called Subaddress */
+               sig_pri_set_subaddress(&ast_channel_dialed(owner)->subaddress,
+                       &ev->ring.called_subaddress);
+               if (!ev->ring.called_subaddress.type
+                       && !ast_strlen_zero((char *) ev->ring.called_subaddress.data)) {
+                       /* NSAP */
+                       pbx_builtin_setvar_helper(owner, "CALLEDSUBADDR",
+                               (char *) ev->ring.called_subaddress.data);
+               }
+       }
+#else
+       if (!ast_strlen_zero(ev->ring.callingsubaddr)) {
+               pbx_builtin_setvar_helper(owner, "CALLINGSUBADDR", ev->ring.callingsubaddr);
+       }
+#endif /* !defined(HAVE_PRI_SUBADDR) */
+       if (ev->ring.ani2 >= 0) {
+               ast_channel_caller(owner)->ani2 = ev->ring.ani2;
+               snprintf(ani2str, sizeof(ani2str), "%d", ev->ring.ani2);
+               pbx_builtin_setvar_helper(owner, "ANI2", ani2str);
+       }
+
+#ifdef SUPPORT_USERUSER
+       if (!ast_strlen_zero(ev->ring.useruserinfo)) {
+               pbx_builtin_setvar_helper(owner, "USERUSERINFO", ev->ring.useruserinfo);
+       }
+#endif
+
+       snprintf(calledtonstr, sizeof(calledtonstr), "%d", ev->ring.calledplan);
+       pbx_builtin_setvar_helper(owner, "CALLEDTON", calledtonstr);
+       ast_channel_dialed(owner)->number.plan = ev->ring.calledplan;
+
+       if (ev->ring.redirectingreason >= 0) {
+               /* This is now just a status variable.  Use REDIRECTING() dialplan function. */
+               pbx_builtin_setvar_helper(owner, "PRIREDIRECTREASON",
+                       redirectingreason2str(ev->ring.redirectingreason));
+       }
+#if defined(HAVE_PRI_REVERSE_CHARGE)
+       pri->pvts[chanpos]->reverse_charging_indication = ev->ring.reversecharge;
+#endif
+#if defined(HAVE_PRI_SETUP_KEYPAD)
+       ast_copy_string(pri->pvts[chanpos]->keypad_digits,
+               ev->ring.keypad_digits, sizeof(pri->pvts[chanpos]->keypad_digits));
+#endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
+
+       /*
+        * It's ok to call this with the owner already locked here
+        * since it will want to do this anyway if there are any
+        * subcmds.
+        */
+       sig_pri_handle_subcmds(pri, chanpos, ev->e, ev->ring.subcmds,
+               ev->ring.call);
+
+       ast_channel_stage_snapshot_done(owner);
+       ast_channel_unlock(owner);
+}
+
+/*!
+ * \internal
+ * \brief Handle the incoming SETUP event from libpri.
+ *
+ * \param pri PRI span control structure.
+ * \param e SETUP event received.
+ *
+ * \note Assumes the pri->lock is already obtained.
+ *
+ * \return Nothing
+ */
+static void sig_pri_handle_setup(struct sig_pri_span *pri, pri_event *e)
+{
+       int exten_exists_or_can_exist;
+       int could_match_more;
+       int need_dialtone;
+       enum sig_pri_law law;
+       int chanpos = -1;
+       ast_callid callid = 0;
+       struct ast_channel *c;
+       char plancallingnum[AST_MAX_EXTENSION];
+       char plancallingani[AST_MAX_EXTENSION];
+       pthread_t threadid;
+
+       if (!ast_strlen_zero(pri->msn_list)
+               && !sig_pri_msn_match(pri->msn_list, e->ring.callednum)) {
+               /* The call is not for us so ignore it. */
+               ast_verb(3,
+                       "Ignoring call to '%s' on span %d.  Its not in the MSN list: %s\n",
+                       e->ring.callednum, pri->span, pri->msn_list);
+               pri_destroycall(pri->pri, e->ring.call);
+               goto setup_exit;
+       }
+       if (sig_pri_is_cis_call(e->ring.channel)) {
+               sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds, e->ring.call);
+               goto setup_exit;
+       }
+       chanpos = pri_find_principle_by_call(pri, e->ring.call);
+       if (-1 < chanpos) {
+               /* Libpri has already filtered out duplicate SETUPs. */
+               ast_log(LOG_WARNING,
+                       "Span %d: Got SETUP with duplicate call ptr (%p).  Dropping call.\n",
+                       pri->span, e->ring.call);
+               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE);
+               goto setup_exit;
+       }
+       if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF) {
+               /* Any channel requested. */
+               chanpos = pri_find_empty_chan(pri, 1);
+               if (-1 < chanpos) {
+                       callid = func_pri_dchannel_new_callid();
+               }
+       } else if (PRI_CHANNEL(e->ring.channel) == 0x00) {
+               /* No channel specified. */
+#if defined(HAVE_PRI_CALL_WAITING)
+               if (!pri->allow_call_waiting_calls)
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+               {
+                       /* We will not accept incoming call waiting calls. */
+                       pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+                       goto setup_exit;
+               }
+#if defined(HAVE_PRI_CALL_WAITING)
+               chanpos = pri_find_empty_nobch(pri);
+               if (chanpos < 0) {
+                       /* We could not find/create a call interface. */
+                       pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+                       goto setup_exit;
+               }
+
+               callid = func_pri_dchannel_new_callid();
+
+               /* Setup the call interface to use. */
+               sig_pri_init_config(pri->pvts[chanpos], pri);
+#endif /* defined(HAVE_PRI_CALL_WAITING) */
+       } else {
+               /* A channel is specified. */
+               callid = func_pri_dchannel_new_callid();
+               chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
+               if (chanpos < 0) {
+                       ast_log(LOG_WARNING,
+                               "Span %d: SETUP on unconfigured channel %d/%d\n",
+                               pri->span, PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel));
+               } else {
+                       switch (pri->pvts[chanpos]->resetting) {
+                       case SIG_PRI_RESET_IDLE:
+                               break;
+                       case SIG_PRI_RESET_ACTIVE:
+                               /*
+                                * The peer may have lost the expected ack or not received the
+                                * RESTART yet.
+                                */
+                               pri->pvts[chanpos]->resetting = SIG_PRI_RESET_NO_ACK;
+                               break;
+                       case SIG_PRI_RESET_NO_ACK:
+                               /* The peer likely is not going to ack the RESTART. */
+                               ast_debug(1,
+                                       "Span %d: Second SETUP while waiting for RESTART ACKNOWLEDGE on channel %d/%d\n",
+                                       pri->span, PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel));
+
+                               /* Assume we got the ack. */
+                               pri->pvts[chanpos]->resetting = SIG_PRI_RESET_IDLE;
+                               if (pri->resetting) {
+                                       /* Go on to the next idle channel to RESTART. */
+                                       pri_check_restart(pri);
+                               }
+                               break;
+                       }
+                       if (!sig_pri_is_chan_available(pri->pvts[chanpos])) {
+                               /* This is where we handle initial glare */
+                               ast_debug(1,
+                                       "Span %d: SETUP requested unavailable channel %d/%d.  Attempting to renegotiate.\n",
+                                       pri->span, PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel));
+                               chanpos = -1;
+                       }
+               }
+#if defined(ALWAYS_PICK_CHANNEL)
+               if (e->ring.flexible) {
+                       chanpos = -1;
+               }
+#endif /* defined(ALWAYS_PICK_CHANNEL) */
+               if (chanpos < 0 && e->ring.flexible) {
+                       /* We can try to pick another channel. */
+                       chanpos = pri_find_empty_chan(pri, 1);
+               }
+       }
+       if (chanpos < 0) {
+               if (e->ring.flexible) {
+                       pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+               } else {
+                       pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
+               }
+               goto setup_exit;
+       }
+
+       sig_pri_lock_private(pri->pvts[chanpos]);
+
+       /* Mark channel as in use so noone else will steal it. */
+       pri->pvts[chanpos]->call = e->ring.call;
+
+       /* Use plancallingnum as a scratch buffer since it is initialized next. */
+       apply_plan_to_existing_number(plancallingnum, sizeof(plancallingnum), pri,
+               e->ring.redirectingnum, e->ring.callingplanrdnis);
+       sig_pri_set_rdnis(pri->pvts[chanpos], plancallingnum);
+
+       /* Setup caller-id info */
+       apply_plan_to_existing_number(plancallingnum, sizeof(plancallingnum), pri,
+               e->ring.callingnum, e->ring.callingplan);
+       pri->pvts[chanpos]->cid_ani2 = 0;
+       if (pri->pvts[chanpos]->use_callerid) {
+               ast_shrink_phone_number(plancallingnum);
+               ast_copy_string(pri->pvts[chanpos]->cid_num, plancallingnum,
+                       sizeof(pri->pvts[chanpos]->cid_num));
+#ifdef PRI_ANI
+               apply_plan_to_existing_number(plancallingani, sizeof(plancallingani),
+                       pri, e->ring.callingani, e->ring.callingplanani);
+               ast_shrink_phone_number(plancallingani);
+               ast_copy_string(pri->pvts[chanpos]->cid_ani, plancallingani,
+                       sizeof(pri->pvts[chanpos]->cid_ani));
+#endif
+               pri->pvts[chanpos]->cid_subaddr[0] = '\0';
+#if defined(HAVE_PRI_SUBADDR)
+               if (e->ring.calling.subaddress.valid) {
+                       struct ast_party_subaddress calling_subaddress;
+
+                       ast_party_subaddress_init(&calling_subaddress);
+                       sig_pri_set_subaddress(&calling_subaddress,
+                               &e->ring.calling.subaddress);
+                       if (calling_subaddress.str) {
+                               ast_copy_string(pri->pvts[chanpos]->cid_subaddr,
+                                       calling_subaddress.str,
+                                       sizeof(pri->pvts[chanpos]->cid_subaddr));
+                       }
+                       ast_party_subaddress_free(&calling_subaddress);
+               }
+#endif /* defined(HAVE_PRI_SUBADDR) */
+               ast_copy_string(pri->pvts[chanpos]->cid_name, e->ring.callingname,
+                       sizeof(pri->pvts[chanpos]->cid_name));
+               /* this is the callingplan (TON/NPI), e->ring.callingplan>>4 would be the TON */
+               pri->pvts[chanpos]->cid_ton = e->ring.callingplan;
+               pri->pvts[chanpos]->callingpres = e->ring.callingpres;
+               if (e->ring.ani2 >= 0) {
+                       pri->pvts[chanpos]->cid_ani2 = e->ring.ani2;
+               }
+       } else {
+               pri->pvts[chanpos]->cid_num[0] = '\0';
+               pri->pvts[chanpos]->cid_subaddr[0] = '\0';
+               pri->pvts[chanpos]->cid_ani[0] = '\0';
+               pri->pvts[chanpos]->cid_name[0] = '\0';
+               pri->pvts[chanpos]->cid_ton = 0;
+               pri->pvts[chanpos]->callingpres = 0;
+       }
+
+       /* Setup the user tag for party id's from this device for this call. */
+       if (pri->append_msn_to_user_tag) {
+               snprintf(pri->pvts[chanpos]->user_tag,
+                       sizeof(pri->pvts[chanpos]->user_tag), "%s_%s",
+                       pri->initial_user_tag,
+                       pri->nodetype == PRI_NETWORK
+                               ? plancallingnum : e->ring.callednum);
+       } else {
+               ast_copy_string(pri->pvts[chanpos]->user_tag,
+                       pri->initial_user_tag, sizeof(pri->pvts[chanpos]->user_tag));
+       }
+
+       sig_pri_set_caller_id(pri->pvts[chanpos]);
+
+       /* Set DNID on all incoming calls -- even immediate */
+       sig_pri_set_dnid(pri->pvts[chanpos], e->ring.callednum);
+
+       if (pri->pvts[chanpos]->immediate) {
+               /* immediate=yes go to s|1 */
+               ast_verb(3, "Going to extension s|1 because of immediate=yes\n");
+               pri->pvts[chanpos]->exten[0] = 's';
+               pri->pvts[chanpos]->exten[1] = '\0';
+       } else if (!ast_strlen_zero(e->ring.callednum)) {
+               /* Get called number */
+               ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum,
+                       sizeof(pri->pvts[chanpos]->exten));
+       } else if (pri->overlapdial) {
+               pri->pvts[chanpos]->exten[0] = '\0';
+       } else {
+               /* Some PRI circuits are set up to send _no_ digits.  Handle them as 's'. */
+               pri->pvts[chanpos]->exten[0] = 's';
+               pri->pvts[chanpos]->exten[1] = '\0';
+       }
+       /* No number yet, but received "sending complete"? */
+       if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) {
+               ast_verb(3, "Going to extension s|1 because of Complete received\n");
+               pri->pvts[chanpos]->exten[0] = 's';
+               pri->pvts[chanpos]->exten[1] = '\0';
+       }
+
+       /* Make sure extension exists (or in overlap dial mode, can exist) */
+       exten_exists_or_can_exist = ((pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)
+               && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context,
+                       pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num))
+                       || ast_exists_extension(NULL, pri->pvts[chanpos]->context,
+                               pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num);
+       if (!exten_exists_or_can_exist) {
+               ast_verb(3,
+                       "Span %d: Extension %s@%s does not exist.  Rejecting call from '%s'.\n",
+                       pri->span, pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context,
+                       pri->pvts[chanpos]->cid_num);
+               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED);
+               pri->pvts[chanpos]->call = NULL;
+               pri->pvts[chanpos]->exten[0] = '\0';
+               sig_pri_unlock_private(pri->pvts[chanpos]);
+               sig_pri_span_devstate_changed(pri);
+               goto setup_exit;
+       }
+
+       /* Select audio companding mode. */
+       switch (e->ring.layer1) {
+       case PRI_LAYER_1_ALAW:
+               law = SIG_PRI_ALAW;
+               break;
+       case PRI_LAYER_1_ULAW:
+               law = SIG_PRI_ULAW;
+               break;
+       default:
+               /* This is a data call to us. */
+               law = SIG_PRI_DEFLAW;
+               break;
+       }
+
+       could_match_more = !e->ring.complete
+               && (pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)
+               && ast_matchmore_extension(NULL, pri->pvts[chanpos]->context,
+                       pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num);
+
+       need_dialtone = could_match_more
+               /*
+                * Must explicitly check the digital capability this
+                * way instead of checking the pvt->digital flag
+                * because the flag hasn't been set yet.
+                */
+               && !(e->ring.ctype & AST_TRANS_CAP_DIGITAL)
+               && !pri->pvts[chanpos]->no_b_channel
+               && (!strlen(pri->pvts[chanpos]->exten)
+                       || ast_ignore_pattern(pri->pvts[chanpos]->context,
+                               pri->pvts[chanpos]->exten));
+
+       if (e->ring.complete || !(pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)) {
+               /* Just announce proceeding */
+               pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_PROCEEDING;
+               pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0);
+       } else if (pri->switchtype == PRI_SWITCH_GR303_TMC) {
+               pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_CONNECT;
+               pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
+       } else {
+               pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_OVERLAP;
+#if defined(HAVE_PRI_SETUP_ACK_INBAND)
+               pri_setup_ack(pri->pri, e->ring.call,
+                       PVT_TO_CHANNEL(pri->pvts[chanpos]), 1, need_dialtone);
+#else  /* !defined(HAVE_PRI_SETUP_ACK_INBAND) */
+               pri_need_more_info(pri->pri, e->ring.call,
+                       PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
+#endif /* !defined(HAVE_PRI_SETUP_ACK_INBAND) */
+       }
+
+       /*
+        * Release the PRI lock while we create the channel so other
+        * threads can send D channel messages.  We must also release
+        * the private lock to prevent deadlock while creating the
+        * channel.
+        */
+       sig_pri_unlock_private(pri->pvts[chanpos]);
+       ast_mutex_unlock(&pri->lock);
+       c = sig_pri_new_ast_channel(pri->pvts[chanpos],
+               could_match_more ? AST_STATE_RESERVED : AST_STATE_RING, law, e->ring.ctype,
+               pri->pvts[chanpos]->exten, NULL, NULL);
+       ast_mutex_lock(&pri->lock);
+       sig_pri_lock_private(pri->pvts[chanpos]);
+
+       if (c) {
+               setup_incoming_channel(pri, chanpos, e);
+
+               /* Start PBX */
+               if (could_match_more) {
+#if !defined(HAVE_PRI_SETUP_ACK_INBAND)
+                       if (need_dialtone) {
+                               /* Indicate that we are providing dialtone. */
+                               pri->pvts[chanpos]->progress = 1;/* No need to send plain PROGRESS again. */
+#ifdef HAVE_PRI_PROG_W_CAUSE
+                               pri_progress_with_cause(pri->pri, e->ring.call,
+                                       PVT_TO_CHANNEL(pri->pvts[chanpos]), 1, -1);/* no cause at all */
+#else
+                               pri_progress(pri->pri, e->ring.call,
+                                       PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
+#endif
+                       }
+#endif /* !defined(HAVE_PRI_SETUP_ACK_INBAND) */
+
+                       if (!ast_pthread_create_detached(&threadid, NULL, pri_ss_thread,
+                               pri->pvts[chanpos])) {
+                               ast_verb(3, "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n",
+                                       plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"),
+                                       pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,
+                                       pri->span);
+                               sig_pri_unlock_private(pri->pvts[chanpos]);
+                               goto setup_exit;
+                       }
+               } else {
+                       if (!ast_pbx_start(c)) {
+                               ast_verb(3, "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n",
+                                       plancallingnum, pri->pvts[chanpos]->exten,
+                                       pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,
+                                       pri->span);
+                               sig_pri_set_echocanceller(pri->pvts[chanpos], 1);
+                               sig_pri_unlock_private(pri->pvts[chanpos]);
+                               goto setup_exit;
+                       }
+               }
+       }
+       ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
+               pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+       if (c) {
+               /* Avoid deadlock while destroying channel */
+               sig_pri_unlock_private(pri->pvts[chanpos]);
+               ast_mutex_unlock(&pri->lock);
+               ast_hangup(c);
+               ast_mutex_lock(&pri->lock);
+       } else {
+               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
+               pri->pvts[chanpos]->call = NULL;
+               sig_pri_unlock_private(pri->pvts[chanpos]);
+               sig_pri_span_devstate_changed(pri);
+       }
+
+setup_exit:;
+       if (callid) {
+               ast_callid_threadassoc_remove();
+       }
+}
+
 static void *pri_dchannel(void *vpri)
 {
        struct sig_pri_span *pri = vpri;
@@ -5754,19 +6224,12 @@ static void *pri_dchannel(void *vpri)
        struct pollfd fds[SIG_PRI_NUM_DCHANS];
        int res;
        int x;
-       int law;
-       struct ast_channel *c;
        struct timeval tv, lowest, *next;
        int doidling=0;
        char *cc;
        time_t t;
        int i, which=-1;
        int numdchans;
-       pthread_t threadid;
-       char ani2str[6];
-       char plancallingnum[AST_MAX_EXTENSION];
-       char plancallingani[AST_MAX_EXTENSION];
-       char calledtonstr[10];
        struct timeval lastidle = { 0, 0 };
        pthread_t p;
        struct ast_channel *idle;
@@ -5797,7 +6260,7 @@ static void *pri_dchannel(void *vpri)
                        ast_log(LOG_WARNING, "Idle dial string '%s' lacks '@context'\n", pri->idleext);
        }
        for (;;) {
-               struct ast_callid *callid = NULL;
+               ast_callid callid = 0;
 
                for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
                        if (!pri->dchans[i])
@@ -5860,7 +6323,7 @@ static void *pri_dchannel(void *vpri)
                                         */
                                        sig_pri_lock_private(pri->pvts[nextidle]);
                                        sig_pri_unlock_private(pri->pvts[nextidle]);
-                                       idle = sig_pri_request(pri->pvts[nextidle], AST_FORMAT_ULAW, NULL, 0);
+                                       idle = sig_pri_request(pri->pvts[nextidle], SIG_PRI_ULAW, NULL, NULL, 0);
                                        ast_mutex_lock(&pri->lock);
                                        if (idle) {
                                                pri->pvts[nextidle]->isidlecall = 1;
@@ -5957,6 +6420,14 @@ static void *pri_dchannel(void *vpri)
                                }
                                if (e)
                                        break;
+
+                               if ((errno != 0) && (errno != EINTR)) {
+                                       ast_log(LOG_NOTICE, "pri_check_event returned error %d (%s)\n",
+                                               errno, strerror(errno));
+                               }
+                               if (errno == ENODEV) {
+                                       pri_destroy_later(pri);
+                               }
                        }
                } else if (errno != EINTR)
                        ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno));
@@ -6074,9 +6545,8 @@ static void *pri_dchannel(void *vpri)
                                                                pri->pvts[chanpos]->call = NULL;
                                                        }
                                                }
-                                               /* Force soft hangup if appropriate */
-                                               if (pri->pvts[chanpos]->owner)
-                                                       ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
+                                               /* Force hangup if appropriate */
+                                               sig_pri_queue_hangup(pri, chanpos);
                                                sig_pri_unlock_private(pri->pvts[chanpos]);
                                        }
                                } else {
@@ -6088,8 +6558,8 @@ static void *pri_dchannel(void *vpri)
                                                                pri_destroycall(pri->pri, pri->pvts[x]->call);
                                                                pri->pvts[x]->call = NULL;
                                                        }
-                                                       if (pri->pvts[x]->owner)
-                                                               ast_channel_softhangup_internal_flag_add(pri->pvts[x]->owner, AST_SOFTHANGUP_DEV);
+                                                       /* Force hangup if appropriate */
+                                                       sig_pri_queue_hangup(pri, x);
                                                        sig_pri_unlock_private(pri->pvts[x]);
                                                }
                                }
@@ -6218,502 +6688,7 @@ static void *pri_dchannel(void *vpri)
                                break;
 #endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */
                        case PRI_EVENT_RING:
-                               if (!ast_strlen_zero(pri->msn_list)
-                                       && !sig_pri_msn_match(pri->msn_list, e->ring.callednum)) {
-                                       /* The call is not for us so ignore it. */
-                                       ast_verb(3,
-                                               "Ignoring call to '%s' on span %d.  Its not in the MSN list: %s\n",
-                                               e->ring.callednum, pri->span, pri->msn_list);
-                                       pri_destroycall(pri->pri, e->ring.call);
-                                       break;
-                               }
-                               if (sig_pri_is_cis_call(e->ring.channel)) {
-                                       sig_pri_handle_cis_subcmds(pri, e->e, e->ring.subcmds,
-                                               e->ring.call);
-                                       break;
-                               }
-                               chanpos = pri_find_principle_by_call(pri, e->ring.call);
-                               if (-1 < chanpos) {
-                                       /* Libpri has already filtered out duplicate SETUPs. */
-                                       ast_log(LOG_WARNING,
-                                               "Span %d: Got SETUP with duplicate call ptr (%p).  Dropping call.\n",
-                                               pri->span, e->ring.call);
-                                       pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE);
-                                       break;
-                               }
-                               if (e->ring.channel == -1 || PRI_CHANNEL(e->ring.channel) == 0xFF) {
-                                       /* Any channel requested. */
-                                       chanpos = pri_find_empty_chan(pri, 1);
-                                       if (-1 < chanpos) {
-                                               callid = func_pri_dchannel_new_callid();
-                                       }
-                               } else if (PRI_CHANNEL(e->ring.channel) == 0x00) {
-                                       /* No channel specified. */
-#if defined(HAVE_PRI_CALL_WAITING)
-                                       if (!pri->allow_call_waiting_calls)
-#endif /* defined(HAVE_PRI_CALL_WAITING) */
-                                       {
-                                               /* We will not accept incoming call waiting calls. */
-                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-                                               break;
-                                       }
-#if defined(HAVE_PRI_CALL_WAITING)
-                                       chanpos = pri_find_empty_nobch(pri);
-                                       if (chanpos < 0) {
-                                               /* We could not find/create a call interface. */
-                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-                                               break;
-                                       }
-
-                                       callid = func_pri_dchannel_new_callid();
-
-                                       /* Setup the call interface to use. */
-                                       sig_pri_init_config(pri->pvts[chanpos], pri);
-#endif /* defined(HAVE_PRI_CALL_WAITING) */
-                               } else {
-                                       /* A channel is specified. */
-                                       callid = func_pri_dchannel_new_callid();
-                                       chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call);
-                                       if (chanpos < 0) {
-                                               ast_log(LOG_WARNING,
-                                                       "Span %d: SETUP on unconfigured channel %d/%d\n",
-                                                       pri->span, PRI_SPAN(e->ring.channel),
-                                                       PRI_CHANNEL(e->ring.channel));
-                                       } else {
-                                               switch (pri->pvts[chanpos]->resetting) {
-                                               case SIG_PRI_RESET_IDLE:
-                                                       break;
-                                               case SIG_PRI_RESET_ACTIVE:
-                                                       /*
-                                                        * The peer may have lost the expected ack or not received the
-                                                        * RESTART yet.
-                                                        */
-                                                       pri->pvts[chanpos]->resetting = SIG_PRI_RESET_NO_ACK;
-                                                       break;
-                                               case SIG_PRI_RESET_NO_ACK:
-                                                       /* The peer likely is not going to ack the RESTART. */
-                                                       ast_debug(1,
-                                                               "Span %d: Second SETUP while waiting for RESTART ACKNOWLEDGE on channel %d/%d\n",
-                                                               pri->span, PRI_SPAN(e->ring.channel),
-                                                               PRI_CHANNEL(e->ring.channel));
-
-                                                       /* Assume we got the ack. */
-                                                       pri->pvts[chanpos]->resetting = SIG_PRI_RESET_IDLE;
-                                                       if (pri->resetting) {
-                                                               /* Go on to the next idle channel to RESTART. */
-                                                               pri_check_restart(pri);
-                                                       }
-                                                       break;
-                                               }
-                                               if (!sig_pri_is_chan_available(pri->pvts[chanpos])) {
-                                                       /* This is where we handle initial glare */
-                                                       ast_debug(1,
-                                                               "Span %d: SETUP requested unavailable channel %d/%d.  Attempting to renegotiate.\n",
-                                                               pri->span, PRI_SPAN(e->ring.channel),
-                                                               PRI_CHANNEL(e->ring.channel));
-                                                       chanpos = -1;
-                                               }
-                                       }
-#if defined(ALWAYS_PICK_CHANNEL)
-                                       if (e->ring.flexible) {
-                                               chanpos = -1;
-                                       }
-#endif /* defined(ALWAYS_PICK_CHANNEL) */
-                                       if (chanpos < 0 && e->ring.flexible) {
-                                               /* We can try to pick another channel. */
-                                               chanpos = pri_find_empty_chan(pri, 1);
-                                       }
-                               }
-                               if (chanpos < 0) {
-                                       if (e->ring.flexible) {
-                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-                                       } else {
-                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
-                                       }
-                                       break;
-                               }
-
-                               sig_pri_lock_private(pri->pvts[chanpos]);
-
-                               /* Mark channel as in use so noone else will steal it. */
-                               pri->pvts[chanpos]->call = e->ring.call;
-
-                               /* Use plancallingnum as a scratch buffer since it is initialized next. */
-                               apply_plan_to_existing_number(plancallingnum, sizeof(plancallingnum), pri,
-                                       e->ring.redirectingnum, e->ring.callingplanrdnis);
-                               sig_pri_set_rdnis(pri->pvts[chanpos], plancallingnum);
-
-                               /* Setup caller-id info */
-                               apply_plan_to_existing_number(plancallingnum, sizeof(plancallingnum), pri,
-                                       e->ring.callingnum, e->ring.callingplan);
-                               pri->pvts[chanpos]->cid_ani2 = 0;
-                               if (pri->pvts[chanpos]->use_callerid) {
-                                       ast_shrink_phone_number(plancallingnum);
-                                       ast_copy_string(pri->pvts[chanpos]->cid_num, plancallingnum, sizeof(pri->pvts[chanpos]->cid_num));
-#ifdef PRI_ANI
-                                       apply_plan_to_existing_number(plancallingani, sizeof(plancallingani),
-                                               pri, e->ring.callingani, e->ring.callingplanani);
-                                       ast_shrink_phone_number(plancallingani);
-                                       ast_copy_string(pri->pvts[chanpos]->cid_ani, plancallingani,
-                                               sizeof(pri->pvts[chanpos]->cid_ani));
-#endif
-                                       pri->pvts[chanpos]->cid_subaddr[0] = '\0';
-#if defined(HAVE_PRI_SUBADDR)
-                                       if (e->ring.calling.subaddress.valid) {
-                                               struct ast_party_subaddress calling_subaddress;
-
-                                               ast_party_subaddress_init(&calling_subaddress);
-                                               sig_pri_set_subaddress(&calling_subaddress,
-                                                       &e->ring.calling.subaddress);
-                                               if (calling_subaddress.str) {
-                                                       ast_copy_string(pri->pvts[chanpos]->cid_subaddr,
-                                                               calling_subaddress.str,
-                                                               sizeof(pri->pvts[chanpos]->cid_subaddr));
-                                               }
-                                               ast_party_subaddress_free(&calling_subaddress);
-                                       }
-#endif /* defined(HAVE_PRI_SUBADDR) */
-                                       ast_copy_string(pri->pvts[chanpos]->cid_name, e->ring.callingname, sizeof(pri->pvts[chanpos]->cid_name));
-                                       pri->pvts[chanpos]->cid_ton = e->ring.callingplan; /* this is the callingplan (TON/NPI), e->ring.callingplan>>4 would be the TON */
-                                       pri->pvts[chanpos]->callingpres = e->ring.callingpres;
-                                       if (e->ring.ani2 >= 0) {
-                                               pri->pvts[chanpos]->cid_ani2 = e->ring.ani2;
-                                       }
-                               } else {
-                                       pri->pvts[chanpos]->cid_num[0] = '\0';
-                                       pri->pvts[chanpos]->cid_subaddr[0] = '\0';
-                                       pri->pvts[chanpos]->cid_ani[0] = '\0';
-                                       pri->pvts[chanpos]->cid_name[0] = '\0';
-                                       pri->pvts[chanpos]->cid_ton = 0;
-                                       pri->pvts[chanpos]->callingpres = 0;
-                               }
-
-                               /* Setup the user tag for party id's from this device for this call. */
-                               if (pri->append_msn_to_user_tag) {
-                                       snprintf(pri->pvts[chanpos]->user_tag,
-                                               sizeof(pri->pvts[chanpos]->user_tag), "%s_%s",
-                                               pri->initial_user_tag,
-                                               pri->nodetype == PRI_NETWORK
-                                                       ? plancallingnum : e->ring.callednum);
-                               } else {
-                                       ast_copy_string(pri->pvts[chanpos]->user_tag,
-                                               pri->initial_user_tag, sizeof(pri->pvts[chanpos]->user_tag));
-                               }
-
-                               sig_pri_set_caller_id(pri->pvts[chanpos]);
-
-                               /* Set DNID on all incoming calls -- even immediate */
-                               sig_pri_set_dnid(pri->pvts[chanpos], e->ring.callednum);
-
-                               /* If immediate=yes go to s|1 */
-                               if (pri->pvts[chanpos]->immediate) {
-                                       ast_verb(3, "Going to extension s|1 because of immediate=yes\n");
-                                       pri->pvts[chanpos]->exten[0] = 's';
-                                       pri->pvts[chanpos]->exten[1] = '\0';
-                               }
-                               /* Get called number */
-                               else if (!ast_strlen_zero(e->ring.callednum)) {
-                                       ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten));
-                               } else if (pri->overlapdial)
-                                       pri->pvts[chanpos]->exten[0] = '\0';
-                               else {
-                                       /* Some PRI circuits are set up to send _no_ digits.  Handle them as 's'. */
-                                       pri->pvts[chanpos]->exten[0] = 's';
-                                       pri->pvts[chanpos]->exten[1] = '\0';
-                               }
-                               /* No number yet, but received "sending complete"? */
-                               if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) {
-                                       ast_verb(3, "Going to extension s|1 because of Complete received\n");
-                                       pri->pvts[chanpos]->exten[0] = 's';
-                                       pri->pvts[chanpos]->exten[1] = '\0';
-                               }
-
-                               /* Make sure extension exists (or in overlap dial mode, can exist) */
-                               if (((pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING) && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) ||
-                                       ast_exists_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) {
-                                       /* Select audio companding mode. */
-                                       switch (e->ring.layer1) {
-                                       case PRI_LAYER_1_ALAW:
-                                               law = SIG_PRI_ALAW;
-                                               break;
-                                       case PRI_LAYER_1_ULAW:
-                                               law = SIG_PRI_ULAW;
-                                               break;
-                                       default:
-                                               /* This is a data call to us. */
-                                               law = SIG_PRI_DEFLAW;
-                                               break;
-                                       }
-
-                                       if (e->ring.complete || !(pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)) {
-                                               /* Just announce proceeding */
-                                               pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_PROCEEDING;
-                                               pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0);
-                                       } else if (pri->switchtype == PRI_SWITCH_GR303_TMC) {
-                                               pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_CONNECT;
-                                               pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
-                                       } else {
-                                               pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_OVERLAP;
-                                               pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
-                                       }
-
-                                       /* Start PBX */
-                                       if (!e->ring.complete
-                                               && (pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING)
-                                               && ast_matchmore_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) {
-                                               /*
-                                                * Release the PRI lock while we create the channel so other
-                                                * threads can send D channel messages.  We must also release
-                                                * the private lock to prevent deadlock while creating the
-                                                * channel.
-                                                */
-                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                               ast_mutex_unlock(&pri->lock);
-                                               c = sig_pri_new_ast_channel(pri->pvts[chanpos],
-                                                       AST_STATE_RESERVED, law, e->ring.ctype,
-                                                       pri->pvts[chanpos]->exten, NULL);
-                                               ast_mutex_lock(&pri->lock);
-                                               sig_pri_lock_private(pri->pvts[chanpos]);
-                                               if (c) {
-                                                       ast_channel_stage_snapshot(c);
-#if defined(HAVE_PRI_SUBADDR)
-                                                       if (e->ring.calling.subaddress.valid) {
-                                                               /* Set Calling Subaddress */
-                                                               sig_pri_lock_owner(pri, chanpos);
-                                                               sig_pri_set_subaddress(
-                                                                       &ast_channel_caller(pri->pvts[chanpos]->owner)->id.subaddress,
-                                                                       &e->ring.calling.subaddress);
-                                                               if (!e->ring.calling.subaddress.type
-                                                                       && !ast_strlen_zero(
-                                                                               (char *) e->ring.calling.subaddress.data)) {
-                                                                       /* NSAP */
-                                                                       pbx_builtin_setvar_helper(c, "CALLINGSUBADDR",
-                                                                               (char *) e->ring.calling.subaddress.data);
-                                                               }
-                                                               ast_channel_unlock(c);
-                                                       }
-                                                       if (e->ring.called_subaddress.valid) {
-                                                               /* Set Called Subaddress */
-                                                               sig_pri_lock_owner(pri, chanpos);
-                                                               sig_pri_set_subaddress(
-                                                                       &ast_channel_dialed(pri->pvts[chanpos]->owner)->subaddress,
-                                                                       &e->ring.called_subaddress);
-                                                               if (!e->ring.called_subaddress.type
-                                                                       && !ast_strlen_zero(
-                                                                               (char *) e->ring.called_subaddress.data)) {
-                                                                       /* NSAP */
-                                                                       pbx_builtin_setvar_helper(c, "CALLEDSUBADDR",
-                                                                               (char *) e->ring.called_subaddress.data);
-                                                               }
-                                                               ast_channel_unlock(c);
-                                                       }
-#else
-                                                       if (!ast_strlen_zero(e->ring.callingsubaddr)) {
-                                                               pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr);
-                                                       }
-#endif /* !defined(HAVE_PRI_SUBADDR) */
-                                                       if (e->ring.ani2 >= 0) {
-                                                               snprintf(ani2str, sizeof(ani2str), "%d", e->ring.ani2);
-                                                               pbx_builtin_setvar_helper(c, "ANI2", ani2str);
-                                                       }
-
-#ifdef SUPPORT_USERUSER
-                                                       if (!ast_strlen_zero(e->ring.useruserinfo)) {
-                                                               pbx_builtin_setvar_helper(c, "USERUSERINFO", e->ring.useruserinfo);
-                                                       }
-#endif
-
-                                                       snprintf(calledtonstr, sizeof(calledtonstr), "%d", e->ring.calledplan);
-                                                       pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr);
-                                                       ast_channel_lock(c);
-                                                       ast_channel_dialed(c)->number.plan = e->ring.calledplan;
-                                                       ast_channel_unlock(c);
-
-                                                       if (e->ring.redirectingreason >= 0) {
-                                                               /* This is now just a status variable.  Use REDIRECTING() dialplan function. */
-                                                               pbx_builtin_setvar_helper(c, "PRIREDIRECTREASON", redirectingreason2str(e->ring.redirectingreason));
-                                                       }
-#if defined(HAVE_PRI_REVERSE_CHARGE)
-                                                       pri->pvts[chanpos]->reverse_charging_indication = e->ring.reversecharge;
-#endif
-#if defined(HAVE_PRI_SETUP_KEYPAD)
-                                                       ast_copy_string(pri->pvts[chanpos]->keypad_digits,
-                                                               e->ring.keypad_digits,
-                                                               sizeof(pri->pvts[chanpos]->keypad_digits));
-#endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
-
-                                                       sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.subcmds,
-                                                               e->ring.call);
-
-                                                       if (!pri->pvts[chanpos]->digital
-                                                               && !pri->pvts[chanpos]->no_b_channel) {
-                                                               /*
-                                                                * Call has a channel.
-                                                                * Indicate that we are providing dialtone.
-                                                                */
-                                                               pri->pvts[chanpos]->progress = 1;/* No need to send plain PROGRESS again. */
-#ifdef HAVE_PRI_PROG_W_CAUSE
-                                                               pri_progress_with_cause(pri->pri, e->ring.call,
-                                                                       PVT_TO_CHANNEL(pri->pvts[chanpos]), 1, -1);/* no cause at all */
-#else
-                                                               pri_progress(pri->pri, e->ring.call,
-                                                                       PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
-#endif
-                                                       }
-                                                       ast_channel_stage_snapshot_done(c);
-                                               }
-                                               if (c && !ast_pthread_create_detached(&threadid, NULL, pri_ss_thread, pri->pvts[chanpos])) {
-                                                       ast_verb(3, "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n",
-                                                               plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"),
-                                                               pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
-                                               } else {
-                                                       ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
-                                                               pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
-                                                       if (c) {
-                                                               /* Avoid deadlock while destroying channel */
-                                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                                               ast_mutex_unlock(&pri->lock);
-                                                               ast_hangup(c);
-                                                               ast_mutex_lock(&pri->lock);
-                                                       } else {
-                                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
-                                                               pri->pvts[chanpos]->call = NULL;
-                                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                                               sig_pri_span_devstate_changed(pri);
-                                                       }
-                                                       break;
-                                               }
-                                       } else {
-                                               /*
-                                                * Release the PRI lock while we create the channel so other
-                                                * threads can send D channel messages.  We must also release
-                                                * the private lock to prevent deadlock while creating the
-                                                * channel.
-                                                */
-                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                               ast_mutex_unlock(&pri->lock);
-                                               c = sig_pri_new_ast_channel(pri->pvts[chanpos],
-                                                       AST_STATE_RING, law, e->ring.ctype,
-                                                       pri->pvts[chanpos]->exten, NULL);
-                                               ast_mutex_lock(&pri->lock);
-                                               sig_pri_lock_private(pri->pvts[chanpos]);
-                                               if (c) {
-                                                       /*
-                                                        * It is reasonably safe to set the following
-                                                        * channel variables while the PRI and DAHDI private
-                                                        * structures are locked.  The PBX has not been
-                                                        * started yet and it is unlikely that any other task
-                                                        * will do anything with the channel we have just
-                                                        * created.
-                                                        */
-                                                       ast_channel_stage_snapshot(c);
-#if defined(HAVE_PRI_SUBADDR)
-                                                       if (e->ring.calling.subaddress.valid) {
-                                                               /* Set Calling Subaddress */
-                                                               sig_pri_lock_owner(pri, chanpos);
-                                                               sig_pri_set_subaddress(
-                                                                       &ast_channel_caller(pri->pvts[chanpos]->owner)->id.subaddress,
-                                                                       &e->ring.calling.subaddress);
-                                                               if (!e->ring.calling.subaddress.type
-                                                                       && !ast_strlen_zero(
-                                                                               (char *) e->ring.calling.subaddress.data)) {
-                                                                       /* NSAP */
-                                                                       pbx_builtin_setvar_helper(c, "CALLINGSUBADDR",
-                                                                               (char *) e->ring.calling.subaddress.data);
-                                                               }
-                                                               ast_channel_unlock(c);
-                                                       }
-                                                       if (e->ring.called_subaddress.valid) {
-                                                               /* Set Called Subaddress */
-                                                               sig_pri_lock_owner(pri, chanpos);
-                                                               sig_pri_set_subaddress(
-                                                                       &ast_channel_dialed(pri->pvts[chanpos]->owner)->subaddress,
-                                                                       &e->ring.called_subaddress);
-                                                               if (!e->ring.called_subaddress.type
-                                                                       && !ast_strlen_zero(
-                                                                               (char *) e->ring.called_subaddress.data)) {
-                                                                       /* NSAP */
-                                                                       pbx_builtin_setvar_helper(c, "CALLEDSUBADDR",
-                                                                               (char *) e->ring.called_subaddress.data);
-                                                               }
-                                                               ast_channel_unlock(c);
-                                                       }
-#else
-                                                       if (!ast_strlen_zero(e->ring.callingsubaddr)) {
-                                                               pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr);
-                                                       }
-#endif /* !defined(HAVE_PRI_SUBADDR) */
-                                                       if (e->ring.ani2 >= 0) {
-                                                               snprintf(ani2str, sizeof(ani2str), "%d", e->ring.ani2);
-                                                               pbx_builtin_setvar_helper(c, "ANI2", ani2str);
-                                                       }
-
-#ifdef SUPPORT_USERUSER
-                                                       if (!ast_strlen_zero(e->ring.useruserinfo)) {
-                                                               pbx_builtin_setvar_helper(c, "USERUSERINFO", e->ring.useruserinfo);
-                                                       }
-#endif
-
-                                                       if (e->ring.redirectingreason >= 0) {
-                                                               /* This is now just a status variable.  Use REDIRECTING() dialplan function. */
-                                                               pbx_builtin_setvar_helper(c, "PRIREDIRECTREASON", redirectingreason2str(e->ring.redirectingreason));
-                                                       }
-#if defined(HAVE_PRI_REVERSE_CHARGE)
-                                                       pri->pvts[chanpos]->reverse_charging_indication = e->ring.reversecharge;
-#endif
-#if defined(HAVE_PRI_SETUP_KEYPAD)
-                                                       ast_copy_string(pri->pvts[chanpos]->keypad_digits,
-                                                               e->ring.keypad_digits,
-                                                               sizeof(pri->pvts[chanpos]->keypad_digits));
-#endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
-
-                                                       snprintf(calledtonstr, sizeof(calledtonstr), "%d", e->ring.calledplan);
-                                                       pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr);
-                                                       ast_channel_lock(c);
-                                                       ast_channel_dialed(c)->number.plan = e->ring.calledplan;
-                                                       ast_channel_unlock(c);
-
-                                                       sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.subcmds,
-                                                               e->ring.call);
-
-                                                       ast_channel_stage_snapshot_done(c);
-                                               }
-                                               if (c && !ast_pbx_start(c)) {
-                                                       ast_verb(3, "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n",
-                                                               plancallingnum, pri->pvts[chanpos]->exten,
-                                                               pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
-                                                       sig_pri_set_echocanceller(pri->pvts[chanpos], 1);
-                                               } else {
-                                                       ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
-                                                               pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
-                                                       if (c) {
-                                                               /* Avoid deadlock while destroying channel */
-                                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                                               ast_mutex_unlock(&pri->lock);
-                                                               ast_hangup(c);
-                                                               ast_mutex_lock(&pri->lock);
-                                                       } else {
-                                                               pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
-                                                               pri->pvts[chanpos]->call = NULL;
-                                                               sig_pri_unlock_private(pri->pvts[chanpos]);
-                                                               sig_pri_span_devstate_changed(pri);
-                                                       }
-                                                       break;
-                                               }
-                                       }
-                               } else {
-                                       ast_verb(3,
-                                               "Span %d: Extension %s@%s does not exist.  Rejecting call from '%s'.\n",
-                                               pri->span, pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context,
-                                               pri->pvts[chanpos]->cid_num);
-                                       pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED);
-                                       pri->pvts[chanpos]->call = NULL;
-                                       pri->pvts[chanpos]->exten[0] = '\0';
-                                       sig_pri_unlock_private(pri->pvts[chanpos]);
-                                       sig_pri_span_devstate_changed(pri);
-                                       break;
-                               }
-                               sig_pri_unlock_private(pri->pvts[chanpos]);
+                               sig_pri_handle_setup(pri, e);
                                break;
                        case PRI_EVENT_RINGING:
                                if (sig_pri_is_cis_call(e->ringing.channel)) {
@@ -6862,8 +6837,15 @@ static void *pri_dchannel(void *vpri)
                                if (!pri->pvts[chanpos]->progress
                                        && !pri->pvts[chanpos]->no_b_channel
 #ifdef PRI_PROGRESS_MASK
-                                       && (e->proceeding.progressmask
-                                               & (PRI_PROG_CALL_NOT_E2E_ISDN | PRI_PROG_INBAND_AVAILABLE))
+                                       /*
+                                        * We only care about PRI_PROG_INBAND_AVAILABLE to open the
+                                        * voice path.
+                                        *
+                                        * We explicitly DO NOT want to check PRI_PROG_CALL_NOT_E2E_ISDN
+                                        * because it will mess up ISDN to SIP interoperability for
+                                        * the ALERTING message.
+                                        */
+                                       && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)
 #else
                                        && e->proceeding.progress == 8
 #endif
@@ -6874,6 +6856,12 @@ static void *pri_dchannel(void *vpri)
                                        sig_pri_set_dialing(pri->pvts[chanpos], 0);
                                        sig_pri_open_media(pri->pvts[chanpos]);
                                } else if (pri->inband_on_proceeding) {
+                                       /*
+                                        * XXX This is to accomodate a broken switch that sends a
+                                        * PROCEEDING without any progress indication ie for
+                                        * inband audio.  This should be part of the conditional
+                                        * test above to bring the voice path up.
+                                        */
                                        sig_pri_set_dialing(pri->pvts[chanpos], 0);
                                }
                                sig_pri_unlock_private(pri->pvts[chanpos]);
@@ -7102,10 +7090,11 @@ static void *pri_dchannel(void *vpri)
                                                break;
                                        }
                                        if (pri->pvts[chanpos]->owner) {
-                                               int do_hangup = 0;
-
                                                snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP (%d)", e->hangup.cause);
                                                pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause);
+                                       }
+                                       if (pri->pvts[chanpos]->owner) {
+                                               int do_hangup = 0;
 
                                                /* Queue a BUSY instead of a hangup if our cause is appropriate */
                                                ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause);
@@ -7143,17 +7132,7 @@ static void *pri_dchannel(void *vpri)
                                                }
 
                                                if (do_hangup) {
-#if defined(HAVE_PRI_AOC_EVENTS)
-                                                       if (detect_aoc_e_subcmd(e->hangup.subcmds)) {
-                                                               /* If a AOC-E msg was sent during the release, we must use a
-                                                                * AST_CONTROL_HANGUP frame to guarantee that frame gets read before hangup */
-                                                               pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP);
-                                                       } else {
-                                                               ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
-                                                       }
-#else
-                                                       ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
-#endif /* defined(HAVE_PRI_AOC_EVENTS) */
+                                                       sig_pri_queue_hangup(pri, chanpos);
                                                }
                                        } else {
                                                /*
@@ -7171,9 +7150,9 @@ static void *pri_dchannel(void *vpri)
                                        pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
                                        pri->pvts[chanpos]->call = NULL;
                                }
-#if defined(FORCE_RESTART_UNAVAIL_CHANS)
                                if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL
                                        && pri->sig != SIG_BRI_PTMP && !pri->resetting
+                                       && pri->force_restart_unavailable_chans
                                        && pri->pvts[chanpos]->resetting == SIG_PRI_RESET_IDLE) {
                                        ast_verb(3,
                                                "Span %d: Forcing restart of channel %d/%d since channel reported in use\n",
@@ -7182,7 +7161,6 @@ static void *pri_dchannel(void *vpri)
                                        pri->pvts[chanpos]->resetting = SIG_PRI_RESET_ACTIVE;
                                        pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
                                }
-#endif /* defined(FORCE_RESTART_UNAVAIL_CHANS) */
                                if (e->hangup.aoc_units > -1)
                                        ast_verb(3, "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
                                                pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
@@ -7257,10 +7235,11 @@ static void *pri_dchannel(void *vpri)
                                        break;
                                }
                                if (pri->pvts[chanpos]->owner) {
-                                       int do_hangup = 0;
-
                                        snprintf(cause_str, sizeof(cause_str), "PRI PRI_EVENT_HANGUP_REQ (%d)", e->hangup.cause);
                                        pri_queue_pvt_cause_data(pri, chanpos, cause_str, e->hangup.cause);
+                               }
+                               if (pri->pvts[chanpos]->owner) {
+                                       int do_hangup = 0;
 
                                        ast_channel_hangupcause_set(pri->pvts[chanpos]->owner, e->hangup.cause);
                                        switch (ast_channel_state(pri->pvts[chanpos]->owner)) {
@@ -7303,16 +7282,11 @@ static void *pri_dchannel(void *vpri)
                                                        && ast_channel_is_bridged(pri->pvts[chanpos]->owner)) {
                                                        sig_pri_send_aoce_termination_request(pri, chanpos,
                                                                pri_get_timer(pri->pri, PRI_TIMER_T305) / 2);
-                                               } else if (detect_aoc_e_subcmd(e->hangup.subcmds)) {
-                                                       /* If a AOC-E msg was sent during the Disconnect, we must use a AST_CONTROL_HANGUP frame
-                                                        * to guarantee that frame gets read before hangup */
-                                                       pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP);
-                                               } else {
-                                                       ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
-                                               }
-#else
-                                               ast_channel_softhangup_internal_flag_add(pri->pvts[chanpos]->owner, AST_SOFTHANGUP_DEV);
+                                               } else
 #endif /* defined(HAVE_PRI_AOC_EVENTS) */
+                                               {
+                                                       sig_pri_queue_hangup(pri, chanpos);
+                                               }
                                        }
                                        ast_verb(3, "Span %d: Channel %d/%d got hangup request, cause %d\n",
                                                pri->span, pri->pvts[chanpos]->logicalspan,
@@ -7325,9 +7299,9 @@ static void *pri_dchannel(void *vpri)
                                        pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
                                        pri->pvts[chanpos]->call = NULL;
                                }
-#if defined(FORCE_RESTART_UNAVAIL_CHANS)
                                if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL
                                        && pri->sig != SIG_BRI_PTMP && !pri->resetting
+                                       && pri->force_restart_unavailable_chans
                                        && pri->pvts[chanpos]->resetting == SIG_PRI_RESET_IDLE) {
                                        ast_verb(3,
                                                "Span %d: Forcing restart of channel %d/%d since channel reported in use\n",
@@ -7336,7 +7310,6 @@ static void *pri_dchannel(void *vpri)
                                        pri->pvts[chanpos]->resetting = SIG_PRI_RESET_ACTIVE;
                                        pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
                                }
-#endif /* defined(FORCE_RESTART_UNAVAIL_CHANS) */
 
 #ifdef SUPPORT_USERUSER
                                if (!ast_strlen_zero(e->hangup.useruserinfo)) {
@@ -7492,7 +7465,30 @@ static void *pri_dchannel(void *vpri)
                                if (!pri->pvts[chanpos]->progress
                                        && (pri->overlapdial & DAHDI_OVERLAPDIAL_OUTGOING)
                                        && !pri->pvts[chanpos]->digital
-                                       && !pri->pvts[chanpos]->no_b_channel) {
+                                       && !pri->pvts[chanpos]->no_b_channel
+#if defined(HAVE_PRI_SETUP_ACK_INBAND)
+                                       /*
+                                        * We only care about PRI_PROG_INBAND_AVAILABLE to open the
+                                        * voice path.
+                                        *
+                                        * We explicitly DO NOT want to check PRI_PROG_CALL_NOT_E2E_ISDN
+                                        * because it will mess up ISDN to SIP interoperability for
+                                        * the ALERTING message.
+                                        *
+                                        * Q.931 Section 5.1.3 says that in scenarios with overlap
+                                        * dialing where no called digits are received and the tone
+                                        * option requires dialtone, the switch MAY send an inband
+                                        * progress indication ie to indicate dialtone presence in
+                                        * the SETUP ACKNOWLEDGE.  Therefore, if we did not send any
+                                        * digits with the SETUP then we must assume that dialtone
+                                        * is present and open the voice path.  Fortunately when
+                                        * interoperating with SIP, we should be sending digits.
+                                        */
+                                       && ((e->setup_ack.progressmask & PRI_PROG_INBAND_AVAILABLE)
+                                               || pri->inband_on_setup_ack
+                                               || pri->pvts[chanpos]->no_dialed_digits)
+#endif /* defined(HAVE_PRI_SETUP_ACK_INBAND) */
+                                       ) {
                                        /*
                                         * Call has a channel.
                                         * Indicate for overlap dialing that dialtone may be present.
@@ -7605,9 +7601,8 @@ static void *pri_dchannel(void *vpri)
                                break;
                        }
 
-                       /* If a callid was set, we need to deref it and remove it from thread storage. */
+                       /* If a callid was set, we need to remove it from thread storage. */
                        if (callid) {
-                               callid = ast_callid_unref(callid);
                                ast_callid_threadassoc_remove();
                        }
                }
@@ -8037,7 +8032,7 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, const char *rd
        if (p->pri->facilityenable)
                pri_facility_enable(p->pri->pri);
 
-       ast_verb(3, "Requested transfer capability: 0x%.2x - %s\n", ast_channel_transfercapability(ast), ast_transfercapability2str(ast_channel_transfercapability(ast)));
+       ast_verb(3, "Requested transfer capability: 0x%02hx - %s\n", ast_channel_transfercapability(ast), ast_transfercapability2str(ast_channel_transfercapability(ast)));
        dp_strip = 0;
        pridialplan = p->pri->dialplan - 1;
        if (pridialplan == -2 || pridialplan == -3) { /* compute dynamically */
@@ -8120,7 +8115,12 @@ int sig_pri_call(struct sig_pri_chan *p, struct ast_channel *ast, const char *rd
        if (!keypad || !ast_strlen_zero(c + p->stripmsd + dp_strip))
 #endif /* defined(HAVE_PRI_SETUP_KEYPAD) */
        {
-               pri_sr_set_called(sr, c + p->stripmsd + dp_strip, pridialplan, s ? 1 : 0);
+               char *called = c + p->stripmsd + dp_strip;
+
+               pri_sr_set_called(sr, called, pridialplan, s ? 1 : 0);
+#if defined(HAVE_PRI_SETUP_ACK_INBAND)
+               p->no_dialed_digits = !called[0];
+#endif /* defined(HAVE_PRI_SETUP_ACK_INBAND) */
        }
 
 #if defined(HAVE_PRI_SUBADDR)
@@ -8370,11 +8370,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
                        p->call_level = SIG_PRI_CALL_LEVEL_PROCEEDING;
                        if (p->pri && p->pri->pri) {
                                pri_grab(p, p->pri);
-                               pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p),
-                                       p->no_b_channel || p->digital ? 0 : 1);
-                               if (!p->no_b_channel && !p->digital) {
-                                       sig_pri_set_dialing(p, 0);
-                               }
+                               pri_proceeding(p->pri->pri,p->call, PVT_TO_CHANNEL(p), 0);
                                pri_rel(p->pri);
                        }
                }
@@ -8585,16 +8581,18 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi
                                        if (p->pri->aoc_passthrough_flag & SIG_PRI_AOC_GRANT_E) {
                                                sig_pri_aoc_e_from_ast(p, decoded);
                                        }
-                                       /* if hangup was delayed for this AOC-E msg, waiting_for_aoc
+                                       /*
+                                        * If hangup was delayed for this AOC-E msg, waiting_for_aoc
                                         * will be set.  A hangup is already occuring via a timeout during
                                         * this delay.  Instead of waiting for that timeout to occur, go ahead
-                                        * and initiate the softhangup since the delay is no longer necessary */
+                                        * and initiate the hangup since the delay is no longer necessary.
+                                        */
                                        if (p->waiting_for_aoce) {
                                                p->waiting_for_aoce = 0;
                                                ast_debug(1,
                                                        "Received final AOC-E msg, continue with hangup on %s\n",
                                                        ast_channel_name(chan));
-                                               ast_softhangup_nolock(chan, AST_SOFTHANGUP_DEV);
+                                               ast_queue_hangup(chan);
                                        }
                                        break;
                                case AST_AOC_REQUEST:
@@ -8797,7 +8795,7 @@ int sig_pri_digit_begin(struct sig_pri_chan *pvt, struct ast_channel *ast, char
                }
                if (pvt->call_level < SIG_PRI_CALL_LEVEL_CONNECT) {
                        ast_log(LOG_WARNING,
-                               "Span %d: Digit '%c' may be ignored by peer. (Call level:%d(%s))\n",
+                               "Span %d: Digit '%c' may be ignored by peer. (Call level:%u(%s))\n",
                                pvt->pri->span, digit, pvt->call_level,
                                sig_pri_call_level2str(pvt->call_level));
                }
@@ -8847,25 +8845,25 @@ void sig_pri_dial_complete(struct sig_pri_chan *pvt, struct ast_channel *ast)
  *
  * \param pri PRI span control structure.
  * \param vm_number Voicemail controlling number (NULL if not present).
- * \param mbox_number Mailbox number
- * \param mbox_context Mailbox context
+ * \param vm_box Voicemail mailbox number
+ * \param mbox_id Mailbox id
  * \param num_messages Number of messages waiting.
  *
  * \return Nothing
  */
-static void sig_pri_send_mwi_indication(struct sig_pri_span *pri, const char *vm_number, const char *mbox_number, const char *mbox_context, int num_messages)
+static void sig_pri_send_mwi_indication(struct sig_pri_span *pri, const char *vm_number, const char *vm_box, const char *mbox_id, int num_messages)
 {
        struct pri_party_id voicemail;
        struct pri_party_id mailbox;
 
-       ast_debug(1, "Send MWI indication for %s@%s vm_number:%s num_messages:%d\n",
-               mbox_number, mbox_context, S_OR(vm_number, "<not-present>"), num_messages);
+       ast_debug(1, "Send MWI indication for %s(%s) vm_number:%s num_messages:%d\n",
+               vm_box, mbox_id, S_OR(vm_number, "<not-present>"), num_messages);
 
        memset(&mailbox, 0, sizeof(mailbox));
        mailbox.number.valid = 1;
        mailbox.number.presentation = PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
        mailbox.number.plan = (PRI_TON_UNKNOWN << 4) | PRI_NPI_UNKNOWN;
-       ast_copy_string(mailbox.number.str, mbox_number, sizeof(mailbox.number.str));
+       ast_copy_string(mailbox.number.str, vm_box, sizeof(mailbox.number.str));
 
        memset(&voicemail, 0, sizeof(voicemail));
        voicemail.number.valid = 1;
@@ -8902,9 +8900,6 @@ static void sig_pri_send_mwi_indication(struct sig_pri_span *pri, const char *vm
 static void sig_pri_mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 {
        struct sig_pri_span *pri = userdata;
-       const char *mbox_context;
-       const char *mbox_number;
-       int num_messages;
        int idx;
        struct ast_mwi_state *mwi_state;
 
@@ -8914,26 +8909,16 @@ static void sig_pri_mwi_event_cb(void *userdata, struct stasis_subscription *sub
 
        mwi_state = stasis_message_data(msg);
 
-       mbox_number = mwi_state->mailbox;
-       if (ast_strlen_zero(mbox_number)) {
-               return;
-       }
-       mbox_context = mwi_state->context;
-       if (ast_strlen_zero(mbox_context)) {
-               return;
-       }
-       num_messages = mwi_state->new_msgs;
-
        for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) {
                if (!pri->mbox[idx].sub) {
                        /* Mailbox slot is empty */
                        continue;
                }
-               if (!strcmp(pri->mbox[idx].number, mbox_number)
-                       && !strcmp(pri->mbox[idx].context, mbox_context)) {
+
+               if (!strcmp(pri->mbox[idx].uniqueid, mwi_state->uniqueid)) {
                        /* Found the mailbox. */
-                       sig_pri_send_mwi_indication(pri, pri->mbox[idx].vm_number, mbox_number,
-                               mbox_context, num_messages);
+                       sig_pri_send_mwi_indication(pri, pri->mbox[idx].vm_number,
+                               pri->mbox[idx].vm_box, pri->mbox[idx].uniqueid, mwi_state->new_msgs);
                        break;
                }
        }
@@ -8953,7 +8938,6 @@ static void sig_pri_mwi_event_cb(void *userdata, struct stasis_subscription *sub
 static void sig_pri_mwi_cache_update(struct sig_pri_span *pri)
 {
        int idx;
-       struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID);
        struct ast_mwi_state *mwi_state;
 
        for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) {
@@ -8963,18 +8947,16 @@ static void sig_pri_mwi_cache_update(struct sig_pri_span *pri)
                        continue;
                }
 
-               ast_str_reset(uniqueid);
-               ast_str_set(&uniqueid, 0, "%s@%s", pri->mbox[idx].number, pri->mbox[idx].context);
-
-               msg = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
+               msg = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(),
+                       pri->mbox[idx].uniqueid);
                if (!msg) {
                        /* No cached event for this mailbox. */
                        continue;
                }
 
                mwi_state = stasis_message_data(msg);
-               sig_pri_send_mwi_indication(pri, pri->mbox[idx].vm_number, pri->mbox[idx].number,
-                       pri->mbox[idx].context, mwi_state->new_msgs);
+               sig_pri_send_mwi_indication(pri, pri->mbox[idx].vm_number, pri->mbox[idx].vm_box,
+                       pri->mbox[idx].uniqueid, mwi_state->new_msgs);
        }
 }
 #endif /* defined(HAVE_PRI_MWI) */
@@ -8996,7 +8978,7 @@ void sig_pri_stop_pri(struct sig_pri_span *pri)
 #if defined(HAVE_PRI_MWI)
        for (idx = 0; idx < ARRAY_LEN(pri->mbox); ++idx) {
                if (pri->mbox[idx].sub) {
-                       pri->mbox[idx].sub = stasis_unsubscribe(pri->mbox[idx].sub);
+                       pri->mbox[idx].sub = stasis_unsubscribe_and_join(pri->mbox[idx].sub);
                }
        }
 #endif /* defined(HAVE_PRI_MWI) */
@@ -9059,8 +9041,6 @@ int sig_pri_start_pri(struct sig_pri_span *pri)
 #if defined(HAVE_PRI_MWI)
        char *saveptr;
        char *prev_vm_number;
-       struct ast_str *mwi_description = ast_str_alloca(64);
-       struct ast_str *uniqueid = ast_str_alloca(AST_MAX_MAILBOX_UNIQUEID);
 #endif /* defined(HAVE_PRI_MWI) */
 
 #if defined(HAVE_PRI_MWI)
@@ -9100,58 +9080,61 @@ int sig_pri_start_pri(struct sig_pri_span *pri)
        }
 
        /*
+        * Split the mwi_vm_boxes configuration string into the mbox[].vm_box:
+        * vm_box{,vm_box}
+        */
+       saveptr = pri->mwi_vm_boxes;
+       for (i = 0; i < ARRAY_LEN(pri->mbox); ++i) {
+               char *vm_box;
+
+               vm_box = strsep(&saveptr, ",");
+               if (vm_box) {
+                       vm_box = ast_strip(vm_box);
+                       if (ast_strlen_zero(vm_box)) {
+                               vm_box = NULL;
+                       }
+               }
+               pri->mbox[i].vm_box = vm_box;
+       }
+
+       /*
         * Split the mwi_mailboxes configuration string into the mbox[]:
-        * mailbox_number[@context]{,mailbox_number[@context]}
+        * vm_mailbox{,vm_mailbox}
         */
        saveptr = pri->mwi_mailboxes;
        for (i = 0; i < ARRAY_LEN(pri->mbox); ++i) {
-               char *mbox_number;
-               char *mbox_context;
+               char *mbox_id;
                struct stasis_topic *mailbox_specific_topic;
 
-               mbox_number = strsep(&saveptr, ",");
-               if (!mbox_number) {
-                       /* No more defined mailboxes. */
-                       break;
-               }
-               /* Split the mailbox_number and context */
-               mbox_context = strchr(mbox_number, '@');
-               if (mbox_context) {
-                       *mbox_context++ = '\0';
-                       mbox_context = ast_strip(mbox_context);
+               mbox_id = strsep(&saveptr, ",");
+               if (mbox_id) {
+                       mbox_id = ast_strip(mbox_id);
+                       if (ast_strlen_zero(mbox_id)) {
+                               mbox_id = NULL;
+                       }
                }
-               mbox_number = ast_strip(mbox_number);
-               if (ast_strlen_zero(mbox_number)) {
-                       /* There is no mailbox number.  Skip it. */
+               pri->mbox[i].uniqueid = mbox_id;
+               if (!pri->mbox[i].vm_box || !mbox_id) {
+                       /* The mailbox position is disabled. */
+                       ast_debug(1, "%s span %d MWI position %d disabled.  vm_box:%s mbox_id:%s.\n",
+                               sig_pri_cc_type_name, pri->span, i,
+                               pri->mbox[i].vm_box ?: "<missing>",
+                               mbox_id ?: "<missing>");
                        continue;
                }
-               if (ast_strlen_zero(mbox_context)) {
-                       /* There was no context so use the default. */
-                       mbox_context = "default";
-               }
-
-               /* Fill the mbox[] element. */
-               pri->mbox[i].number = mbox_number;
-               pri->mbox[i].context = mbox_context;
-
-               ast_str_reset(uniqueid);
-               ast_str_set(&uniqueid, 0, "%s@%s", mbox_number, mbox_context);
-
-               ast_str_set(&mwi_description, -1, "%s span %d[%d] MWI mailbox %s@%s",
-                       sig_pri_cc_type_name, pri->span, i, mbox_number, mbox_context);
 
-               mailbox_specific_topic = ast_mwi_topic(ast_str_buffer(uniqueid));
+               mailbox_specific_topic = ast_mwi_topic(mbox_id);
                if (mailbox_specific_topic) {
-                       pri->mbox[i].sub = stasis_subscribe(mailbox_specific_topic, sig_pri_mwi_event_cb, pri);
+                       pri->mbox[i].sub = stasis_subscribe_pool(mailbox_specific_topic, sig_pri_mwi_event_cb, pri);
                }
                if (!pri->mbox[i].sub) {
-                       ast_log(LOG_ERROR, "%s span %d could not subscribe to MWI events for %s@%s.",
-                               sig_pri_cc_type_name, pri->span, mbox_number, mbox_context);
+                       ast_log(LOG_ERROR, "%s span %d could not subscribe to MWI events for %s(%s).\n",
+                               sig_pri_cc_type_name, pri->span, pri->mbox[i].vm_box, mbox_id);
                }
 #if defined(HAVE_PRI_MWI_V2)
                if (ast_strlen_zero(pri->mbox[i].vm_number)) {
-                       ast_log(LOG_WARNING, "%s span %d MWI voicemail number for %s@%s is empty.\n",
-                               sig_pri_cc_type_name, pri->span, mbox_number, mbox_context);
+                       ast_log(LOG_WARNING, "%s span %d MWI voicemail number for %s(%s) is empty.\n",
+                               sig_pri_cc_type_name, pri->span, pri->mbox[i].vm_box, mbox_id);
                }
 #endif /* defined(HAVE_PRI_MWI_V2) */
        }