chan_dahdi: Adds support for major update to libss7.
authorRichard Mudgett <rmudgett@digium.com>
Mon, 16 Jun 2014 18:27:51 +0000 (18:27 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Mon, 16 Jun 2014 18:27:51 +0000 (18:27 +0000)
* SS7 support now requires libss7 v2.0 or later.  The new libss7 is not
backwards compatible.

* Added SS7 support for connected line and redirecting.

* Most SS7 CLI commands are reworked as well as new SS7 commands added.
See online CLI help.

* Added several SS7 config option parameters described in
chan_dahdi.conf.sample.

* ISUP timer support reworked and now requires explicit configuration.
See ss7.timers.sample.

Special thanks to Kaloyan Kovachev for his support and persistence in
getting the original patch by adomjan updated and ready for release.

SS7-27 #close
Reported by: adomjan

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@416416 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
UPGRADE.txt
channels/chan_dahdi.c
channels/chan_dahdi.h
channels/sig_ss7.c
channels/sig_ss7.h
configs/chan_dahdi.conf.sample
configs/ss7.timers.sample [new file with mode: 0644]
configure
configure.ac

diff --git a/CHANGES b/CHANGES
index 031533f..c3774fa 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,18 @@ AMI
    res_manager_presence_state.so. If the high frequency of these events is
    problematic for you, do not load these modules.
 
+chan_dahdi
+------------------
+ * SS7 support now requires libss7 v2.0 or later.
+
+ * Added SS7 support for connected line and redirecting.
+
+ * Most SS7 CLI commands are reworked as well as new SS7 commands added.
+   See online CLI help.
+
+ * Added several SS7 config option parameters described in
+   chan_dahdi.conf.sample.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
 ------------------------------------------------------------------------------
index 9d3f462..82dad8c 100644 (file)
@@ -117,6 +117,9 @@ CDRs:
    handler subroutine). In general, this is not the preferred default: this
    causes extra CDRs to be generated for a channel in many common dialplans.
 
+chan_dahdi:
+ - SS7 support now requires libss7 v2.0 or later.
+
 chan_sip:
  - Made set SIPREFERREDBYHDR as inheritable for better chan_pjsip
    interoperability.
index 71e5831..adc025a 100644 (file)
@@ -81,7 +81,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #if defined(HAVE_SS7)
 #include "sig_ss7.h"
-#if defined(LIBSS7_ABI_COMPATIBILITY)
+#if !defined(LIBSS7_ABI_COMPATIBILITY)
+#error "Upgrade your libss7"
+#elif LIBSS7_ABI_COMPATIBILITY != 2
 #error "Your installed libss7 is not compatible"
 #endif
 #endif /* defined(HAVE_SS7) */
@@ -611,6 +613,7 @@ struct dahdi_ss7 {
 static struct dahdi_ss7 linksets[NUM_SPANS];
 
 static int cur_ss7type = -1;
+static int cur_slc = -1;
 static int cur_linkset = -1;
 static int cur_pointcode = -1;
 static int cur_cicbeginswith = -1;
@@ -758,8 +761,8 @@ const char * const subnames[] = {
        MEMBER(dahdi_pvt, mwimonitoractive, AST_DATA_BOOLEAN)                   \
        MEMBER(dahdi_pvt, mwisendactive, AST_DATA_BOOLEAN)                      \
        MEMBER(dahdi_pvt, inservice, AST_DATA_BOOLEAN)                          \
-       MEMBER(dahdi_pvt, locallyblocked, AST_DATA_BOOLEAN)                     \
-       MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_BOOLEAN)                    \
+       MEMBER(dahdi_pvt, locallyblocked, AST_DATA_UNSIGNED_INTEGER)            \
+       MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_UNSIGNED_INTEGER)           \
        MEMBER(dahdi_pvt, manages_span_alarms, AST_DATA_BOOLEAN)                \
        MEMBER(dahdi_pvt, use_smdi, AST_DATA_BOOLEAN)                           \
        MEMBER(dahdi_pvt, context, AST_DATA_STRING)                             \
@@ -873,7 +876,8 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
                        .internationalprefix = "",
                        .nationalprefix = "",
                        .subscriberprefix = "",
-                       .unknownprefix = ""
+                       .unknownprefix = "",
+                       .networkroutedprefix = ""
                },
 #endif /* defined(HAVE_SS7) */
 #ifdef HAVE_OPENR2
@@ -3022,6 +3026,34 @@ static void my_ss7_set_loopback(void *pvt, int enable)
 #if defined(HAVE_SS7)
 /*!
  * \internal
+ * \brief Find the linkset to which SS7 belongs.
+ * \since 11.0
+ *
+ * \param ss7 structure to match on.
+ *
+ * \retval linkset if found.
+ * \retval NULL if not found.
+ */
+static struct sig_ss7_linkset *my_ss7_find_linkset(struct ss7 *ss7)
+{
+       int idx;
+
+       if (!ss7) {
+               return NULL;
+       }
+
+       for (idx = 0; idx < NUM_SPANS; ++idx) {
+               if (linksets[idx].ss7.ss7 == ss7) {
+                       return &linksets[idx].ss7;
+               }
+       }
+       return NULL;
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+/*!
+ * \internal
  * \brief Create a new asterisk channel structure for SS7.
  * \since 1.8
  *
@@ -3128,6 +3160,7 @@ struct sig_ss7_callback sig_ss7_callbacks =
        .set_callerid = my_set_callerid,
        .set_dnid = my_set_dnid,
        .open_media = my_pri_ss7_open_media,
+       .find_linkset = my_ss7_find_linkset,
 };
 #endif /* defined(HAVE_SS7) */
 
@@ -10688,9 +10721,10 @@ static int mwi_send_process_buffer(struct dahdi_pvt * pvt, int num_read)
                        break;
                case MWI_SEND_SPILL:
                        /* We read some number of bytes.  Write an equal amount of data */
-                       if(0 < num_read) {
-                               if (num_read > pvt->cidlen - pvt->cidpos)
+                       if (0 < num_read) {
+                               if (num_read > pvt->cidlen - pvt->cidpos) {
                                        num_read = pvt->cidlen - pvt->cidpos;
+                               }
                                res = write(pvt->subs[SUB_REAL].dfd, pvt->cidspill + pvt->cidpos, num_read);
                                if (res > 0) {
                                        pvt->cidpos += res;
@@ -10741,7 +10775,7 @@ static int mwi_send_process_event(struct dahdi_pvt * pvt, int event)
        if (MWI_SEND_DONE != pvt->mwisend_data.mwisend_current) {
                switch (event) {
                case DAHDI_EVENT_RINGEROFF:
-                       if(pvt->mwisend_data.mwisend_current == MWI_SEND_SA_WAIT) {
+                       if (pvt->mwisend_data.mwisend_current == MWI_SEND_SA_WAIT) {
                                handled = 1;
 
                                if (dahdi_set_hook(pvt->subs[SUB_REAL].dfd, DAHDI_RINGOFF) ) {
@@ -11946,6 +11980,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                ast_copy_string(ss7->ss7.nationalprefix, conf->ss7.ss7.nationalprefix, sizeof(ss7->ss7.nationalprefix));
                                ast_copy_string(ss7->ss7.subscriberprefix, conf->ss7.ss7.subscriberprefix, sizeof(ss7->ss7.subscriberprefix));
                                ast_copy_string(ss7->ss7.unknownprefix, conf->ss7.ss7.unknownprefix, sizeof(ss7->ss7.unknownprefix));
+                               ast_copy_string(ss7->ss7.networkroutedprefix, conf->ss7.ss7.networkroutedprefix, sizeof(ss7->ss7.networkroutedprefix));
 
                                ss7->ss7.called_nai = conf->ss7.ss7.called_nai;
                                ss7->ss7.calling_nai = conf->ss7.ss7.calling_nai;
@@ -12452,12 +12487,12 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                tmp->named_pickupgroups = ast_ref_namedgroups(conf->chan.named_pickupgroups);
                if (conf->chan.vars) {
                        struct ast_variable *v, *tmpvar;
-                       for (v = conf->chan.vars ; v ; v = v->next) {
-                               if ((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
-                                       tmpvar->next = tmp->vars;
-                                       tmp->vars = tmpvar;
-                               }
-                       }
+                       for (v = conf->chan.vars ; v ; v = v->next) {
+                               if ((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
+                                       tmpvar->next = tmp->vars;
+                                       tmp->vars = tmpvar;
+                               }
+                       }
                }
                tmp->hwrxgain_enabled = conf->chan.hwrxgain_enabled;
                tmp->hwtxgain_enabled = conf->chan.hwtxgain_enabled;
@@ -12566,6 +12601,9 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 #if defined(HAVE_SS7)
                        case SIG_SS7:
                                tmp->inservice = 0;
+                               if (tmp->ss7->flags & LINKSET_FLAG_INITIALHWBLO) {
+                                       tmp->remotelyblocked |= SS7_BLOCKED_HARDWARE;
+                               }
                                break;
 #endif /* defined(HAVE_SS7) */
                        default:
@@ -12600,6 +12638,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                case SIG_SS7:
                        if (ss7_chan) {
                                ss7_chan->inalarm = tmp->inalarm;
+                               ss7_chan->inservice = tmp->inservice;
 
                                ss7_chan->stripmsd = tmp->stripmsd;
                                ss7_chan->hidecallerid = tmp->hidecallerid;
@@ -14891,6 +14930,9 @@ static int dahdi_restart(void)
        }
        ss7_set_error(dahdi_ss7_error);
        ss7_set_message(dahdi_ss7_message);
+       ss7_set_hangup(sig_ss7_cb_hangup);
+       ss7_set_notinservice(sig_ss7_cb_notinservice);
+       ss7_set_call_null(sig_ss7_cb_call_null);
 #endif /* defined(HAVE_SS7) */
 
        if (setup_dahdi(2) != 0) {
@@ -14943,9 +14985,8 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
        ast_group_t targetnum = 0;
        int filtertype = 0;
        struct dahdi_pvt *tmp = NULL;
-       char tmps[20] = "";
-       char statestr[20] = "";
-       char blockstr[20] = "";
+       char tmps[20];
+       char blockstr[20];
 
        switch (cmd) {
        case CLI_INIT:
@@ -14961,8 +15002,9 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
 
        /* syntax: dahdi show channels [ group <group> | context <context> ] */
 
-       if (!((a->argc == 3) || (a->argc == 5)))
+       if (!((a->argc == 3) || (a->argc == 5))) {
                return CLI_SHOWUSAGE;
+       }
 
        if (a->argc == 5) {
                if (!strcasecmp(a->argv[3], "group")) {
@@ -14977,7 +15019,7 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
                }
        }
 
-       ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "State", "Description");
+       ast_cli(a->fd, FORMAT2, "Chan", "Extension", "Context", "Language", "MOH Interpret", "Blocked", "In Service", "Description");
        ast_mutex_lock(&iflock);
        for (tmp = iflist; tmp; tmp = tmp->next) {
                if (filtertype) {
@@ -14998,24 +15040,15 @@ static char *dahdi_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
                }
                if (tmp->channel > 0) {
                        snprintf(tmps, sizeof(tmps), "%d", tmp->channel);
-               } else
+               } else {
                        ast_copy_string(tmps, "pseudo", sizeof(tmps));
+               }
 
-               if (tmp->locallyblocked)
-                       blockstr[0] = 'L';
-               else
-                       blockstr[0] = ' ';
-
-               if (tmp->remotelyblocked)
-                       blockstr[1] = 'R';
-               else
-                       blockstr[1] = ' ';
-
+               blockstr[0] = tmp->locallyblocked ? 'L' : ' ';
+               blockstr[1] = tmp->remotelyblocked ? 'R' : ' ';
                blockstr[2] = '\0';
 
-               snprintf(statestr, sizeof(statestr), "%s", "In Service");
-
-               ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, statestr, tmp->description);
+               ast_cli(a->fd, FORMAT, tmps, tmp->exten, tmp->context, tmp->language, tmp->mohinterpret, blockstr, tmp->inservice ? "Yes" : "No", tmp->description);
        }
        ast_mutex_unlock(&iflock);
        return CLI_SUCCESS;
@@ -16023,7 +16056,7 @@ static int linkset_addsigchan(int sigchan)
                (params.sigtype == DAHDI_SIG_MTP2)
                        ? SS7_TRANSPORT_DAHDIMTP2
                        : SS7_TRANSPORT_DAHDIDCHAN,
-               si.alarms, cur_networkindicator, cur_pointcode, cur_adjpointcode);
+               si.alarms, cur_networkindicator, cur_pointcode, cur_adjpointcode, cur_slc);
        if (res) {
                dahdi_close_ss7_fd(link, curfd);
                return -1;
@@ -16049,8 +16082,11 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
        case CLI_GENERATE:
                return NULL;
        }
-       if (a->argc < 6)
+
+       if (a->argc < 6) {
                return CLI_SHOWUSAGE;
+       }
+
        span = atoi(a->argv[5]);
        if ((span < 1) || (span > NUM_SPANS)) {
                ast_cli(a->fd, "Invalid linkset %s.  Should be a number from %d to %d\n", a->argv[5], 1, NUM_SPANS);
@@ -16075,24 +16111,35 @@ static char *handle_ss7_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 #endif /* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_cic_blocking(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        int linkset, cic;
-       int blocked = -1, i;
+       int blocked, i;
+       int do_block = 0;
+       unsigned int dpc;
+
        switch (cmd) {
        case CLI_INIT:
-               e->command = "ss7 block cic";
+               e->command = "ss7 {block|unblock} cic";
                e->usage =
-                       "Usage: ss7 block cic <linkset> <CIC>\n"
-                       "       Sends a remote blocking request for the given CIC on the specified linkset\n";
+                       "Usage: ss7 {block|unblock} cic <linkset> <dpc> <CIC>\n"
+                       "       Sends a remote {blocking|unblocking} request for the given CIC on the specified linkset\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
        }
-       if (a->argc == 5)
+
+       if (a->argc == 6) {
                linkset = atoi(a->argv[3]);
-       else
+       } else {
                return CLI_SHOWUSAGE;
+       }
+
+       if (!strcasecmp(a->argv[1], "block")) {
+               do_block = 1;
+       } else if (strcasecmp(a->argv[1], "unblock")) {
+               return CLI_SHOWUSAGE;
+       }
 
        if ((linkset < 1) || (linkset > NUM_SPANS)) {
                ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
@@ -16104,63 +16151,157 @@ static char *handle_ss7_block_cic(struct ast_cli_entry *e, int cmd, struct ast_c
                return CLI_SUCCESS;
        }
 
-       cic = atoi(a->argv[4]);
-
+       cic = atoi(a->argv[5]);
        if (cic < 1) {
                ast_cli(a->fd, "Invalid CIC specified!\n");
                return CLI_SUCCESS;
        }
 
+       dpc = atoi(a->argv[4]);
+       if (dpc < 1) {
+               ast_cli(a->fd, "Invalid DPC specified!\n");
+               return CLI_SUCCESS;
+       }
+
        for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-               if (linksets[linkset-1].ss7.pvts[i]->cic == cic) {
+               if (linksets[linkset-1].ss7.pvts[i] && linksets[linkset-1].ss7.pvts[i]->cic == cic && linksets[linkset-1].ss7.pvts[i]->dpc == dpc) {
                        blocked = linksets[linkset-1].ss7.pvts[i]->locallyblocked;
-                       if (!blocked) {
-                               ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-                               isup_blo(linksets[linkset-1].ss7.ss7, cic, linksets[linkset-1].ss7.pvts[i]->dpc);
-                               ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+                       if (!do_block ^ !(blocked & SS7_BLOCKED_MAINTENANCE)) {
+                               if (sig_ss7_cic_blocking(&linksets[linkset-1].ss7, do_block, i) < 0) {
+                                       ast_cli(a->fd, "Unable to allocate new ss7call\n");
+                               } else {
+                                       ast_cli(a->fd, "Sent %sblocking request for linkset %d on CIC %d DPC %d\n", (do_block) ? "" : "un", linkset, cic, dpc);
+                               }
+                       } else if (!do_block && blocked) {
+                               ast_cli(a->fd, "CIC %d is hardware locally blocked!\n", cic);
+                       } else {
+                               ast_cli(a->fd, "CIC %d %s locally blocked\n", cic, do_block ? "already" : "is not");
                        }
+                       return CLI_SUCCESS;
                }
        }
 
-       if (blocked < 0) {
-               ast_cli(a->fd, "Invalid CIC specified!\n");
+       ast_cli(a->fd, "Invalid CIC specified!\n");
+       return CLI_SUCCESS;
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_linkset_mng(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       int linkset, i;
+       enum {
+               DO_BLOCK,
+               DO_UNBLOCK,
+               DO_RESET,
+       } do_what;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "ss7 {reset|block|unblock} linkset";
+               e->usage =
+                       "Usage: ss7 {reset|block|unblock} linkset <linkset number>\n"
+                       "       Sends a remote {reset|blocking|unblocking} request for all CICs on the given linkset\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc == 4) {
+               linkset = atoi(a->argv[3]);
+       } else {
+               return CLI_SHOWUSAGE;
+       }
+
+       if (!strcasecmp(a->argv[1], "block")) {
+               do_what = DO_BLOCK;
+       } else if (!strcasecmp(a->argv[1], "unblock")) {
+               do_what = DO_UNBLOCK;
+       } else if (!strcasecmp(a->argv[1], "reset")) {
+               do_what = DO_RESET;
+       } else {
+               return CLI_SHOWUSAGE;
+       }
+
+       if ((linkset < 1) || (linkset > NUM_SPANS)) {
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
                return CLI_SUCCESS;
        }
 
-       if (!blocked)
-               ast_cli(a->fd, "Sent blocking request for linkset %d on CIC %d\n", linkset, cic);
-       else
-               ast_cli(a->fd, "CIC %d already locally blocked\n", cic);
+       if (!linksets[linkset - 1].ss7.ss7) {
+               ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+               return CLI_SUCCESS;
+       }
 
-       /* Break poll on the linkset so it sends our messages */
-       pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       for (i = 0; i < linksets[linkset - 1].ss7.numchans; i++) {
+               /* XXX Should be done with GRS/CGB/CGU instead - see ss7_reset_linkset() */
+               if (linksets[linkset - 1].ss7.pvts[i]) {
+                       switch (do_what) {
+                       case DO_BLOCK:
+                       case DO_UNBLOCK:
+                               if (sig_ss7_cic_blocking(&linksets[linkset - 1].ss7, do_what == DO_BLOCK, i)) {
+                                       ast_cli(a->fd, "Sent remote %s request on CIC %d\n",
+                                               (do_what == DO_BLOCK) ? "blocking" : "unblocking",
+                                               linksets[linkset - 1].ss7.pvts[i]->cic);
+                               }
+                               break;
+                       case DO_RESET:
+                               if (sig_ss7_reset_cic(&linksets[linkset - 1].ss7,
+                                       linksets[linkset - 1].ss7.pvts[i]->cic,
+                                       linksets[linkset - 1].ss7.pvts[i]->dpc)) {
+                                       ast_cli(a->fd, "Sent reset request on CIC %d\n",
+                                               linksets[linkset - 1].ss7.pvts[i]->cic);
+                               }
+                               break;
+                       }
+               }
+       }
 
        return CLI_SUCCESS;
 }
 #endif /* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_block_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_group_blocking(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       int linkset;
-       int i;
+       int linkset, cic, range, chanpos;
+       int i, dpc, orient = 0;
+       int do_block = 0;
+       unsigned char state[255];
+
        switch (cmd) {
        case CLI_INIT:
-               e->command = "ss7 block linkset";
+               e->command = "ss7 {block|unblock} group";
                e->usage =
-                       "Usage: ss7 block linkset <linkset number>\n"
-                       "       Sends a remote blocking request for all CICs on the given linkset\n";
+                       "Usage: ss7 {block|unblock} group <linkset> <dpc> <1st. CIC> <range> [H]\n"
+                       "       Sends a remote {blocking|unblocking} request for CIC range on the specified linkset\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
        }
-       if (a->argc == 4)
+
+       if (a->argc == 7 || a->argc == 8) {
                linkset = atoi(a->argv[3]);
-       else
+       } else {
                return CLI_SHOWUSAGE;
+       }
+
+       if (!strcasecmp(a->argv[1], "block")) {
+               do_block = 1;
+       } else if (strcasecmp(a->argv[1], "unblock")) {
+               return CLI_SHOWUSAGE;
+       }
+
+       if (a->argc == 8) {
+               if (!strcasecmp(a->argv[7], "H")) {
+                       orient = 1;
+               } else {
+                       return CLI_SHOWUSAGE;
+               }
+       }
 
        if ((linkset < 1) || (linkset > NUM_SPANS)) {
-               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS);
                return CLI_SUCCESS;
        }
 
@@ -16169,43 +16310,81 @@ static char *handle_ss7_block_linkset(struct ast_cli_entry *e, int cmd, struct a
                return CLI_SUCCESS;
        }
 
-       for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-               ast_cli(a->fd, "Sending remote blocking request on CIC %d\n", linksets[linkset-1].ss7.pvts[i]->cic);
-               ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-               isup_blo(linksets[linkset-1].ss7.ss7, linksets[linkset-1].ss7.pvts[i]->cic, linksets[linkset-1].ss7.pvts[i]->dpc);
+       cic = atoi(a->argv[5]);
+       if (cic < 1) {
+               ast_cli(a->fd, "Invalid CIC specified!\n");
+               return CLI_SUCCESS;
+       }
+
+       range = atoi(a->argv[6]);
+       /* ITU-T Q.763 3.43 - range 0 is reserved, which makes a range of 2 CICs a minimum group */
+       if (range < 1 || range > (linksets[linkset - 1].ss7.type == SS7_ANSI ? 24 : 31)) {
+               ast_cli(a->fd, "Invalid range specified!\n");
+               return CLI_SUCCESS;
+       }
+
+       dpc = atoi(a->argv[4]);
+       if (dpc < 1) {
+               ast_cli(a->fd, "Invalid DPC specified!\n");
+               return CLI_SUCCESS;
+       }
+
+       ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+       if (!sig_ss7_find_cic_range(&linksets[linkset-1].ss7, cic, cic + range, dpc)) {
                ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+               ast_cli(a->fd, "Invalid CIC/RANGE\n");
+               return CLI_SHOWUSAGE;
        }
 
-       /* Break poll on the linkset so it sends our messages */
-       pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       memset(state, 0, sizeof(state));
+       for (i = 0; i <= range; ++i) {
+               state[i] = 1;
+       }
 
+       /* We are guaranteed to find chanpos because of sig_ss7_find_cic_range() includes it. */
+       chanpos = sig_ss7_find_cic(&linksets[linkset-1].ss7, cic, dpc);
+       if (sig_ss7_group_blocking(&linksets[linkset-1].ss7, do_block, chanpos, cic + range, state, orient)) {
+               ast_cli(a->fd, "Unable allocate new ss7call\n");
+       } else {
+               ast_cli(a->fd, "Sending remote%s %sblocking request linkset %d on CIC %d range %d\n",
+                       orient ? " hardware" : "", do_block ? "" : "un", linkset, cic, range);
+       }
+
+       ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+       /* Break poll on the linkset so it sends our messages */
+       if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+               pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       }
        return CLI_SUCCESS;
 }
 #endif /* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_group_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       int linkset, cic;
-       int i, blocked = -1;
+       int linkset, cic, range;
+       unsigned int dpc;
+
        switch (cmd) {
        case CLI_INIT:
-               e->command = "ss7 unblock cic";
+               e->command = "ss7 reset group";
                e->usage =
-                       "Usage: ss7 unblock cic <linkset> <CIC>\n"
-                       "       Sends a remote unblocking request for the given CIC on the specified linkset\n";
+                       "Usage: ss7 reset group <linkset> <dpc> <1st CIC> <range>\n"
+                       "       Send a GRS for the given CIC range on the specified linkset\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
        }
 
-       if (a->argc == 5)
+       if (a->argc == 7) {
                linkset = atoi(a->argv[3]);
-       else
+       } else {
                return CLI_SHOWUSAGE;
+       }
 
        if ((linkset < 1) || (linkset > NUM_SPANS)) {
-               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[4], 1, NUM_SPANS);
                return CLI_SUCCESS;
        }
 
@@ -16214,54 +16393,69 @@ static char *handle_ss7_unblock_cic(struct ast_cli_entry *e, int cmd, struct ast
                return CLI_SUCCESS;
        }
 
-       cic = atoi(a->argv[4]);
+       cic = atoi(a->argv[5]);
 
        if (cic < 1) {
                ast_cli(a->fd, "Invalid CIC specified!\n");
                return CLI_SUCCESS;
        }
 
-       for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-               if (linksets[linkset-1].ss7.pvts[i]->cic == cic) {
-                       blocked = linksets[linkset-1].ss7.pvts[i]->locallyblocked;
-                       if (blocked) {
-                               ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-                               isup_ubl(linksets[linkset-1].ss7.ss7, cic, linksets[linkset-1].ss7.pvts[i]->dpc);
-                               ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
-                       }
-               }
+       range = atoi(a->argv[6]);
+       if (range < 1 || range > (linksets[linkset - 1].ss7.type == SS7_ANSI ? 24 : 31)) {
+               ast_cli(a->fd, "Invalid range specified!\n");
+               return CLI_SUCCESS;
        }
 
-       if (blocked > 0)
-               ast_cli(a->fd, "Sent unblocking request for linkset %d on CIC %d\n", linkset, cic);
+       dpc = atoi(a->argv[4]);
+       if (dpc < 1) {
+               ast_cli(a->fd, "Invalid DPC specified!\n");
+               return CLI_SUCCESS;
+       }
 
-       /* Break poll on the linkset so it sends our messages */
-       pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+       if (!sig_ss7_find_cic_range(&linksets[linkset-1].ss7, cic, cic + range, dpc)) {
+               ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+               ast_cli(a->fd, "Invalid CIC/RANGE\n");
+               return CLI_SHOWUSAGE;
+       }
 
+       if (sig_ss7_reset_group(&linksets[linkset-1].ss7, cic, dpc, range)) {
+               ast_cli(a->fd, "Unable to allocate new ss7call\n");
+       } else {
+               ast_cli(a->fd, "GRS sent ... \n");
+       }
+
+       ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+       /* Break poll on the linkset so it sends our messages */
+       if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+               pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       }
        return CLI_SUCCESS;
 }
 #endif /* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
-static char *handle_ss7_unblock_linkset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *handle_ss7_show_calls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        int linkset;
-       int i;
+
        switch (cmd) {
        case CLI_INIT:
-               e->command = "ss7 unblock linkset";
+               e->command = "ss7 show calls";
                e->usage =
-                       "Usage: ss7 unblock linkset <linkset number>\n"
-                       "       Sends a remote unblocking request for all CICs on the specified linkset\n";
+                       "Usage: ss7 show calls <linkset>\n"
+                       "       Show SS7 calls on the specified linkset\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
        }
 
-       if (a->argc == 4)
+       if (a->argc == 4) {
                linkset = atoi(a->argv[3]);
-       else
+       } else {
                return CLI_SHOWUSAGE;
+       }
 
        if ((linkset < 1) || (linkset > NUM_SPANS)) {
                ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
@@ -16273,15 +16467,164 @@ static char *handle_ss7_unblock_linkset(struct ast_cli_entry *e, int cmd, struct
                return CLI_SUCCESS;
        }
 
-       for (i = 0; i < linksets[linkset-1].ss7.numchans; i++) {
-               ast_cli(a->fd, "Sending remote unblock request on CIC %d\n", linksets[linkset-1].ss7.pvts[i]->cic);
-               ast_mutex_lock(&linksets[linkset-1].ss7.lock);
-               isup_ubl(linksets[linkset-1].ss7.ss7, linksets[linkset-1].ss7.pvts[i]->cic, linksets[linkset-1].ss7.pvts[i]->dpc);
-               ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+       ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+       isup_show_calls(linksets[linkset-1].ss7.ss7, &ast_cli, a->fd);
+       ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+       return CLI_SUCCESS;
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_reset_cic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       int linkset, cic, res;
+       unsigned int dpc;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "ss7 reset cic";
+               e->usage =
+                       "Usage: ss7 reset cic <linkset> <dpc> <CIC>\n"
+                       "       Send a RSC for the given CIC on the specified linkset\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc == 6) {
+               linkset = atoi(a->argv[3]);
+       } else {
+               return CLI_SHOWUSAGE;
+       }
+
+       if ((linkset < 1) || (linkset > NUM_SPANS)) {
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+               return CLI_SUCCESS;
+       }
+
+       if (!linksets[linkset-1].ss7.ss7) {
+               ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+               return CLI_SUCCESS;
+       }
+
+       cic = atoi(a->argv[5]);
+
+       if (cic < 1) {
+               ast_cli(a->fd, "Invalid CIC specified!\n");
+               return CLI_SUCCESS;
+       }
+
+       dpc = atoi(a->argv[4]);
+       if (dpc < 1) {
+               ast_cli(a->fd, "Invalid DPC specified!\n");
+               return CLI_SUCCESS;
+       }
+
+       res = sig_ss7_reset_cic(&linksets[linkset-1].ss7, cic, dpc);
+
+       ast_cli(a->fd, "%s RSC for linkset %d on CIC %d DPC %d\n", res ? "Sent" : "Failed", linkset, cic, dpc);
+
+       return CLI_SUCCESS;
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_net_mng(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       int linkset;
+       unsigned int slc;
+       unsigned int arg = 0;
+       const char *res;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "ss7 mtp3";
+               e->usage =
+                       "Usage: ss7 mtp3 <linkset> <slc> coo|coa|cbd|cba|eco|eca|tfp|tfa|lin|lun|lia|lua|lid|lfu <arg>\n"
+                       "       Send a NET MNG message\n"
+                       "       WARNING!!! WARNING!!! We are not a STP, just for testing/development purposes\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc < 5) {
+               return CLI_SHOWUSAGE;
        }
 
+       linkset = atoi(a->argv[2]);
+       if ((linkset < 1) || (linkset > NUM_SPANS)) {
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[2], 1, NUM_SPANS);
+               return CLI_SUCCESS;
+       }
+       if (!linksets[linkset-1].ss7.ss7) {
+               ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+               return CLI_SUCCESS;
+       }
+
+       slc = atoi(a->argv[3]);
+
+       if (a->argc == 6) {
+               arg = atoi(a->argv[5]);
+       }
+
+       ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+       res = mtp3_net_mng(linksets[linkset-1].ss7.ss7, slc, a->argv[4], arg);
+       ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
        /* Break poll on the linkset so it sends our messages */
-       pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+               pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       }
+
+       ast_cli(a->fd, "%s", res);
+
+       return CLI_SUCCESS;
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static char *handle_ss7_mtp3_restart(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       int linkset;
+       unsigned int slc = 0;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "ss7 restart mtp3";
+               e->usage =
+                       "Usage: ss7 restart mtp3 <linkset> <slc>\n"
+                       "       Restart link\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc < 5) {
+               return CLI_SHOWUSAGE;
+       }
+
+       linkset = atoi(a->argv[3]);
+       if ((linkset < 1) || (linkset > NUM_SPANS)) {
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[2], 1, NUM_SPANS);
+               return CLI_SUCCESS;
+       }
+       if (!linksets[linkset-1].ss7.ss7) {
+               ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+               return CLI_SUCCESS;
+       }
+
+       slc = atoi(a->argv[4]);
+
+       ast_mutex_lock(&linksets[linkset-1].ss7.lock);
+       mtp3_init_restart(linksets[linkset-1].ss7.ss7, slc);
+       ast_mutex_unlock(&linksets[linkset-1].ss7.lock);
+
+       /* Break poll on the linkset so it sends our messages */
+       if (linksets[linkset-1].ss7.master != AST_PTHREADT_NULL) {
+               pthread_kill(linksets[linkset-1].ss7.master, SIGURG);
+       }
 
        return CLI_SUCCESS;
 }
@@ -16303,8 +16646,10 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as
                return NULL;
        }
 
-       if (a->argc < 4)
+       if (a->argc < 4) {
                return CLI_SHOWUSAGE;
+       }
+
        linkset = atoi(a->argv[3]);
        if ((linkset < 1) || (linkset > NUM_SPANS)) {
                ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
@@ -16316,7 +16661,16 @@ static char *handle_ss7_show_linkset(struct ast_cli_entry *e, int cmd, struct as
                return CLI_SUCCESS;
        }
 
+       ast_cli(a->fd, "SS7 flags: 0x%x\n", ss7->flags);
        ast_cli(a->fd, "SS7 linkset %d status: %s\n", linkset, (ss7->state == LINKSET_STATE_UP) ? "Up" : "Down");
+       ast_cli(a->fd, "SS7 calling nai: %i\n", ss7->calling_nai);
+       ast_cli(a->fd, "SS7 called nai: %i\n", ss7->called_nai);
+       ast_cli(a->fd, "SS7 nationalprefix: %s\n", ss7->nationalprefix);
+       ast_cli(a->fd, "SS7 internationalprefix: %s\n", ss7->internationalprefix);
+       ast_cli(a->fd, "SS7 unknownprefix: %s\n", ss7->unknownprefix);
+       ast_cli(a->fd, "SS7 networkroutedprefix: %s\n", ss7->networkroutedprefix);
+       ast_cli(a->fd, "SS7 subscriberprefix: %s\n", ss7->subscriberprefix);
+       ss7_show_linkset(ss7->ss7, &ast_cli, a->fd);
 
        return CLI_SUCCESS;
 }
@@ -16338,8 +16692,9 @@ static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct a
                return NULL;
        }
 
