Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2010 Digium, Inc.
*
* Richard Mudgett <rmudgett@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief SS7 signaling module.
*
* \author Matthew Fredrickson <creslin@digium.com>
* \author Richard Mudgett <rmudgett@digium.com>
*
* See Also:
* \arg \ref AstCREDITS
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
#if defined(HAVE_SS7)
#include <signal.h>
#include "asterisk/pbx.h"
#include "asterisk/causes.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/callerid.h"
#if !defined(LIBSS7_ABI_COMPATIBILITY)
#error "Upgrade your libss7"
#elif LIBSS7_ABI_COMPATIBILITY != 2
#error "Your installed libss7 is not compatible"
#endif
/* ------------------------------------------------------------------- */
static const char *sig_ss7_call_level2str(enum sig_ss7_call_level level)
{
switch (level) {
case SIG_SS7_CALL_LEVEL_IDLE:
return "Idle";
case SIG_SS7_CALL_LEVEL_ALLOCATED:
return "Allocated";
case SIG_SS7_CALL_LEVEL_CONTINUITY:
return "Continuity";
case SIG_SS7_CALL_LEVEL_SETUP:
return "Setup";
case SIG_SS7_CALL_LEVEL_PROCEEDING:
return "Proceeding";
case SIG_SS7_CALL_LEVEL_ALERTING:
return "Alerting";
case SIG_SS7_CALL_LEVEL_CONNECT:
return "Connect";
}
return "Unknown";
}
static void sig_ss7_unlock_private(struct sig_ss7_chan *p)
{
if (sig_ss7_callbacks.unlock_private) {
sig_ss7_callbacks.unlock_private(p->chan_pvt);
}
}
static void sig_ss7_lock_private(struct sig_ss7_chan *p)
{
if (sig_ss7_callbacks.lock_private) {
sig_ss7_callbacks.lock_private(p->chan_pvt);
static void sig_ss7_deadlock_avoidance_private(struct sig_ss7_chan *p)
{
if (sig_ss7_callbacks.deadlock_avoidance_private) {
sig_ss7_callbacks.deadlock_avoidance_private(p->chan_pvt);
} else {
/* Fallback to the old way if callback not present. */
Richard Mudgett
committed
sig_ss7_unlock_private(p);
sched_yield();
sig_ss7_lock_private(p);
}
}
void sig_ss7_set_alarm(struct sig_ss7_chan *p, int in_alarm)
{
p->inalarm = in_alarm;
if (sig_ss7_callbacks.set_alarm) {
sig_ss7_callbacks.set_alarm(p->chan_pvt, in_alarm);
}
}
static void sig_ss7_set_dialing(struct sig_ss7_chan *p, int is_dialing)
{
if (sig_ss7_callbacks.set_dialing) {
sig_ss7_callbacks.set_dialing(p->chan_pvt, is_dialing);
}
}
static void sig_ss7_set_digital(struct sig_ss7_chan *p, int is_digital)
{
if (sig_ss7_callbacks.set_digital) {
sig_ss7_callbacks.set_digital(p->chan_pvt, is_digital);
static void sig_ss7_set_outgoing(struct sig_ss7_chan *p, int is_outgoing)
{
p->outgoing = is_outgoing;
if (sig_ss7_callbacks.set_outgoing) {
sig_ss7_callbacks.set_outgoing(p->chan_pvt, is_outgoing);
static void sig_ss7_set_inservice(struct sig_ss7_chan *p, int is_inservice)
{
p->inservice = is_inservice;
if (sig_ss7_callbacks.set_inservice) {
sig_ss7_callbacks.set_inservice(p->chan_pvt, is_inservice);
static void sig_ss7_set_locallyblocked(struct sig_ss7_chan *p, int is_blocked, int type)
if (is_blocked) {
p->locallyblocked |= type;
} else {
p->locallyblocked &= ~type;
}
if (sig_ss7_callbacks.set_locallyblocked) {
sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, p->locallyblocked);
static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked, int type)
if (is_blocked) {
p->remotelyblocked |= type;
} else {
p->remotelyblocked &= ~type;
}
if (sig_ss7_callbacks.set_remotelyblocked) {
sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, p->remotelyblocked);
/*!
* \internal
* \brief Open the SS7 channel media path.
* \since 1.8.12
*
* \param p Channel private control structure.
*
* \return Nothing
*/
static void sig_ss7_open_media(struct sig_ss7_chan *p)
{
if (sig_ss7_callbacks.open_media) {
sig_ss7_callbacks.open_media(p->chan_pvt);
}
}
/*!
* \internal
* \brief Set the caller id information in the parent module.
* \since 1.8
*
* \param p sig_ss7 channel structure.
*
* \return Nothing
*/
static void sig_ss7_set_caller_id(struct sig_ss7_chan *p)
{
struct ast_party_caller caller;
if (sig_ss7_callbacks.set_callerid) {
caller.id.name.str = p->cid_name;
caller.id.name.presentation = p->callingpres;
caller.id.name.valid = 1;
caller.id.number.str = p->cid_num;
caller.id.number.plan = p->cid_ton;
caller.id.number.presentation = p->callingpres;
caller.id.number.valid = 1;
if (!ast_strlen_zero(p->cid_subaddr)) {
caller.id.subaddress.valid = 1;
//caller.id.subaddress.type = 0;/* nsap */
//caller.id.subaddress.odd_even_indicator = 0;
caller.id.subaddress.str = p->cid_subaddr;
}
caller.ani.number.str = p->cid_ani;
//caller.ani.number.plan = p->xxx;
//caller.ani.number.presentation = p->xxx;
caller.ani.number.valid = 1;
sig_ss7_callbacks.set_callerid(p->chan_pvt, &caller);
}
}
/*!
* \internal
* \brief Set the Dialed Number Identifier.
* \since 1.8
*
* \param p sig_ss7 channel structure.
* \param dnid Dialed Number Identifier string.
*
* \return Nothing
*/
static void sig_ss7_set_dnid(struct sig_ss7_chan *p, const char *dnid)
{
if (sig_ss7_callbacks.set_dnid) {
sig_ss7_callbacks.set_dnid(p->chan_pvt, dnid);
}
}
static int sig_ss7_play_tone(struct sig_ss7_chan *p, enum sig_ss7_tone tone)
{
int res;
if (sig_ss7_callbacks.play_tone) {
res = sig_ss7_callbacks.play_tone(p->chan_pvt, tone);
} else {
res = -1;
}
return res;
}
static int sig_ss7_set_echocanceller(struct sig_ss7_chan *p, int enable)
{
if (sig_ss7_callbacks.set_echocanceller) {
return sig_ss7_callbacks.set_echocanceller(p->chan_pvt, enable);
}
return -1;
}
static void sig_ss7_loopback(struct sig_ss7_chan *p, int enable)
{
if (p->loopedback != enable) {
p->loopedback = enable;
if (sig_ss7_callbacks.set_loopback) {
sig_ss7_callbacks.set_loopback(p->chan_pvt, enable);
Richard Mudgett
committed
static struct ast_channel *sig_ss7_new_ast_channel(struct sig_ss7_chan *p, int state,
int ulaw, int transfercapability, char *exten,
const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor)
if (sig_ss7_callbacks.new_ast_channel) {
Richard Mudgett
committed
ast = sig_ss7_callbacks.new_ast_channel(p->chan_pvt, state, ulaw, exten,
assignedids, requestor);
if (!ast) {
return NULL;
}
if (!p->owner) {
p->owner = ast;
}
if (p->outgoing) {
p->do_hangup = SS7_HANGUP_FREE_CALL;
} else {
p->do_hangup = SS7_HANGUP_SEND_REL;
}
ast_channel_transfercapability_set(ast, transfercapability);
pbx_builtin_setvar_helper(ast, "TRANSFERCAPABILITY",
ast_transfercapability2str(transfercapability));
if (transfercapability & AST_TRANS_CAP_DIGITAL) {
sig_ss7_set_digital(p, 1);
}
return ast;
}
static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int which)
{
if (sig_ss7_callbacks.handle_link_exception) {
sig_ss7_callbacks.handle_link_exception(linkset, which);
static struct sig_ss7_linkset *sig_ss7_find_linkset(struct ss7 *ss7)
{
if (sig_ss7_callbacks.find_linkset) {
return sig_ss7_callbacks.find_linkset(ss7);
}
return NULL;
}
Richard Mudgett
committed
/*!
* \internal
* \brief Determine if a private channel structure is available.
*
* \param pvt Channel to determine if available.
*
* \return TRUE if the channel is available.
*/
static int sig_ss7_is_chan_available(struct sig_ss7_chan *pvt)
{
if (pvt->inservice && !pvt->inalarm && !pvt->owner && !pvt->ss7call
Richard Mudgett
committed
&& pvt->call_level == SIG_SS7_CALL_LEVEL_IDLE
&& !pvt->locallyblocked && !pvt->remotelyblocked) {
return 1;
}
return 0;
}
/*!
* \internal
* \brief Obtain the sig_ss7 owner channel lock if the owner exists.
* \since 1.8
*
* \param ss7 SS7 linkset control structure.
* \param chanpos Channel position in the span.
*
* \note Assumes the ss7->lock is already obtained.
* \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
*
* \return Nothing
*/
static void sig_ss7_lock_owner(struct sig_ss7_linkset *ss7, int chanpos)
{
for (;;) {
if (!ss7->pvts[chanpos]->owner) {
/* There is no owner lock to get. */
break;
}
if (!ast_channel_trylock(ss7->pvts[chanpos]->owner)) {
/* We got the lock */
break;
}
Richard Mudgett
committed
/* Avoid deadlock */
sig_ss7_unlock_private(ss7->pvts[chanpos]);
DEADLOCK_AVOIDANCE(&ss7->lock);
sig_ss7_lock_private(ss7->pvts[chanpos]);
}
}
/*!
* \internal
* \brief Queue the given frame onto the owner channel.
* \since 1.8
*
* \param ss7 SS7 linkset control structure.
* \param chanpos Channel position in the span.
* \param frame Frame to queue onto the owner channel.
*
* \note Assumes the ss7->lock is already obtained.
* \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
*
* \return Nothing
*/
static void sig_ss7_queue_frame(struct sig_ss7_linkset *ss7, int chanpos, struct ast_frame *frame)
{
sig_ss7_lock_owner(ss7, chanpos);
if (ss7->pvts[chanpos]->owner) {
ast_queue_frame(ss7->pvts[chanpos]->owner, frame);
ast_channel_unlock(ss7->pvts[chanpos]->owner);
}
}
/*!
* \internal
* \brief Queue a control frame of the specified subclass onto the owner channel.
* \since 1.8
*
* \param ss7 SS7 linkset control structure.
* \param chanpos Channel position in the span.
* \param subclass Control frame subclass to queue onto the owner channel.
*
* \note Assumes the ss7->lock is already obtained.
* \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
*
* \return Nothing
*/
static void sig_ss7_queue_control(struct sig_ss7_linkset *ss7, int chanpos, int subclass)
{
struct ast_frame f = {AST_FRAME_CONTROL, };
struct sig_ss7_chan *p = ss7->pvts[chanpos];
if (sig_ss7_callbacks.queue_control) {
sig_ss7_callbacks.queue_control(p->chan_pvt, subclass);
}
f.subclass.integer = subclass;
sig_ss7_queue_frame(ss7, chanpos, &f);
}
/*!
* \internal
* \brief Queue a PVT_CAUSE_CODE frame onto the owner channel.
*
* \param owner Owner channel of the pvt.
* \param cause String describing the cause to be placed into the frame.
*
* \note Assumes the linkset->lock is already obtained.
* \note Assumes the sig_ss7_lock_private(linkset->pvts[chanpos]) is already obtained.
* \note Assumes linkset->pvts[chanpos]->owner is non-NULL and its lock is already obtained.
*
* \return Nothing
*/
static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *cause, int ast_cause)
{
struct ast_control_pvt_cause_code *cause_code;
int datalen = sizeof(*cause_code) + strlen(cause);
cause_code = ast_alloca(datalen);
memset(cause_code, 0, datalen);
ast_copy_string(cause_code->chan_name, ast_channel_name(owner), AST_CHANNEL_NAME);
ast_copy_string(cause_code->code, cause, datalen + 1 - sizeof(*cause_code));
ast_queue_control_data(owner, AST_CONTROL_PVT_CAUSE_CODE, cause_code, datalen);
ast_channel_hangupcause_hash_set(owner, cause_code, datalen);
/*!
* \brief Find the channel position by CIC/DPC.
*
* \param linkset SS7 linkset control structure.
* \param cic Circuit Identification Code
* \param dpc Destination Point Code
*
* \retval chanpos on success.
* \retval -1 on error.
*/
int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
{
int i;
int winner = -1;
for (i = 0; i < linkset->numchans; i++) {
if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && linkset->pvts[i]->cic == cic)) {
winner = i;
break;
}
}
return winner;
}
/*!
* \internal
* \brief Find the channel position by CIC/DPC and gripe if not found.
*
* \param linkset SS7 linkset control structure.
* \param cic Circuit Identification Code
* \param dpc Destination Point Code
* \param msg_name Message type name that failed.
*
* \retval chanpos on success.
* \retval -1 on error.
*/
static int ss7_find_cic_gripe(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, const char *msg_name)
{
int chanpos;
chanpos = sig_ss7_find_cic(linkset, cic, dpc);
if (chanpos < 0) {
ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested on unconfigured CIC/DPC %d/%d.\n",
linkset->span, msg_name, cic, dpc);
return -1;
}
return chanpos;
}
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
static struct sig_ss7_chan *ss7_find_pvt(struct ss7 *ss7, int cic, unsigned int dpc)
{
int chanpos;
struct sig_ss7_linkset *winner;
winner = sig_ss7_find_linkset(ss7);
if (winner && (chanpos = sig_ss7_find_cic(winner, cic, dpc)) > -1) {
return winner->pvts[chanpos];
}
return NULL;
}
int sig_ss7_cb_hangup(struct ss7 *ss7, int cic, unsigned int dpc, int cause, int do_hangup)
{
struct sig_ss7_chan *p;
int res;
if (!(p = ss7_find_pvt(ss7, cic, dpc))) {
return SS7_CIC_NOT_EXISTS;
}
sig_ss7_lock_private(p);
if (p->owner) {
ast_channel_hangupcause_set(p->owner, cause);
ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV);
p->do_hangup = do_hangup;
res = SS7_CIC_USED;
} else {
res = SS7_CIC_IDLE;
}
sig_ss7_unlock_private(p);
return res;
}
void sig_ss7_cb_call_null(struct ss7 *ss7, struct isup_call *call, int lock)
{
int i;
struct sig_ss7_linkset *winner;
winner = sig_ss7_find_linkset(ss7);
if (!winner) {
return;
}
for (i = 0; i < winner->numchans; i++) {
if (winner->pvts[i] && (winner->pvts[i]->ss7call == call)) {
if (lock) {
sig_ss7_lock_private(winner->pvts[i]);
}
winner->pvts[i]->ss7call = NULL;
if (winner->pvts[i]->owner) {
ast_channel_hangupcause_set(winner->pvts[i]->owner, AST_CAUSE_NORMAL_TEMPORARY_FAILURE);
ast_channel_softhangup_internal_flag_add(winner->pvts[i]->owner, AST_SOFTHANGUP_DEV);
}
if (lock) {
sig_ss7_unlock_private(winner->pvts[i]);
}
ast_log(LOG_WARNING, "libss7 asked set ss7 call to NULL on CIC %d DPC %d\n", winner->pvts[i]->cic, winner->pvts[i]->dpc);
}
}
}
void sig_ss7_cb_notinservice(struct ss7 *ss7, int cic, unsigned int dpc)
{
struct sig_ss7_chan *p;
if (!(p = ss7_find_pvt(ss7, cic, dpc))) {
return;
}
sig_ss7_lock_private(p);
sig_ss7_set_inservice(p, 0);
sig_ss7_unlock_private(p);
}
/*!
* \internal
* \brief Check if CICs in a range belong to the linkset for a given DPC.
* \since 11.0
*
* \param linkset SS7 linkset control structure.
* \param startcic Circuit Identification Code to start from
* \param endcic Circuit Identification Code to search up-to
* \param dpc Destination Point Code
* \param state Array containing the status of the search
*
* \retval Nothing.
*/
static void ss7_check_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char *state)
{
int cic;
for (cic = startcic; cic <= endcic; cic++) {
if (state[cic - startcic] && sig_ss7_find_cic(linkset, cic, dpc) == -1) {
state[cic - startcic] = 0;
}
}
}
static int ss7_match_range(struct sig_ss7_chan *pvt, int startcic, int endcic, unsigned int dpc)
{
if (pvt && pvt->dpc == dpc && pvt->cic >= startcic && pvt->cic <= endcic) {
return 1;
}
return 0;
}
/*!
* \internal
* \brief Check if a range is defined for the given DPC.
* \since 11.0
*
* \param linkset SS7 linkset control structure.
* \param startcic Start CIC of the range to clear.
* \param endcic End CIC of the range to clear.
* \param dpc Destination Point Code.
*
* \note Assumes the linkset->lock is already obtained.
*
* \return TRUE if all CICs in the range are present
*/
int sig_ss7_find_cic_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
{
int i, found = 0;
for (i = 0; i < linkset->numchans; i++) {
if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
found++;
}
}
if (found == endcic - startcic + 1) {
return 1;
}
return 0;
}
static void ss7_handle_cqm(struct sig_ss7_linkset *linkset, ss7_event *e)
{
unsigned char status[32];
struct sig_ss7_chan *p = NULL;
int i;
int offset;
int chanpos;
memset(status, 0, sizeof(status));
for (i = 0; i < linkset->numchans; i++) {
if (ss7_match_range(linkset->pvts[i], e->cqm.startcic, e->cqm.endcic, e->cqm.opc)) {
sig_ss7_lock_private(p);
offset = p->cic - e->cqm.startcic;
if (p->locallyblocked) {
status[offset] |= (1 << 0) | (1 << 4);
}
if (p->remotelyblocked) {
status[offset] |= (1 << 1) | (1 << 5);
if (p->outgoing) {
}
sig_ss7_unlock_private(p);
if (p) {
isup_cqr(linkset->ss7, e->cqm.startcic, e->cqm.endcic, e->cqm.opc, status);
} else {
ast_log(LOG_WARNING, "Could not find any equipped circuits within CQM CICs\n");
chanpos = sig_ss7_find_cic(linkset, e->cqm.startcic, e->cqm.opc);
if (chanpos < 0) {
isup_free_call(linkset->ss7, e->cqm.call);
return;
}
p = linkset->pvts[chanpos];
sig_ss7_lock_private(p);
p->ss7call = e->cqm.call;
if (!p->owner) {
p->ss7call = isup_free_call_if_clear(linkset->ss7, e->cqm.call);
}
sig_ss7_unlock_private(p);
}
static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
{
int i;
for (i = 0; i < linkset->numchans; i++) {
if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
sig_ss7_lock_private(linkset->pvts[i]);
Richard Mudgett
committed
sig_ss7_lock_owner(linkset, i);
if (linkset->pvts[i]->owner) {
ast_softhangup_nolock(linkset->pvts[i]->owner, AST_SOFTHANGUP_DEV);
ast_channel_unlock(linkset->pvts[i]->owner);
}
sig_ss7_unlock_private(linkset->pvts[i]);
}
}
}
/*!
* \param linkset SS7 linkset control structure.
* \param startcic Start CIC of the range to clear.
* \param endcic End CIC of the range to clear.
* \param dpc Destination Point Code.
* \param state Affected CICs from the operation. NULL for all CICs in the range.
* \param block Operation to perform. TRUE to block.
* \param remotely Direction of the blocking. TRUE to block/unblock remotely.
* \param type Blocking type - hardware or maintenance.
*
* \note Assumes the linkset->lock is already obtained.
* \note Must be called without sig_ss7_lock_private() obtained.
*
* \return Nothing.
*/
static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char state[], int block, int remotely, int type)
{
int i;
for (i = 0; i < linkset->numchans; i++) {
if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
sig_ss7_lock_private(linkset->pvts[i]);
if (state[linkset->pvts[i]->cic - startcic]) {
if (remotely) {
sig_ss7_set_remotelyblocked(linkset->pvts[i], block, type);
} else {
sig_ss7_set_locallyblocked(linkset->pvts[i], block, type);
}
sig_ss7_lock_owner(linkset, i);
if (linkset->pvts[i]->owner) {
if (ast_channel_state(linkset->pvts[i]->owner) == AST_STATE_DIALING
&& linkset->pvts[i]->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
ast_channel_hangupcause_set(linkset->pvts[i]->owner, SS7_CAUSE_TRY_AGAIN);
}
ast_channel_unlock(linkset->pvts[i]->owner);
}
}
} else {
if (remotely) {
sig_ss7_set_remotelyblocked(linkset->pvts[i], block, type);
} else {
sig_ss7_set_locallyblocked(linkset->pvts[i], block, type);
}
}
sig_ss7_unlock_private(linkset->pvts[i]);
/*!
* \param linkset SS7 linkset control structure.
* \param startcic Start CIC of the range to set in service.
* \param endcic End CIC of the range to set in service.
* \param dpc Destination Point Code.
*
* \note Must be called without sig_ss7_lock_private() obtained.
*
* \return Nothing.
*/
static void ss7_inservice(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
{
int i;
for (i = 0; i < linkset->numchans; i++) {
if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
sig_ss7_lock_private(linkset->pvts[i]);
sig_ss7_set_inservice(linkset->pvts[i], 1);
sig_ss7_unlock_private(linkset->pvts[i]);
}
}
}
static int ss7_find_alloc_call(struct sig_ss7_chan *p)
{
if (!p) {
return 0;
}
if (!p->ss7call) {
p->ss7call = isup_new_call(p->ss7->ss7, p->cic, p->dpc, 0);
if (!p->ss7call) {
return 0;
}
/*
* XXX This routine is not tolerant of holes in the pvts[] array.
* XXX This routine assumes the cic's in the pvts[] array are sorted.
*
* Probably the easiest way to deal with the invalid assumptions
* is to have a local pvts[] array and sort it by dpc and cic.
* Then the existing algorithm could work.
*/
static void ss7_reset_linkset(struct sig_ss7_linkset *linkset)
{
int i, startcic, endcic, dpc;
struct sig_ss7_chan *p;
if (linkset->numchans <= 0) {
startcic = linkset->pvts[0]->cic;
p = linkset->pvts[0];
/* DB: CIC's DPC fix */
dpc = linkset->pvts[0]->dpc;
for (i = 0; i < linkset->numchans; i++) {
if (linkset->pvts[i+1]
&& linkset->pvts[i+1]->dpc == dpc
&& linkset->pvts[i+1]->cic - linkset->pvts[i]->cic == 1
&& linkset->pvts[i]->cic - startcic < (linkset->type == SS7_ANSI ? 24 : 31)) {
continue;
} else {
endcic = linkset->pvts[i]->cic;
ast_verb(1, "Resetting CICs %d to %d\n", startcic, endcic);
sig_ss7_lock_private(p);
if (!ss7_find_alloc_call(p)) {
ast_log(LOG_ERROR, "Unable to allocate new ss7call\n");
} else if (!(endcic - startcic)) { /* GRS range can not be 0 - use RSC instead */
isup_rsc(linkset->ss7, p->ss7call);
} else {
isup_grs(linkset->ss7, p->ss7call, endcic);
}
sig_ss7_unlock_private(p);
/* DB: CIC's DPC fix */
if (linkset->pvts[i+1]) {
startcic = linkset->pvts[i+1]->cic;
dpc = linkset->pvts[i+1]->dpc;
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
p = linkset->pvts[i+1];
}
}
}
}
/*!
* \internal
* \brief Complete the RSC procedure started earlier
* \since 11.0
*
* \param p Signaling private structure pointer.
*
* \note Assumes the ss7->lock is already obtained.
* \note Assumes sig_ss7_lock_private(p) is already obtained.
*
* \return Nothing.
*/
static void ss7_do_rsc(struct sig_ss7_chan *p)
{
if (!p || !p->ss7call) {
return;
}
isup_rsc(p->ss7->ss7, p->ss7call);
if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
isup_blo(p->ss7->ss7, p->ss7call);
} else {
sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
}
}
/*!
* \internal
* \brief Start RSC procedure on a specific link
* \since 11.0
*
* \param ss7 SS7 linkset control structure.
* \param which Channel position in the span.
*
* \note Assumes the ss7->lock is already obtained.
* \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
*
* \return TRUE on success
*/
static int ss7_start_rsc(struct sig_ss7_linkset *linkset, int which)
{
if (!linkset->pvts[which]) {
return 0;
}
if (!ss7_find_alloc_call(linkset->pvts[which])) {
return 0;
}
sig_ss7_set_remotelyblocked(linkset->pvts[which], 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
sig_ss7_set_inservice(linkset->pvts[which], 0);
sig_ss7_loopback(linkset->pvts[which], 0);
sig_ss7_lock_owner(linkset, which);
if (linkset->pvts[which]->owner) {
ast_channel_hangupcause_set(linkset->pvts[which]->owner, AST_CAUSE_NORMAL_CLEARING);
ast_softhangup_nolock(linkset->pvts[which]->owner, AST_SOFTHANGUP_DEV);
ast_channel_unlock(linkset->pvts[which]->owner);
linkset->pvts[which]->do_hangup = SS7_HANGUP_SEND_RSC;
} else {
ss7_do_rsc(linkset->pvts[which]);
}
return 1;
}
/*!
* \internal
* \brief Determine if a private channel structure is available.
* \since 11.0
*
* \param linkset SS7 linkset control structure.
* \param startcic Start CIC of the range to clear.
* \param endcic End CIC of the range to clear.
* \param dpc Destination Point Code.
* \param do_hangup What we have to do to clear the call.
*
* \note Assumes the linkset->lock is already obtained.
* \note Must be called without sig_ss7_lock_private() obtained.
*
* \return Nothing.
*/
static void ss7_clear_channels(struct sig_ss7_linkset *linkset, int startcic, int endcic, int dpc, int do_hangup)
{
int i;
for (i = 0; i < linkset->numchans; i++) {
if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
sig_ss7_lock_private(linkset->pvts[i]);
sig_ss7_set_inservice(linkset->pvts[i], 0);
sig_ss7_lock_owner(linkset, i);
if (linkset->pvts[i]->owner) {
ast_channel_hangupcause_set(linkset->pvts[i]->owner,
AST_CAUSE_NORMAL_CLEARING);
ast_softhangup_nolock(linkset->pvts[i]->owner, AST_SOFTHANGUP_DEV);
ast_channel_unlock(linkset->pvts[i]->owner);
linkset->pvts[i]->do_hangup = (linkset->pvts[i]->cic != startcic) ?
do_hangup : SS7_HANGUP_DO_NOTHING;
} else if (linkset->pvts[i] && linkset->pvts[i]->cic != startcic) {
isup_free_call(linkset->pvts[i]->ss7->ss7, linkset->pvts[i]->ss7call);
linkset->pvts[i]->ss7call = NULL;
sig_ss7_unlock_private(linkset->pvts[i]);
/*!
* \internal
*
* \param p Signaling private structure pointer.
* \param linkset SS7 linkset control structure.
*
* \note Assumes the linkset->lock is already obtained.
* \note Assumes the sig_ss7_lock_private(ss7->pvts[chanpos]) is already obtained.
*
* \return Nothing.
*/
static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *linkset)
{
struct ss7 *ss7 = linkset->ss7;
int law;
struct ast_channel *c;
char tmp[256];
ast_callid callid = 0;
int callid_created = ast_callid_threadstorage_auto(&callid);
if (!(linkset->flags & LINKSET_FLAG_EXPLICITACM)) {
p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING;
} else {
p->call_level = SIG_SS7_CALL_LEVEL_SETUP;
/* Companding law is determined by SS7 signaling type. */
if (linkset->type == SS7_ITU) {
law = SIG_SS7_ALAW;
} else {
law = SIG_SS7_ULAW;
}
isup_set_echocontrol(p->ss7call, (linkset->flags & LINKSET_FLAG_DEFAULTECHOCONTROL) ? 1 : 0);
* Release the SS7 lock while we create the channel so other
* threads can send messages. We must also release the private