Skip to content
Snippets Groups Projects
sig_ss7.c 101 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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/callerid.h"
    
    #include "asterisk/transcap.h"
    
    #include "asterisk/stasis_channels.h"
    
    
    #include "sig_ss7.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. */
    
    		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) {
    
    		ast_party_caller_init(&caller);
    
    
    		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;
    
    
    		caller.ani2 = p->cid_ani2;
    
    		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);
    
    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)
    
    {
    	struct ast_channel *ast;
    
    
    	if (sig_ss7_callbacks.new_ast_channel) {
    
    		ast = sig_ss7_callbacks.new_ast_channel(p->chan_pvt, state, ulaw, exten,
    			assignedids, requestor);
    
    	} else {
    		return NULL;
    	}
    
    	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;
    }
    
    
    /*!
     * \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
    
    		&& 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;
    		}
    
    
    		/* 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);
    
    	cause_code->ast_cause = ast_cause;
    
    	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);
    
    		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;
    }
    
    
    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)) {
    
    			p = linkset->pvts[i];
    
    			sig_ss7_lock_private(p);
    			offset = p->cic - e->cqm.startcic;
    
    			status[offset] = 0;
    
    			if (p->locallyblocked) {
    
    				status[offset] |= (1 << 0) | (1 << 4);
    
    			}
    			if (p->remotelyblocked) {
    
    				status[offset] |= (1 << 1) | (1 << 5);
    
    			if (p->ss7call) {
    
    					status[offset] |= (1 << 3);
    
    					status[offset] |= (1 << 2);
    
    				status[offset] |= 0x3 << 2;
    
    			}
    			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]);
    
    			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) {
    
    				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;
    
    	/* 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;
    
    				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;
    
    		isup_acm(ss7, p->ss7call);
    
    	} 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