-       if (a->argc != 3)
+       if (a->argc != 3) {
                return CLI_SHOWUSAGE;
+       }
 
        sig_ss7_cli_show_channels_header(a->fd);
        for (linkset = 0; linkset < NUM_SPANS; ++linkset) {
@@ -16352,6 +16707,110 @@ static char *handle_ss7_show_channels(struct ast_cli_entry *e, int cmd, struct a
 #endif /* defined(HAVE_SS7) */
 
 #if defined(HAVE_SS7)
+static char *handle_ss7_show_cics(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%5s %5s %6s %12s   %-12s\n"
+#define FORMAT2 "%5i %5i %6i %12s   %-12s\n"
+       int i, linkset, dpc = 0;
+       struct sig_ss7_linkset *ss7;
+       char *state;
+       char blocking[12];
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "ss7 show cics";
+               e->usage =
+                       "Usage: ss7 show cics <linkset> [dpc]\n"
+                       "       Shows the cics of an SS7 linkset.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc < 4 || a->argc > 5) {
+               return CLI_SHOWUSAGE;
+       }
+
+       linkset = atoi(a->argv[3]);
+
+       if ((linkset < 1) || (linkset > NUM_SPANS)) {
+               ast_cli(a->fd, "Invalid linkset %s.  Should be a number %d to %d\n", a->argv[3], 1, NUM_SPANS);
+               return CLI_SUCCESS;
+       }
+
+       if (!linksets[linkset-1].ss7.ss7) {
+               ast_cli(a->fd, "No SS7 running on linkset %d\n", linkset);
+               return CLI_SUCCESS;
+       }
+       ss7 = &linksets[linkset-1].ss7;
+
+       if (a->argc == 5) {
+               dpc = atoi(a->argv[4]);
+               if (dpc < 1) {
+                       ast_cli(a->fd, "Invalid DPC specified!\n");
+                       return CLI_SUCCESS;
+               }
+       }
+
+       ast_cli(a->fd, FORMAT, "CIC", "DPC", "DAHDI", "STATE", "BLOCKING");
+
+       for (i = 0; i < ss7->numchans; i++) {
+               if (!dpc || (ss7->pvts[i] && ss7->pvts[i]->dpc == dpc)) {
+                       struct dahdi_pvt *p = ss7->pvts[i]->chan_pvt;
+
+                       if (ss7->pvts[i]->owner) {
+                               state = "Used";
+                       } else if (ss7->pvts[i]->ss7call) {
+                               state = "Pending";
+                       } else if (!p->inservice) {
+                               state = "NotInServ";
+                       } else {
+                               state = "Idle";
+                       }
+
+                       if (p->locallyblocked) {
+                               strcpy(blocking, "L:");
+                               if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+                                       strcat(blocking, "M");
+                               } else {
+                                       strcat(blocking, " ");
+                               }
+
+                               if (p->locallyblocked & SS7_BLOCKED_HARDWARE) {
+                                       strcat(blocking, "H");
+                               } else {
+                                       strcat(blocking, " ");
+                               }
+                       } else {
+                               strcpy(blocking, "    ");
+                       }
+
+                       if (p->remotelyblocked) {
+                               strcat(blocking, " R:");
+                               if (p->remotelyblocked & SS7_BLOCKED_MAINTENANCE) {
+                                       strcat(blocking, "M");
+                               } else {
+                                       strcat(blocking, " ");
+                               }
+
+                               if (p->remotelyblocked & SS7_BLOCKED_HARDWARE) {
+                                       strcat(blocking, "H");
+                               } else {
+                                       strcat(blocking, " ");
+                               }
+                       }
+
+                       ast_cli(a->fd, FORMAT2, ss7->pvts[i]->cic, ss7->pvts[i]->dpc, ss7->pvts[i]->channel, state, blocking);
+               }
+       }
+
+       return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
 static char *handle_ss7_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        switch (cmd) {
@@ -16374,12 +16833,17 @@ static char *handle_ss7_version(struct ast_cli_entry *e, int cmd, struct ast_cli
 #if defined(HAVE_SS7)
 static struct ast_cli_entry dahdi_ss7_cli[] = {
        AST_CLI_DEFINE(handle_ss7_debug, "Enables SS7 debugging on a linkset"),
-       AST_CLI_DEFINE(handle_ss7_block_cic, "Blocks the given CIC"),
-       AST_CLI_DEFINE(handle_ss7_unblock_cic, "Unblocks the given CIC"),
-       AST_CLI_DEFINE(handle_ss7_block_linkset, "Blocks all CICs on a linkset"),
-       AST_CLI_DEFINE(handle_ss7_unblock_linkset, "Unblocks all CICs on a linkset"),
+       AST_CLI_DEFINE(handle_ss7_cic_blocking, "Blocks/Unblocks the given CIC"),
+       AST_CLI_DEFINE(handle_ss7_linkset_mng, "Resets/Blocks/Unblocks all CICs on a linkset"),
+       AST_CLI_DEFINE(handle_ss7_group_blocking, "Blocks/Unblocks the given CIC range"),
+       AST_CLI_DEFINE(handle_ss7_reset_cic, "Resets the given CIC"),
+       AST_CLI_DEFINE(handle_ss7_group_reset, "Resets the given CIC range"),
+       AST_CLI_DEFINE(handle_ss7_mtp3_restart, "Restart a link"),
+       AST_CLI_DEFINE(handle_ss7_net_mng, "Send an NET MNG message"),
        AST_CLI_DEFINE(handle_ss7_show_linkset, "Shows the status of a linkset"),
        AST_CLI_DEFINE(handle_ss7_show_channels, "Displays SS7 channel information"),
+       AST_CLI_DEFINE(handle_ss7_show_calls, "Show ss7 calls"),
+       AST_CLI_DEFINE(handle_ss7_show_cics, "Show cics on a linkset"),
        AST_CLI_DEFINE(handle_ss7_version, "Displays libss7 version"),
 };
 #endif /* defined(HAVE_SS7) */
@@ -16581,6 +17045,10 @@ static int __unload_module(void)
                for (j = 0; j < SIG_SS7_NUM_DCHANS; j++) {
                        dahdi_close_ss7_fd(&(linksets[i]), j);
                }
+               if (linksets[i].ss7.ss7) {
+                       ss7_destroy(linksets[i].ss7.ss7);
+                       linksets[i].ss7.ss7 = NULL;
+               }
        }
 #endif /* defined(HAVE_SS7) */
        ast_cond_destroy(&ss_thread_complete);
@@ -16856,7 +17324,7 @@ static void parse_busy_pattern(struct ast_variable *v, struct ast_dsp_busy_patte
 
        for (; ;) {
                /* Scans the string for the next value in the pattern. If none, it checks to see if any have been entered so far. */
-               if(!sscanf(v->value, "%30d", &norval) && count_pattern == 0) {
+               if (!sscanf(v->value, "%30d", &norval) && count_pattern == 0) {
                        ast_log(LOG_ERROR, "busypattern= expects either busypattern=tonelength,quietlength or busypattern=t1length, q1length, t2length, q2length at line %d.\n", v->lineno);
                        break;
                }
@@ -17220,23 +17688,23 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 #else
                        /* Default is fsk, to turn it off you must specify nofsk */
                        memset(&confp->chan.mwisend_setting, 0, sizeof(confp->chan.mwisend_setting));
-                       if (strcasestr(v->value, "nofsk")) {            /* NoFSK */
+                       if (strcasestr(v->value, "nofsk")) {            /* NoFSK */
                                confp->chan.mwisend_fsk = 0;
                        } else {                                        /* Default FSK */
                                confp->chan.mwisend_fsk = 1;
                        }
-                       if (strcasestr(v->value, "rpas")) {             /* Ring Pulse Alert Signal, normally followed by FSK */
+                       if (strcasestr(v->value, "rpas")) {             /* Ring Pulse Alert Signal, normally followed by FSK */
                                confp->chan.mwisend_rpas = 1;
                        } else {
                                confp->chan.mwisend_rpas = 0;
                        }
-                       if (strcasestr(v->value, "lrev")) {             /* Line Reversal */
+                       if (strcasestr(v->value, "lrev")) {             /* Line Reversal */
                                confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_LREV;
                        }
-                       if (strcasestr(v->value, "hvdc")) {             /* HV 90VDC */
+                       if (strcasestr(v->value, "hvdc")) {             /* HV 90VDC */
                                confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_HVDC;
                        }
-                       if ( (strcasestr(v->value, "neon")) || (strcasestr(v->value, "hvac")) ){        /* 90V DC pulses */
+                       if ( (strcasestr(v->value, "neon")) || (strcasestr(v->value, "hvac")) ) {       /* 90V DC pulses */
                                confp->chan.mwisend_setting.vmwi_type |= DAHDI_VMWI_HVAC;
                        }
 #endif
@@ -17744,8 +18212,11 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                        cur_ss7type = SS7_ITU;
                                } else if (!strcasecmp(v->value, "ansi")) {
                                        cur_ss7type = SS7_ANSI;
-                               } else
+                               } else {
                                        ast_log(LOG_WARNING, "'%s' is an unknown ss7 switch type at line %d.!\n", v->value, v->lineno);
+                               }
+                       } else if (!strcasecmp(v->name, "slc")) {
+                               cur_slc = atoi(v->value);
                        } else if (!strcasecmp(v->name, "linkset")) {
                                cur_linkset = atoi(v->value);
                        } else if (!strcasecmp(v->name, "pointcode")) {
@@ -17757,16 +18228,17 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                        } else if (!strcasecmp(v->name, "cicbeginswith")) {
                                cur_cicbeginswith = atoi(v->value);
                        } else if (!strcasecmp(v->name, "networkindicator")) {
-                               if (!strcasecmp(v->value, "national"))
+                               if (!strcasecmp(v->value, "national")) {
                                        cur_networkindicator = SS7_NI_NAT;
-                               else if (!strcasecmp(v->value, "national_spare"))
+                               } else if (!strcasecmp(v->value, "national_spare")) {
                                        cur_networkindicator = SS7_NI_NAT_SPARE;
-                               else if (!strcasecmp(v->value, "international"))
+                               } else if (!strcasecmp(v->value, "international")) {
                                        cur_networkindicator = SS7_NI_INT;
-                               else if (!strcasecmp(v->value, "international_spare"))
+                               } else if (!strcasecmp(v->value, "international_spare")) {
                                        cur_networkindicator = SS7_NI_INT_SPARE;
-                               else
+                               } else {
                                        cur_networkindicator = -1;
+                               }
                        } else if (!strcasecmp(v->name, "ss7_internationalprefix")) {
                                ast_copy_string(confp->ss7.ss7.internationalprefix, v->value, sizeof(confp->ss7.ss7.internationalprefix));
                        } else if (!strcasecmp(v->name, "ss7_nationalprefix")) {
@@ -17775,6 +18247,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                ast_copy_string(confp->ss7.ss7.subscriberprefix, v->value, sizeof(confp->ss7.ss7.subscriberprefix));
                        } else if (!strcasecmp(v->name, "ss7_unknownprefix")) {
                                ast_copy_string(confp->ss7.ss7.unknownprefix, v->value, sizeof(confp->ss7.ss7.unknownprefix));
+                       } else if (!strcasecmp(v->name, "ss7_networkroutedprefix")) {
+                               ast_copy_string(confp->ss7.ss7.networkroutedprefix, v->value, sizeof(confp->ss7.ss7.networkroutedprefix));
                        } else if (!strcasecmp(v->name, "ss7_called_nai")) {
                                if (!strcasecmp(v->value, "national")) {
                                        confp->ss7.ss7.called_nai = SS7_NAI_NATIONAL;
@@ -17807,9 +18281,9 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                int sigchan, res;
                                sigchan = atoi(v->value);
                                res = linkset_addsigchan(sigchan);
-                               if (res < 0)
+                               if (res < 0) {
                                        return -1;
-
+                               }
                        } else if (!strcasecmp(v->name, "ss7_explicitacm")) {
                                struct dahdi_ss7 *link;
                                link = ss7_resolve_linkset(cur_linkset);
@@ -17817,8 +18291,148 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                        ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
                                        return -1;
                                }
-                               if (ast_true(v->value))
+                               if (ast_true(v->value)) {
                                        link->ss7.flags |= LINKSET_FLAG_EXPLICITACM;
+                               } else {
+                                       link->ss7.flags &= ~LINKSET_FLAG_EXPLICITACM;
+                               }
+                       } else if (!strcasecmp(v->name, "ss7_autoacm")) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (ast_true(v->value)) {
+                                       link->ss7.flags |= LINKSET_FLAG_AUTOACM;
+                               } else {
+                                       link->ss7.flags &= ~LINKSET_FLAG_AUTOACM;
+                               }
+                       } else if (!strcasecmp(v->name, "ss7_initialhwblo")) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (ast_true(v->value)) {
+                                       link->ss7.flags |= LINKSET_FLAG_INITIALHWBLO;
+                               } else {
+                                       link->ss7.flags &= ~LINKSET_FLAG_INITIALHWBLO;
+                               }
+                       } else if (!strcasecmp(v->name, "ss7_use_echocontrol")) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (ast_true(v->value)) {
+                                       link->ss7.flags |= LINKSET_FLAG_USEECHOCONTROL;
+                               } else {
+                                       link->ss7.flags &= ~LINKSET_FLAG_USEECHOCONTROL;
+                               }
+                       } else if (!strcasecmp(v->name, "ss7_default_echocontrol")) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (ast_true(v->value)) {
+                                       link->ss7.flags |= LINKSET_FLAG_DEFAULTECHOCONTROL;
+                               } else {
+                                       link->ss7.flags &= ~LINKSET_FLAG_DEFAULTECHOCONTROL;
+                               }
+                       } else if (!strncasecmp(v->name, "isup_timer.", 11)) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (!link->ss7.ss7) {
+                                       ast_log(LOG_ERROR, "Please specify isup timers after sigchan!\n");
+                               } else if (!ss7_set_isup_timer(link->ss7.ss7, strstr(v->name, ".") + 1, atoi(v->value))) {
+                                       ast_log(LOG_ERROR, "Invalid isup timer %s\n", v->name);
+                               }
+                       } else if (!strncasecmp(v->name, "mtp3_timer.", 11)) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (!link->ss7.ss7) {
+                                       ast_log(LOG_ERROR, "Please specify mtp3 timers after sigchan!\n");
+                               } else if (!ss7_set_mtp3_timer(link->ss7.ss7, strstr(v->name, ".") + 1, atoi(v->value))) {
+                                       ast_log(LOG_ERROR, "Invalid mtp3 timer %s\n", v->name);
+                               }
+                       } else if (!strcasecmp(v->name, "inr_if_no_calling")) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (!link->ss7.ss7) {
+                                       ast_log(LOG_ERROR, "Please specify inr_if_no_calling after sigchan!\n");
+                               } else if (ast_true(v->value)) {
+                                       ss7_set_flags(link->ss7.ss7, SS7_INR_IF_NO_CALLING);
+                               } else {
+                                       ss7_clear_flags(link->ss7.ss7, SS7_INR_IF_NO_CALLING);
+                               }
+                       } else if (!strcasecmp(v->name, "non_isdn_access")) {
+                               struct dahdi_ss7 *link;
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (!link->ss7.ss7) {
+                                       ast_log(LOG_ERROR, "Please specify non_isdn_access after sigchan!\n");
+                               } else if (ast_true(v->value)) {
+                                       ss7_clear_flags(link->ss7.ss7, SS7_ISDN_ACCESS_INDICATOR);
+                               } else {
+                                       ss7_set_flags(link->ss7.ss7, SS7_ISDN_ACCESS_INDICATOR);
+                               }
+                       } else if (!strcasecmp(v->name, "sls_shift")) {
+                               struct dahdi_ss7 *link;
+                               int sls_shift = atoi(v->value);
+
+                               if (sls_shift < 0 || sls_shift > 7) {
+                                       ast_log(LOG_ERROR, "Invalid sls_shift value.  Must be between 0 and 7\n");
+                                       return -1;
+                               }
+
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (!link->ss7.ss7) {
+                                       ast_log(LOG_ERROR, "Please specify sls_shift after sigchan!\n");
+                               } else {
+                                       ss7_set_sls_shift(link->ss7.ss7, sls_shift);
+                               }
+                       } else if (!strcasecmp(v->name, "cause_location")) {
+                               struct dahdi_ss7 *link;
+                               int cause_location = atoi(v->value);
+
+                               if (cause_location < 0 || cause_location > 15) {
+                                       ast_log(LOG_ERROR, "Invalid cause_location value.  Must be between 0 and 15\n");
+                                       return -1;
+                               }
+                               link = ss7_resolve_linkset(cur_linkset);
+                               if (!link) {
+                                       ast_log(LOG_ERROR, "Invalid linkset number.  Must be between 1 and %d\n", NUM_SPANS + 1);
+                                       return -1;
+                               }
+                               if (!link->ss7.ss7) {
+                                       ast_log(LOG_ERROR, "Please specify cause_location after sigchan!\n");
+                               } else {
+                                       ss7_set_cause_location(link->ss7.ss7, cause_location);
+                               }
 #endif /* defined(HAVE_SS7) */
 #ifdef HAVE_OPENR2
                        } else if (!strcasecmp(v->name, "mfcr2_advanced_protocol_file")) {
@@ -17881,12 +18495,12 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                confp->mfcr2.call_files = ast_true(v->value) ? 1 : 0;
                        } else if (!strcasecmp(v->name, "mfcr2_max_ani")) {
                                confp->mfcr2.max_ani = atoi(v->value);
-                               if (confp->mfcr2.max_ani >= AST_MAX_EXTENSION){
+                               if (confp->mfcr2.max_ani >= AST_MAX_EXTENSION) {
                                        confp->mfcr2.max_ani = AST_MAX_EXTENSION - 1;
                                }
                        } else if (!strcasecmp(v->name, "mfcr2_max_dnis")) {
                                confp->mfcr2.max_dnis = atoi(v->value);
-                               if (confp->mfcr2.max_dnis >= AST_MAX_EXTENSION){
+                               if (confp->mfcr2.max_dnis >= AST_MAX_EXTENSION) {
                                        confp->mfcr2.max_dnis = AST_MAX_EXTENSION - 1;
                                }
                        } else if (!strcasecmp(v->name, "mfcr2_category")) {
@@ -18677,6 +19291,9 @@ static int load_module(void)
        }
        ss7_set_error(dahdi_ss7_error);
        ss7_set_message(dahdi_ss7_message);
+       ss7_set_hangup(sig_ss7_cb_hangup);
+       ss7_set_notinservice(sig_ss7_cb_notinservice);
+       ss7_set_call_null(sig_ss7_cb_call_null);
 #endif /* defined(HAVE_SS7) */
        res = setup_dahdi(0);
        /* Make sure we can register our DAHDI channel type */
index d302bd7..e4a689a 100644 (file)
@@ -389,19 +389,27 @@ struct dahdi_pvt {
        unsigned int mwisendactive:1;
        /*!
         * \brief TRUE if channel is out of reset and ready
-        * \note Set but not used.
+        * \note Used by SS7.  Otherwise set but not used.
         */
        unsigned int inservice:1;
        /*!
-        * \brief TRUE if the channel is locally blocked.
+        * \brief Bitmask for the channel being locally blocked.
         * \note Applies to SS7 and MFCR2 channels.
+        * \note For MFCR2 only the first bit is used - TRUE if blocked
+        * \note For SS7 two bits are used
+        * \note Bit 0 - TRUE if maintenance blocked
+        * \note Bit 1 - TRUE if hardware blocked
         */
-       unsigned int locallyblocked:1;
+       unsigned int locallyblocked:2;
        /*!
-        * \brief TRUE if the channel is remotely blocked.
+        * \brief Bitmask for the channel being remotely blocked. 1 maintenance, 2 blocked in hardware.
         * \note Applies to SS7 and MFCR2 channels.
+        * \note For MFCR2 only the first bit is used - TRUE if blocked
+        * \note For SS7 two bits are used
+        * \note Bit 0 - TRUE if maintenance blocked
+        * \note Bit 1 - TRUE if hardware blocked
         */
-       unsigned int remotelyblocked:1;
+       unsigned int remotelyblocked:2;
        /*!
         * \brief TRUE if the channel alarms will be managed also as Span ones
         * \note Applies to all channels
index 2e39189..a7df9e9 100644 (file)
 #include "asterisk/causes.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/cli.h"
+#include "asterisk/callerid.h"
 #include "asterisk/transcap.h"
 #include "asterisk/stasis_channels.h"
 
 #include "sig_ss7.h"
-#if defined(LIBSS7_ABI_COMPATIBILITY)
+#if !defined(LIBSS7_ABI_COMPATIBILITY)
+#error "Upgrade your libss7"
+#elif LIBSS7_ABI_COMPATIBILITY != 2
 #error "Your installed libss7 is not compatible"
 #endif
 
@@ -68,8 +71,6 @@ static const char *sig_ss7_call_level2str(enum sig_ss7_call_level level)
                return "Alerting";
        case SIG_SS7_CALL_LEVEL_CONNECT:
                return "Connect";
-       case SIG_SS7_CALL_LEVEL_GLARE:
-               return "Glare";
        }
        return "Unknown";
 }
@@ -132,24 +133,35 @@ static void sig_ss7_set_outgoing(struct sig_ss7_chan *p, int 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)
+static void sig_ss7_set_locallyblocked(struct sig_ss7_chan *p, int is_blocked, int type)
 {
-       p->locallyblocked = is_blocked;
+       if (is_blocked) {
+               p->locallyblocked |= type;
+       } else {
+               p->locallyblocked &= ~type;
+       }
+
        if (sig_ss7_callbacks.set_locallyblocked) {
-               sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, is_blocked);
+               sig_ss7_callbacks.set_locallyblocked(p->chan_pvt, p->locallyblocked);
        }
 }
 
-static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked)
+static void sig_ss7_set_remotelyblocked(struct sig_ss7_chan *p, int is_blocked, int type)
 {
-       p->remotelyblocked = is_blocked;
+       if (is_blocked) {
+               p->remotelyblocked |= type;
+       } else {
+               p->remotelyblocked &= ~type;
+       }
+
        if (sig_ss7_callbacks.set_remotelyblocked) {
-               sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, is_blocked);
+               sig_ss7_callbacks.set_remotelyblocked(p->chan_pvt, p->remotelyblocked);
        }
 }
 
@@ -277,7 +289,13 @@ static struct ast_channel *sig_ss7_new_ast_channel(struct sig_ss7_chan *p, int s
        if (!p->owner) {
                p->owner = ast;
        }
-       p->alreadyhungup = 0;
+
+       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));
@@ -295,6 +313,14 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w
        }
 }
 
+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.
@@ -305,7 +331,7 @@ static void sig_ss7_handle_link_exception(struct sig_ss7_linkset *linkset, int w
  */
 static int sig_ss7_is_chan_available(struct sig_ss7_chan *pvt)
 {
-       if (!pvt->inalarm && !pvt->owner && !pvt->ss7call
+       if (pvt->inservice && !pvt->inalarm && !pvt->owner && !pvt->ss7call
                && pvt->call_level == SIG_SS7_CALL_LEVEL_IDLE
                && !pvt->locallyblocked && !pvt->remotelyblocked) {
                return 1;
@@ -398,7 +424,7 @@ static void sig_ss7_queue_control(struct sig_ss7_linkset *ss7, int chanpos, int
 /*!
  * \internal
  * \brief Queue a PVT_CAUSE_CODE frame onto the owner channel.
- * \since 11
+ * \since 11.0
  *
  * \param owner Owner channel of the pvt.
  * \param cause String describing the cause to be placed into the frame.
@@ -425,7 +451,6 @@ static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *caus
 
 
 /*!
- * \internal
  * \brief Find the channel position by CIC/DPC.
  *
  * \param linkset SS7 linkset control structure.
@@ -435,7 +460,7 @@ static void ss7_queue_pvt_cause_data(struct ast_channel *owner, const char *caus
  * \retval chanpos on success.
  * \retval -1 on error.
  */
-static int ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
+int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
 {
        int i;
        int winner = -1;
@@ -464,45 +489,206 @@ static int ss7_find_cic_gripe(struct sig_ss7_linkset *linkset, int cic, unsigned
 {
        int chanpos;
 
-       chanpos = ss7_find_cic(linkset, cic, dpc);
+       chanpos = sig_ss7_find_cic(linkset, cic, dpc);
        if (chanpos < 0) {
-               ast_log(LOG_WARNING, "Linkset %d: SS7 %s requested unconfigured CIC/DPC %d/%d.\n",
+               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 void ss7_handle_cqm(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc)
+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, offset;
+       int i;
+       int offset;
+       int chanpos;
 
+       memset(status, 0, sizeof(status));
        for (i = 0; i < linkset->numchans; i++) {
-               if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) {
+               if (ss7_match_range(linkset->pvts[i], e->cqm.startcic, e->cqm.endcic, e->cqm.opc)) {
                        p = linkset->pvts[i];
-                       offset = p->cic - startcic;
+                       sig_ss7_lock_private(p);
+                       offset = p->cic - e->cqm.startcic;
                        status[offset] = 0;
-                       if (p->locallyblocked)
+                       if (p->locallyblocked) {
                                status[offset] |= (1 << 0) | (1 << 4);
-                       if (p->remotelyblocked)
+                       }
+                       if (p->remotelyblocked) {
                                status[offset] |= (1 << 1) | (1 << 5);
+                       }
                        if (p->ss7call) {
-                               if (p->outgoing)
+                               if (p->outgoing) {
                                        status[offset] |= (1 << 3);
-                               else
+                               } else {
                                        status[offset] |= (1 << 2);
-                       } else
+                               }
+                       } else {
                                status[offset] |= 0x3 << 2;
+                       }
+                       sig_ss7_unlock_private(p);
                }
        }
 
-       if (p)
-               isup_cqr(linkset->ss7, startcic, endcic, dpc, status);
-       else
+       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)
@@ -510,7 +696,7 @@ static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic
        int i;
 
        for (i = 0; i < linkset->numchans; i++) {
-               if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) {
+               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) {
@@ -522,67 +708,274 @@ static inline void ss7_hangup_cics(struct sig_ss7_linkset *linkset, int startcic
        }
 }
 
-static inline void ss7_block_cics(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc, unsigned char state[], int block)
+/*!
+ * \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;
 
-       /* XXX the use of state here seems questionable about matching up with the linkset channels */
        for (i = 0; i < linkset->numchans; i++) {
-               if (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic)))) {
+               if (ss7_match_range(linkset->pvts[i], startcic, endcic, dpc)) {
+                       sig_ss7_lock_private(linkset->pvts[i]);
                        if (state) {
-                               if (state[i])
-                                       sig_ss7_set_remotelyblocked(linkset->pvts[i], block);
-                       } else
-                               sig_ss7_set_remotelyblocked(linkset->pvts[i], block);
+                               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 (linkset->pvts[i] && (linkset->pvts[i]->dpc == dpc && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic))))
+               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;
+               }
        }
+       return 1;
 }
 
+/*
+ * 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 = -1, endcic, dpc;
+       int i, startcic, endcic, dpc;
+       struct sig_ss7_chan *p;
 
-       if (linkset->numchans <= 0)
+       if (linkset->numchans <= 0) {
                return;
+       }
 
        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 < 31)) {
+               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_verbose("Resetting CICs %d to %d\n", startcic, endcic);
-                       isup_grs(linkset->ss7, startcic, endcic, dpc);
+                       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]);
                }
        }
 }
 
-/* This function is assumed to be called with the private channel lock and linkset lock held */
+/*!
+ * \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];
+       char *strp;
        struct ast_callid *callid = NULL;
        int callid_created = ast_callid_threadstorage_auto(&callid);
 
@@ -600,6 +993,8 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
                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
@@ -614,7 +1009,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
                sig_ss7_lock_private(p);
                isup_rel(linkset->ss7, p->ss7call, AST_CAUSE_SWITCH_CONGESTION);
                p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
-               p->alreadyhungup = 1;
                ast_callid_threadstorage_auto_clean(callid, callid_created);
                return;
        }
@@ -623,8 +1017,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
        ast_channel_lock(c);
        sig_ss7_lock_private(p);
 
-       sig_ss7_set_echocanceller(p, 1);
-
        ast_channel_stage_snapshot(c);
 
        /*
@@ -658,11 +1050,6 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
                /* Clear this after we set it */
                p->gen_dig_number[0] = 0;
        }
-       if (!ast_strlen_zero(p->orig_called_num)) {
-               pbx_builtin_setvar_helper(c, "SS7_ORIG_CALLED_NUM", p->orig_called_num);
-               /* Clear this after we set it */
-               p->orig_called_num[0] = 0;
-       }
 
        snprintf(tmp, sizeof(tmp), "%d", p->gen_dig_type);
        pbx_builtin_setvar_helper(c, "SS7_GENERIC_DIGTYPE", tmp);
@@ -695,28 +1082,187 @@ static void ss7_start_call(struct sig_ss7_chan *p, struct sig_ss7_linkset *links
        /* Clear this after we set it */
        p->calling_party_cat = 0;
 
-       if (!ast_strlen_zero(p->redirecting_num)) {
-               pbx_builtin_setvar_helper(c, "SS7_REDIRECTING_NUMBER", p->redirecting_num);
-               /* Clear this after we set it */
-               p->redirecting_num[0] = 0;
-       }
-       if (!ast_strlen_zero(p->generic_name)) {
-               pbx_builtin_setvar_helper(c, "SS7_GENERIC_NAME", p->generic_name);
+       if (p->redirect_counter) {
+               struct ast_party_redirecting redirecting;
+
+               switch (p->redirect_info_ind) {
+               case 0:
+                       strp = "NO_REDIRECTION";
+                       break;
+               case 1:
+                       strp = "CALL_REROUTED_PRES_ALLOWED";
+                       break;
+               case 2:
+                       strp = "CALL_REROUTED_INFO_RESTRICTED";
+                       break;
+               case 3:
+                       strp = "CALL_DIVERTED_PRES_ALLOWED";
+                       break;
+               case 4:
+                       strp = "CALL_DIVERTED_INFO_RESTRICTED";
+                       break;
+               case 5:
+                       strp = "CALL_REROUTED_PRES_RESTRICTED";
+                       break;
+               case 6:
+                       strp = "CALL_DIVERTED_PRES_RESTRICTED";
+                       break;
+               case 7:
+                       strp = "SPARE";
+                       break;
+               default:
+                       strp = "NO_REDIRECTION";
+                       break;
+               }
+               pbx_builtin_setvar_helper(c, "SS7_REDIRECT_INFO_IND", strp);
                /* Clear this after we set it */
-               p->generic_name[0] = 0;
-       }
+               p->redirect_info_ind = 0;
 
-       ast_channel_stage_snapshot_done(c);
+               ast_party_redirecting_init(&redirecting);
 
-       sig_ss7_unlock_private(p);
-       ast_channel_unlock(c);
+               if (p->redirect_info_counter) {
+                       redirecting.count = p->redirect_info_counter;
+                       if (p->redirect_info_counter != p->redirect_counter) {
+                               if (p->redirect_info_counter < p->redirect_counter) {
+                                       redirecting.count = p->redirect_counter;
+                               }
+                               ast_log(LOG_WARNING, "Redirect counters differ: %u while info says %u - using %u\n",
+                                       p->redirect_counter, p->redirect_info_counter, redirecting.count);
+                       }
+                       /* Clear this after we set it */
+                       p->redirect_info_counter = 0;
+                       p->redirect_counter = 0;
+               }
 
-       if (ast_pbx_start(c)) {
-               ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", ast_channel_name(c), p->cic);
-               ast_hangup(c);
-       } else {
-               ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
-       }
+               if (p->redirect_counter) {
+                       redirecting.count = p->redirect_counter;
+                       /* Clear this after we set it */
+                       p->redirect_counter = 0;
+               }
+
+               switch (p->redirect_info_orig_reas) {
+               case SS7_REDIRECTING_REASON_UNKNOWN:
+                       redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                       break;
+               case SS7_REDIRECTING_REASON_USER_BUSY:
+                       redirecting.orig_reason.code = AST_REDIRECTING_REASON_USER_BUSY;
+                       break;
+               case SS7_REDIRECTING_REASON_NO_ANSWER:
+                       redirecting.orig_reason.code = AST_REDIRECTING_REASON_NO_ANSWER;
+                       break;
+               case SS7_REDIRECTING_REASON_UNCONDITIONAL:
+                       redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL;
+                       break;
+               default:
+                       redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                       break;
+               }
+
+               switch (p->redirect_info_reas) {
+               case SS7_REDIRECTING_REASON_UNKNOWN:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                       break;
+               case SS7_REDIRECTING_REASON_USER_BUSY:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_USER_BUSY;
+                       if (!p->redirect_info_orig_reas && redirecting.count == 1) {
+                               redirecting.orig_reason.code = AST_REDIRECTING_REASON_USER_BUSY;
+                       }
+                       break;
+               case SS7_REDIRECTING_REASON_NO_ANSWER:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_NO_ANSWER;
+                       if (!p->redirect_info_orig_reas && redirecting.count == 1) {
+                               redirecting.orig_reason.code = AST_REDIRECTING_REASON_NO_ANSWER;
+                       }
+                       break;
+               case SS7_REDIRECTING_REASON_UNCONDITIONAL:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL;
+                       if (!p->redirect_info_orig_reas && redirecting.count == 1) {
+                               redirecting.orig_reason.code = AST_REDIRECTING_REASON_UNCONDITIONAL;
+                       }
+                       break;
+               case SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING:
+               case SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_DEFLECTION;
+                       break;
+               case SS7_REDIRECTING_REASON_UNAVAILABLE:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_UNAVAILABLE;
+                       break;
+               default:
+                       redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                       break;
+               }
+               /* Clear this after we set it */
+               p->redirect_info_orig_reas = 0;
+               p->redirect_info_reas = 0;
+
+               if (!ast_strlen_zero(p->redirecting_num)) {
+                       redirecting.from.number.str = ast_strdup(p->redirecting_num);
+                       redirecting.from.number.presentation = p->redirecting_presentation;
+                       redirecting.from.number.valid = 1;
+                       /* Clear this after we set it */
+                       p->redirecting_num[0] = 0;
+               }
+
+               if (!ast_strlen_zero(p->generic_name)) {
+                       redirecting.from.name.str = ast_strdup(p->generic_name);
+                       redirecting.from.name.presentation = p->redirecting_presentation;
+                       redirecting.from.name.valid = 1;
+                       /* Clear this after we set it */
+                       p->generic_name[0] = 0;
+               }
+
+               if (!ast_strlen_zero(p->orig_called_num)) {
+                       redirecting.orig.number.str = ast_strdup(p->orig_called_num);
+                       redirecting.orig.number.presentation = p->orig_called_presentation;
+                       redirecting.orig.number.valid = 1;
+                       /* Clear this after we set it */
+                       p->orig_called_num[0] = 0;
+               } else if (redirecting.count == 1) {
+                       ast_party_id_copy(&redirecting.orig, &redirecting.from);
+               }
+
+               ast_channel_update_redirecting(c, &redirecting, NULL);
+               ast_party_redirecting_free(&redirecting);
+       }
+
+       if (p->cug_indicator != ISUP_CUG_NON) {
+               sprintf(tmp, "%d", p->cug_interlock_code);
+               pbx_builtin_setvar_helper(c, "SS7_CUG_INTERLOCK_CODE", tmp);
+
+               switch (p->cug_indicator) {
+               case ISUP_CUG_NON:
+                       strp = "NON_CUG";
+                       break;
+               case ISUP_CUG_OUTGOING_ALLOWED:
+                       strp = "OUTGOING_ALLOWED";
+                       break;
+               case ISUP_CUG_OUTGOING_NOT_ALLOWED:
+                       strp = "OUTGOING_NOT_ALLOWED";
+                       break;
+               default:
+                       strp = "SPARE";
+                       break;
+               }
+               pbx_builtin_setvar_helper(c, "SS7_CUG_INDICATOR", strp);
+
+               if (!ast_strlen_zero(p->cug_interlock_ni)) {
+                       pbx_builtin_setvar_helper(c, "SS7_CUG_INTERLOCK_NI", p->cug_interlock_ni);
+               }
+
+               p->cug_indicator = ISUP_CUG_NON;
+       }
+
+       ast_channel_stage_snapshot_done(c);
+
+       sig_ss7_unlock_private(p);
+       ast_channel_unlock(c);
+
+       if (ast_pbx_start(c)) {
+               ast_log(LOG_WARNING, "Unable to start PBX on %s (CIC %d)\n", ast_channel_name(c), p->cic);
+               ast_hangup(c);
+       } else {
+               ast_verb(3, "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
+       }
 
        /* Must return with linkset and private lock. */
        ast_mutex_lock(&linkset->lock);
@@ -745,6 +1291,9 @@ static void ss7_apply_plan_to_number(char *buf, size_t size, const struct sig_ss
        case SS7_NAI_UNKNOWN:
                snprintf(buf, size, "%s%s", ss7->unknownprefix, number);
                break;
+       case SS7_NAI_NETWORKROUTED:
+               snprintf(buf, size, "%s%s", ss7->networkroutedprefix, number);
+               break;
        default:
                snprintf(buf, size, "%s", number);
                break;
@@ -786,6 +1335,50 @@ static struct ast_callid *func_ss7_linkset_callid(struct sig_ss7_linkset *linkse
        return callid;
 }
 
+/*!
+ * \internal
+ * \brief Proceed with the call based on the extension matching status
+ * is matching in the dialplan.
+ * \since 11.0
+ *
+ * \param linkset ss7 span control structure.
+ * \param p Signaling private structure pointer.
+ * \param e Event causing the match.
+ *
+ * \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_match_extension(struct sig_ss7_linkset *linkset, struct sig_ss7_chan *p, ss7_event *e)
+{
+       ast_verb(3, "SS7 exten: %s complete: %d\n", p->exten, p->called_complete);
+
+       if (!p->called_complete
+               && linkset->type == SS7_ITU /* ANSI does not support overlap dialing. */
+               && ast_matchmore_extension(NULL, p->context, p->exten, 1, p->cid_num)
+               && !isup_start_digittimeout(linkset->ss7, p->ss7call)) {
+               /* Wait for more digits. */
+               return;
+       }
+       if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+               /* DNID is complete */
+               p->called_complete = 1;
+               sig_ss7_set_dnid(p, p->exten);
+
+               /* If COT successful start call! */
+               if ((e->e == ISUP_EVENT_IAM)
+                       ? !(e->iam.cot_check_required || e->iam.cot_performed_on_previous_cic)
+                       : (!(e->sam.cot_check_required || e->sam.cot_performed_on_previous_cic) || e->sam.cot_check_passed)) {
+                       ss7_start_call(p, linkset);
+               }
+               return;
+       }
+
+       ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten);
+       isup_rel(linkset->ss7, (e->e == ISUP_EVENT_IAM) ? e->iam.call : e->sam.call, AST_CAUSE_UNALLOCATED);
+}
+
 /* This is a thread per linkset that handles all received events from libss7. */
 void *ss7_linkset(void *data)
 {
@@ -796,6 +1389,7 @@ void *ss7_linkset(void *data)
        ss7_event *e = NULL;
        struct sig_ss7_chan *p;
        struct pollfd pollers[SIG_SS7_NUM_DCHANS];
+       unsigned char mb_state[255];
        int nextms;
 
 #define SS7_MAX_POLL   60000   /* Maximum poll time in ms. */
@@ -843,16 +1437,13 @@ void *ss7_linkset(void *data)
                pthread_testcancel();
                pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
 
+               ast_mutex_lock(&linkset->lock);
                if ((res < 0) && (errno != EINTR)) {
                        ast_log(LOG_ERROR, "poll(%s)\n", strerror(errno));
                } else if (!res) {
-                       ast_mutex_lock(&linkset->lock);
                        ss7_schedule_run(ss7);
-                       ast_mutex_unlock(&linkset->lock);
-                       continue;
                }
 
-               ast_mutex_lock(&linkset->lock);
                for (i = 0; i < linkset->numsigchans; i++) {
                        if (pollers[i].revents & POLLPRI) {
                                sig_ss7_handle_link_exception(linkset, i);
@@ -881,23 +1472,26 @@ void *ss7_linkset(void *data)
                        switch (e->e) {
                        case SS7_EVENT_UP:
                                if (linkset->state != LINKSET_STATE_UP) {
-                                       ast_verbose("--- SS7 Up ---\n");
+                                       ast_verb(1, "--- SS7 Up ---\n");
                                        ss7_reset_linkset(linkset);
                                }
                                linkset->state = LINKSET_STATE_UP;
                                break;
                        case SS7_EVENT_DOWN:
-                               ast_verbose("--- SS7 Down ---\n");
+                               ast_verb(1, "--- SS7 Down ---\n");
                                linkset->state = LINKSET_STATE_DOWN;
                                for (i = 0; i < linkset->numchans; i++) {
                                        p = linkset->pvts[i];
                                        if (p) {
-                                               sig_ss7_set_alarm(p, 1);
+                                               sig_ss7_set_inservice(p, 0);
+                                               if (linkset->flags & LINKSET_FLAG_INITIALHWBLO) {
+                                                       sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_HARDWARE);
+                                               }
                                        }
                                }
                                break;
                        case MTP2_LINK_UP:
-                               ast_verbose("MTP2 link up (SLC %d)\n", e->gen.data);
+                               ast_verb(1, "MTP2 link up (SLC %d)\n", e->gen.data);
                                break;
                        case MTP2_LINK_DOWN:
                                ast_log(LOG_WARNING, "MTP2 link down (SLC %d)\n", e->gen.data);
@@ -905,6 +1499,7 @@ void *ss7_linkset(void *data)
                        case ISUP_EVENT_CPG:
                                chanpos = ss7_find_cic_gripe(linkset, e->cpg.cic, e->cpg.opc, "CPG");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->cpg.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
@@ -919,6 +1514,21 @@ void *ss7_linkset(void *data)
                                        sig_ss7_lock_owner(linkset, chanpos);
                                        if (p->owner) {
                                                ast_setstate(p->owner, AST_STATE_RINGING);
+                                               if (!ast_strlen_zero(e->cpg.connected_num)) {
+                                                       struct ast_party_connected_line ast_connected;
+                                                       char connected_num[AST_MAX_EXTENSION];
+
+                                                       ast_party_connected_line_init(&ast_connected);
+                                                       ast_connected.id.number.presentation =
+                                                               ss7_pres_scr2cid_pres(e->cpg.connected_presentation_ind,
+                                                               e->cpg.connected_screening_ind);
+                                                       ss7_apply_plan_to_number(connected_num, sizeof(connected_num),
+                                                               linkset, e->cpg.connected_num, e->cpg.connected_nai);
+                                                       ast_connected.id.number.str = ast_strdup(connected_num);
+                                                       ast_connected.id.number.valid = 1;
+                                                       ast_channel_queue_connected_line_update(p->owner, &ast_connected, NULL);
+                                                       ast_party_connected_line_free(&ast_connected);
+                                               }
                                                ast_channel_unlock(p->owner);
                                        }
                                        sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING);
@@ -941,95 +1551,201 @@ void *ss7_linkset(void *data)
                                sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_RSC:
-                               ast_verbose("Resetting CIC %d\n", e->rsc.cic);
+                               ast_verb(1, "Resetting CIC %d\n", e->rsc.cic);
                                chanpos = ss7_find_cic_gripe(linkset, e->rsc.cic, e->rsc.opc, "RSC");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->rsc.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
                                sig_ss7_lock_private(p);
+                               p->ss7call = e->rsc.call;
                                callid = func_ss7_linkset_callid(linkset, chanpos);
                                sig_ss7_set_inservice(p, 1);
-                               sig_ss7_set_remotelyblocked(p, 0);
+                               sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+
+                               if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+                                       isup_blo(ss7, e->rsc.call);
+                               } else if (p->locallyblocked & SS7_BLOCKED_HARDWARE) {
+                                       sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_HARDWARE);
+                               }
+
                                isup_set_call_dpc(e->rsc.call, p->dpc);
                                sig_ss7_lock_owner(linkset, chanpos);
-                               p->ss7call = NULL;
                                if (p->owner) {
+                                       p->do_hangup = SS7_HANGUP_SEND_RLC;
+                                       if (!(e->rsc.got_sent_msg & ISUP_SENT_IAM)) {
+                                               /* Q.784 6.2.3 */
+                                               ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
+                                       } else {
+                                               ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN);
+                                       }
+
                                        ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_RSC", AST_CAUSE_INTERWORKING);
+
                                        ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
                                        ast_channel_unlock(p->owner);
+                               } else {
+                                       isup_rlc(ss7, e->rsc.call);
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->rsc.call);
                                }
+                               /* End the loopback if we have one */
+                               sig_ss7_loopback(p, 0);
+
                                sig_ss7_unlock_private(p);
-                               isup_rlc(ss7, e->rsc.call);
                                break;
                        case ISUP_EVENT_GRS:
-                               ast_debug(1, "Got Reset for CICs %d to %d: Acknowledging\n", e->grs.startcic, e->grs.endcic);
-                               chanpos = ss7_find_cic_gripe(linkset, e->grs.startcic, e->grs.opc, "GRS");
-                               if (chanpos < 0) {
+                               if (!sig_ss7_find_cic_range(linkset, e->grs.startcic, e->grs.endcic,
+                                       e->grs.opc)) {
+                                       ast_log(LOG_WARNING, "GRS on unconfigured range CIC %d - %d PC %d\n",
+                                               e->grs.startcic, e->grs.endcic, e->grs.opc);
+                                       chanpos = sig_ss7_find_cic(linkset, e->grs.startcic, e->grs.opc);
+                                       if (chanpos < 0) {
+                                               isup_free_call(ss7, e->grs.call);
+                                               break;
+                                       }
+                                       p = linkset->pvts[chanpos];
+                                       sig_ss7_lock_private(p);
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->grs.call);
+                                       sig_ss7_unlock_private(p);
                                        break;
                                }
-                               p = linkset->pvts[chanpos];
-                               isup_gra(ss7, e->grs.startcic, e->grs.endcic, e->grs.opc);
-                               ss7_block_cics(linkset, e->grs.startcic, e->grs.endcic, e->grs.opc, NULL, 0);
-                               ss7_hangup_cics(linkset, e->grs.startcic, e->grs.endcic, e->grs.opc);
+
+                               /* Leave startcic last to collect all cics mb_state */
+                               for (i = e->grs.endcic - e->grs.startcic; 0 <= i; --i) {
+                                       /*
+                                        * We are guaranteed to find chanpos because
+                                        * sig_ss7_find_cic_range() includes it.
+                                        */
+                                       chanpos = sig_ss7_find_cic(linkset, e->grs.startcic + i, e->grs.opc);
+                                       p = linkset->pvts[chanpos];
+                                       sig_ss7_lock_private(p);
+
+                                       if (p->locallyblocked & SS7_BLOCKED_MAINTENANCE) {
+                                               mb_state[i] = 1;
+                                       } else {
+                                               mb_state[i] = 0;
+                                               sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_HARDWARE);
+                                       }
+
+                                       sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+
+                                       if (!i) {
+                                               p->ss7call = e->grs.call;
+                                               isup_gra(ss7, p->ss7call, e->grs.endcic, mb_state);
+                                       }
+
+                                       sig_ss7_lock_owner(linkset, chanpos);
+                                       if (p->owner) {
+                                               ast_channel_softhangup_internal_flag_add(p->owner, AST_SOFTHANGUP_DEV);
+                                               if (ast_channel_state(p->owner) == AST_STATE_DIALING
+                                                       && linkset->pvts[i]->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
+                                                       ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN);
+                                               } else {
+                                                       ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CLEARING);
+                                               }
+                                               p->do_hangup = SS7_HANGUP_FREE_CALL;
+                                               ast_channel_unlock(p->owner);
+                                       } else if (!i) {
+                                               p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                                       } else if (p->ss7call) {
+                                               /* clear any other session */
+                                               isup_free_call(ss7, p->ss7call);
+                                               p->ss7call = NULL;
+                                       }
+                                       sig_ss7_set_inservice(p, 1);
+                                       sig_ss7_unlock_private(p);
+                               }
                                break;
                        case ISUP_EVENT_CQM:
