diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 3f1d9e0518961d0d77d310ebe616919e3a3b65c8..e1c4a26bc8816d62b92c21d104855577cb6e4f38 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -3238,12 +3238,7 @@ static void dahdi_pri_update_span_devstate(struct sig_pri_span *pri) if (pri->pvts[idx] && !pri->pvts[idx]->no_b_channel) { /* This is a B channel interface. */ ++num_b_chans; - if (pri->pvts[idx]->owner -#if defined(HAVE_PRI_SERVICE_MESSAGES) - /* Out-of-service B channels are "in-use". */ - || pri->pvts[idx]->service_status -#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ - ) { + if (!sig_pri_is_chan_available(pri->pvts[idx])) { ++in_use; } if (!pri->pvts[idx]->inalarm) { @@ -6458,11 +6453,9 @@ hangup_out: p->cidspill = NULL; ast_mutex_unlock(&p->lock); - ast_module_unref(ast_module_info->self); ast_verb(3, "Hungup '%s'\n", ast->name); ast_mutex_lock(&iflock); - if (p->restartpending) { num_restart_pending--; } @@ -6471,6 +6464,8 @@ hangup_out: destroy_channel(p, 0); } ast_mutex_unlock(&iflock); + + ast_module_unref(ast_module_info->self); return 0; } @@ -8771,14 +8766,27 @@ static struct ast_frame *dahdi_exception(struct ast_channel *ast) static struct ast_frame *dahdi_read(struct ast_channel *ast) { - struct dahdi_pvt *p = ast->tech_pvt; + struct dahdi_pvt *p; int res; int idx; void *readbuf; struct ast_frame *f; + /* + * For analog channels, we must do deadlock avoidance because + * analog ports can have more than one Asterisk channel using + * the same private structure. + */ + p = ast->tech_pvt; while (ast_mutex_trylock(&p->lock)) { CHANNEL_DEADLOCK_AVOIDANCE(ast); + + /* + * For PRI channels, we must refresh the private pointer because + * the call could move to another B channel while the Asterisk + * channel is unlocked. + */ + p = ast->tech_pvt; } idx = dahdi_get_index(ast, p, 0); @@ -11317,7 +11325,9 @@ static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event) switch (i->sig) { #if defined(HAVE_PRI) case SIG_PRI_LIB_HANDLE_CASES: + ast_mutex_lock(&i->lock); sig_pri_chan_alarm_notify(i->sig_pvt, 1); + ast_mutex_unlock(&i->lock); break; #endif /* defined(HAVE_PRI) */ #if defined(HAVE_SS7) @@ -11335,7 +11345,9 @@ static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event) switch (i->sig) { #if defined(HAVE_PRI) case SIG_PRI_LIB_HANDLE_CASES: + ast_mutex_lock(&i->lock); sig_pri_chan_alarm_notify(i->sig_pvt, 0); + ast_mutex_unlock(&i->lock); break; #endif /* defined(HAVE_PRI) */ #if defined(HAVE_SS7) @@ -12745,7 +12757,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, switch (tmp->sig) { #ifdef HAVE_PRI case SIG_PRI_LIB_HANDLE_CASES: + ast_mutex_lock(&tmp->lock); sig_pri_chan_alarm_notify(tmp->sig_pvt, si.alarms); + ast_mutex_unlock(&tmp->lock); break; #endif #if defined(HAVE_SS7) @@ -13489,6 +13503,14 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap tmp = analog_request(p->sig_pvt, &callwait, requestor); #ifdef HAVE_PRI } else if (dahdi_sig_pri_lib_handles(p->sig)) { + /* + * We already have the B channel reserved for this call. We + * just need to make sure that dahdi_hangup() has completed + * cleaning up before continuing. + */ + ast_mutex_lock(&p->lock); + ast_mutex_unlock(&p->lock); + sig_pri_extract_called_num_subaddr(p->sig_pvt, data, p->dnid, sizeof(p->dnid)); tmp = sig_pri_request(p->sig_pvt, SIG_PRI_DEFLAW, requestor, transcapdigital); @@ -13503,18 +13525,23 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap if (!tmp) { p->outgoing = 0; #if defined(HAVE_PRI) -#if defined(HAVE_PRI_CALL_WAITING) switch (p->sig) { case SIG_PRI_LIB_HANDLE_CASES: +#if defined(HAVE_PRI_CALL_WAITING) if (((struct sig_pri_chan *) p->sig_pvt)->is_call_waiting) { ((struct sig_pri_chan *) p->sig_pvt)->is_call_waiting = 0; ast_atomic_fetchadd_int(&p->pri->num_call_waiting_calls, -1); } +#endif /* defined(HAVE_PRI_CALL_WAITING) */ + /* + * This should be the last thing to clear when we are done with + * the channel. + */ + ((struct sig_pri_chan *) p->sig_pvt)->allocated = 0; break; default: break; } -#endif /* defined(HAVE_PRI_CALL_WAITING) */ #endif /* defined(HAVE_PRI) */ } else { snprintf(p->dialstring, sizeof(p->dialstring), "DAHDI/%s", (char *) data); @@ -15194,6 +15221,9 @@ static char *dahdi_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli ast_cli(a->fd, "Resetting "); if (chan->call) ast_cli(a->fd, "Call "); + if (chan->allocated) { + ast_cli(a->fd, "Allocated "); + } ast_cli(a->fd, "\n"); if (tmp->logicalspan) ast_cli(a->fd, "PRI Logical Span: %d\n", tmp->logicalspan); diff --git a/channels/sig_pri.c b/channels/sig_pri.c index f36a7e2f0795e06ab46ab356c6eabeb08426cf4e..1df3972f4450bf6be6ff785bf7f52adeb0e6b49e 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -56,6 +56,21 @@ /* define this to send PRI user-user information elements */ #undef SUPPORT_USERUSER +/*! + * Define to make always pick a channel if allowed. Useful for + * testing channel shifting. + */ +//#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. */ @@ -1037,6 +1052,38 @@ static int pri_find_dchan(struct sig_pri_span *pri) return 0; } +/*! + * \internal + * \brief Determine if a private channel structure is in use. + * \since 1.8 + * + * \param pvt Channel to determine if in use. + * + * \return TRUE if the channel is in use. + */ +static int sig_pri_is_chan_in_use(struct sig_pri_chan *pvt) +{ + return pvt->owner || pvt->call || pvt->allocated || pvt->resetting || pvt->inalarm; +} + +/*! + * \brief Determine if a private channel structure is available. + * \since 1.8 + * + * \param pvt Channel to determine if available. + * + * \return TRUE if the channel is available. + */ +int sig_pri_is_chan_available(struct sig_pri_chan *pvt) +{ + return !sig_pri_is_chan_in_use(pvt) +#if defined(HAVE_PRI_SERVICE_MESSAGES) + /* And not out-of-service */ + && !pvt->service_status +#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ + ; +} + /*! * \internal * \brief Obtain the sig_pri owner channel lock if the owner exists. @@ -1148,6 +1195,41 @@ static int pri_find_principle_by_call(struct sig_pri_span *pri, q931_call *call) return -1; } +/*! + * \internal + * \brief Kill the call. + * \since 1.10 + * + * \param pri PRI span control structure. + * \param call LibPRI opaque call pointer to find. + * \param cause Reason call was killed. + * + * \note Assumes the pvt->pri->lock is already obtained. + * + * \return Nothing + */ +static void sig_pri_kill_call(struct sig_pri_span *pri, q931_call *call, int cause) +{ + int chanpos; + + chanpos = pri_find_principle_by_call(pri, call); + if (chanpos < 0) { + pri_hangup(pri->pri, call, cause); + return; + } + sig_pri_lock_private(pri->pvts[chanpos]); + if (!pri->pvts[chanpos]->owner) { + pri_hangup(pri->pri, call, cause); + pri->pvts[chanpos]->call = NULL; + sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); + return; + } + pri->pvts[chanpos]->owner->hangupcause = cause; + pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP); + sig_pri_unlock_private(pri->pvts[chanpos]); +} + /*! * \internal * \brief Find the private structure for the libpri call. @@ -1255,7 +1337,7 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal ast_verb(3, "Moving call (%s) from channel %d to %d.\n", old_chan->owner ? old_chan->owner->name : "", old_chan->channel, new_chan->channel); - if (new_chan->owner) { + if (!sig_pri_is_chan_available(new_chan)) { ast_log(LOG_WARNING, "Can't move call (%s) from channel %d to %d. It is already in use.\n", old_chan->owner ? old_chan->owner->name : "", @@ -1286,6 +1368,7 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal new_chan->alreadyhungup = old_chan->alreadyhungup; new_chan->isidlecall = old_chan->isidlecall; new_chan->progress = old_chan->progress; + new_chan->allocated = old_chan->allocated; new_chan->outgoing = old_chan->outgoing; new_chan->digital = old_chan->digital; #if defined(HAVE_PRI_CALL_WAITING) @@ -1300,6 +1383,7 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal old_chan->alreadyhungup = 0; old_chan->isidlecall = 0; old_chan->progress = 0; + old_chan->allocated = 0; old_chan->outgoing = 0; old_chan->digital = 0; #if defined(HAVE_PRI_CALL_WAITING) @@ -1367,6 +1451,52 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal return -1; } +/*! + * \internal + * \brief Find and fixup the private structure associated with the libpri call. + * + * \param pri PRI span control structure. + * \param channel LibPRI encoded channel ID. + * \param call LibPRI opaque call pointer. + * + * \details + * This is a combination of pri_find_principle() and pri_fixup_principle() + * to reduce code redundancy and to make handling several PRI_EVENT_xxx's + * consistent for the current architecture. + * + * \note Assumes the pri->lock is already obtained. + * + * \retval array-index into private pointer array on success. + * \retval -1 on error. + */ +static int pri_find_fixup_principle(struct sig_pri_span *pri, int channel, q931_call *call) +{ + int chanpos; + + chanpos = pri_find_principle(pri, channel, call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Span %d: PRI requested channel %d/%d is unconfigured.\n", + pri->span, PRI_SPAN(channel), PRI_CHANNEL(channel)); + sig_pri_kill_call(pri, call, PRI_CAUSE_IDENTIFIED_CHANNEL_NOTEXIST); + return -1; + } + chanpos = pri_fixup_principle(pri, chanpos, call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Span %d: PRI requested channel %d/%d is not available.\n", + pri->span, PRI_SPAN(channel), PRI_CHANNEL(channel)); + /* + * Using Q.931 section 5.2.3.1 b) as the reason for picking + * PRI_CAUSE_CHANNEL_UNACCEPTABLE. Receiving a + * PRI_CAUSE_REQUESTED_CHAN_UNAVAIL would cause us to restart + * that channel (which is not specified by Q.931) and kill some + * other call which would be bad. + */ + sig_pri_kill_call(pri, call, PRI_CAUSE_CHANNEL_UNACCEPTABLE); + return -1; + } + return chanpos; +} + static char * redirectingreason2str(int redirectingreason) { switch (redirectingreason) { @@ -1415,39 +1545,49 @@ static void apply_plan_to_number(char *buf, size_t size, const struct sig_pri_sp } } -/*! \note Assumes the pri->lock is already obtained. */ -static int pri_check_restart(struct sig_pri_span *pri) +/*! + * \internal + * \brief Restart the next channel we think is idle on the span. + * + * \param pri PRI span control structure. + * + * \note Assumes the pri->lock is already obtained. + * + * \return Nothing + */ +static void pri_check_restart(struct sig_pri_span *pri) { #if defined(HAVE_PRI_SERVICE_MESSAGES) -tryanotherpos: + unsigned why; #endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ - do { - pri->resetpos++; - } while (pri->resetpos < pri->numchans - && (!pri->pvts[pri->resetpos] + + for (++pri->resetpos; pri->resetpos < pri->numchans; ++pri->resetpos) { + if (!pri->pvts[pri->resetpos] || pri->pvts[pri->resetpos]->no_b_channel - || pri->pvts[pri->resetpos]->call - || pri->pvts[pri->resetpos]->resetting)); - if (pri->resetpos < pri->numchans) { + || sig_pri_is_chan_in_use(pri->pvts[pri->resetpos])) { + continue; + } #if defined(HAVE_PRI_SERVICE_MESSAGES) - unsigned why; - why = pri->pvts[pri->resetpos]->service_status; if (why) { - ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), not sending RESTART\n", pri->span, - pri->pvts[pri->resetpos]->channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end"); - goto tryanotherpos; + ast_log(LOG_NOTICE, + "Span %d: channel %d out-of-service (reason: %s), not sending RESTART\n", + pri->span, pri->pvts[pri->resetpos]->channel, + (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end"); + continue; } #endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ - + break; + } + if (pri->resetpos < pri->numchans) { /* Mark the channel as resetting and restart it */ pri->pvts[pri->resetpos]->resetting = 1; pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos])); } else { pri->resetting = 0; time(&pri->lastreset); + sig_pri_span_devstate_changed(pri); } - return 0; } #if defined(HAVE_PRI_CALL_WAITING) @@ -1482,6 +1622,18 @@ static void sig_pri_init_config(struct sig_pri_chan *pvt, struct sig_pri_span *p } #endif /* defined(HAVE_PRI_CALL_WAITING) */ +/*! + * \internal + * \brief Find an empty B-channel interface to use. + * + * \param pri PRI span control structure. + * \param backwards TRUE if the search starts from higher channels. + * + * \note Assumes the pri->lock is already obtained. + * + * \retval array-index into private pointer array on success. + * \retval -1 on error. + */ static int pri_find_empty_chan(struct sig_pri_span *pri, int backwards) { int x; @@ -1496,8 +1648,7 @@ static int pri_find_empty_chan(struct sig_pri_span *pri, int backwards) break; if (pri->pvts[x] && !pri->pvts[x]->no_b_channel - && !pri->pvts[x]->inalarm - && !pri->pvts[x]->owner) { + && sig_pri_is_chan_available(pri->pvts[x])) { ast_debug(1, "Found empty available channel %d/%d\n", pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset); return x; @@ -1530,8 +1681,7 @@ static int pri_find_empty_nobch(struct sig_pri_span *pri) for (idx = 0; idx < pri->numchans; ++idx) { if (pri->pvts[idx] && pri->pvts[idx]->no_b_channel - && !pri->pvts[idx]->inalarm - && !pri->pvts[idx]->owner) { + && sig_pri_is_chan_available(pri->pvts[idx])) { ast_debug(1, "Found empty available no B channel interface\n"); return idx; } @@ -1717,6 +1867,9 @@ static void *pri_ss_thread(void *data) p->exten[0] = '\0'; /* Since we send release complete here, we won't get one */ p->call = NULL; + ast_mutex_lock(&p->pri->lock); + sig_pri_span_devstate_changed(p->pri); + ast_mutex_unlock(&p->pri->lock); } return NULL; } @@ -4183,42 +4336,6 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev } } -#if defined(HAVE_PRI_CALL_HOLD) -/*! - * \internal - * \brief Kill the call. - * \since 1.10 - * - * \param pri PRI span control structure. - * \param call LibPRI opaque call pointer to find. - * \param cause Reason call was killed. - * - * \note Assumes the pvt->pri->lock is already obtained. - * - * \return Nothing - */ -static void sig_pri_kill_call(struct sig_pri_span *pri, q931_call *call, int cause) -{ - int chanpos; - - chanpos = pri_find_principle_by_call(pri, call); - if (chanpos < 0) { - pri_hangup(pri->pri, call, cause); - return; - } - sig_pri_lock_private(pri->pvts[chanpos]); - if (!pri->pvts[chanpos]->owner) { - pri_hangup(pri->pri, call, cause); - pri->pvts[chanpos]->call = NULL; - sig_pri_unlock_private(pri->pvts[chanpos]); - return; - } - pri->pvts[chanpos]->owner->hangupcause = cause; - pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP); - sig_pri_unlock_private(pri->pvts[chanpos]); -} -#endif /* defined(HAVE_PRI_CALL_HOLD) */ - /*! * \internal * \brief Convert the MOH state to string. @@ -4899,11 +5016,9 @@ static int sig_pri_handle_hold(struct sig_pri_span *pri, pri_event *ev) struct ast_channel *bridged; struct ast_channel *owner; - chanpos_old = pri_find_principle(pri, ev->hold.channel, ev->hold.call); + chanpos_old = pri_find_principle_by_call(pri, ev->hold.call); if (chanpos_old < 0) { - ast_log(LOG_WARNING, - "Received HOLD on unconfigured channel %d/%d span %d\n", - PRI_SPAN(ev->hold.channel), PRI_CHANNEL(ev->hold.channel), pri->span); + ast_log(LOG_WARNING, "Span %d: Received HOLD for unknown call.\n", pri->span); return -1; } if (pri->pvts[chanpos_old]->no_b_channel) { @@ -4911,23 +5026,22 @@ static int sig_pri_handle_hold(struct sig_pri_span *pri, pri_event *ev) return -1; } + chanpos_new = -1; + sig_pri_lock_private(pri->pvts[chanpos_old]); sig_pri_lock_owner(pri, chanpos_old); owner = pri->pvts[chanpos_old]->owner; if (!owner) { - retval = -1; goto done_with_private; } bridged = ast_bridged_channel(owner); if (!bridged) { /* Cannot hold a call that is not bridged. */ - retval = -1; goto done_with_owner; } chanpos_new = pri_find_empty_nobch(pri); if (chanpos_new < 0) { /* No hold channel available. */ - retval = -1; goto done_with_owner; } sig_pri_handle_subcmds(pri, chanpos_old, ev->e, ev->hold.channel, ev->hold.subcmds, @@ -4937,11 +5051,8 @@ static int sig_pri_handle_hold(struct sig_pri_span *pri, pri_event *ev) if (chanpos_new < 0) { /* Should never happen. */ pri_queue_control(pri, chanpos_old, AST_CONTROL_UNHOLD); - retval = -1; } else { sig_pri_ami_hold_event(owner, 1); - sig_pri_span_devstate_changed(pri); - retval = 0; } done_with_owner:; @@ -4949,6 +5060,13 @@ done_with_owner:; done_with_private:; sig_pri_unlock_private(pri->pvts[chanpos_old]); + if (chanpos_new < 0) { + retval = -1; + } else { + sig_pri_span_devstate_changed(pri); + retval = 0; + } + return retval; } #endif /* defined(HAVE_PRI_CALL_HOLD) */ @@ -4996,6 +5114,7 @@ static void sig_pri_handle_hold_ack(struct sig_pri_span *pri, pri_event *ev) sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], SIG_PRI_MOH_EVENT_HOLD_ACK); sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); } #endif /* defined(HAVE_PRI_CALL_HOLD) */ @@ -5020,6 +5139,7 @@ static void sig_pri_handle_hold_rej(struct sig_pri_span *pri, pri_event *ev) if (chanpos < 0) { ast_log(LOG_WARNING, "Span %d: Could not find principle for HOLD_REJECT\n", pri->span); + sig_pri_kill_call(pri, ev->hold_rej.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE); return; } chanpos = pri_fixup_principle(pri, chanpos, ev->hold_rej.call); @@ -5058,23 +5178,28 @@ static void sig_pri_handle_retrieve(struct sig_pri_span *pri, pri_event *ev) { int chanpos; - if (!(ev->retrieve.channel & PRI_HELD_CALL) - || pri_find_principle(pri, ev->retrieve.channel, ev->retrieve.call) < 0) { + if (!(ev->retrieve.channel & PRI_HELD_CALL)) { /* The call is not currently held. */ pri_retrieve_rej(pri->pri, ev->retrieve.call, PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED); return; } + if (pri_find_principle_by_call(pri, ev->retrieve.call) < 0) { + ast_log(LOG_WARNING, "Span %d: Received RETRIEVE for unknown call.\n", pri->span); + pri_retrieve_rej(pri->pri, ev->retrieve.call, + PRI_CAUSE_RESOURCE_UNAVAIL_UNSPECIFIED); + return; + } if (PRI_CHANNEL(ev->retrieve.channel) == 0xFF) { chanpos = pri_find_empty_chan(pri, 1); } else { chanpos = pri_find_principle(pri, ev->retrieve.channel & ~PRI_HELD_CALL, ev->retrieve.call); if (ev->retrieve.flexible - && (chanpos < 0 || pri->pvts[chanpos]->owner)) { + && (chanpos < 0 || !sig_pri_is_chan_available(pri->pvts[chanpos]))) { /* * Channel selection is flexible and the requested channel - * is bad or already in use. Pick another channel. + * is bad or not available. Pick another channel. */ chanpos = pri_find_empty_chan(pri, 1); } @@ -5127,16 +5252,9 @@ static void sig_pri_handle_retrieve_ack(struct sig_pri_span *pri, pri_event *ev) { int chanpos; - chanpos = pri_find_principle(pri, ev->retrieve_ack.channel, ev->retrieve_ack.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, - "Span %d: Could not find principle for RETRIEVE_ACKNOWLEDGE\n", pri->span); - return; - } - chanpos = pri_fixup_principle(pri, chanpos, ev->retrieve_ack.call); + chanpos = pri_find_fixup_principle(pri, ev->retrieve_ack.channel, + ev->retrieve_ack.call); if (chanpos < 0) { - /* Very bad news. The channel is already in use. */ - sig_pri_kill_call(pri, ev->retrieve_ack.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL); return; } @@ -5146,6 +5264,7 @@ static void sig_pri_handle_retrieve_ack(struct sig_pri_span *pri, pri_event *ev) sig_pri_moh_fsm_event(pri->pvts[chanpos]->owner, pri->pvts[chanpos], SIG_PRI_MOH_EVENT_RETRIEVE_ACK); sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); } #endif /* defined(HAVE_PRI_CALL_HOLD) */ @@ -5170,6 +5289,7 @@ static void sig_pri_handle_retrieve_rej(struct sig_pri_span *pri, pri_event *ev) if (chanpos < 0) { ast_log(LOG_WARNING, "Span %d: Could not find principle for RETRIEVE_REJECT\n", pri->span); + sig_pri_kill_call(pri, ev->retrieve_rej.call, PRI_CAUSE_NORMAL_TEMPORARY_FAILURE); return; } chanpos = pri_fixup_principle(pri, chanpos, ev->retrieve_rej.call); @@ -5218,6 +5338,7 @@ static void *pri_dchannel(void *vpri) int nextidle = -1; int haveidles; int activeidles; + unsigned int len; gettimeofday(&lastidle, NULL); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); @@ -5252,8 +5373,12 @@ static void *pri_dchannel(void *vpri) ast_mutex_lock(&pri->lock); if (pri->switchtype != PRI_SWITCH_GR303_TMC && (pri->sig != SIG_BRI_PTMP) && (pri->resetinterval > 0)) { if (pri->resetting && pri_is_up(pri)) { - if (pri->resetpos < 0) + if (pri->resetpos < 0) { pri_check_restart(pri); + if (pri->resetting) { + sig_pri_span_devstate_changed(pri); + } + } } else { if (!pri->resetting && (t - pri->lastreset) >= pri->resetinterval) { pri->resetting = 1; @@ -5267,32 +5392,50 @@ static void *pri_dchannel(void *vpri) haveidles = 0; activeidles = 0; for (x = pri->numchans; x >= 0; x--) { - if (pri->pvts[x] - && !pri->pvts[x]->owner - && !pri->pvts[x]->call - && !pri->pvts[x]->no_b_channel) { - if (haveidles < pri->minunused) { - haveidles++; - } else if (!pri->pvts[x]->resetting) { - nextidle = x; - break; + if (pri->pvts[x] && !pri->pvts[x]->no_b_channel) { + if (sig_pri_is_chan_available(pri->pvts[x])) { + if (haveidles < pri->minunused) { + haveidles++; + } else { + nextidle = x; + break; + } + } else if (pri->pvts[x]->owner && pri->pvts[x]->isidlecall) { + activeidles++; } - } else if (pri->pvts[x] && pri->pvts[x]->owner && pri->pvts[x]->isidlecall) - activeidles++; + } } if (nextidle > -1) { if (ast_tvdiff_ms(ast_tvnow(), lastidle) > 1000) { /* Don't create a new idle call more than once per second */ snprintf(idlen, sizeof(idlen), "%d/%s", pri->pvts[nextidle]->channel, pri->idledial); + pri->pvts[nextidle]->allocated = 1; + /* + * Release the PRI lock while we create the channel so other + * threads can send D channel messages. + */ + ast_mutex_unlock(&pri->lock); + /* + * We already have the B channel reserved for this call. We + * just need to make sure that sig_pri_hangup() has completed + * cleaning up before continuing. + */ + 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); + ast_mutex_lock(&pri->lock); if (idle) { pri->pvts[nextidle]->isidlecall = 1; if (ast_pthread_create_background(&p, NULL, do_idle_thread, pri->pvts[nextidle])) { ast_log(LOG_WARNING, "Unable to start new thread for idle channel '%s'\n", idle->name); + ast_mutex_unlock(&pri->lock); ast_hangup(idle); + ast_mutex_lock(&pri->lock); } - } else + } else { + pri->pvts[nextidle]->allocated = 0; ast_log(LOG_WARNING, "Unable to request channel 'DAHDI/%s' for idle call\n", idlen); + } gettimeofday(&lastidle, NULL); } } else if ((haveidles < pri->minunused) && @@ -5415,11 +5558,12 @@ static void *pri_dchannel(void *vpri) pri->lastreset -= pri->resetinterval; pri->lastreset += 5; } - pri->resetting = 0; /* Take the channels from inalarm condition */ + pri->resetting = 0; for (i = 0; i < pri->numchans; i++) { if (pri->pvts[i]) { sig_pri_set_alarm(pri->pvts[i], 0); + pri->pvts[i]->resetting = 0; } } sig_pri_span_devstate_changed(pri); @@ -5427,41 +5571,41 @@ static void *pri_dchannel(void *vpri) case PRI_EVENT_DCHAN_DOWN: pri_find_dchan(pri); if (!pri_is_up(pri)) { - pri->resetting = 0; if (pri->sig == SIG_BRI_PTMP) { /* For PTMP connections with non persistent layer 2 we want * to *not* declare inalarm unless there actually is an alarm */ break; } /* Hangup active channels and put them in alarm mode */ + pri->resetting = 0; for (i = 0; i < pri->numchans; i++) { struct sig_pri_chan *p = pri->pvts[i]; + if (p) { - if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { - /* T309 is not enabled : hangup calls when alarm occurs */ + if (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { + /* T309 is not enabled : destroy calls when alarm occurs */ if (p->call) { - if (p->pri && p->pri->pri) { - pri_hangup(p->pri->pri, p->call, -1); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; - } else - ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n"); + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; } if (p->owner) p->owner->_softhangup |= AST_SOFTHANGUP_DEV; } sig_pri_set_alarm(p, 1); + p->resetting = 0; } } sig_pri_span_devstate_changed(pri); } break; case PRI_EVENT_RESTART: - if (e->restart.channel > -1) { + if (e->restart.channel > -1 && PRI_CHANNEL(e->ring.channel) != 0xFF) { chanpos = pri_find_principle(pri, e->restart.channel, NULL); if (chanpos < 0) - ast_log(LOG_WARNING, "Restart requested on odd/unavailable channel number %d/%d on span %d\n", - PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); + ast_log(LOG_WARNING, + "Span %d: Restart requested on odd/unavailable channel number %d/%d\n", + pri->span, PRI_SPAN(e->restart.channel), + PRI_CHANNEL(e->restart.channel)); else { int skipit = 0; #if defined(HAVE_PRI_SERVICE_MESSAGES) @@ -5470,16 +5614,18 @@ static void *pri_dchannel(void *vpri) why = pri->pvts[chanpos]->service_status; if (why) { ast_log(LOG_NOTICE, - "span '%d' channel '%d' out-of-service (reason: %s), ignoring RESTART\n", - pri->span, PRI_CHANNEL(e->restart.channel), + "Span %d: Channel %d/%d out-of-service (reason: %s), ignoring RESTART\n", + pri->span, PRI_SPAN(e->restart.channel), + PRI_CHANNEL(e->restart.channel), (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end"); skipit = 1; } #endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ sig_pri_lock_private(pri->pvts[chanpos]); if (!skipit) { - ast_verb(3, "B-channel %d/%d restarted on span %d\n", - PRI_SPAN(e->restart.channel), PRI_CHANNEL(e->restart.channel), pri->span); + ast_verb(3, "Span %d: Channel %d/%d restarted\n", pri->span, + PRI_SPAN(e->restart.channel), + PRI_CHANNEL(e->restart.channel)); if (pri->pvts[chanpos]->call) { pri_destroycall(pri->pri, pri->pvts[chanpos]->call); pri->pvts[chanpos]->call = NULL; @@ -5504,6 +5650,7 @@ static void *pri_dchannel(void *vpri) sig_pri_unlock_private(pri->pvts[x]); } } + sig_pri_span_devstate_changed(pri); break; case PRI_EVENT_KEYPAD_DIGIT: if (sig_pri_is_cis_call(e->digit.channel)) { @@ -5511,33 +5658,29 @@ static void *pri_dchannel(void *vpri) e->digit.call); break; } - chanpos = pri_find_principle(pri, e->digit.channel, e->digit.call); + chanpos = pri_find_principle_by_call(pri, e->digit.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "KEYPAD_DIGITs received on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->digit.channel), PRI_CHANNEL(e->digit.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->digit.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->digit.channel, - e->digit.subcmds, e->digit.call); - /* queue DTMF frame if the PBX for this call was already started (we're forwarding KEYPAD_DIGITs further on */ - if ((pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING) - && pri->pvts[chanpos]->call == e->digit.call - && pri->pvts[chanpos]->owner) { - /* how to do that */ - int digitlen = strlen(e->digit.digits); - int i; - - for (i = 0; i < digitlen; i++) { - struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = e->digit.digits[i], }; - - pri_queue_frame(pri, chanpos, &f); - } - } - sig_pri_unlock_private(pri->pvts[chanpos]); + ast_log(LOG_WARNING, + "Span %d: Received keypad digits for unknown call.\n", pri->span); + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->digit.channel, + e->digit.subcmds, e->digit.call); + /* queue DTMF frame if the PBX for this call was already started (we're forwarding KEYPAD_DIGITs further on */ + if ((pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING) + && pri->pvts[chanpos]->owner) { + /* how to do that */ + int digitlen = strlen(e->digit.digits); + int i; + + for (i = 0; i < digitlen; i++) { + struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = e->digit.digits[i], }; + + pri_queue_frame(pri, chanpos, &f); } } + sig_pri_unlock_private(pri->pvts[chanpos]); break; case PRI_EVENT_INFO_RECEIVED: @@ -5546,33 +5689,29 @@ static void *pri_dchannel(void *vpri) e->ring.call); break; } - chanpos = pri_find_principle(pri, e->ring.channel, e->ring.call); + chanpos = pri_find_principle_by_call(pri, e->ring.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "INFO received on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->ring.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.channel, - e->ring.subcmds, e->ring.call); - /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */ - if ((pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING) - && pri->pvts[chanpos]->call == e->ring.call - && pri->pvts[chanpos]->owner) { - /* how to do that */ - int digitlen = strlen(e->ring.callednum); - int i; - - for (i = 0; i < digitlen; i++) { - struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = e->ring.callednum[i], }; - - pri_queue_frame(pri, chanpos, &f); - } - } - sig_pri_unlock_private(pri->pvts[chanpos]); + ast_log(LOG_WARNING, + "Span %d: Received INFORMATION for unknown call.\n", pri->span); + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.channel, + e->ring.subcmds, e->ring.call); + /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */ + if ((pri->overlapdial & DAHDI_OVERLAPDIAL_INCOMING) + && pri->pvts[chanpos]->owner) { + /* how to do that */ + int digitlen = strlen(e->ring.callednum); + int i; + + for (i = 0; i < digitlen; i++) { + struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = e->ring.callednum[i], }; + + pri_queue_frame(pri, chanpos, &f); } } + sig_pri_unlock_private(pri->pvts[chanpos]); break; #if defined(HAVE_PRI_SERVICE_MESSAGES) case PRI_EVENT_SERVICE: @@ -5645,6 +5784,15 @@ static void *pri_dchannel(void *vpri) 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. Dropping call.\n", + pri->span); + 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); @@ -5671,373 +5819,400 @@ static void *pri_dchannel(void *vpri) } else { /* A channel is specified. */ 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 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 no channel specified find one empty */ if (chanpos < 0) { - ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - } else { - sig_pri_lock_private(pri->pvts[chanpos]); - if (pri->pvts[chanpos]->owner) { - if (pri->pvts[chanpos]->call == e->ring.call) { - ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - sig_pri_unlock_private(pri->pvts[chanpos]); - break; - } else { - /* This is where we handle initial glare */ - ast_debug(1, "Ring requested on channel %d/%d already in use or previously requested on span %d. Attempting to renegotiating channel.\n", - PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span); - sig_pri_unlock_private(pri->pvts[chanpos]); - chanpos = -1; - } + 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); } - if (chanpos > -1) - sig_pri_unlock_private(pri->pvts[chanpos]); + break; } - if ((chanpos < 0) && (e->ring.flexible)) - chanpos = pri_find_empty_chan(pri, 1); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - pri->pvts[chanpos]->call = e->ring.call; - - /* Use plancallingnum as a scratch buffer since it is initialized next. */ - apply_plan_to_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_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)); + + 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_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_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 - if (!ast_strlen_zero(e->ring.callingani)) { - apply_plan_to_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)); - } else { - pri->pvts[chanpos]->cid_ani[0] = '\0'; - } + if (!ast_strlen_zero(e->ring.callingani)) { + apply_plan_to_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)); + } else { + pri->pvts[chanpos]->cid_ani[0] = '\0'; + } #endif - pri->pvts[chanpos]->cid_subaddr[0] = '\0'; + 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); + 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; + 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)); - } + /* 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]); + 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); + /* 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'; - } + /* 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)) { - /* Setup law */ - 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); - } + /* 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)) { + /* Setup law */ + 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. - */ - ast_mutex_unlock(&pri->lock); - c = sig_pri_new_ast_channel(pri->pvts[chanpos], - AST_STATE_RESERVED, - (e->ring.layer1 == PRI_LAYER_1_ALAW) - ? SIG_PRI_ALAW : SIG_PRI_ULAW, - e->ring.ctype, pri->pvts[chanpos]->exten, NULL); - ast_mutex_lock(&pri->lock); - if (c) { + /* 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, + (e->ring.layer1 == PRI_LAYER_1_ALAW) + ? SIG_PRI_ALAW : SIG_PRI_ULAW, + e->ring.ctype, pri->pvts[chanpos]->exten, NULL); + ast_mutex_lock(&pri->lock); + sig_pri_lock_private(pri->pvts[chanpos]); + if (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( - &pri->pvts[chanpos]->owner->caller.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.calling.subaddress.valid) { + /* Set Calling Subaddress */ + sig_pri_lock_owner(pri, chanpos); + sig_pri_set_subaddress( + &pri->pvts[chanpos]->owner->caller.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); } - if (e->ring.called_subaddress.valid) { - /* Set Called Subaddress */ - sig_pri_lock_owner(pri, chanpos); - sig_pri_set_subaddress( - &pri->pvts[chanpos]->owner->dialed.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); + ast_channel_unlock(c); + } + if (e->ring.called_subaddress.valid) { + /* Set Called Subaddress */ + sig_pri_lock_owner(pri, chanpos); + sig_pri_set_subaddress( + &pri->pvts[chanpos]->owner->dialed.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); - } + 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); - } + 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); - } + 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); - 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)); - } + snprintf(calledtonstr, sizeof(calledtonstr), "%d", e->ring.calledplan); + pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr); + 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; + 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)); + 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.channel, - e->ring.subcmds, e->ring.call); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.channel, + 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. */ + 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 */ + 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); + pri_progress(pri->pri, e->ring.call, + PVT_TO_CHANNEL(pri->pvts[chanpos]), 1); #endif - } } - 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); + } + 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 { - 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) - ast_hangup(c); - else { - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); - pri->pvts[chanpos]->call = NULL; - } + 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); } - } else { + 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, + (e->ring.layer1 == PRI_LAYER_1_ALAW) + ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, + pri->pvts[chanpos]->exten, NULL); + ast_mutex_lock(&pri->lock); + sig_pri_lock_private(pri->pvts[chanpos]); + if (c) { /* - * Release the PRI lock while we create the channel - * so other threads can send D channel messages. + * 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_mutex_unlock(&pri->lock); - c = sig_pri_new_ast_channel(pri->pvts[chanpos], - AST_STATE_RING, - (e->ring.layer1 == PRI_LAYER_1_ALAW) - ? SIG_PRI_ALAW : SIG_PRI_ULAW, e->ring.ctype, - pri->pvts[chanpos]->exten, NULL); - ast_mutex_lock(&pri->lock); - 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. - */ #if defined(HAVE_PRI_SUBADDR) - if (e->ring.calling.subaddress.valid) { - /* Set Calling Subaddress */ - sig_pri_lock_owner(pri, chanpos); - sig_pri_set_subaddress( - &pri->pvts[chanpos]->owner->caller.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.calling.subaddress.valid) { + /* Set Calling Subaddress */ + sig_pri_lock_owner(pri, chanpos); + sig_pri_set_subaddress( + &pri->pvts[chanpos]->owner->caller.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); } - if (e->ring.called_subaddress.valid) { - /* Set Called Subaddress */ - sig_pri_lock_owner(pri, chanpos); - sig_pri_set_subaddress( - &pri->pvts[chanpos]->owner->dialed.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); + ast_channel_unlock(c); + } + if (e->ring.called_subaddress.valid) { + /* Set Called Subaddress */ + sig_pri_lock_owner(pri, chanpos); + sig_pri_set_subaddress( + &pri->pvts[chanpos]->owner->dialed.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); - } + 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); - } + 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); - } + 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 (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; + 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)); + 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); + snprintf(calledtonstr, sizeof(calledtonstr), "%d", e->ring.calledplan); + pbx_builtin_setvar_helper(c, "CALLEDTON", calledtonstr); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.channel, - e->ring.subcmds, e->ring.call); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->ring.channel, + e->ring.subcmds, e->ring.call); - } - 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); + } + 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 { - 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) { - ast_hangup(c); - } else { - pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION); - pri->pvts[chanpos]->call = NULL; - } + 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, "Extension '%s' in context '%s' from '%s' does not exist. Rejecting call on channel %d/%d, span %d\n", - pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context, pri->pvts[chanpos]->cid_num, pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - 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]); } else { - 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); + 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]); break; case PRI_EVENT_RINGING: if (sig_pri_is_cis_call(e->ringing.channel)) { @@ -6045,55 +6220,47 @@ static void *pri_dchannel(void *vpri) e->ringing.call); break; } - chanpos = pri_find_principle(pri, e->ringing.channel, e->ringing.call); + chanpos = pri_find_fixup_principle(pri, e->ringing.channel, + e->ringing.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->ringing.channel), PRI_CHANNEL(e->ringing.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->ringing.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Ringing requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->ringing.channel), PRI_CHANNEL(e->ringing.channel), pri->span); - } else { - sig_pri_lock_private(pri->pvts[chanpos]); + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->ringing.channel, - e->ringing.subcmds, e->ringing.call); - sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCNR); - sig_pri_set_echocanceller(pri->pvts[chanpos], 1); - pri_queue_control(pri, chanpos, AST_CONTROL_RINGING); - if (pri->pvts[chanpos]->call_level < SIG_PRI_CALL_LEVEL_ALERTING) { - pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_ALERTING; - } + sig_pri_handle_subcmds(pri, chanpos, e->e, e->ringing.channel, + e->ringing.subcmds, e->ringing.call); + sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCNR); + sig_pri_set_echocanceller(pri->pvts[chanpos], 1); + pri_queue_control(pri, chanpos, AST_CONTROL_RINGING); + if (pri->pvts[chanpos]->call_level < SIG_PRI_CALL_LEVEL_ALERTING) { + pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_ALERTING; + } - if ( + if ( #ifdef PRI_PROGRESS_MASK - e->ringing.progressmask & PRI_PROG_INBAND_AVAILABLE + e->ringing.progressmask & PRI_PROG_INBAND_AVAILABLE #else - e->ringing.progress == 8 + e->ringing.progress == 8 #endif - ) { - sig_pri_open_media(pri->pvts[chanpos]); - } - + ) { + sig_pri_open_media(pri->pvts[chanpos]); + } #ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->ringing.useruserinfo)) { - struct ast_channel *owner; - - sig_pri_lock_owner(pri, chanpos); - owner = pri->pvts[chanpos]->owner; - if (owner) { - pbx_builtin_setvar_helper(owner, "USERUSERINFO", - e->ringing.useruserinfo); - ast_channel_unlock(owner); - } - } -#endif + if (!ast_strlen_zero(e->ringing.useruserinfo)) { + struct ast_channel *owner; - sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (owner) { + pbx_builtin_setvar_helper(owner, "USERUSERINFO", + e->ringing.useruserinfo); + ast_channel_unlock(owner); } } +#endif + + sig_pri_unlock_private(pri->pvts[chanpos]); break; case PRI_EVENT_PROGRESS: if (sig_pri_is_cis_call(e->proceeding.channel)) { @@ -6101,44 +6268,48 @@ static void *pri_dchannel(void *vpri) e->proceeding.call); break; } - chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->proceeding.channel, - e->proceeding.subcmds, e->proceeding.call); + chanpos = pri_find_fixup_principle(pri, e->proceeding.channel, + e->proceeding.call); + if (chanpos < 0) { + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->proceeding.channel, + e->proceeding.subcmds, e->proceeding.call); - if (e->proceeding.cause > -1) { - ast_verb(3, "PROGRESS with cause code %d received\n", e->proceeding.cause); + if (e->proceeding.cause > -1) { + ast_verb(3, "PROGRESS with cause code %d received\n", e->proceeding.cause); - /* Work around broken, out of spec USER_BUSY cause in a progress message */ - if (e->proceeding.cause == AST_CAUSE_USER_BUSY) { - if (pri->pvts[chanpos]->owner) { - ast_verb(3, "PROGRESS with 'user busy' received, signaling AST_CONTROL_BUSY instead of AST_CONTROL_PROGRESS\n"); + /* Work around broken, out of spec USER_BUSY cause in a progress message */ + if (e->proceeding.cause == AST_CAUSE_USER_BUSY) { + if (pri->pvts[chanpos]->owner) { + ast_verb(3, "PROGRESS with 'user busy' received, signaling AST_CONTROL_BUSY instead of AST_CONTROL_PROGRESS\n"); - pri->pvts[chanpos]->owner->hangupcause = e->proceeding.cause; - pri_queue_control(pri, chanpos, AST_CONTROL_BUSY); - } + pri->pvts[chanpos]->owner->hangupcause = e->proceeding.cause; + pri_queue_control(pri, chanpos, AST_CONTROL_BUSY); } } + } - if (!pri->pvts[chanpos]->progress - && !pri->pvts[chanpos]->no_b_channel + if (!pri->pvts[chanpos]->progress + && !pri->pvts[chanpos]->no_b_channel #ifdef PRI_PROGRESS_MASK - && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) + && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) #else - && e->proceeding.progress == 8 + && e->proceeding.progress == 8 #endif - ) { - /* Bring voice path up */ - ast_debug(1, "Queuing frame from PRI_EVENT_PROGRESS on channel %d/%d span %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span); - pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); - pri->pvts[chanpos]->progress = 1; - sig_pri_set_dialing(pri->pvts[chanpos], 0); - sig_pri_open_media(pri->pvts[chanpos]); - } - sig_pri_unlock_private(pri->pvts[chanpos]); + ) { + /* Bring voice path up */ + ast_debug(1, + "Queuing frame from PRI_EVENT_PROGRESS on channel %d/%d span %d\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, + pri->span); + pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); + pri->pvts[chanpos]->progress = 1; + sig_pri_set_dialing(pri->pvts[chanpos], 0); + sig_pri_open_media(pri->pvts[chanpos]); } + sig_pri_unlock_private(pri->pvts[chanpos]); break; case PRI_EVENT_PROCEEDING: if (sig_pri_is_cis_call(e->proceeding.channel)) { @@ -6146,33 +6317,37 @@ static void *pri_dchannel(void *vpri) e->proceeding.call); break; } - chanpos = pri_find_principle(pri, e->proceeding.channel, e->proceeding.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->proceeding.channel, - e->proceeding.subcmds, e->proceeding.call); - if (pri->pvts[chanpos]->call_level < SIG_PRI_CALL_LEVEL_PROCEEDING) { - pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_PROCEEDING; - ast_debug(1, "Queuing frame from PRI_EVENT_PROCEEDING on channel %d/%d span %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset,pri->span); - pri_queue_control(pri, chanpos, AST_CONTROL_PROCEEDING); - } - if (!pri->pvts[chanpos]->progress - && !pri->pvts[chanpos]->no_b_channel + chanpos = pri_find_fixup_principle(pri, e->proceeding.channel, + e->proceeding.call); + if (chanpos < 0) { + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->proceeding.channel, + e->proceeding.subcmds, e->proceeding.call); + if (pri->pvts[chanpos]->call_level < SIG_PRI_CALL_LEVEL_PROCEEDING) { + pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_PROCEEDING; + ast_debug(1, + "Queuing frame from PRI_EVENT_PROCEEDING on channel %d/%d span %d\n", + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, + pri->span); + pri_queue_control(pri, chanpos, AST_CONTROL_PROCEEDING); + } + if (!pri->pvts[chanpos]->progress + && !pri->pvts[chanpos]->no_b_channel #ifdef PRI_PROGRESS_MASK - && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) + && (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) #else - && e->proceeding.progress == 8 + && e->proceeding.progress == 8 #endif - ) { - /* Bring voice path up */ - pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); - pri->pvts[chanpos]->progress = 1; - sig_pri_set_dialing(pri->pvts[chanpos], 0); - sig_pri_open_media(pri->pvts[chanpos]); - } - sig_pri_unlock_private(pri->pvts[chanpos]); + ) { + /* Bring voice path up */ + pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); + pri->pvts[chanpos]->progress = 1; + sig_pri_set_dialing(pri->pvts[chanpos], 0); + sig_pri_open_media(pri->pvts[chanpos]); } + sig_pri_unlock_private(pri->pvts[chanpos]); break; case PRI_EVENT_FACILITY: if (!e->facility.call || sig_pri_is_cis_call(e->facility.channel)) { @@ -6186,27 +6361,21 @@ static void *pri_dchannel(void *vpri) #endif /* !defined(HAVE_PRI_CALL_REROUTING) */ break; } - chanpos = pri_find_principle(pri, e->facility.channel, e->facility.call); + chanpos = pri_find_principle_by_call(pri, e->facility.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Facility requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->facility.channel), PRI_CHANNEL(e->facility.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->facility.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Facility requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->facility.channel), PRI_CHANNEL(e->facility.channel), pri->span); - } else { - sig_pri_lock_private(pri->pvts[chanpos]); + ast_log(LOG_WARNING, "Span %d: Received facility for unknown call.\n", + pri->span); + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); #if defined(HAVE_PRI_CALL_REROUTING) - sig_pri_handle_subcmds(pri, chanpos, e->e, e->facility.channel, - e->facility.subcmds, e->facility.subcall); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->facility.channel, + e->facility.subcmds, e->facility.subcall); #else - sig_pri_handle_subcmds(pri, chanpos, e->e, e->facility.channel, - e->facility.subcmds, e->facility.call); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->facility.channel, + e->facility.subcmds, e->facility.call); #endif /* !defined(HAVE_PRI_CALL_REROUTING) */ - sig_pri_unlock_private(pri->pvts[chanpos]); - } - } + sig_pri_unlock_private(pri->pvts[chanpos]); break; case PRI_EVENT_ANSWER: if (sig_pri_is_cis_call(e->answer.channel)) { @@ -6218,16 +6387,8 @@ static void *pri_dchannel(void *vpri) e->answer.call); break; } - chanpos = pri_find_principle(pri, e->answer.channel, e->answer.call); + chanpos = pri_find_fixup_principle(pri, e->answer.channel, e->answer.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Answer on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span); - break; - } - chanpos = pri_fixup_principle(pri, chanpos, e->answer.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Answer requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span); break; } #if defined(HAVE_PRI_CALL_WAITING) @@ -6240,11 +6401,23 @@ static void *pri_dchannel(void *vpri) * kill the call with PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION. */ new_chanpos = pri_find_empty_chan(pri, 1); + if (0 <= new_chanpos) { + new_chanpos = pri_fixup_principle(pri, new_chanpos, + e->answer.call); + } if (new_chanpos < 0) { + /* + * Either no channel was available or someone stole + * the channel! + */ + ast_verb(3, + "Span %d: Channel not available for call waiting call.\n", + pri->span); sig_pri_lock_private(pri->pvts[chanpos]); sig_pri_handle_subcmds(pri, chanpos, e->e, e->answer.channel, e->answer.subcmds, e->answer.call); sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + sig_pri_lock_owner(pri, chanpos); if (pri->pvts[chanpos]->owner) { pri->pvts[chanpos]->owner->hangupcause = PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION; switch (pri->pvts[chanpos]->owner->_state) { @@ -6256,6 +6429,7 @@ static void *pri_dchannel(void *vpri) pri_queue_control(pri, chanpos, AST_CONTROL_CONGESTION); break; } + ast_channel_unlock(pri->pvts[chanpos]->owner); } else { pri->pvts[chanpos]->is_call_waiting = 0; ast_atomic_fetchadd_int(&pri->num_call_waiting_calls, -1); @@ -6263,17 +6437,13 @@ static void *pri_dchannel(void *vpri) pri->pvts[chanpos]->call = NULL; } sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); break; } - chanpos = pri_fixup_principle(pri, new_chanpos, e->answer.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, - "Unable to move call waiting call channel on span %d\n", - pri->span); - break; - } + chanpos = new_chanpos; } pri_connect_ack(pri->pri, e->answer.call, PVT_TO_CHANNEL(pri->pvts[chanpos])); + sig_pri_span_devstate_changed(pri); } else { /* Call is normal so do normal CONNECT_ACKNOWLEDGE. */ pri_connect_ack(pri->pri, e->answer.call, 0); @@ -6285,7 +6455,6 @@ static void *pri_dchannel(void *vpri) if (pri->pvts[chanpos]->is_call_waiting) { pri->pvts[chanpos]->is_call_waiting = 0; ast_atomic_fetchadd_int(&pri->num_call_waiting_calls, -1); - sig_pri_span_devstate_changed(pri); } #endif /* defined(HAVE_PRI_CALL_WAITING) */ sig_pri_handle_subcmds(pri, chanpos, e->e, e->answer.channel, @@ -6322,28 +6491,18 @@ static void *pri_dchannel(void *vpri) e->connect_ack.call); break; } - chanpos = pri_find_principle(pri, e->connect_ack.channel, + chanpos = pri_find_fixup_principle(pri, e->connect_ack.channel, e->connect_ack.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Connect ACK on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->connect_ack.channel), - PRI_CHANNEL(e->connect_ack.channel), pri->span); - break; - } - chanpos = pri_fixup_principle(pri, chanpos, e->connect_ack.call); - if (chanpos < 0) { - ast_log(LOG_WARNING, "Connect ACK requested on channel %d/%d not in use on span %d\n", - PRI_SPAN(e->connect_ack.channel), - PRI_CHANNEL(e->connect_ack.channel), pri->span); break; } sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_span_devstate_changed(pri); sig_pri_handle_subcmds(pri, chanpos, e->e, e->connect_ack.channel, e->connect_ack.subcmds, e->connect_ack.call); sig_pri_open_media(pri->pvts[chanpos]); sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); break; #endif /* defined(HAVE_PRI_CALL_WAITING) */ case PRI_EVENT_HANGUP: @@ -6353,130 +6512,140 @@ static void *pri_dchannel(void *vpri) pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); break; } - chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call); + chanpos = pri_find_principle_by_call(pri, e->hangup.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); /* * Continue hanging up the call even though - * it is on an unconfigured channel. + * we do not remember it (if we ever did). */ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->hangup.channel, - e->hangup.subcmds, e->hangup.call); - if (!pri->pvts[chanpos]->alreadyhungup) { - /* we're calling here dahdi_hangup so once we get there we need to clear p->call after calling pri_hangup */ - pri->pvts[chanpos]->alreadyhungup = 1; + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->hangup.channel, + e->hangup.subcmds, e->hangup.call); + switch (e->hangup.cause) { + case PRI_CAUSE_INVALID_CALL_REFERENCE: + /* + * The peer denies the existence of this call so we must + * continue hanging it up and forget about it. + */ + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); + pri->pvts[chanpos]->call = NULL; + break; + default: + break; + } + if (!pri->pvts[chanpos]->alreadyhungup) { + /* we're calling here dahdi_hangup so once we get there we need to clear p->call after calling pri_hangup */ + pri->pvts[chanpos]->alreadyhungup = 1; + switch (e->hangup.cause) { + case PRI_CAUSE_USER_BUSY: + case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: + sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + break; + default: + break; + } + if (pri->pvts[chanpos]->owner) { + int do_hangup = 0; + + /* Queue a BUSY instead of a hangup if our cause is appropriate */ + pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; + switch (pri->pvts[chanpos]->owner->_state) { + case AST_STATE_BUSY: + case AST_STATE_UP: + do_hangup = 1; + break; + default: + if (!pri->pvts[chanpos]->outgoing) { + /* + * The incoming call leg hung up before getting + * connected so just hangup the call. + */ + do_hangup = 1; + break; + } switch (e->hangup.cause) { case PRI_CAUSE_USER_BUSY: + pri_queue_control(pri, chanpos, AST_CONTROL_BUSY); + break; + case PRI_CAUSE_CALL_REJECTED: + case PRI_CAUSE_NETWORK_OUT_OF_ORDER: case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: - sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + case PRI_CAUSE_SWITCH_CONGESTION: + case PRI_CAUSE_DESTINATION_OUT_OF_ORDER: + case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE: + pri_queue_control(pri, chanpos, AST_CONTROL_CONGESTION); break; default: + do_hangup = 1; break; } - if (pri->pvts[chanpos]->owner) { - int do_hangup = 0; - /* Queue a BUSY instead of a hangup if our cause is appropriate */ - pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; - switch (pri->pvts[chanpos]->owner->_state) { - case AST_STATE_BUSY: - case AST_STATE_UP: - do_hangup = 1; - break; - default: - if (!pri->pvts[chanpos]->outgoing) { - /* - * The incoming call leg hung up before getting - * connected so just hangup the call. - */ - do_hangup = 1; - break; - } - switch (e->hangup.cause) { - case PRI_CAUSE_USER_BUSY: - pri_queue_control(pri, chanpos, AST_CONTROL_BUSY); - break; - case PRI_CAUSE_CALL_REJECTED: - case PRI_CAUSE_NETWORK_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: - case PRI_CAUSE_SWITCH_CONGESTION: - case PRI_CAUSE_DESTINATION_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE: - pri_queue_control(pri, chanpos, AST_CONTROL_CONGESTION); - break; - default: - do_hangup = 1; - break; - } - break; - } + break; + } - if (do_hangup) { + 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 { - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } -#else - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; -#endif /* 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 { - /* - * Continue hanging up the call even though - * we do not have an owner. - */ - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); - pri->pvts[chanpos]->call = NULL; - } - ast_verb(3, "Channel %d/%d, span %d got hangup, cause %d\n", - pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, e->hangup.cause); - } else { - /* Continue hanging up the call. */ - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); - pri->pvts[chanpos]->call = NULL; - } - if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - ast_verb(3, "Forcing restart of channel %d/%d on span %d since channel reported in use\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; - } - 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"); - -#ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->hangup.useruserinfo)) { - struct ast_channel *owner; - - sig_pri_lock_owner(pri, chanpos); - owner = pri->pvts[chanpos]->owner; - if (owner) { - pbx_builtin_setvar_helper(owner, "USERUSERINFO", - e->hangup.useruserinfo); - ast_channel_unlock(owner); + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; } +#else + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; +#endif /* defined(HAVE_PRI_AOC_EVENTS) */ } -#endif - - sig_pri_unlock_private(pri->pvts[chanpos]); } else { /* * Continue hanging up the call even though - * we do not remember it (if we ever did). + * we do not have an owner. */ - pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); + pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); + pri->pvts[chanpos]->call = NULL; } + ast_verb(3, "Span %d: Channel %d/%d got hangup, cause %d\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset, e->hangup.cause); + } else { + /* Continue hanging up the call. */ + 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->pvts[chanpos]->resetting) { + ast_verb(3, + "Span %d: Forcing restart of channel %d/%d since channel reported in use\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); + pri->pvts[chanpos]->resetting = 1; + 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"); + +#ifdef SUPPORT_USERUSER + if (!ast_strlen_zero(e->hangup.useruserinfo)) { + struct ast_channel *owner; + + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (owner) { + pbx_builtin_setvar_helper(owner, "USERUSERINFO", + e->hangup.useruserinfo); + ast_channel_unlock(owner); + } + } +#endif + + sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); break; case PRI_EVENT_HANGUP_REQ: if (sig_pri_is_cis_call(e->hangup.channel)) { @@ -6485,136 +6654,143 @@ static void *pri_dchannel(void *vpri) pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); break; } - chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call); + chanpos = pri_find_principle_by_call(pri, e->hangup.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); /* * Continue hanging up the call even though - * it is on an unconfigured channel. + * we do not remember it (if we ever did). */ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->hangup.channel, - e->hangup.subcmds, e->hangup.call); + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->hangup.channel, + e->hangup.subcmds, e->hangup.call); #if defined(HAVE_PRI_CALL_HOLD) - if (e->hangup.call_active && e->hangup.call_held - && pri->hold_disconnect_transfer) { - /* We are to transfer the call instead of simply hanging up. */ - sig_pri_unlock_private(pri->pvts[chanpos]); - if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1, - e->hangup.call_active, 0, NULL, NULL)) { - break; - } - sig_pri_lock_private(pri->pvts[chanpos]); - } + if (e->hangup.call_active && e->hangup.call_held + && pri->hold_disconnect_transfer) { + /* We are to transfer the call instead of simply hanging up. */ + sig_pri_unlock_private(pri->pvts[chanpos]); + if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1, + e->hangup.call_active, 0, NULL, NULL)) { + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + } #endif /* defined(HAVE_PRI_CALL_HOLD) */ + switch (e->hangup.cause) { + case PRI_CAUSE_USER_BUSY: + case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: + sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + break; + case PRI_CAUSE_INVALID_CALL_REFERENCE: + /* + * The peer denies the existence of this call so we must + * continue hanging it up and forget about it. We should not + * get this cause here, but for completeness we will handle it + * anyway. + */ + pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); + pri->pvts[chanpos]->call = NULL; + break; + default: + break; + } + if (pri->pvts[chanpos]->owner) { + int do_hangup = 0; + + pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; + switch (pri->pvts[chanpos]->owner->_state) { + case AST_STATE_BUSY: + case AST_STATE_UP: + do_hangup = 1; + break; + default: + if (!pri->pvts[chanpos]->outgoing) { + /* + * The incoming call leg hung up before getting + * connected so just hangup the call. + */ + do_hangup = 1; + break; + } switch (e->hangup.cause) { case PRI_CAUSE_USER_BUSY: + pri_queue_control(pri, chanpos, AST_CONTROL_BUSY); + break; + case PRI_CAUSE_CALL_REJECTED: + case PRI_CAUSE_NETWORK_OUT_OF_ORDER: case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: - sig_pri_cc_generic_check(pri, chanpos, AST_CC_CCBS); + case PRI_CAUSE_SWITCH_CONGESTION: + case PRI_CAUSE_DESTINATION_OUT_OF_ORDER: + case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE: + pri_queue_control(pri, chanpos, AST_CONTROL_CONGESTION); break; default: + do_hangup = 1; break; } - if (pri->pvts[chanpos]->owner) { - int do_hangup = 0; - - pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause; - switch (pri->pvts[chanpos]->owner->_state) { - case AST_STATE_BUSY: - case AST_STATE_UP: - do_hangup = 1; - break; - default: - if (!pri->pvts[chanpos]->outgoing) { - /* - * The incoming call leg hung up before getting - * connected so just hangup the call. - */ - do_hangup = 1; - break; - } - switch (e->hangup.cause) { - case PRI_CAUSE_USER_BUSY: - pri_queue_control(pri, chanpos, AST_CONTROL_BUSY); - break; - case PRI_CAUSE_CALL_REJECTED: - case PRI_CAUSE_NETWORK_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION: - case PRI_CAUSE_SWITCH_CONGESTION: - case PRI_CAUSE_DESTINATION_OUT_OF_ORDER: - case PRI_CAUSE_NORMAL_TEMPORARY_FAILURE: - pri_queue_control(pri, chanpos, AST_CONTROL_CONGESTION); - break; - default: - do_hangup = 1; - break; - } - break; - } + break; + } - if (do_hangup) { + if (do_hangup) { #if defined(HAVE_PRI_AOC_EVENTS) - if (!pri->pvts[chanpos]->holding_aoce - && pri->aoce_delayhangup - && ast_bridged_channel(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 { - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } -#else - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; -#endif /* defined(HAVE_PRI_AOC_EVENTS) */ - } - ast_verb(3, "Channel %d/%d, span %d got hangup request, cause %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span, e->hangup.cause); + if (!pri->pvts[chanpos]->holding_aoce + && pri->aoce_delayhangup + && ast_bridged_channel(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 { - /* - * Continue hanging up the call even though - * we do not have an owner. - */ - pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause); - pri->pvts[chanpos]->call = NULL; - } - if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) { - ast_verb(3, "Forcing restart of channel %d/%d span %d since channel reported in use\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos])); - pri->pvts[chanpos]->resetting = 1; + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; } +#else + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; +#endif /* defined(HAVE_PRI_AOC_EVENTS) */ + } + ast_verb(3, "Span %d: Channel %d/%d got hangup request, cause %d\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset, e->hangup.cause); + } else { + /* + * Continue hanging up the call even though + * we do not have an owner. + */ + 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->pvts[chanpos]->resetting) { + ast_verb(3, + "Span %d: Forcing restart of channel %d/%d since channel reported in use\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); + pri->pvts[chanpos]->resetting = 1; + 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)) { - struct ast_channel *owner; - - sig_pri_lock_owner(pri, chanpos); - owner = pri->pvts[chanpos]->owner; - if (owner) { - pbx_builtin_setvar_helper(owner, "USERUSERINFO", - e->hangup.useruserinfo); - ast_channel_unlock(owner); - } - } -#endif + if (!ast_strlen_zero(e->hangup.useruserinfo)) { + struct ast_channel *owner; - sig_pri_unlock_private(pri->pvts[chanpos]); - } else { - /* - * Continue hanging up the call even though - * we do not remember it (if we ever did). - */ - pri_hangup(pri->pri, e->hangup.call, e->hangup.cause); + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (owner) { + pbx_builtin_setvar_helper(owner, "USERUSERINFO", + e->hangup.useruserinfo); + ast_channel_unlock(owner); } } +#endif + + sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); break; case PRI_EVENT_HANGUP_ACK: if (sig_pri_is_cis_call(e->hangup.channel)) { @@ -6622,35 +6798,31 @@ static void *pri_dchannel(void *vpri) e->hangup.call); break; } - chanpos = pri_find_principle(pri, e->hangup.channel, e->hangup.call); + chanpos = pri_find_principle_by_call(pri, e->hangup.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Hangup ACK requested on unconfigured channel number %d/%d span %d\n", - PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call); - if (chanpos > -1) { - sig_pri_lock_private(pri->pvts[chanpos]); - pri->pvts[chanpos]->call = NULL; - pri->pvts[chanpos]->resetting = 0; - if (pri->pvts[chanpos]->owner) { - ast_verb(3, "Channel %d/%d, span %d got hangup ACK\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span); - } + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + pri->pvts[chanpos]->call = NULL; + if (pri->pvts[chanpos]->owner) { + ast_verb(3, "Span %d: Channel %d/%d got hangup ACK\n", pri->span, + pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset); + } #ifdef SUPPORT_USERUSER - if (!ast_strlen_zero(e->hangup.useruserinfo)) { - struct ast_channel *owner; + if (!ast_strlen_zero(e->hangup.useruserinfo)) { + struct ast_channel *owner; - sig_pri_lock_owner(pri, chanpos); - owner = pri->pvts[chanpos]->owner; - if (owner) { - pbx_builtin_setvar_helper(owner, "USERUSERINFO", - e->hangup.useruserinfo); - ast_channel_unlock(owner); - } - } -#endif - sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_lock_owner(pri, chanpos); + owner = pri->pvts[chanpos]->owner; + if (owner) { + pbx_builtin_setvar_helper(owner, "USERUSERINFO", + e->hangup.useruserinfo); + ast_channel_unlock(owner); } } +#endif + sig_pri_unlock_private(pri->pvts[chanpos]); + sig_pri_span_devstate_changed(pri); break; case PRI_EVENT_CONFIG_ERR: ast_log(LOG_WARNING, "PRI Error on span %d: %s\n", pri->span, e->err.err); @@ -6665,16 +6837,22 @@ static void *pri_dchannel(void *vpri) if (pri->pvts[x] && pri->pvts[x]->resetting) { chanpos = x; sig_pri_lock_private(pri->pvts[chanpos]); - ast_debug(1, "Assuming restart ack is really for channel %d/%d span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); + ast_debug(1, + "Span %d: Assuming restart ack is for channel %d/%d\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); if (pri->pvts[chanpos]->owner) { - ast_log(LOG_WARNING, "Got restart ack on channel %d/%d with owner on span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); + ast_log(LOG_WARNING, + "Span %d: Got restart ack on channel %d/%d with owner\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; } pri->pvts[chanpos]->resetting = 0; - ast_verb(3, "B-channel %d/%d successfully restarted on span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); + ast_verb(3, + "Span %d: Channel %d/%d successfully restarted\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); sig_pri_unlock_private(pri->pvts[chanpos]); if (pri->resetting) pri_check_restart(pri); @@ -6682,24 +6860,28 @@ static void *pri_dchannel(void *vpri) } } if (chanpos < 0) { - ast_log(LOG_WARNING, "Restart ACK requested on strange channel %d/%d span %d\n", - PRI_SPAN(e->restartack.channel), PRI_CHANNEL(e->restartack.channel), pri->span); + ast_log(LOG_WARNING, + "Span %d: Restart ACK on strange channel %d/%d\n", + pri->span, PRI_SPAN(e->restartack.channel), + PRI_CHANNEL(e->restartack.channel)); } } else { - if (pri->pvts[chanpos]) { - sig_pri_lock_private(pri->pvts[chanpos]); - if (pri->pvts[chanpos]->owner) { - ast_log(LOG_WARNING, "Got restart ack on channel %d/%d span %d with owner\n", - PRI_SPAN(e->restartack.channel), PRI_CHANNEL(e->restartack.channel), pri->span); - pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; - } - pri->pvts[chanpos]->resetting = 0; - ast_verb(3, "B-channel %d/%d successfully restarted on span %d\n", pri->pvts[chanpos]->logicalspan, - pri->pvts[chanpos]->prioffset, pri->span); - sig_pri_unlock_private(pri->pvts[chanpos]); - if (pri->resetting) - pri_check_restart(pri); + sig_pri_lock_private(pri->pvts[chanpos]); + if (pri->pvts[chanpos]->owner) { + ast_log(LOG_WARNING, + "Span %d: Got restart ack on channel %d/%d with owner\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); + pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV; } + pri->pvts[chanpos]->resetting = 0; + ast_verb(3, + "Span %d: Channel %d/%d successfully restarted\n", + pri->span, pri->pvts[chanpos]->logicalspan, + pri->pvts[chanpos]->prioffset); + sig_pri_unlock_private(pri->pvts[chanpos]); + if (pri->resetting) + pri_check_restart(pri); } break; case PRI_EVENT_SETUP_ACK: @@ -6708,47 +6890,40 @@ static void *pri_dchannel(void *vpri) e->setup_ack.call); break; } - chanpos = pri_find_principle(pri, e->setup_ack.channel, e->setup_ack.call); + chanpos = pri_find_fixup_principle(pri, e->setup_ack.channel, + e->setup_ack.call); if (chanpos < 0) { - ast_log(LOG_WARNING, "Received SETUP_ACKNOWLEDGE on unconfigured channel %d/%d span %d\n", - PRI_SPAN(e->setup_ack.channel), PRI_CHANNEL(e->setup_ack.channel), pri->span); - } else { - chanpos = pri_fixup_principle(pri, chanpos, e->setup_ack.call); - if (chanpos > -1) { - unsigned int len; - - sig_pri_lock_private(pri->pvts[chanpos]); - sig_pri_handle_subcmds(pri, chanpos, e->e, e->setup_ack.channel, - e->setup_ack.subcmds, e->setup_ack.call); - if (pri->pvts[chanpos]->call_level < SIG_PRI_CALL_LEVEL_OVERLAP) { - pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_OVERLAP; - } + break; + } + sig_pri_lock_private(pri->pvts[chanpos]); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->setup_ack.channel, + e->setup_ack.subcmds, e->setup_ack.call); + if (pri->pvts[chanpos]->call_level < SIG_PRI_CALL_LEVEL_OVERLAP) { + pri->pvts[chanpos]->call_level = SIG_PRI_CALL_LEVEL_OVERLAP; + } - /* Send any queued digits */ - len = strlen(pri->pvts[chanpos]->dialdest); - for (x = 0; x < len; ++x) { - ast_debug(1, "Sending pending digit '%c'\n", pri->pvts[chanpos]->dialdest[x]); - pri_information(pri->pri, pri->pvts[chanpos]->call, - pri->pvts[chanpos]->dialdest[x]); - } + /* Send any queued digits */ + len = strlen(pri->pvts[chanpos]->dialdest); + for (x = 0; x < len; ++x) { + ast_debug(1, "Sending pending digit '%c'\n", pri->pvts[chanpos]->dialdest[x]); + pri_information(pri->pri, pri->pvts[chanpos]->call, + pri->pvts[chanpos]->dialdest[x]); + } - if (!pri->pvts[chanpos]->progress - && (pri->overlapdial & DAHDI_OVERLAPDIAL_OUTGOING) - && !pri->pvts[chanpos]->digital - && !pri->pvts[chanpos]->no_b_channel) { - /* - * Call has a channel. - * Indicate for overlap dialing that dialtone may be present. - */ - pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); - pri->pvts[chanpos]->progress = 1;/* Claim to have seen inband-information */ - sig_pri_set_dialing(pri->pvts[chanpos], 0); - sig_pri_open_media(pri->pvts[chanpos]); - } - sig_pri_unlock_private(pri->pvts[chanpos]); - } else - ast_log(LOG_WARNING, "Unable to move channel %d!\n", e->setup_ack.channel); + if (!pri->pvts[chanpos]->progress + && (pri->overlapdial & DAHDI_OVERLAPDIAL_OUTGOING) + && !pri->pvts[chanpos]->digital + && !pri->pvts[chanpos]->no_b_channel) { + /* + * Call has a channel. + * Indicate for overlap dialing that dialtone may be present. + */ + pri_queue_control(pri, chanpos, AST_CONTROL_PROGRESS); + pri->pvts[chanpos]->progress = 1;/* Claim to have seen inband-information */ + sig_pri_set_dialing(pri->pvts[chanpos], 0); + sig_pri_open_media(pri->pvts[chanpos]); } + sig_pri_unlock_private(pri->pvts[chanpos]); break; case PRI_EVENT_NOTIFY: if (sig_pri_is_cis_call(e->notify.channel)) { @@ -6761,36 +6936,46 @@ static void *pri_dchannel(void *vpri) break; } #if defined(HAVE_PRI_CALL_HOLD) - chanpos = pri_find_principle(pri, e->notify.channel, e->notify.call); + chanpos = pri_find_principle_by_call(pri, e->notify.call); + if (chanpos < 0) { + ast_log(LOG_WARNING, "Span %d: Received NOTIFY for unknown call.\n", + pri->span); + break; + } #else + /* + * This version of libpri does not supply a call pointer for + * this message. We are just going to have to trust that the + * correct principle is found. + */ chanpos = pri_find_principle(pri, e->notify.channel, NULL); -#endif /* !defined(HAVE_PRI_CALL_HOLD) */ if (chanpos < 0) { ast_log(LOG_WARNING, "Received NOTIFY on unconfigured channel %d/%d span %d\n", PRI_SPAN(e->notify.channel), PRI_CHANNEL(e->notify.channel), pri->span); - } else { - sig_pri_lock_private(pri->pvts[chanpos]); + break; + } +#endif /* !defined(HAVE_PRI_CALL_HOLD) */ + sig_pri_lock_private(pri->pvts[chanpos]); #if defined(HAVE_PRI_CALL_HOLD) - sig_pri_handle_subcmds(pri, chanpos, e->e, e->notify.channel, - e->notify.subcmds, e->notify.call); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->notify.channel, + e->notify.subcmds, e->notify.call); #else - sig_pri_handle_subcmds(pri, chanpos, e->e, e->notify.channel, - e->notify.subcmds, NULL); + sig_pri_handle_subcmds(pri, chanpos, e->e, e->notify.channel, + e->notify.subcmds, NULL); #endif /* !defined(HAVE_PRI_CALL_HOLD) */ - switch (e->notify.info) { - case PRI_NOTIFY_REMOTE_HOLD: - if (!pri->discardremoteholdretrieval) { - pri_queue_control(pri, chanpos, AST_CONTROL_HOLD); - } - break; - case PRI_NOTIFY_REMOTE_RETRIEVAL: - if (!pri->discardremoteholdretrieval) { - pri_queue_control(pri, chanpos, AST_CONTROL_UNHOLD); - } - break; + switch (e->notify.info) { + case PRI_NOTIFY_REMOTE_HOLD: + if (!pri->discardremoteholdretrieval) { + pri_queue_control(pri, chanpos, AST_CONTROL_HOLD); } - sig_pri_unlock_private(pri->pvts[chanpos]); + break; + case PRI_NOTIFY_REMOTE_RETRIEVAL: + if (!pri->discardremoteholdretrieval) { + pri_queue_control(pri, chanpos, AST_CONTROL_UNHOLD); + } + break; } + sig_pri_unlock_private(pri->pvts[chanpos]); break; #if defined(HAVE_PRI_CALL_HOLD) case PRI_EVENT_HOLD: @@ -6860,7 +7045,6 @@ void sig_pri_init_pri(struct sig_pri_span *pri) int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) { - int res; #ifdef SUPPORT_USERUSER const char *useruser = pbx_builtin_getvar_helper(ast, "USERUSERINFO"); #endif @@ -6871,7 +7055,6 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) return 0; } - p->owner = NULL; p->outgoing = 0; sig_pri_set_digital(p, 0); /* push up to parent for EC*/ #if defined(HAVE_PRI_CALL_WAITING) @@ -6890,60 +7073,59 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast) sig_pri_set_dialing(p, 0); /* Make sure we have a call (or REALLY have a call in the case of a PRI) */ - if (!pri_grab(p, p->pri)) { - sig_pri_moh_fsm_event(ast, p, SIG_PRI_MOH_EVENT_RESET); - if (p->call) { - if (p->alreadyhungup) { - ast_debug(1, "Already hungup... Calling hangup once, and clearing call\n"); + pri_grab(p, p->pri); + sig_pri_moh_fsm_event(ast, p, SIG_PRI_MOH_EVENT_RESET); + if (p->call) { + if (p->alreadyhungup) { + ast_debug(1, "Already hungup... Calling hangup once, and clearing call\n"); #ifdef SUPPORT_USERUSER - pri_call_set_useruser(p->call, useruser); + pri_call_set_useruser(p->call, useruser); #endif #if defined(HAVE_PRI_AOC_EVENTS) - if (p->holding_aoce) { - pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e); - } + if (p->holding_aoce) { + pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e); + } #endif /* defined(HAVE_PRI_AOC_EVENTS) */ - pri_hangup(p->pri->pri, p->call, -1); - p->call = NULL; - } else { - const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE"); - int icause = ast->hangupcause ? ast->hangupcause : -1; - ast_debug(1, "Not yet hungup... Calling hangup once with icause, and clearing call\n"); + pri_hangup(p->pri->pri, p->call, -1); + p->call = NULL; + } else { + const char *cause = pbx_builtin_getvar_helper(ast,"PRI_CAUSE"); + int icause = ast->hangupcause ? ast->hangupcause : -1; + ast_debug(1, "Not yet hungup... Calling hangup once with icause, and clearing call\n"); #ifdef SUPPORT_USERUSER - pri_call_set_useruser(p->call, useruser); + pri_call_set_useruser(p->call, useruser); #endif - p->alreadyhungup = 1; - if (cause) { - if (atoi(cause)) - icause = atoi(cause); - } + p->alreadyhungup = 1; + if (cause) { + if (atoi(cause)) + icause = atoi(cause); + } #if defined(HAVE_PRI_AOC_EVENTS) - if (p->holding_aoce) { - pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e); - } -#endif /* defined(HAVE_PRI_AOC_EVENTS) */ - pri_hangup(p->pri->pri, p->call, icause); + if (p->holding_aoce) { + pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e); } +#endif /* defined(HAVE_PRI_AOC_EVENTS) */ + pri_hangup(p->pri->pri, p->call, icause); } - sig_pri_span_devstate_changed(p->pri); - pri_rel(p->pri); - res = 0; - } else { - ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->pri->span); - res = -1; } - #if defined(HAVE_PRI_AOC_EVENTS) p->aoc_s_request_invoke_id_valid = 0; p->holding_aoce = 0; p->waiting_for_aoce = 0; #endif /* defined(HAVE_PRI_AOC_EVENTS) */ + + p->allocated = 0; + p->owner = NULL; + + sig_pri_span_devstate_changed(p->pri); + pri_rel(p->pri); + ast->tech_pvt = NULL; - return res; + return 0; } /*! @@ -7807,16 +7989,10 @@ int sig_pri_answer(struct sig_pri_chan *p, struct ast_channel *ast) static int sig_pri_available_check(struct sig_pri_chan *pvt) { /* - * If no owner, interface has a B channel, not resetting, not already with call, - * not in alarm, and in-service then available. + * If interface has a B channel and is available for use + * then the channel is available. */ - if (!pvt->owner && !pvt->no_b_channel && !pvt->resetting && !pvt->call - && !pvt->inalarm) { -#if defined(HAVE_PRI_SERVICE_MESSAGES) - if (pvt->service_status) { - return 0; - } -#endif /* defined(HAVE_PRI_SERVICE_MESSAGES) */ + if (!pvt->no_b_channel && sig_pri_is_chan_available(pvt)) { return 1; } return 0; @@ -7830,6 +8006,8 @@ static int sig_pri_available_check(struct sig_pri_chan *pvt) * * \param pri PRI span control structure. * + * \note Assumes the pri->lock is already obtained. + * * \retval cw Call waiting interface to use. * \retval NULL if no call waiting interface available. */ @@ -7839,7 +8017,6 @@ static struct sig_pri_chan *sig_pri_cw_available(struct sig_pri_span *pri) int idx; cw = NULL; - ast_mutex_lock(&pri->lock); if (pri->num_call_waiting_calls < pri->max_call_waiting_calls) { if (!pri->num_call_waiting_calls) { /* @@ -7850,7 +8027,6 @@ static struct sig_pri_chan *sig_pri_cw_available(struct sig_pri_span *pri) for (idx = 0; idx < pri->numchans; ++idx) { if (pri->pvts[idx] && sig_pri_available_check(pri->pvts[idx])) { /* There is another channel that is available on this span. */ - ast_mutex_unlock(&pri->lock); return cw; } } @@ -7864,7 +8040,6 @@ static struct sig_pri_chan *sig_pri_cw_available(struct sig_pri_span *pri) ast_atomic_fetchadd_int(&pri->num_call_waiting_calls, 1); } } - ast_mutex_unlock(&pri->lock); return cw; } #endif /* defined(HAVE_PRI_CALL_WAITING) */ @@ -7872,12 +8047,15 @@ static struct sig_pri_chan *sig_pri_cw_available(struct sig_pri_span *pri) int sig_pri_available(struct sig_pri_chan **pvt, int is_specific_channel) { struct sig_pri_chan *p = *pvt; + struct sig_pri_span *pri; if (!p->pri) { /* Something is wrong here. A PRI channel without the pri pointer? */ return 0; } + pri = p->pri; + ast_mutex_lock(&pri->lock); if ( #if defined(HAVE_PRI_CALL_WAITING) /* @@ -7886,9 +8064,11 @@ int sig_pri_available(struct sig_pri_chan **pvt, int is_specific_channel) * want new calls to steal a B channel * freed for an earlier call waiting call. */ - !p->pri->num_call_waiting_calls && + !pri->num_call_waiting_calls && #endif /* defined(HAVE_PRI_CALL_WAITING) */ sig_pri_available_check(p)) { + p->allocated = 1; + ast_mutex_unlock(&pri->lock); return 1; } @@ -7896,14 +8076,17 @@ int sig_pri_available(struct sig_pri_chan **pvt, int is_specific_channel) if (!is_specific_channel) { struct sig_pri_chan *cw; - cw = sig_pri_cw_available(p->pri); + cw = sig_pri_cw_available(pri); if (cw) { /* We have a call waiting interface to use instead. */ + cw->allocated = 1; *pvt = cw; + ast_mutex_unlock(&pri->lock); return 1; } } #endif /* defined(HAVE_PRI_CALL_WAITING) */ + ast_mutex_unlock(&pri->lock); return 0; } @@ -8303,28 +8486,34 @@ int sig_pri_start_pri(struct sig_pri_span *pri) return 0; } +/*! + * \brief Notify new alarm status. + * + * \param p Channel private pointer. + * \param noalarm Non-zero if not in alarm mode. + * + * \note Assumes the sig_pri_lock_private(p) is already obtained. + * + * \return Nothing + */ void sig_pri_chan_alarm_notify(struct sig_pri_chan *p, int noalarm) { + pri_grab(p, p->pri); + p->resetting = 0; sig_pri_set_alarm(p, !noalarm); if (!noalarm) { - if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) { - /* T309 is not enabled : hangup calls when alarm occurs */ + if (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) { + /* T309 is not enabled : destroy calls when alarm occurs */ if (p->call) { - if (p->pri && p->pri->pri) { - if (!pri_grab(p, p->pri)) { - pri_hangup(p->pri->pri, p->call, -1); - pri_destroycall(p->pri->pri, p->call); - p->call = NULL; - pri_rel(p->pri); - } else - ast_log(LOG_WARNING, "Failed to grab PRI!\n"); - } else - ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n"); + pri_destroycall(p->pri->pri, p->call); + p->call = NULL; } if (p->owner) p->owner->_softhangup |= AST_SOFTHANGUP_DEV; } } + sig_pri_span_devstate_changed(p->pri); + pri_rel(p->pri); } struct sig_pri_chan *sig_pri_chan_new(void *pvt_data, struct sig_pri_callback *callback, struct sig_pri_span *pri, int logicalspan, int channo, int trunkgroup) @@ -8360,12 +8549,12 @@ void sig_pri_chan_delete(struct sig_pri_chan *doomed) ast_free(doomed); } -#define SIG_PRI_SC_HEADER "%-4s %4s %-4s %-10s %-4s %s\n" -#define SIG_PRI_SC_LINE "%4d %4d %-4s %-10s %-4s %s" +#define SIG_PRI_SC_HEADER "%-4s %4s %-4s %-4s %-10s %-4s %s\n" +#define SIG_PRI_SC_LINE "%4d %4d %-4s %-4s %-10s %-4s %s" void sig_pri_cli_show_channels_header(int fd) { - ast_cli(fd, SIG_PRI_SC_HEADER, "PRI", "", "B", "Call", "PRI", "Channel"); - ast_cli(fd, SIG_PRI_SC_HEADER, "Span", "Chan", "Chan", "Level", "Call", "Name"); + ast_cli(fd, SIG_PRI_SC_HEADER, "PRI", "", "B", "Chan", "Call", "PRI", "Channel"); + ast_cli(fd, SIG_PRI_SC_HEADER, "Span", "Chan", "Chan", "Idle", "Level", "Call", "Name"); } void sig_pri_cli_show_channels(int fd, struct sig_pri_span *pri) @@ -8382,7 +8571,7 @@ void sig_pri_cli_show_channels(int fd, struct sig_pri_span *pri) pvt = pri->pvts[idx]; sig_pri_lock_private(pvt); sig_pri_lock_owner(pri, idx); - if (pvt->no_b_channel && !pvt->call && !pvt->owner) { + if (pvt->no_b_channel && sig_pri_is_chan_available(pvt)) { /* Don't show held/call-waiting channels if they are not in use. */ sig_pri_unlock_private(pvt); continue; @@ -8392,6 +8581,7 @@ void sig_pri_cli_show_channels(int fd, struct sig_pri_span *pri) pri->span, pvt->channel, pvt->no_b_channel ? "No" : "Yes",/* Has media */ + sig_pri_is_chan_available(pvt) ? "Yes" : "No", sig_pri_call_level2str(pvt->call_level), pvt->call ? "Yes" : "No", pvt->owner ? pvt->owner->name : ""); diff --git a/channels/sig_pri.h b/channels/sig_pri.h index f4aef912d22898abb5aa6bbcaa28b9aa1c31500b..f75646e3ffe60d521eb9e217d047b30c5ed115eb 100644 --- a/channels/sig_pri.h +++ b/channels/sig_pri.h @@ -305,6 +305,17 @@ struct sig_pri_chan { unsigned int progress:1; /*!< TRUE if the call has seen inband-information progress through the network */ unsigned int resetting:1; /*!< TRUE if this channel is being reset/restarted */ + /*! + * \brief TRUE when this channel is allocated. + * + * \details + * Needed to hold an outgoing channel allocation before the + * owner pointer is created. + * + * \note This is one of several items to check to see if a + * channel is available for use. + */ + unsigned int allocated:1; unsigned int outgoing:1; unsigned int digital:1; /*! \brief TRUE if this interface has no B channel. (call hold and call waiting) */ @@ -558,6 +569,7 @@ int sig_pri_indicate(struct sig_pri_chan *p, struct ast_channel *chan, int condi int sig_pri_answer(struct sig_pri_chan *p, struct ast_channel *ast); +int sig_pri_is_chan_available(struct sig_pri_chan *pvt); int sig_pri_available(struct sig_pri_chan **pvt, int is_specific_channel); void sig_pri_init_pri(struct sig_pri_span *pri);