-                               ast_debug(1, "Got Circuit group query message from CICs %d to %d\n", e->cqm.startcic, e->cqm.endcic);
-                               ss7_handle_cqm(linkset, e->cqm.startcic, e->cqm.endcic, e->cqm.opc);
+                               ast_debug(1, "Got Circuit group query message from CICs %d to %d\n",
+                                                       e->cqm.startcic, e->cqm.endcic);
+                               ss7_handle_cqm(linkset, e);
                                break;
                        case ISUP_EVENT_GRA:
-                               ast_verbose("Got reset acknowledgement from CIC %d to %d.\n", e->gra.startcic, e->gra.endcic);
+                               if (!sig_ss7_find_cic_range(linkset, e->gra.startcic,
+                                                       e->gra.endcic, e->gra.opc)) {   /* Never will be true */
+                                       ast_log(LOG_WARNING, "GRA on unconfigured range CIC %d - %d PC %d\n",
+                                                       e->gra.startcic, e->gra.endcic, e->gra.opc);
+                                       isup_free_call(ss7, e->gra.call);
+                                       break;
+                               }
+                               ast_verb(1, "Got reset acknowledgement from CIC %d to %d DPC: %d\n",
+                                       e->gra.startcic, e->gra.endcic, e->gra.opc);
+                               ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc,
+                                       e->gra.status, 1, 1, SS7_BLOCKED_MAINTENANCE);
                                ss7_inservice(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc);
-                               ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.opc, e->gra.status, 1);
+
+                               chanpos = sig_ss7_find_cic(linkset, e->gra.startcic, e->gra.opc);
+                               if (chanpos < 0) {
+                                       isup_free_call(ss7, e->gra.call);
+                                       break;
+                               }
+
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+
+                               /* we may send a CBD with GRS! */
+                               p->ss7call = isup_free_call_if_clear(ss7, e->gra.call);
+                               sig_ss7_unlock_private(p);
                                break;
-                       case ISUP_EVENT_IAM:
-                               ast_debug(1, "Got IAM for CIC %d and called number %s, calling number %s\n", e->iam.cic, e->iam.called_party_num, e->iam.calling_party_num);
-                               chanpos = ss7_find_cic_gripe(linkset, e->iam.cic, e->iam.opc, "IAM");
+                       case ISUP_EVENT_SAM:
+                               chanpos = ss7_find_cic_gripe(linkset, e->sam.cic, e->sam.opc, "SAM");
                                if (chanpos < 0) {
-                                       isup_rel(ss7, e->iam.call, -1);
+                                       isup_free_call(ss7, e->sam.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
                                sig_ss7_lock_private(p);
                                sig_ss7_lock_owner(linkset, chanpos);
-                               if (p->call_level != SIG_SS7_CALL_LEVEL_IDLE) {
-                                       /*
-                                        * Detected glare/dual-seizure
-                                        *
-                                        * Always abort both calls since we can't implement the dual
-                                        * seizure procedures due to our channel assignment architecture
-                                        * and the fact that we cannot tell libss7 to discard its call
-                                        * structure to ignore the incoming IAM.
-                                        */
-                                       ast_debug(1,
-                                               "Linkset %d: SS7 IAM glare on CIC/DPC %d/%d.  Dropping both calls.\n",
-                                               linkset->span, e->iam.cic, e->iam.opc);
-                                       if (p->call_level == SIG_SS7_CALL_LEVEL_ALLOCATED) {
-                                               /*
-                                                * We have not sent our IAM yet and we never will at this point.
-                                                */
-                                               p->alreadyhungup = 1;
-                                               isup_rel(ss7, e->iam.call, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-                                       }
-                                       p->call_level = SIG_SS7_CALL_LEVEL_GLARE;
-                                       if (p->owner) {
-                                               ss7_queue_pvt_cause_data(p->owner, "SS7 ISUP_EVENT_IAM (glare)", AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-                                               ast_channel_hangupcause_set(p->owner, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
-                                               ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
-                                               ast_channel_unlock(p->owner);
-                                       }
+                               if (p->owner) {
+                                       ast_log(LOG_WARNING, "SAM on CIC %d PC %d already have call\n", e->sam.cic, e->sam.opc);
+                                       ast_channel_unlock(p->owner);
                                        sig_ss7_unlock_private(p);
                                        break;
                                }
+                               p->called_complete = 0;
+                               if (!ast_strlen_zero(e->sam.called_party_num)) {
+                                       char *st;
+                                       strncat(p->exten, e->sam.called_party_num, sizeof(p->exten) - strlen(p->exten) - 1);
+                                       st = strchr(p->exten, '#');
+                                       if (st) {
+                                               *st = '\0';
+                                               p->called_complete = 1;
+                                       }
+                                       ss7_match_extension(linkset, p, e);
+                               }
+                               sig_ss7_unlock_private(p);
+                               break;
+                       case ISUP_EVENT_IAM:
+                               ast_debug(1, "Got IAM for CIC %d and called number %s, calling number %s\n", e->iam.cic, e->iam.called_party_num, e->iam.calling_party_num);
+                               chanpos = ss7_find_cic_gripe(linkset, e->iam.cic, e->iam.opc, "IAM");
+                               if (chanpos < 0) {
+                                       isup_free_call(ss7, e->iam.call);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
                                /*
-                                * The channel should not have an owner at this point since we
+                                * The channel should be idle and not have an owner at this point since we
                                 * are in the process of creating an owner for it.
                                 */
-                               ast_assert(!p->owner);
+                               ast_assert(!p->owner && p->call_level == SIG_SS7_CALL_LEVEL_IDLE);
+
+                               if (p->remotelyblocked) {
+                                       ast_log(LOG_NOTICE, "Got IAM on remotely blocked CIC %d DPC %d remove blocking\n", e->iam.cic, e->iam.opc);
+                                       sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+                                       sig_ss7_set_inservice(p, 1);
+                               }
 
                                if (!sig_ss7_is_chan_available(p)) {
                                        /* Circuit is likely blocked or in alarm. */
                                        isup_rel(ss7, e->iam.call, AST_CAUSE_NORMAL_CIRCUIT_CONGESTION);
+                                       if (p->locallyblocked) {
+                                               isup_clear_callflags(ss7, e->iam.call, ISUP_GOT_IAM);
+                                               p->ss7call = isup_free_call_if_clear(ss7, e->iam.call);
+                                               ast_log(LOG_WARNING, "Got IAM on locally blocked CIC %d DPC %d, ignore\n", e->iam.cic, e->iam.opc);
+                                       }
                                        sig_ss7_unlock_private(p);
                                        break;
                                }
@@ -1043,18 +1759,14 @@ void *ss7_linkset(void *data)
                                if ((p->use_callerid) && (!ast_strlen_zero(e->iam.calling_party_num))) {
                                        ss7_apply_plan_to_number(p->cid_num, sizeof(p->cid_num), linkset, e->iam.calling_party_num, e->iam.calling_nai);
                                        p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind);
-                               } else
-                                       p->cid_num[0] = 0;
-
-                               /* Set DNID */
-                               if (!ast_strlen_zero(e->iam.called_party_num)) {
-                                       ss7_apply_plan_to_number(p->exten, sizeof(p->exten), linkset,
-                                               e->iam.called_party_num, e->iam.called_nai);
                                } else {
-                                       p->exten[0] = '\0';
+                                       p->cid_num[0] = 0;
+                                       if (e->iam.presentation_ind) {
+                                               p->callingpres = ss7_pres_scr2cid_pres(e->iam.presentation_ind, e->iam.screening_ind);
+                                       }
                                }
-                               sig_ss7_set_dnid(p, p->exten);
 
+                               p->called_complete = 0;
                                if (p->immediate) {
                                        p->exten[0] = 's';
                                        p->exten[1] = '\0';
@@ -1064,16 +1776,18 @@ void *ss7_linkset(void *data)
                                        st = strchr(p->exten, '#');
                                        if (st) {
                                                *st = '\0';
+                                               p->called_complete = 1;
                                        }
                                } else {
                                        p->exten[0] = '\0';
                                }
 
                                p->cid_ani[0] = '\0';
-                               if ((p->use_callerid) && (!ast_strlen_zero(e->iam.generic_name)))
+                               if ((p->use_callerid) && (!ast_strlen_zero(e->iam.generic_name))) {
                                        ast_copy_string(p->cid_name, e->iam.generic_name, sizeof(p->cid_name));
-                               else
+                               } else {
                                        p->cid_name[0] = '\0';
+                               }
 
                                p->cid_ani2 = e->iam.oli_ani2;
                                p->cid_ton = 0;
@@ -1087,81 +1801,162 @@ void *ss7_linkset(void *data)
                                p->gen_dig_type = e->iam.gen_dig_type;
                                p->gen_dig_scheme = e->iam.gen_dig_scheme;
                                ast_copy_string(p->jip_number, e->iam.jip_number, sizeof(p->jip_number));
-                               ast_copy_string(p->orig_called_num, e->iam.orig_called_num, sizeof(p->orig_called_num));
-                               ast_copy_string(p->redirecting_num, e->iam.redirecting_num, sizeof(p->redirecting_num));
+                               if (!ast_strlen_zero(e->iam.orig_called_num)) {
+                                       ss7_apply_plan_to_number(p->orig_called_num, sizeof(p->orig_called_num), linkset, e->iam.orig_called_num, e->iam.orig_called_nai);
+                                       p->orig_called_presentation = ss7_pres_scr2cid_pres(e->iam.orig_called_pres_ind, e->iam.orig_called_screening_ind);
+                               }
+                               if (!ast_strlen_zero(e->iam.redirecting_num)) {
+                                       ss7_apply_plan_to_number(p->redirecting_num, sizeof(p->redirecting_num), linkset, e->iam.redirecting_num, e->iam.redirecting_num_nai);
+                                       p->redirecting_presentation = ss7_pres_scr2cid_pres(e->iam.redirecting_num_presentation_ind, e->iam.redirecting_num_screening_ind);
+                               }
                                ast_copy_string(p->generic_name, e->iam.generic_name, sizeof(p->generic_name));
                                p->calling_party_cat = e->iam.calling_party_cat;
+                               p->redirect_counter = e->iam.redirect_counter;
+                               p->redirect_info = e->iam.redirect_info;
+                               p->redirect_info_ind = e->iam.redirect_info_ind;
+                               p->redirect_info_orig_reas = e->iam.redirect_info_orig_reas;
+                               p->redirect_info_counter = e->iam.redirect_info_counter;
+                               if (p->redirect_info_counter && !p->redirect_counter) {
+                                       p->redirect_counter = p->redirect_info_counter;
+                               }
+                               p->redirect_info_reas = e->iam.redirect_info_reas;
+                               p->cug_indicator = e->iam.cug_indicator;
+                               p->cug_interlock_code = e->iam.cug_interlock_code;
+                               ast_copy_string(p->cug_interlock_ni, e->iam.cug_interlock_ni, sizeof(p->cug_interlock_ni));
+
+                               if (e->iam.cot_check_required) {
+                                       sig_ss7_loopback(p, 1);
+                               }
 
+                               p->echocontrol_ind = e->iam.echocontrol_ind;
                                sig_ss7_set_caller_id(p);
+                               ss7_match_extension(linkset, p, e);
+                               sig_ss7_unlock_private(p);
 
-                               if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
-                                       if (e->iam.cot_check_required) {
-                                               p->call_level = SIG_SS7_CALL_LEVEL_CONTINUITY;
+                               if (e->iam.cot_performed_on_previous_cic) {
+                                       chanpos = sig_ss7_find_cic(linkset, (e->iam.cic - 1), e->iam.opc);
+                                       if (chanpos < 0) {
+                                               /* some stupid switch do this */
+                                               ast_verb(1, "COT request on previous nonexistent CIC %d in IAM PC %d\n", (e->iam.cic - 1), e->iam.opc);
+                                               break;
+                                       }
+                                       ast_verb(1, "COT request on previous CIC %d in IAM PC %d\n", (e->iam.cic - 1), e->iam.opc);
+                                       p = linkset->pvts[chanpos];
+                                       sig_ss7_lock_private(p);
+                                       if (sig_ss7_is_chan_available(p)) {
+                                               sig_ss7_set_inservice(p, 0);    /* to prevent to use this circuit */
                                                sig_ss7_loopback(p, 1);
-                                       } else {
+                                       } /* If already have a call don't loop */
+                                       sig_ss7_unlock_private(p);
+                               }
+                               break;
+                       case ISUP_EVENT_DIGITTIMEOUT:
+                               chanpos = ss7_find_cic_gripe(linkset, e->digittimeout.cic, e->digittimeout.opc, "DIGITTIMEOUT");
+                               if (chanpos < 0) {
+                                       isup_free_call(ss7, e->digittimeout.call);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               ast_debug(1, "Digittimeout on CIC: %d PC: %d\n", e->digittimeout.cic, e->digittimeout.opc);
+                               if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+                                       /* DNID is complete */
+                                       p->called_complete = 1;
+                                       sig_ss7_set_dnid(p, p->exten);
+
+                                       /* If COT successful start call! */
+                                       if (!(e->digittimeout.cot_check_required || e->digittimeout.cot_performed_on_previous_cic) || e->digittimeout.cot_check_passed) {
                                                ss7_start_call(p, linkset);
                                        }
                                } else {
                                        ast_debug(1, "Call on CIC for unconfigured extension %s\n", p->exten);
-                                       p->alreadyhungup = 1;
-                                       isup_rel(ss7, e->iam.call, AST_CAUSE_UNALLOCATED);
+                                       isup_rel(linkset->ss7, e->digittimeout.call, AST_CAUSE_UNALLOCATED);
                                }
                                sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_COT:
+                               if (e->cot.cot_performed_on_previous_cic) {
+                                       chanpos = sig_ss7_find_cic(linkset, (e->cot.cic - 1), e->cot.opc);
+                                       /* some stupid switches do this!!! */
+                                       if (-1 < chanpos) {
+                                               p = linkset->pvts[chanpos];
+                                               sig_ss7_lock_private(p);
+                                               sig_ss7_set_inservice(p, 1);
+                                               sig_ss7_loopback(p, 0);
+                                               sig_ss7_unlock_private(p);;
+                                               ast_verb(1, "Loop turned off on CIC: %d PC: %d\n",  (e->cot.cic - 1), e->cot.opc);
+                                       }
+                               }
+
                                chanpos = ss7_find_cic_gripe(linkset, e->cot.cic, e->cot.opc, "COT");
                                if (chanpos < 0) {
-                                       isup_rel(ss7, e->cot.call, -1);
+                                       isup_free_call(ss7, e->cot.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
 
                                sig_ss7_lock_private(p);
+                               p->ss7call = e->cot.call;
+
                                if (p->loopedback) {
                                        sig_ss7_loopback(p, 0);
+                                       ast_verb(1, "Loop turned off on CIC: %d PC: %d\n",  e->cot.cic, e->cot.opc);
+                               }
+
+                               /* Don't start call if we didn't get IAM or COT failed! */
+                               if ((e->cot.got_sent_msg & ISUP_GOT_IAM) && e->cot.passed && p->called_complete) {
                                        ss7_start_call(p, linkset);
                                }
+
+                               p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
                                sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_CCR:
                                ast_debug(1, "Got CCR request on CIC %d\n", e->ccr.cic);
                                chanpos = ss7_find_cic_gripe(linkset, e->ccr.cic, e->ccr.opc, "CCR");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->ccr.call);
                                        break;
                                }
 
                                p = linkset->pvts[chanpos];
 
                                sig_ss7_lock_private(p);
+                               p->ss7call = e->ccr.call;
                                sig_ss7_loopback(p, 1);
+                               if (linkset->type == SS7_ANSI) {
+                                       isup_lpa(linkset->ss7, e->ccr.cic, p->dpc);
+                               }
                                sig_ss7_unlock_private(p);
-
-                               isup_lpa(linkset->ss7, e->ccr.cic, p->dpc);
                                break;
                        case ISUP_EVENT_CVT:
                                ast_debug(1, "Got CVT request on CIC %d\n", e->cvt.cic);
                                chanpos = ss7_find_cic_gripe(linkset, e->cvt.cic, e->cvt.opc, "CVT");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->cvt.call);
                                        break;
                                }
 
                                p = linkset->pvts[chanpos];
 
                                sig_ss7_lock_private(p);
+                               p->ss7call = e->cvt.call;
                                sig_ss7_loopback(p, 1);
-                               sig_ss7_unlock_private(p);
-
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->cvt.call);
+                               }
                                isup_cvr(linkset->ss7, e->cvt.cic, p->dpc);
+                               sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_REL:
                                chanpos = ss7_find_cic_gripe(linkset, e->rel.cic, e->rel.opc, "REL");
                                if (chanpos < 0) {
-                                       /* Continue hanging up the call anyway. */
-                                       isup_rlc(ss7, e->rel.call);
+                                       isup_free_call(ss7, e->rel.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
                                sig_ss7_lock_private(p);
+                               p->ss7call = e->rel.call;
                                callid = func_ss7_linkset_callid(linkset, chanpos);
                                sig_ss7_lock_owner(linkset, chanpos);
                                if (p->owner) {
@@ -1170,128 +1965,193 @@ void *ss7_linkset(void *data)
 
                                        ast_channel_hangupcause_set(p->owner, e->rel.cause);
                                        ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+                                       p->do_hangup = SS7_HANGUP_SEND_RLC;
                                        ast_channel_unlock(p->owner);
+                               } else {
+                                       ast_verb(1, "REL on CIC %d DPC %d without owner!\n", p->cic, p->dpc);
+                                       isup_rlc(ss7, p->ss7call);
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
                                }
 
                                /* End the loopback if we have one */
                                sig_ss7_loopback(p, 0);
 
-                               isup_rlc(ss7, e->rel.call);
-                               p->ss7call = NULL;
-
+                               /* the rel is not complete here!!! */
                                sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_ACM:
                                chanpos = ss7_find_cic_gripe(linkset, e->acm.cic, e->acm.opc, "ACM");
                                if (chanpos < 0) {
-                                       isup_rel(ss7, e->acm.call, -1);
+                                       isup_free_call(ss7, e->acm.call);
                                        break;
                                }
-                               {
-                                       p = linkset->pvts[chanpos];
 
-                                       ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic);
+                               p = linkset->pvts[chanpos];
 
-                                       if (e->acm.call_ref_ident > 0) {
-                                               p->rlt = 1; /* Setting it but not using it here*/
-                                       }
+                               ast_debug(1, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic);
 
-                                       sig_ss7_lock_private(p);
-                                       callid = func_ss7_linkset_callid(linkset, chanpos);
-                                       sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_PROCEEDING);
-                                       if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
-                                               p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING;
+                               if (e->acm.call_ref_ident > 0) {
+                                       p->rlt = 1; /* Setting it but not using it here*/
+                               }
+
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->acm.call;
+                               callid = func_ss7_linkset_callid(linkset, chanpos);
+                               sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_PROCEEDING);
+                               if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING) {
+                                       p->call_level = SIG_SS7_CALL_LEVEL_PROCEEDING;
+                               }
+                               sig_ss7_set_dialing(p, 0);
+                               /* Send alerting if subscriber is free */
+                               if (e->acm.called_party_status_ind == 1) {
+                                       if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING) {
+                                               p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
                                        }
-                                       sig_ss7_set_dialing(p, 0);
-                                       /* Send alerting if subscriber is free */
-                                       if (e->acm.called_party_status_ind == 1) {
-                                               if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING) {
-                                                       p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
-                                               }
-                                               sig_ss7_lock_owner(linkset, chanpos);
-                                               if (p->owner) {
-                                                       ast_setstate(p->owner, AST_STATE_RINGING);
-                                                       ast_channel_unlock(p->owner);
-                                               }
-                                               sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING);
+                                       sig_ss7_lock_owner(linkset, chanpos);
+                                       if (p->owner) {
+                                               ast_setstate(p->owner, AST_STATE_RINGING);
+                                               ast_channel_unlock(p->owner);
                                        }
-                                       sig_ss7_unlock_private(p);
+                                       sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_RINGING);
                                }
+                               p->echocontrol_ind = e->acm.echocontrol_ind;
+                               sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_CGB:
                                chanpos = ss7_find_cic_gripe(linkset, e->cgb.startcic, e->cgb.opc, "CGB");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->cgb.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
-                               ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, 1);
-                               isup_cgba(linkset->ss7, e->cgb.startcic, e->cgb.endcic, e->cgb.opc, e->cgb.status, e->cgb.type);
+                               ss7_check_range(linkset, e->cgb.startcic, e->cgb.endcic,
+                                       e->cgb.opc, e->cgb.status);
+                               ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic,
+                                       e->cgb.opc, e->cgb.status, 1, 1,
+                                       (e->cgb.type) ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->cgb.call;
+
+                               isup_cgba(linkset->ss7, p->ss7call, e->cgb.endcic, e->cgb.status);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->cgb.call);
+                               }
+                               sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_CGU:
                                chanpos = ss7_find_cic_gripe(linkset, e->cgu.startcic, e->cgu.opc, "CGU");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->cgu.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
-                               ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, 0);
-                               isup_cgua(linkset->ss7, e->cgu.startcic, e->cgu.endcic, e->cgu.opc, e->cgu.status, e->cgu.type);
+                               ss7_check_range(linkset, e->cgu.startcic, e->cgu.endcic,
+                                       e->cgu.opc, e->cgu.status);
+                               ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic,
+                                       e->cgu.opc, e->cgu.status, 0, 1,
+                                       e->cgu.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->cgu.call;
+
+                               isup_cgua(linkset->ss7, p->ss7call, e->cgu.endcic, e->cgu.status);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->cgu.call);
+                               }
+                               sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_UCIC:
                                chanpos = ss7_find_cic_gripe(linkset, e->ucic.cic, e->ucic.opc, "UCIC");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->ucic.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
                                ast_debug(1, "Unequiped Circuit Id Code on CIC %d\n", e->ucic.cic);
                                sig_ss7_lock_private(p);
-                               sig_ss7_set_remotelyblocked(p, 1);
+                               sig_ss7_lock_owner(linkset, chanpos);
+                               if (p->owner) {
+                                       ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+                                       ast_channel_unlock(p->owner);
+                               }
+                               sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_MAINTENANCE);
                                sig_ss7_set_inservice(p, 0);
+                               p->ss7call = NULL;
+                               isup_free_call(ss7, e->ucic.call);
                                sig_ss7_unlock_private(p);/* doesn't require a SS7 acknowledgement */
                                break;
                        case ISUP_EVENT_BLO:
                                chanpos = ss7_find_cic_gripe(linkset, e->blo.cic, e->blo.opc, "BLO");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->blo.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
                                ast_debug(1, "Blocking CIC %d\n", e->blo.cic);
                                sig_ss7_lock_private(p);
-                               sig_ss7_set_remotelyblocked(p, 1);
+                               p->ss7call = e->blo.call;
+                               sig_ss7_set_remotelyblocked(p, 1, SS7_BLOCKED_MAINTENANCE);
+                               isup_bla(linkset->ss7, e->blo.call);
+                               sig_ss7_lock_owner(linkset, chanpos);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->blo.call);
+                               } else {
+                                       if (e->blo.got_sent_msg & ISUP_SENT_IAM) {
+                                               /* Q.784 6.2.2 */
+                                               ast_channel_hangupcause_set(p->owner, SS7_CAUSE_TRY_AGAIN);
+                                       }
+                                       ast_channel_unlock(p->owner);
+                               }
                                sig_ss7_unlock_private(p);
-                               isup_bla(linkset->ss7, e->blo.cic, p->dpc);
                                break;
                        case ISUP_EVENT_BLA:
                                chanpos = ss7_find_cic_gripe(linkset, e->bla.cic, e->bla.opc, "BLA");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->bla.call);
                                        break;
                                }
-                               ast_debug(1, "Blocking CIC %d\n", e->bla.cic);
+                               ast_debug(1, "Locally blocking CIC %d\n", e->bla.cic);
                                p = linkset->pvts[chanpos];
                                sig_ss7_lock_private(p);
-                               sig_ss7_set_locallyblocked(p, 1);
+                               p->ss7call = e->bla.call;
+                               sig_ss7_set_locallyblocked(p, 1, SS7_BLOCKED_MAINTENANCE);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                               }
                                sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_UBL:
                                chanpos = ss7_find_cic_gripe(linkset, e->ubl.cic, e->ubl.opc, "UBL");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->ubl.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
-                               ast_debug(1, "Unblocking CIC %d\n", e->ubl.cic);
+                               ast_debug(1, "Remotely unblocking CIC %d PC %d\n", e->ubl.cic, e->ubl.opc);
                                sig_ss7_lock_private(p);
-                               sig_ss7_set_remotelyblocked(p, 0);
+                               p->ss7call = e->ubl.call;
+                               sig_ss7_set_remotelyblocked(p, 0, SS7_BLOCKED_MAINTENANCE);
+                               isup_uba(linkset->ss7, e->ubl.call);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                               }
                                sig_ss7_unlock_private(p);
-                               isup_uba(linkset->ss7, e->ubl.cic, p->dpc);
                                break;
                        case ISUP_EVENT_UBA:
                                chanpos = ss7_find_cic_gripe(linkset, e->uba.cic, e->uba.opc, "UBA");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->uba.call);
                                        break;
                                }
                                p = linkset->pvts[chanpos];
-                               ast_debug(1, "Unblocking CIC %d\n", e->uba.cic);
+                               ast_debug(1, "Locally unblocking CIC %d PC %d\n", e->uba.cic, e->uba.opc);
                                sig_ss7_lock_private(p);
-                               sig_ss7_set_locallyblocked(p, 0);
+                               p->ss7call = e->uba.call;
+                               sig_ss7_set_locallyblocked(p, 0, SS7_BLOCKED_MAINTENANCE);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                               }
                                sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_CON:
@@ -1299,49 +2159,92 @@ void *ss7_linkset(void *data)
                                if (e->e == ISUP_EVENT_CON) {
                                        chanpos = ss7_find_cic_gripe(linkset, e->con.cic, e->con.opc, "CON");
                                        if (chanpos < 0) {
-                                               isup_rel(ss7, e->con.call, -1);
+                                               isup_free_call(ss7, e->con.call);
                                                break;
                                        }
                                } else {
                                        chanpos = ss7_find_cic_gripe(linkset, e->anm.cic, e->anm.opc, "ANM");
                                        if (chanpos < 0) {
-                                               isup_rel(ss7, e->anm.call, -1);
+                                               isup_free_call(ss7, e->anm.call);
                                                break;
                                        }
                                }
 
-                               {
-                                       p = linkset->pvts[chanpos];
-                                       sig_ss7_lock_private(p);
-                                       callid = func_ss7_linkset_callid(linkset, chanpos);
-                                       if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
-                                               p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
-                                       }
-                                       sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_ANSWER);
-                                       sig_ss7_set_dialing(p, 0);
-                                       sig_ss7_open_media(p);
-                                       sig_ss7_set_echocanceller(p, 1);
-                                       sig_ss7_unlock_private(p);
-                               }
-                               break;
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               p->ss7call = (e->e == ISUP_EVENT_ANM) ?  e->anm.call : e->con.call;
+                               callid = func_ss7_linkset_callid(linkset, chanpos);
+                               if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
+                                       p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
+                               }
+
+                               if (!ast_strlen_zero((e->e == ISUP_EVENT_ANM)
+                                       ? e->anm.connected_num : e->con.connected_num)) {
+                                       sig_ss7_lock_owner(linkset, chanpos);
+                                       if (p->owner) {
+                                               struct ast_party_connected_line ast_connected;
+                                               char connected_num[AST_MAX_EXTENSION];
+
+                                               ast_party_connected_line_init(&ast_connected);
+                                               if (e->e == ISUP_EVENT_ANM) {
+                                                       ast_connected.id.number.presentation = ss7_pres_scr2cid_pres(
+                                                               e->anm.connected_presentation_ind,
+                                                               e->anm.connected_screening_ind);
+                                                       ss7_apply_plan_to_number(connected_num, sizeof(connected_num),
+                                                               linkset, e->anm.connected_num, e->anm.connected_nai);
+                                                       ast_connected.id.number.str = ast_strdup(connected_num);
+                                               } else {
+                                                       ast_connected.id.number.presentation = ss7_pres_scr2cid_pres(
+                                                               e->con.connected_presentation_ind,
+                                                               e->con.connected_screening_ind);
+                                                       ss7_apply_plan_to_number(connected_num, sizeof(connected_num),
+                                                               linkset, e->con.connected_num, e->con.connected_nai);
+                                                       ast_connected.id.number.str = ast_strdup(connected_num);
+                                               }
+                                               ast_connected.id.number.valid = 1;
+                                               ast_connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+                                               ast_channel_queue_connected_line_update(p->owner, &ast_connected, NULL);
+                                               ast_party_connected_line_free(&ast_connected);
+                                               ast_channel_unlock(p->owner);
+                                       }
+                               }
+
+                               sig_ss7_queue_control(linkset, chanpos, AST_CONTROL_ANSWER);
+                               sig_ss7_set_dialing(p, 0);
+                               sig_ss7_open_media(p);
+                               if (((e->e == ISUP_EVENT_ANM) ? !e->anm.echocontrol_ind  :
+                                               !e->con.echocontrol_ind) || !(linkset->flags & LINKSET_FLAG_USEECHOCONTROL)) {
+                                       sig_ss7_set_echocanceller(p, 1);
+                               }
+                               sig_ss7_unlock_private(p);
+                               break;
                        case ISUP_EVENT_RLC:
-                               /* XXX Call ptr should be passed up from libss7! */
                                chanpos = ss7_find_cic_gripe(linkset, e->rlc.cic, e->rlc.opc, "RLC");
                                if (chanpos < 0) {
+                                       isup_free_call(ss7, e->rlc.call);
                                        break;
                                }
-                               {
-                                       p = linkset->pvts[chanpos];
-                                       sig_ss7_lock_private(p);
-                                       callid = func_ss7_linkset_callid(linkset, chanpos);
-                                       if (p->alreadyhungup) {
-                                               if (!p->owner) {
-                                                       p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
-                                               }
-                                               p->ss7call = NULL;
+
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->rlc.call;
+                               callid = func_ss7_linkset_callid(linkset, chanpos);
+                               if (e->rlc.got_sent_msg & (ISUP_SENT_RSC | ISUP_SENT_REL)) {
+                                       sig_ss7_loopback(p, 0);
+                                       if (e->rlc.got_sent_msg & ISUP_SENT_RSC) {
+                                               sig_ss7_set_inservice(p, 1);
                                        }
-                                       sig_ss7_unlock_private(p);
                                }
+                               sig_ss7_lock_owner(linkset, chanpos);
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, e->rlc.call);
+                                       p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
+                               } else {
+                                       p->do_hangup = SS7_HANGUP_DO_NOTHING;
+                                       ast_softhangup_nolock(p->owner, AST_SOFTHANGUP_DEV);
+                                       ast_channel_unlock(p->owner);
+                               }
+                               sig_ss7_unlock_private(p);
                                break;
                        case ISUP_EVENT_FAA:
                                /*!
@@ -1350,25 +2253,87 @@ void *ss7_linkset(void *data)
                                 */
                                chanpos = ss7_find_cic_gripe(linkset, e->faa.cic, e->faa.opc, "FAA");
                                if (chanpos < 0) {
-                                       isup_rel(linkset->ss7, e->faa.call, -1);
+                                       isup_free_call(ss7, e->faa.call);
                                        break;
                                }
-                               {
-                                       /* XXX FAR and FAA used for something dealing with transfers? */
-                                       p = linkset->pvts[chanpos];
-                                       ast_debug(1, "FAA received on CIC %d\n", e->faa.cic);
-                                       sig_ss7_lock_private(p);
-                                       callid = func_ss7_linkset_callid(linkset, chanpos);
-                                       if (p->alreadyhungup){
-                                               if (!p->owner) {
-                                                       p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
-                                               }
-                                               /* XXX We seem to be leaking the isup call structure here. */
-                                               p->ss7call = NULL;
-                                               ast_log(LOG_NOTICE, "Received FAA and we haven't sent FAR.  Ignoring.\n");
-                                       }
-                                       sig_ss7_unlock_private(p);
+
+                               /* XXX FAR and FAA used for something dealing with transfers? */
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               callid = func_ss7_linkset_callid(linkset, chanpos);
+                               ast_debug(1, "FAA received on CIC %d\n", e->faa.cic);
+                               p->ss7call = isup_free_call_if_clear(ss7, e->faa.call);
+                               sig_ss7_unlock_private(p);
+                               break;
+                       case ISUP_EVENT_CGBA:
+                               chanpos = ss7_find_cic_gripe(linkset, e->cgba.startcic, e->cgba.opc, "CGBA");
+                               if (chanpos < 0) {      /* Never will be true */
+                                       isup_free_call(ss7, e->cgba.call);
+                                       break;
+                               }
+
+                               ss7_block_cics(linkset, e->cgba.startcic, e->cgba.endcic,
+                                       e->cgba.opc, e->cgba.status, 1, 0,
+                                       e->cgba.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->cgba.call;
+
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                               }
+                               sig_ss7_unlock_private(p);
+                               break;
+                       case ISUP_EVENT_CGUA:
+                               chanpos = ss7_find_cic_gripe(linkset, e->cgua.startcic, e->cgua.opc, "CGUA");
+                               if (chanpos < 0) { /* Never will be true */
+                                       isup_free_call(ss7, e->cgua.call);
+                                       break;
+                               }
+
+                               ss7_block_cics(linkset, e->cgua.startcic, e->cgua.endcic,
+                                       e->cgua.opc, e->cgua.status, 0, 0,
+                                       e->cgba.type ? SS7_BLOCKED_HARDWARE : SS7_BLOCKED_MAINTENANCE);
+
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->cgua.call;
+
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                               }
+                               sig_ss7_unlock_private(p);
+                               break;
+                       case ISUP_EVENT_SUS:
+                               chanpos = ss7_find_cic_gripe(linkset, e->sus.cic, e->sus.opc, "SUS");
+                               if (chanpos < 0) {
+                                       isup_free_call(ss7, e->sus.call);
+                                       break;
+                               }
+
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->sus.call;
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
+                               }
+                               sig_ss7_unlock_private(p);
+                               break;
+                       case ISUP_EVENT_RES:
+                               chanpos = ss7_find_cic_gripe(linkset, e->res.cic, e->res.opc, "RES");
+                               if (chanpos < 0) {
+                                       isup_free_call(ss7, e->res.call);
+                                       break;
+                               }
+
+                               p = linkset->pvts[chanpos];
+                               sig_ss7_lock_private(p);
+                               p->ss7call = e->res.call;
+                               if (!p->owner) {
+                                       p->ss7call = isup_free_call_if_clear(ss7, p->ss7call);
                                }
+                               sig_ss7_unlock_private(p);
                                break;
                        default:
                                ast_debug(1, "Unknown event %s\n", ss7_event2str(e->e));
@@ -1387,9 +2352,15 @@ void *ss7_linkset(void *data)
        return 0;
 }
 
-static inline void ss7_rel(struct sig_ss7_linkset *ss7)
+static void ss7_rel(struct sig_ss7_linkset *ss7)
 {
+       /* Release the lock first */
        ast_mutex_unlock(&ss7->lock);
+
+       /* Then break the poll to send our messages */
+       if (ss7->master != AST_PTHREADT_NULL) {
+               pthread_kill(ss7->master, SIGURG);
+       }
 }
 
 static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7)
@@ -1399,10 +2370,177 @@ static void ss7_grab(struct sig_ss7_chan *pvt, struct sig_ss7_linkset *ss7)
                /* Avoid deadlock */
                sig_ss7_deadlock_avoidance_private(pvt);
        }
-       /* Then break the poll */
-       if (ss7->master != AST_PTHREADT_NULL) {
-               pthread_kill(ss7->master, SIGURG);
+}
+
+/*!
+ * \brief Reset a specific CIC.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param cic Circuit Identification Code
+ * \param dpc Destination Point Code
+ *
+ * \return TRUE on success
+ */
+int sig_ss7_reset_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc)
+{
+       int i;
+
+       ast_mutex_lock(&linkset->lock);
+       for (i = 0; i < linkset->numchans; i++) {
+               if (linkset->pvts[i] && linkset->pvts[i]->cic == cic && linkset->pvts[i]->dpc == dpc) {
+                       int res;
+
+                       sig_ss7_lock_private(linkset->pvts[i]);
+                       sig_ss7_set_locallyblocked(linkset->pvts[i], 0, SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+                       res = ss7_start_rsc(linkset, i);
+                       sig_ss7_unlock_private(linkset->pvts[i]);
+                       ss7_rel(linkset);       /* Also breaks the poll to send our messages */
+                       return res;
+               }
+       }
+       ss7_rel(linkset);
+
+       return 0;
+}
+
+/*!
+ * \brief Block or Unblock a specific CIC.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param do_block Action to perform. Block if TRUE.
+ * \param which On which CIC to perform the operation.
+ *
+ * \return 0 on success
+ */
+int sig_ss7_cic_blocking(struct sig_ss7_linkset *linkset, int do_block, int which)
+{
+       ast_mutex_lock(&linkset->lock);
+       sig_ss7_lock_private(linkset->pvts[which]);
+       if (!ss7_find_alloc_call(linkset->pvts[which])) {
+               sig_ss7_unlock_private(linkset->pvts[which]);
+               ss7_rel(linkset);
+               return -1;
+       }
+
+       if (do_block) {
+               isup_blo(linkset->ss7, linkset->pvts[which]->ss7call);
+       } else {
+               isup_ubl(linkset->ss7, linkset->pvts[which]->ss7call);
+       }
+
+       sig_ss7_unlock_private(linkset->pvts[which]);
+       ss7_rel(linkset);       /* Also breaks the poll to send our messages */
+
+       return 0;
+}
+
+/*!
+ * \brief Block or Unblock a range of CICs.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param do_block Action to perform. Block if TRUE.
+ * \param chanpos Channel position to start from.
+ * \param endcic Circuit Identification Code of the end of the range.
+ * \param state Array of CIC blocking status.
+ * \param type Type of the blocking - maintenance or hardware
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ *
+ * \return 0 on success
+ */
+int sig_ss7_group_blocking(struct sig_ss7_linkset *linkset, int do_block, int chanpos, int endcic, unsigned char state[], int type)
+{
+       sig_ss7_lock_private(linkset->pvts[chanpos]);
+       if (!ss7_find_alloc_call(linkset->pvts[chanpos])) {
+               sig_ss7_unlock_private(linkset->pvts[chanpos]);
+               return -1;
+       }
+
+       if (do_block) {
+               isup_cgb(linkset->ss7, linkset->pvts[chanpos]->ss7call, endcic, state, type);
+       } else {
+               isup_cgu(linkset->ss7, linkset->pvts[chanpos]->ss7call, endcic, state, type);
+       }
+
+       sig_ss7_unlock_private(linkset->pvts[chanpos]);
+       return 0;
+}
+
+/*!
+ * \brief Reset a group of CICs.
+ * \since 11.0
+ *
+ * \param linkset linkset control structure.
+ * \param cic Circuit Identification Code
+ * \param dpc Destination Point Code
+ * \param range Range of the CICs to reset
+ *
+ * \note Assumes the linkset->lock is already obtained.
+ *
+ * \return 0 on success
+ */
+int sig_ss7_reset_group(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, int range)
+{
+       int i;
+
+       for (i = 0; i < linkset->numchans; i++) {
+               if (linkset->pvts[i] && linkset->pvts[i]->cic == cic && linkset->pvts[i]->dpc == dpc) {
+                       ss7_clear_channels(linkset, cic, cic + range, dpc, SS7_HANGUP_FREE_CALL);
+                       ss7_block_cics(linkset, cic, cic + range, dpc, NULL, 0, 1,
+                               SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+                       ss7_block_cics(linkset, cic, cic + range, dpc, NULL, 0, 0,
+                               SS7_BLOCKED_MAINTENANCE | SS7_BLOCKED_HARDWARE);
+
+                       sig_ss7_lock_private(linkset->pvts[i]);
+                       if (!ss7_find_alloc_call(linkset->pvts[i])) {
+                               sig_ss7_unlock_private(linkset->pvts[i]);
+                               return -1;
+                       }
+                       isup_grs(linkset->ss7, linkset->pvts[i]->ss7call, linkset->pvts[i]->cic + range);
+                       sig_ss7_unlock_private(linkset->pvts[i]);
+                       break;
+               }
        }
+       return 0;
+}
+
+void sig_ss7_free_isup_call(struct sig_ss7_linkset *linkset, int channel)
+{
+       sig_ss7_lock_private(linkset->pvts[channel]);
+       if (linkset->pvts[channel]->ss7call) {
+               isup_free_call(linkset->ss7, linkset->pvts[channel]->ss7call);
+               linkset->pvts[channel]->ss7call = NULL;
+       }
+       sig_ss7_unlock_private(linkset->pvts[channel]);
+}
+
+static int ss7_parse_prefix(struct sig_ss7_chan *p, const char *number, char *nai)
+{
+       int strip = 0;
+
+       if (strncmp(number, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
+               strip = strlen(p->ss7->internationalprefix);
+               *nai = SS7_NAI_INTERNATIONAL;
+       } else if (strncmp(number, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
+               strip = strlen(p->ss7->nationalprefix);
+               *nai = SS7_NAI_NATIONAL;
+       } else if (strncmp(number, p->ss7->networkroutedprefix, strlen(p->ss7->networkroutedprefix)) == 0) {
+               strip = strlen(p->ss7->networkroutedprefix);
+               *nai = SS7_NAI_NETWORKROUTED;
+       } else if (strncmp(number, p->ss7->unknownprefix, strlen(p->ss7->unknownprefix)) == 0) {
+               strip = strlen(p->ss7->unknownprefix);
+               *nai = SS7_NAI_UNKNOWN;
+       } else if (strncmp(number, p->ss7->subscriberprefix, strlen(p->ss7->subscriberprefix)) == 0) {
+               strip = strlen(p->ss7->subscriberprefix);
+               *nai = SS7_NAI_SUBSCRIBER;
+       } else {
+               *nai = SS7_NAI_SUBSCRIBER;
+       }
+
+       return strip;
 }
 
 /*!
@@ -1453,7 +2591,7 @@ void sig_ss7_link_noalarm(struct sig_ss7_linkset *linkset, int which)
  * \retval 0 on success.
  * \retval -1 on error.
  */
-int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode)
+int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode, int cur_slc)
 {
        if (!linkset->ss7) {
                linkset->type = ss7type;
@@ -1467,7 +2605,7 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type,
        ss7_set_network_ind(linkset->ss7, networkindicator);
        ss7_set_pc(linkset->ss7, pointcode);
 
-       if (ss7_add_link(linkset->ss7, transport, linkset->fds[which])) {
+       if (ss7_add_link(linkset->ss7, transport, linkset->fds[which], cur_slc, adjpointcode)) {
                ast_log(LOG_WARNING, "Could not add SS7 link!\n");
        }
 
@@ -1479,8 +2617,6 @@ int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type,
                ss7_link_noalarm(linkset->ss7, linkset->fds[which]);
        }
 
-       ss7_set_adjpc(linkset->ss7, linkset->fds[which], adjpointcode);
-
        return 0;
 }
 
@@ -1505,7 +2641,13 @@ int sig_ss7_available(struct sig_ss7_chan *p)
        ast_mutex_lock(&p->ss7->lock);
        available = sig_ss7_is_chan_available(p);
        if (available) {
-               p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED;
+               p->ss7call = isup_new_call(p->ss7->ss7, p->cic, p->dpc, 1);
+               if (!p->ss7call) {
+                       ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n");
+                       available = 0;
+               } else {
+                       p->call_level = SIG_SS7_CALL_LEVEL_ALLOCATED;
+               }
        }
        ast_mutex_unlock(&p->ss7->lock);
 
@@ -1522,6 +2664,159 @@ static unsigned char cid_pres2ss7screen(int cid_pres)
        return cid_pres & 0x03;
 }
 
+static void ss7_connected_line_update(struct sig_ss7_chan *p, struct ast_party_connected_line *connected)
+{
+       int connected_strip = 0;
+       char connected_nai;
+       unsigned char connected_pres;
+       unsigned char connected_screen;
+       const char *connected_num;
+
+       if (!connected->id.number.valid) {
+               return;
+       }
+
+       connected_num = S_OR(connected->id.number.str, "");
+       if (p->ss7->called_nai ==  SS7_NAI_DYNAMIC) {
+               connected_strip = ss7_parse_prefix(p, connected_num, &connected_nai);
+       } else {
+               connected_nai = p->ss7->called_nai;
+       }
+
+       connected_pres = cid_pres2ss7pres(connected->id.number.presentation);
+       connected_screen = cid_pres2ss7screen(connected->id.number.presentation);
+
+       isup_set_connected(p->ss7call, connected_num + connected_strip, connected_nai, connected_pres, connected_screen);
+}
+
+static unsigned char ss7_redirect_reason(struct sig_ss7_chan *p, struct ast_party_redirecting *redirecting, int orig)
+{
+       int reason = (orig) ? redirecting->orig_reason.code : redirecting->reason.code;
+
+       switch (reason) {
+       case AST_REDIRECTING_REASON_USER_BUSY:
+               return SS7_REDIRECTING_REASON_USER_BUSY;
+       case AST_REDIRECTING_REASON_NO_ANSWER:
+               return SS7_REDIRECTING_REASON_NO_ANSWER;
+       case AST_REDIRECTING_REASON_UNCONDITIONAL:
+               return SS7_REDIRECTING_REASON_UNCONDITIONAL;
+       }
+
+       if (orig || reason == AST_REDIRECTING_REASON_UNKNOWN) {
+               return SS7_REDIRECTING_REASON_UNKNOWN;
+       }
+
+       if (reason == AST_REDIRECTING_REASON_UNAVAILABLE) {
+               return SS7_REDIRECTING_REASON_UNAVAILABLE;
+       }
+
+       if (reason == AST_REDIRECTING_REASON_DEFLECTION) {
+               if (p->call_level > SIG_SS7_CALL_LEVEL_PROCEEDING) {
+                       return SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING;
+               }
+               return SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE;
+       }
+
+       return SS7_REDIRECTING_REASON_UNKNOWN;
+}
+
+static unsigned char ss7_redirect_info_ind(struct ast_channel *ast)
+{
+       const char *redirect_info_ind;
+       struct ast_party_redirecting *redirecting = ast_channel_redirecting(ast);
+
+       redirect_info_ind = pbx_builtin_getvar_helper(ast, "SS7_REDIRECT_INFO_IND");
+       if (!ast_strlen_zero(redirect_info_ind)) {
+               if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_PRES_ALLOWED")) {
+                       return SS7_INDICATION_REROUTED_PRES_ALLOWED;
+               }
+               if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_INFO_RESTRICTED")) {
+                       return SS7_INDICATION_REROUTED_INFO_RESTRICTED;
+               }
+               if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_PRES_ALLOWED")) {
+                       return SS7_INDICATION_DIVERTED_PRES_ALLOWED;
+               }
+               if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_INFO_RESTRICTED")) {
+                       return SS7_INDICATION_DIVERTED_INFO_RESTRICTED;
+               }
+               if (!strcasecmp(redirect_info_ind, "CALL_REROUTED_PRES_RESTRICTED")) {
+                       return SS7_INDICATION_REROUTED_PRES_RESTRICTED;
+               }
+               if (!strcasecmp(redirect_info_ind, "CALL_DIVERTED_PRES_RESTRICTED")) {
+                       return SS7_INDICATION_DIVERTED_PRES_RESTRICTED;
+               }
+               if (!strcasecmp(redirect_info_ind, "SPARE")) {
+                       return SS7_INDICATION_SPARE;
+               }
+               return SS7_INDICATION_NO_REDIRECTION;
+       }
+
+       if (redirecting->reason.code == AST_REDIRECTING_REASON_DEFLECTION) {
+               if ((redirecting->to.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+                       if ((redirecting->orig.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+                               return SS7_INDICATION_DIVERTED_PRES_ALLOWED;
+                       }
+                       return SS7_INDICATION_DIVERTED_PRES_RESTRICTED;
+               }
+               return SS7_INDICATION_DIVERTED_INFO_RESTRICTED;
+       }
+
+       if ((redirecting->to.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+               if ((redirecting->orig.number.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) {
+                       return SS7_INDICATION_REROUTED_PRES_ALLOWED;
+               }
+               return SS7_INDICATION_REROUTED_PRES_RESTRICTED;
+       }
+       return SS7_INDICATION_REROUTED_INFO_RESTRICTED;
+}
+
+static void ss7_redirecting_update(struct sig_ss7_chan *p, struct ast_channel *ast)
+{
+       int num_nai_strip = 0;
+       struct ast_party_redirecting *redirecting = ast_channel_redirecting(ast);
+
+       if (!redirecting->count) {
+               return;
+       }
+
+       isup_set_redirect_counter(p->ss7call, redirecting->count);
+
+       if (redirecting->orig.number.valid) {
+               char ss7_orig_called_nai = p->ss7->called_nai;
+               const char *ss7_orig_called_num = S_OR(redirecting->orig.number.str, "");
+
+               if (ss7_orig_called_nai == SS7_NAI_DYNAMIC) {
+                       num_nai_strip = ss7_parse_prefix(p, ss7_orig_called_num, &ss7_orig_called_nai);
+               } else {
+                       num_nai_strip = 0;
+               }
+               isup_set_orig_called_num(p->ss7call, ss7_orig_called_num + num_nai_strip,
+                       ss7_orig_called_nai,
+                       cid_pres2ss7pres(redirecting->orig.number.presentation),
+                       cid_pres2ss7screen(redirecting->orig.number.presentation));
+       }
+
+       if (redirecting->from.number.valid) {
+               char ss7_redirecting_num_nai = p->ss7->calling_nai;
+               const char *redirecting_number = S_OR(redirecting->from.number.str, "");
+
+               if (ss7_redirecting_num_nai == SS7_NAI_DYNAMIC) {
+                       num_nai_strip = ss7_parse_prefix(p, redirecting_number, &ss7_redirecting_num_nai);
+               } else {
+                       num_nai_strip = 0;
+               }
+
+               isup_set_redirecting_number(p->ss7call, redirecting_number + num_nai_strip,
+                       ss7_redirecting_num_nai,
+                       cid_pres2ss7pres(redirecting->from.number.presentation),
+                       cid_pres2ss7screen(redirecting->from.number.presentation));
+       }
+
+       isup_set_redirection_info(p->ss7call, ss7_redirect_info_ind(ast),
+               ss7_redirect_reason(p, ast_channel_redirecting(ast), 1),
+               redirecting->count, ss7_redirect_reason(p, ast_channel_redirecting(ast), 0));
+}
+
 /*!
  * \brief Dial out using the specified SS7 channel.
  * \since 1.8
@@ -1539,6 +2834,13 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
        int called_nai_strip;
        char ss7_calling_nai;
        int calling_nai_strip;
+       const char *col_req = NULL;
+       const char *ss7_cug_indicator_str;
+       const char *ss7_cug_interlock_ni;
+       const char *ss7_cug_interlock_code;
+       const char *ss7_interworking_indicator;
+       const char *ss7_forward_indicator_pmbits;
+       unsigned char ss7_cug_indicator;
        const char *charge_str = NULL;
        const char *gen_address = NULL;
        const char *gen_digits = NULL;
@@ -1551,6 +2853,7 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
        const char *call_ref_id = NULL;
        const char *call_ref_pc = NULL;
        const char *send_far = NULL;
+       const char *tmr = NULL;
        char *c;
        char *l;
        char dest[256];
@@ -1582,47 +2885,27 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
                return -1;
        }
 
-       p->ss7call = isup_new_call(p->ss7->ss7);
-       if (!p->ss7call) {
-               ss7_rel(p->ss7);
-               ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n");
-               return -1;
-       }
-
        called_nai_strip = 0;
        ss7_called_nai = p->ss7->called_nai;
        if (ss7_called_nai == SS7_NAI_DYNAMIC) { /* compute dynamically */
-               if (strncmp(c + p->stripmsd, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
-                       called_nai_strip = strlen(p->ss7->internationalprefix);
-                       ss7_called_nai = SS7_NAI_INTERNATIONAL;
-               } else if (strncmp(c + p->stripmsd, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
-                       called_nai_strip = strlen(p->ss7->nationalprefix);
-                       ss7_called_nai = SS7_NAI_NATIONAL;
-               } else {
-                       ss7_called_nai = SS7_NAI_SUBSCRIBER;
-               }
+               called_nai_strip = ss7_parse_prefix(p, c + p->stripmsd, &ss7_called_nai);
        }
        isup_set_called(p->ss7call, c + p->stripmsd + called_nai_strip, ss7_called_nai, p->ss7->ss7);
 
        calling_nai_strip = 0;
        ss7_calling_nai = p->ss7->calling_nai;
        if ((l != NULL) && (ss7_calling_nai == SS7_NAI_DYNAMIC)) { /* compute dynamically */
-               if (strncmp(l, p->ss7->internationalprefix, strlen(p->ss7->internationalprefix)) == 0) {
-                       calling_nai_strip = strlen(p->ss7->internationalprefix);
-                       ss7_calling_nai = SS7_NAI_INTERNATIONAL;
-               } else if (strncmp(l, p->ss7->nationalprefix, strlen(p->ss7->nationalprefix)) == 0) {
-                       calling_nai_strip = strlen(p->ss7->nationalprefix);
-                       ss7_calling_nai = SS7_NAI_NATIONAL;
-               } else {
-                       ss7_calling_nai = SS7_NAI_SUBSCRIBER;
-               }
+               calling_nai_strip = ss7_parse_prefix(p, l, &ss7_calling_nai);
        }
+
        isup_set_calling(p->ss7call, l ? (l + calling_nai_strip) : NULL, ss7_calling_nai,
-               p->use_callingpres ? cid_pres2ss7pres(ast_channel_connected(ast)->id.number.presentation) : (l ? SS7_PRESENTATION_ALLOWED : SS7_PRESENTATION_RESTRICTED),
+               p->use_callingpres ? cid_pres2ss7pres(ast_channel_connected(ast)->id.number.presentation)
+                       : (l ? SS7_PRESENTATION_ALLOWED
+                               : (ast_channel_connected(ast)->id.number.presentation == AST_PRES_UNAVAILABLE
+                                       ? SS7_PRESENTATION_ADDR_NOT_AVAILABLE : SS7_PRESENTATION_RESTRICTED)),
                p->use_callingpres ? cid_pres2ss7screen(ast_channel_connected(ast)->id.number.presentation) : SS7_SCREENING_USER_PROVIDED);
 
        isup_set_oli(p->ss7call, ast_channel_connected(ast)->ani2);
-       isup_init_call(p->ss7->ss7, p->ss7call, p->cic, p->dpc);
 
        /* Set the charge number if it is set */
        charge_str = pbx_builtin_getvar_helper(ast, "SS7_CHARGE_NUMBER");
@@ -1664,10 +2947,66 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
        }
 
        send_far = pbx_builtin_getvar_helper(ast, "SS7_SEND_FAR");
-       if ((send_far) && ((strncmp("NO", send_far, strlen(send_far))) != 0 ))
-               (isup_far(p->ss7->ss7, p->ss7call));
+       if (send_far && strncmp("NO", send_far, strlen(send_far)) != 0) {
+               isup_far(p->ss7->ss7, p->ss7call);
+       }
+
+       tmr = pbx_builtin_getvar_helper(ast, "SS7_TMR_NUM");
+       if (tmr) {
+               isup_set_tmr(p->ss7call, atoi(tmr));
+       } else if ((tmr = pbx_builtin_getvar_helper(ast, "SS7_TMR")) && tmr[0] != '\0') {
+               if (!strcasecmp(tmr, "SPEECH")) {
+                       isup_set_tmr(p->ss7call, SS7_TMR_SPEECH);
+               } else if (!strcasecmp(tmr, "SPARE")) {
+                       isup_set_tmr(p->ss7call, SS7_TMR_SPARE);
+               } else if (!strcasecmp(tmr, "3K1_AUDIO")) {
+                       isup_set_tmr(p->ss7call, SS7_TMR_3K1_AUDIO);
+               } else if (!strcasecmp(tmr, "64K_UNRESTRICTED")) {
+                       isup_set_tmr(p->ss7call, SS7_TMR_64K_UNRESTRICTED);
+               } else {
+                       isup_set_tmr(p->ss7call, SS7_TMR_N64K_OR_SPARE);
+               }
+       }
+
+       col_req = pbx_builtin_getvar_helper(ast, "SS7_COL_REQUEST");
+       if (ast_true(col_req)) {
+               isup_set_col_req(p->ss7call);
+       }
+
+       ss7_cug_indicator_str = pbx_builtin_getvar_helper(ast, "SS7_CUG_INDICATOR");
+       if (!ast_strlen_zero(ss7_cug_indicator_str)) {
+               if (!strcasecmp(ss7_cug_indicator_str, "OUTGOING_ALLOWED")) {
+                       ss7_cug_indicator = ISUP_CUG_OUTGOING_ALLOWED;
+               } else if (!strcasecmp(ss7_cug_indicator_str, "OUTGOING_NOT_ALLOWED")) {
+                       ss7_cug_indicator = ISUP_CUG_OUTGOING_NOT_ALLOWED;
+               } else {
+                       ss7_cug_indicator = ISUP_CUG_NON;
+               }
+
+               if (ss7_cug_indicator != ISUP_CUG_NON) {
+                       ss7_cug_interlock_code = pbx_builtin_getvar_helper(ast, "SS7_CUG_INTERLOCK_CODE");
+                       ss7_cug_interlock_ni = pbx_builtin_getvar_helper(ast, "SS7_CUG_INTERLOCK_NI");
+                       if (ss7_cug_interlock_code && ss7_cug_interlock_ni && strlen(ss7_cug_interlock_ni) == 4) {
+                               isup_set_cug(p->ss7call, ss7_cug_indicator, ss7_cug_interlock_ni, atoi(ss7_cug_interlock_code));
+                       }
+               }
+       }
+
+       ss7_redirecting_update(p, ast);
+
+       isup_set_echocontrol(p->ss7call, (p->ss7->flags & LINKSET_FLAG_DEFAULTECHOCONTROL) ? 1 : 0);
+       ss7_interworking_indicator = pbx_builtin_getvar_helper(ast, "SS7_INTERWORKING_INDICATOR");
+       if (ss7_interworking_indicator) {
+               isup_set_interworking_indicator(p->ss7call, ast_true(ss7_interworking_indicator));
+       }
+
+       ss7_forward_indicator_pmbits = pbx_builtin_getvar_helper(ast, "SS7_FORWARD_INDICATOR_PMBITS");
+       if (ss7_forward_indicator_pmbits) {
+               isup_set_forward_indicator_pmbits(p->ss7call, atoi(ss7_forward_indicator_pmbits));
+       }
 
        p->call_level = SIG_SS7_CALL_LEVEL_SETUP;
+       p->do_hangup = SS7_HANGUP_SEND_REL;
        isup_iam(p->ss7->ss7, p->ss7call);
        sig_ss7_set_dialing(p, 1);
        ast_setstate(ast, AST_STATE_DIALING);
@@ -1687,8 +3026,6 @@ int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rd
  */
 int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast)
 {
-       int res = 0;
-
        if (!ast_channel_tech_pvt(ast)) {
                ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
                return 0;
@@ -1704,22 +3041,51 @@ int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast)
        ss7_grab(p, p->ss7);
        p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
        if (p->ss7call) {
-               if (!p->alreadyhungup) {
-                       const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
-                       int icause = ast_channel_hangupcause(ast) ? ast_channel_hangupcause(ast) : -1;
-
-                       if (cause) {
-                               if (atoi(cause)) {
-                                       icause = atoi(cause);
+               switch (p->do_hangup) {
+               case SS7_HANGUP_SEND_REL:
+                       {
+                               const char *cause = pbx_builtin_getvar_helper(ast,"SS7_CAUSE");
+                               int icause = ast_channel_hangupcause(ast) ? ast_channel_hangupcause(ast) : -1;
+
+                               if (cause) {
+                                       if (atoi(cause)) {
+                                               icause = atoi(cause);
+                                       }
+                               }
+                               if (icause > 255) {
+                                       icause = 16;
                                }
+
+                               isup_rel(p->ss7->ss7, p->ss7call, icause);
+                               p->do_hangup = SS7_HANGUP_DO_NOTHING;
                        }
-                       isup_rel(p->ss7->ss7, p->ss7call, icause);
-                       p->alreadyhungup = 1;
+                       break;
+               case SS7_HANGUP_SEND_RSC:
+                       ss7_do_rsc(p);
+                       p->do_hangup = SS7_HANGUP_DO_NOTHING;
+                       break;
+               case SS7_HANGUP_SEND_RLC:
+                       isup_rlc(p->ss7->ss7, p->ss7call);
+                       p->do_hangup = SS7_HANGUP_DO_NOTHING;
+                       p->ss7call = isup_free_call_if_clear(p->ss7->ss7, p->ss7call);
+                       break;
+               case SS7_HANGUP_FREE_CALL:
+                       p->do_hangup = SS7_HANGUP_DO_NOTHING;
+                       isup_free_call(p->ss7->ss7, p->ss7call);
+                       p->ss7call = NULL;
+                       break;
+               case SS7_HANGUP_REEVENT_IAM:
+                       isup_event_iam(p->ss7->ss7, p->ss7call, p->dpc);
+                       p->do_hangup = SS7_HANGUP_SEND_REL;
+                       break;
+               case SS7_HANGUP_DO_NOTHING:
+                       p->ss7call = isup_free_call_if_clear(p->ss7->ss7, p->ss7call);
+                       break;
                }
        }
        ss7_rel(p->ss7);
 
-       return res;
+       return 0;
 }
 
 /*!
@@ -1738,10 +3104,14 @@ int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast)
 
        ss7_grab(p, p->ss7);
        if (p->call_level < SIG_SS7_CALL_LEVEL_CONNECT) {
+               if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && (p->ss7->flags & LINKSET_FLAG_AUTOACM)) {
+                       isup_acm(p->ss7->ss7, p->ss7call);
+               }
                p->call_level = SIG_SS7_CALL_LEVEL_CONNECT;
        }
-       sig_ss7_open_media(p);
+
        res = isup_anm(p->ss7->ss7, p->ss7call);
+       sig_ss7_open_media(p);
        ss7_rel(p->ss7);
        return res;
 }
@@ -1764,7 +3134,7 @@ void sig_ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, str
 }
 
 /*!
- * \brief SS7 answer channel.
+ * \brief SS7 indication.
  * \since 1.8
  *
  * \param p Signaling private structure pointer.
@@ -1793,15 +3163,20 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi
        case AST_CONTROL_RINGING:
                ss7_grab(p, p->ss7);
                if (p->call_level < SIG_SS7_CALL_LEVEL_ALERTING && !p->outgoing) {
-                       p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
                        if ((isup_far(p->ss7->ss7, p->ss7call)) != -1) {
                                p->rlt = 1;
                        }
 
+                       if (p->call_level < SIG_SS7_CALL_LEVEL_PROCEEDING && (p->ss7->flags & LINKSET_FLAG_AUTOACM)) {
+                               isup_acm(p->ss7->ss7, p->ss7call);
+                       }
+
                        /* No need to send CPG if call will be RELEASE */
                        if (p->rlt != 1) {
                                isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING);
                        }
+
+                       p->call_level = SIG_SS7_CALL_LEVEL_ALERTING;
                }
                ss7_rel(p->ss7);
 
@@ -1833,15 +3208,15 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi
                ast_debug(1,"Received AST_CONTROL_PROGRESS on %s\n",ast_channel_name(chan));
                ss7_grab(p, p->ss7);
                if (!p->progress && p->call_level < SIG_SS7_CALL_LEVEL_ALERTING && !p->outgoing) {
-                       p->progress = 1;/* No need to send inband-information progress again. */
+                       p->progress = 1;        /* No need to send inband-information progress again. */
                        isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_INBANDINFO);
-                       ss7_rel(p->ss7);
 
                        /* enable echo canceler here on SS7 calls */
-                       sig_ss7_set_echocanceller(p, 1);
-               } else {
-                       ss7_rel(p->ss7);
+                       if (!p->echocontrol_ind || !(p->ss7->flags & LINKSET_FLAG_USEECHOCONTROL)) {
+                               sig_ss7_set_echocanceller(p, 1);
+                       }
                }
+               ss7_rel(p->ss7);
                /* don't continue in ast_indicate */
                res = 0;
                break;
@@ -1873,6 +3248,14 @@ int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condi
        case AST_CONTROL_SRCUPDATE:
                res = 0;
                break;
+       case AST_CONTROL_CONNECTED_LINE:
+               ss7_connected_line_update(p, ast_channel_connected(chan));
+               res = 0;
+               break;
+       case AST_CONTROL_REDIRECTING:
+               ss7_redirecting_update(p, chan);
+               res = 0;
+               break;
        case -1:
                res = sig_ss7_play_tone(p, -1);
                break;
@@ -1914,6 +3297,7 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law
                /* Release the allocated channel.  Only have to deal with the linkset lock. */
                ast_mutex_lock(&p->ss7->lock);
                p->call_level = SIG_SS7_CALL_LEVEL_IDLE;
+               isup_free_call(p->ss7->ss7, p->ss7call);
                ast_mutex_unlock(&p->ss7->lock);
        }
        return ast;
index 56d393c..e2bc8e4 100644 (file)
@@ -66,6 +66,13 @@ extern "C" {
 #define SS7_NAI_DYNAMIC                -1
 
 #define LINKSET_FLAG_EXPLICITACM (1 << 0)
+#define LINKSET_FLAG_INITIALHWBLO (1 << 1)
+#define LINKSET_FLAG_USEECHOCONTROL (1 << 2)
+#define LINKSET_FLAG_DEFAULTECHOCONTROL (1 << 3)
+#define LINKSET_FLAG_AUTOACM (1 << 4)
+
+#define SS7_BLOCKED_MAINTENANCE        (1 << 0)
+#define SS7_BLOCKED_HARDWARE   (1 << 1)
 
 
 enum sig_ss7_tone {
@@ -84,6 +91,27 @@ enum sig_ss7_law {
        SIG_SS7_ALAW
 };
 
+enum sig_ss7_redirect_idication {
+       SS7_INDICATION_NO_REDIRECTION = 0,
+       SS7_INDICATION_REROUTED_PRES_ALLOWED,
+       SS7_INDICATION_REROUTED_INFO_RESTRICTED,
+       SS7_INDICATION_DIVERTED_PRES_ALLOWED,
+       SS7_INDICATION_DIVERTED_INFO_RESTRICTED,
+       SS7_INDICATION_REROUTED_PRES_RESTRICTED,
+       SS7_INDICATION_DIVERTED_PRES_RESTRICTED,
+       SS7_INDICATION_SPARE
+};
+
+enum sig_ss7_redirect_reason {
+       SS7_REDIRECTING_REASON_UNKNOWN = 0,
+       SS7_REDIRECTING_REASON_USER_BUSY,
+       SS7_REDIRECTING_REASON_NO_ANSWER,
+       SS7_REDIRECTING_REASON_UNCONDITIONAL,
+       SS7_REDIRECTING_REASON_DEFLECTION_DURING_ALERTING,
+       SS7_REDIRECTING_REASON_DEFLECTION_IMMEDIATE_RESPONSE,
+       SS7_REDIRECTING_REASON_UNAVAILABLE
+};
+
 /*! Call establishment life cycle level for simple comparisons. */
 enum sig_ss7_call_level {
        /*! Call does not exist. */
@@ -118,8 +146,6 @@ enum sig_ss7_call_level {
         * We have sent or received CON/ANM.
         */
        SIG_SS7_CALL_LEVEL_CONNECT,
-       /*! Call has collided with incoming call. */
-       SIG_SS7_CALL_LEVEL_GLARE,
 };
 
 struct sig_ss7_linkset;
@@ -153,6 +179,8 @@ struct sig_ss7_callback {
 
        void (* const queue_control)(void *pvt, int subclass);
        void (* const open_media)(void *pvt);
+
+       struct sig_ss7_linkset *(* const find_linkset)(struct ss7 *ss7);
 };
 
 /*! Global sig_ss7 callbacks to the upper layer. */
@@ -192,10 +220,19 @@ struct sig_ss7_chan {
        unsigned int use_callingpres:1;
        unsigned int immediate:1;               /*!< Answer before getting digits? */
 
-       /*! \brief TRUE if the channel is locally blocked.  Set by user and link. */
-       unsigned int locallyblocked:1;
-       /*! \brief TRUE if the channel is remotely blocked.  Set by user and link. */
-       unsigned int remotelyblocked:1;
+       /*!
+        * \brief Bitmask for the channel being locally blocked.
+        * \note 1 maintenance blocked, 2 blocked in hardware.
+        * \note Set by user and link.
+        */
+       unsigned int locallyblocked:2;
+
+       /*!
+        * \brief Bitmask for the channel being remotely blocked.
+        * \note 1 maintenance blocked, 2 blocked in hardware.
+        * \note Set by user and link.
+        */
+       unsigned int remotelyblocked:2;
 
        char context[AST_MAX_CONTEXT];
        char mohinterpret[MAX_MUSICCLASS];
@@ -215,7 +252,15 @@ struct sig_ss7_chan {
        char gen_add_number[50];
        char gen_dig_number[50];
        char orig_called_num[50];
+       int orig_called_presentation;
        char redirecting_num[50];
+       int redirecting_presentation;
+       unsigned char redirect_counter;
+       unsigned char redirect_info;
+       unsigned char redirect_info_ind;
+       unsigned char redirect_info_orig_reas;
+       unsigned char redirect_info_counter;
+       unsigned char redirect_info_reas;
        char generic_name[50];
        unsigned char gen_add_num_plan;
        unsigned char gen_add_nai;
@@ -233,22 +278,41 @@ struct sig_ss7_chan {
        unsigned int call_ref_ident;
        unsigned int call_ref_pc;
        unsigned char calling_party_cat;
+       unsigned int do_hangup; /* What we have to do to clear the call */
+       unsigned int echocontrol_ind;
 
        /*
         * Channel status bits.
         */
-       /*! TRUE if channel is associated with a link that is down. */
+       /*! \brief TRUE if channel is associated with a link that is down. */
        unsigned int inalarm:1;
-       /*! TRUE if this channel is being used for an outgoing call. */
+       /*! \brief TRUE if channel is in service. */
+       unsigned int inservice:1;
+       /*! \brief TRUE if this channel is being used for an outgoing call. */
        unsigned int outgoing:1;
+       /*! \brief TRUE if the channel has completed collecting digits. */
+       unsigned int called_complete:1;
        /*! \brief TRUE if the call has seen inband-information progress through the network. */
        unsigned int progress:1;
-       /*! \brief TRUE if the call has already gone/hungup */
-       unsigned int alreadyhungup:1;
        /*! \brief XXX BOOLEAN Purpose??? */
        unsigned int rlt:1;
-       /*! TRUE if this channel is in loopback. */
+       /*! \brief TRUE if this channel is in loopback. */
        unsigned int loopedback:1;
+
+       /*
+        * Closed User Group fields Q.735.1
+        */
+       /*! \brief Network Identify Code as per Q.763 3.15.a */
+       char cug_interlock_ni[5];
+       /*! \brief Binari Code to uniquely identify a CUG inside the network. */
+       unsigned short cug_interlock_code;
+       /*!
+        * \brief Indication of the call being a CUG call and its permissions.
+        * \note 0 or 1 - non-CUG call
+        * \note 2 - CUG call, outgoing access alowed
+        * \note 3 - CUG call, outgoing access not alowed
+        */
+       unsigned char cug_indicator;
 };
 
 struct sig_ss7_linkset {
@@ -276,6 +340,7 @@ struct sig_ss7_linkset {
        char nationalprefix[10];                        /*!< area access code ('0' for european dialplans) */
        char subscriberprefix[20];                      /*!< area access code + area code ('0'+area code for european dialplans) */
        char unknownprefix[20];                         /*!< for unknown dialplans */
+       char networkroutedprefix[20];
 };
 
 void sig_ss7_set_alarm(struct sig_ss7_chan *p, int in_alarm);
@@ -284,12 +349,19 @@ void *ss7_linkset(void *data);
 
 void sig_ss7_link_alarm(struct sig_ss7_linkset *linkset, int which);
 void sig_ss7_link_noalarm(struct sig_ss7_linkset *linkset, int which);
-int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode);
+int sig_ss7_add_sigchan(struct sig_ss7_linkset *linkset, int which, int ss7type, int transport, int inalarm, int networkindicator, int pointcode, int adjpointcode, int cur_slc);
+
+int sig_ss7_reset_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc);
+int sig_ss7_reset_group(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc, int range);
+int sig_ss7_cic_blocking(struct sig_ss7_linkset *linkset, int do_block, int cic);
+int sig_ss7_group_blocking(struct sig_ss7_linkset *linkset, int do_block, int startcic, int endcic, unsigned char state[], int type);
 
 int sig_ss7_available(struct sig_ss7_chan *p);
 int sig_ss7_call(struct sig_ss7_chan *p, struct ast_channel *ast, const char *rdest);
 int sig_ss7_hangup(struct sig_ss7_chan *p, struct ast_channel *ast);
 int sig_ss7_answer(struct sig_ss7_chan *p, struct ast_channel *ast);
+int sig_ss7_find_cic(struct sig_ss7_linkset *linkset, int cic, unsigned int dpc);
+int sig_ss7_find_cic_range(struct sig_ss7_linkset *linkset, int startcic, int endcic, unsigned int dpc);
 void sig_ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, struct sig_ss7_chan *pchan);
 int sig_ss7_indicate(struct sig_ss7_chan *p, struct ast_channel *chan, int condition, const void *data, size_t datalen);
 struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law,
@@ -298,10 +370,14 @@ struct ast_channel *sig_ss7_request(struct sig_ss7_chan *p, enum sig_ss7_law law
 void sig_ss7_chan_delete(struct sig_ss7_chan *doomed);
 struct sig_ss7_chan *sig_ss7_chan_new(void *pvt_data, struct sig_ss7_linkset *ss7);
 void sig_ss7_init_linkset(struct sig_ss7_linkset *ss7);
+void sig_ss7_free_isup_call(struct sig_ss7_linkset *linkset, int channel);
 
 void sig_ss7_cli_show_channels_header(int fd);
 void sig_ss7_cli_show_channels(int fd, struct sig_ss7_linkset *linkset);
 
+int sig_ss7_cb_hangup(struct ss7 *ss7, int cic, unsigned int dpc, int cause, int do_hangup);
+void sig_ss7_cb_call_null(struct ss7 *ss7, struct isup_call *c, int lock);
+void sig_ss7_cb_notinservice(struct ss7 *ss7, int cic, unsigned int dpc);
 
 /* ------------------------------------------------------------------- */
 
index 83701ef..2c0488a 100644 (file)
@@ -1371,12 +1371,35 @@ pickupgroup=1
 
 ; This option is used to disable automatic sending of ACM when the call is started
 ; in the dialplan.  If you do use this option, you will need to use the Proceeding()
-; application in the dialplan to send ACM.
-;ss7_explictacm=yes
+; application in the dialplan to send ACM or enable ss7_autoacm below.
+;ss7_explicitacm=yes
+
+; Use this option to automatically send ACM when the call rings or is answered and
+; has not seen proceeding yet. If you use this option, you should disable ss7_explicitacm.
+; You may still use Proceeding() to explicitly send an ACM from the dialplan.
+;ss7_autoacm=yes
+
+; Create the linkset with all CICs in hardware remotely blocked state.
+;ss7_initialhwblo=yes
+
+; This option is whether or not to trust the remote echo control indication.  This means
+; that in cases where echo control is reported by the remote end, we will trust them and
+; not enable echo cancellation on the call.
+;ss7_use_echocontrol=yes
+
+; This option is to set what our echo control indication is to the other end.  Set to
+; yes to indicate that we are using echo cancellation or no if we are not.
+;ss7_default_echocontrol=yes
 
 ; All settings apply to linkset 1
 ;linkset = 1
 
+; Set the Signaling Link Code (SLC) for each sigchan.
+; If you manually set any you need to manually set all.
+; Should be defined before sigchan.
+; The default SLC starts with zero and increases for each defined sigchan.
+;slc=
+
 ; Point code of the linkset.  For ITU, this is the decimal number
 ; format of the point code.  For ANSI, this can either be in decimal
 ; number format or in the xxx-xxx-xxx format
@@ -1410,6 +1433,30 @@ pickupgroup=1
 ; Channels to associate with CICs on this linkset
 ;channel = 25-47
 ;
+
+; Set this option if you wish to send an Information Request Message (INR) request
+; if no calling party number is specified. This will attempt to tell the other end
+; to send it anyways. Should be defined after sigchan.
+;inr_if_no_calling=yes
+
+; Set this to set whether or not the originating access is (non) ISDN in the forward and
+; backward call indicators. Should be defined after sigchan
+;non_isdn_access=yes
+
+; This sets the number of binary places to shift the CIC when doing load balancing between
+; sigchans on a linkset. Should be defined after sigchan. Default 0
+;sls_shift = 0
+
+; Send custom cause_location value
+; Should be defined after sigchan. Default 1 (private local)
+;cause_location=1
+
+; SS7 timers (ISUP and MTP3) should be explicitly defined for each linkset to be used.
+; For a full list of supported timers and their default values (applicable for both ITU
+; and ANSI) see ss7.timers
+; Should be defined after sigchan
+;#include ss7.timers
+
 ; For more information on setting up SS7, see the README file in libss7 or
 ; https://wiki.asterisk.org/wiki/display/AST/Signaling+System+Number+7
 ; ----------------- SS7 Options ----------------------------------------
diff --git a/configs/ss7.timers.sample b/configs/ss7.timers.sample
new file mode 100644 (file)
index 0000000..9cf9bd1
--- /dev/null
@@ -0,0 +1,65 @@
+;;;;;  ITU-T Q.707 timers
+
+;mtp3_timer.q707_t1 = 4000
+;mtp3_timer.q707_t2 = 30000
+
+;;;;;  MTP3 timers as specified in ITU-T Q.704 or ANSI T1.111-2001
+
+mtp3_timer.t1 = 500
+mtp3_timer.t2 = 700
+mtp3_timer.t3 = 500
+mtp3_timer.t4 = 500
+mtp3_timer.t5 = 500
+mtp3_timer.t6 = 500
+mtp3_timer.t7 = 1000
+
+mtp3_timer.t10 = 60000
+
+mtp3_timer.t12 = 800
+mtp3_timer.t13 = 800
+mtp3_timer.t14 = 2000
+
+;      enable for ITU only. Timers after T17 are defined differently for ANSI
+;mtp3_timer.t19 = 67000
+;mtp3_timer.t21 = 63000
+;
+;mtp3_timer.t22 = 300000
+;mtp3_timer.t23 = 300000
+
+
+;;;;;  ISUP timers as specified in ITU-T Q.764 or ANSI T1.113-2000
+
+isup_timer.t1 = 15000
+;isup_timer.t2 = 180000                ; ITU only
+
+;isup_timer.t5 = 300000                ; for ITU
+;isup_timer.t5 = 60000         ; for ANSI
+isup_timer.t6 = 30000
+isup_timer.t7 = 20000
+isup_timer.t8 = 10000
+
+;isup_timer.t10 = 4000         ; ITU only
+
+isup_timer.t12 = 15000
+;isup_timer.t13 = 300000       ; for ITU
+;isup_timer.t13 = 60000                ; for ANSI
+isup_timer.t14 = 15000
+;isup_timer.t15 = 300000       ; for ITU
+;isup_timer.t15 = 60000                ; for ANSI
+isup_timer.t16 = 15000
+;isup_timer.t17 = 300000       ; for ITU
+;isup_timer.t17 = 60000                ; for ANSI
+isup_timer.t18 = 15000
+;isup_timer.t19 = 300000       ; for ITU
+;isup_timer.t19 = 60000                ; for ANSI
+isup_timer.t20 = 15000
+;isup_timer.t21 = 300000       ; for ITU
+;isup_timer.t21 = 60000                ; for ANSI
+isup_timer.t22 = 15000
+;isup_timer.t23 = 300000       ; for ITU
+;isup_timer.t23 = 60000                ; for ANSI
+
+isup_timer.t27 = 240000
+
+isup_timer.t33 = 12000
+;isup_timer.t35 = 15000                ; ITU only
index a028170..59d48c6 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 412977 .
+# From configure.ac Revision: 413772 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.68 for asterisk trunk.
 #
@@ -26664,7 +26664,7 @@ fi
 
 fi
 
-# Check for libss7 v1.0 branch compatible version.
+# Check for libss7 v2.0 branch compatible version.
 
 if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then
    pbxlibdir=""
@@ -26676,7 +26676,7 @@ if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then
          pbxlibdir="-L${SS7_DIR}"
       fi
    fi
-   pbxfuncname="ss7_set_adjpc"
+   pbxfuncname="ss7_set_isup_timer"
    if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
       AST_SS7_FOUND=yes
    else
index da9d2d7..95b7dd0 100644 (file)
@@ -2096,8 +2096,8 @@ if test "x${PBX_SPANDSP}" = "x1" ; then
        AST_EXT_LIB_CHECK([SPANDSP], [spandsp], [t38_terminal_init], [spandsp.h], [-ltiff])
 fi
 
-# Check for libss7 v1.0 branch compatible version.
-AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_set_adjpc], [libss7.h])
+# Check for libss7 v2.0 branch compatible version.
+AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_set_isup_timer], [libss7.h])
 
 AST_EXT_LIB_CHECK([OPENR2], [openr2], [openr2_chan_new], [openr2.h])