Merge in SS7 changes.... need to still cleanup zapata.conf
authorMatthew Fredrickson <creslin@digium.com>
Thu, 21 Sep 2006 18:41:33 +0000 (18:41 +0000)
committerMatthew Fredrickson <creslin@digium.com>
Thu, 21 Sep 2006 18:41:33 +0000 (18:41 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@43427 65c4cc65-6c06-0410-ace0-fbb531ad65f3

build_tools/menuselect-deps.in
channels/chan_zap.c
configs/zapata.conf.sample
configure.ac
include/asterisk/autoconfig.h.in
makeopts.in

index 5c790e0..7017e8a 100644 (file)
@@ -18,6 +18,7 @@ OSSAUDIO=@PBX_OSS@
 PGSQL=@PBX_PGSQL@
 POPT=@PBX_POPT@
 PRI=@PBX_PRI@
+SS7=@PBX_SS7@
 QT=@PBX_QT@
 RADIUS=@PBX_RADIUS@
 SPEEX=@PBX_SPEEX@
index f39e25b..5ed8344 100644 (file)
@@ -41,6 +41,7 @@
        <depend>zaptel</depend>
        <depend>tonezone</depend>
        <use>pri</use>
+       <use>ss7</use>
  ***/
 
 #include "asterisk.h"
@@ -71,6 +72,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <libpri.h>
 #endif
 
+#ifdef HAVE_SS7
+#include <libss7.h>
+#endif
+
 #include "asterisk/lock.h"
 #include "asterisk/channel.h"
 #include "asterisk/config.h"
@@ -158,6 +163,9 @@ static const char tdesc[] = "Zapata Telephony Driver"
 #ifdef HAVE_PRI
                " w/PRI"
 #endif
+#ifdef HAVEL_LIBSS7
+              "w/SS7"
+#endif
 ;
 
 static const char config[] = "zapata.conf";
@@ -178,6 +186,7 @@ static const char config[] = "zapata.conf";
 #define SIG_FXOGS      ZT_SIG_FXOGS
 #define SIG_FXOKS      ZT_SIG_FXOKS
 #define SIG_PRI                ZT_SIG_CLEAR
+#define SIG_SS7                (0x1000000 | ZT_SIG_CLEAR)
 #define        SIG_SF          ZT_SIG_SF
 #define SIG_SFWINK     (0x0100000 | ZT_SIG_SF)
 #define SIG_SF_FEATD   (0x0200000 | ZT_SIG_SF)
@@ -402,6 +411,36 @@ struct zt_pvt;
 
 static int ringt_base = DEFAULT_RINGT;
 
+#ifdef HAVE_SS7
+
+#define LINKSTATE_INALARM      (1 << 0)
+#define LINKSTATE_STARTING     (1 << 1)
+#define LINKSTATE_UP           (1 << 2)
+#define LINKSTATE_DOWN         (1 << 3)
+
+struct zt_ss7 {
+       pthread_t master;                                               /*!< Thread of master */
+       ast_mutex_t lock;
+       int fds[NUM_DCHANS];
+       int numsigchans;
+       int linkstate[NUM_DCHANS];
+       int numchans;
+       int type;
+       struct ss7 *ss7;
+       struct zt_pvt *pvts[MAX_CHANNELS];                              /*!< Member channel pvt structs */
+};
+
+static struct zt_ss7 linksets[NUM_SPANS];
+
+static int cur_ss7type = -1;
+static int cur_linkset = -1;
+static int cur_pointcode = -1;
+static int cur_cicbeginswith = -1;
+static int cur_adjpointcode = -1;
+static int cur_networkindicator = -1;
+static int cur_defaultdpc = -1;
+#endif /* HAVE_SS7 */
+
 #ifdef HAVE_PRI
 
 #define PVT_TO_CHANNEL(p) (((p)->prioffset) | ((p)->logicalspan << 8) | (p->pri->mastertrunkgroup ? 0x10000 : 0))
@@ -605,6 +644,9 @@ static struct zt_pvt {
        unsigned int resetting:1;
        unsigned int setup_ack:1;
 #endif
+#if defined(HAVE_SS7)
+       unsigned int blocked:1;
+#endif
        unsigned int use_smdi:1;                /* Whether to use SMDI on this channel */
        struct ast_smdi_interface *smdi_iface;  /* The serial port to listen for SMDI data on */
 
@@ -691,6 +733,12 @@ static struct zt_pvt {
 #endif 
        int polarity;
        int dsp_features;
+#ifdef HAVE_SS7
+       struct zt_ss7 *ss7;
+       struct isup_call *ss7call;
+       int transcap;
+       int cic;                                                        /*!< CIC associated with channel */
+#endif
        char begindigit;
 } *iflist = NULL, *ifend = NULL;
 
@@ -758,6 +806,30 @@ static inline int pri_grab(struct zt_pvt *pvt, struct zt_pri *pri)
 }
 #endif
 
+#ifdef HAVE_SS7
+static inline void ss7_rel(struct zt_ss7 *ss7)
+{
+       ast_mutex_unlock(&ss7->lock);
+}
+
+static inline int ss7_grab(struct zt_pvt *pvt, struct zt_ss7 *pri)
+{
+       int res;
+       /* Grab the lock first */
+       do {
+               res = ast_mutex_trylock(&pri->lock);
+               if (res) {
+                       ast_mutex_unlock(&pvt->lock);
+                       /* Release the lock and try again */
+                       usleep(1);
+                       ast_mutex_lock(&pvt->lock);
+               }
+       } while (res);
+       /* Then break the poll */
+       pthread_kill(pri->master, SIGURG);
+       return 0;
+}
+#endif
 #define NUM_CADENCE_MAX 25
 static int num_cadence = 4;
 static int user_has_defined_cadences = 0;
@@ -833,16 +905,32 @@ static void wakeup_sub(struct zt_pvt *p, int a, void *pri)
 #endif                 
 }
 
+static void zap_queue_frame(struct zt_pvt *p, struct ast_frame *f, void *data)
+{
 #ifdef HAVE_PRI
-static void zap_queue_frame(struct zt_pvt *p, struct ast_frame *f, struct zt_pri *pri)
-#else
-static void zap_queue_frame(struct zt_pvt *p, struct ast_frame *f, void *pri)
+       struct zt_pri *pri = (struct zt_pri*) data;
+#endif
+#ifdef HAVE_SS7
+       struct zt_ss7 *ss7 = (struct zt_ss7*) data;
 #endif
-{
        /* We must unlock the PRI to avoid the possibility of a deadlock */
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+       if (data) {
+               switch (p->sig) {
 #ifdef HAVE_PRI
-       if (pri)
-               ast_mutex_unlock(&pri->lock);
+               case SIG_PRI:
+                       ast_mutex_unlock(&pri->lock);
+                       break;
+#endif
+#ifdef HAVE_SS7
+               case SIG_SS7:
+                       ast_mutex_unlock(&ss7->lock);
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
 #endif         
        for (;;) {
                if (p->owner) {
@@ -858,9 +946,24 @@ static void zap_queue_frame(struct zt_pvt *p, struct ast_frame *f, void *pri)
                } else
                        break;
        }
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+       if (data) {
+               switch (p->sig) {
 #ifdef HAVE_PRI
-       if (pri)
-               ast_mutex_lock(&pri->lock);
+               case SIG_PRI:
+                       ast_mutex_lock(&pri->lock);
+                       break;
+#endif
+#ifdef HAVE_SS7
+               case SIG_SS7:
+                       ast_mutex_lock(&ss7->lock);
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
+
 #endif         
 }
 
@@ -1220,6 +1323,8 @@ static char *zap_sig2str(int sig)
                return "FXO Kewlstart";
        case SIG_PRI:
                return "PRI Signalling";
+       case SIG_SS7:
+               return "SS7 Signalling";
        case SIG_SF:
                return "SF (Tone) Signalling Immediate";
        case SIG_SFWINK:
@@ -1443,7 +1548,7 @@ static void zt_enable_ec(struct zt_pvt *p)
                return;
        }
        if (p->echocancel) {
-               if (p->sig == SIG_PRI) {
+               if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7)) {
                        x = 1;
                        res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &x);
                        if (res)
@@ -1645,7 +1750,7 @@ static inline int zt_confmute(struct zt_pvt *p, int muted)
 {
        int x, y, res;
        x = muted;
-       if (p->sig == SIG_PRI) {
+       if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7)) {
                y = 1;
                res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &y);
                if (res)
@@ -2051,6 +2156,7 @@ static int zt_call(struct ast_channel *ast, char *rdest, int timeout)
                ast_setstate(ast, AST_STATE_UP);
                break;          
        case SIG_PRI:
+       case SIG_SS7:
                /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */
                p->dialdest[0] = '\0';
                break;
@@ -2059,6 +2165,37 @@ static int zt_call(struct ast_channel *ast, char *rdest, int timeout)
                ast_mutex_unlock(&p->lock);
                return -1;
        }
+#ifdef HAVE_SS7
+       if (p->ss7) {
+               c = strchr(dest, '/');
+               if (c)
+                       c++;
+               else
+                       c = dest;
+
+               if (!p->hidecallerid) {
+                       l = ast->cid.cid_num;
+               } else {
+                       l = NULL;
+               }
+
+               ss7_grab(p, p->ss7);
+               p->digital = IS_DIGITAL(ast->transfercapability);
+               p->ss7call = isup_new_call(p->ss7->ss7);
+
+               if (!p->ss7call) {
+                       ss7_rel(p->ss7);
+                       ast_mutex_unlock(&p->lock);
+                       ast_log(LOG_ERROR, "Unable to allocate new SS7 call!\n");
+                       return -1;
+               }
+
+               isup_init_call(p->ss7->ss7, p->ss7call, p->cic, c + p->stripmsd, l);
+
+               isup_iam(p->ss7->ss7, p->ss7call);
+               ss7_rel(p->ss7);
+       }
+#endif /* HAVE_SS7 */
 #ifdef HAVE_PRI
        if (p->pri) {
                struct pri_sr *sr;
@@ -2438,7 +2575,7 @@ static int zt_hangup(struct ast_channel *ast)
        
        index = zt_get_index(ast, p, 1);
 
-       if (p->sig == SIG_PRI) {
+       if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7)) {
                x = 1;
                ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
        }
@@ -2592,6 +2729,23 @@ static int zt_hangup(struct ast_channel *ast)
                if (res < 0) 
                        ast_log(LOG_WARNING, "Unable to set law on channel %d to default\n", p->channel);
                /* Perform low level hangup if no owner left */
+#ifdef HAVE_SS7
+               if (p->ss7) {
+                       if (p->ss7call) {
+                               if (!ss7_grab(p, p->ss7)) {
+                                       if (!p->alreadyhungup) {
+                                               isup_rel(p->ss7->ss7, p->ss7call, ast->hangupcause ? ast->hangupcause : -1);
+                                               ss7_rel(p->ss7);
+                                               p->alreadyhungup = 1;
+                                       } else
+                                               ast_log(LOG_WARNING, "Trying to hangup twice!\n");
+                               } else {
+                                       ast_log(LOG_WARNING, "Unable to grab SS7 on CIC %d\n", p->cic);
+                                       res = -1;
+                               }
+                       }
+               }
+#endif
 #ifdef HAVE_PRI
                if (p->pri) {
 #ifdef SUPPORT_USERUSER
@@ -2645,7 +2799,7 @@ static int zt_hangup(struct ast_channel *ast)
                        }
                }
 #endif
-               if (p->sig && (p->sig != SIG_PRI))
+               if (p->sig && ((p->sig != SIG_PRI) && (p->sig != SIG_SS7)))
                        res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
                if (res < 0) {
                        ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
@@ -2696,7 +2850,7 @@ static int zt_hangup(struct ast_channel *ast)
                update_conf(p);
                reset_conf(p);
                /* Restore data mode */
-               if (p->sig == SIG_PRI) {
+               if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7)) {
                        x = 0;
                        ast_channel_setoption(ast,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
                }
@@ -2819,6 +2973,18 @@ static int zt_answer(struct ast_channel *ast)
                }
                break;
 #endif
+#ifdef HAVE_SS7
+       case SIG_SS7:
+               if (!ss7_grab(p, p->ss7)) {
+                       p->proceeding = 1;
+                       res = isup_anm(p->ss7->ss7, p->ss7call);
+                       ss7_rel(p->ss7);
+               } else {
+                       ast_log(LOG_WARNING, "Unable to grab SS7 on span %d\n", p->span);
+                       res = -1;
+               }
+               break;
+#endif
        case 0:
                ast_mutex_unlock(&p->lock);
                return 0;
@@ -3746,7 +3912,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                                                "Alarm: %s\r\n"
                                                                "Channel: %d\r\n",
                                                                alarm2str(res), p->channel);
-#ifdef HAVE_LIBPRI
+#ifdef HAVE_PRI
                        if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) {
                                /* fall through intentionally */
                        } else {
@@ -5028,7 +5194,19 @@ static int zt_indicate(struct ast_channel *chan, int condition, const void *data
                                }
                                p->alerting = 1;
                        }
+
+#endif
+#ifdef HAVE_SS7
+                       if ((!p->alerting) && (p->sig == SIG_SS7) && p->ss7 && !p->outgoing && (chan->_state != AST_STATE_UP)) {
+                               if (p->ss7->ss7) {
+                                       ss7_grab(p, p->ss7);
+                                       isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_ALERTING);
+                                       p->alerting = 1;
+                                       ss7_rel(p->ss7);
+                               }
+                       }
 #endif
+
                        res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_RINGTONE);
                        if (chan->_state != AST_STATE_UP) {
                                if ((chan->_state != AST_STATE_RING) ||
@@ -5053,6 +5231,17 @@ static int zt_indicate(struct ast_channel *chan, int condition, const void *data
                                p->proceeding = 1;
                        }
 #endif
+#ifdef HAVE_SS7
+                       if (!p->proceeding && p->sig==SIG_SS7 && p->ss7 && !p->outgoing) {
+                               if (p->ss7->ss7) {
+                                       ss7_grab(p, p->ss7);
+                                       isup_cpg(p->ss7->ss7, p->ss7call, CPG_EVENT_INBANDINFO);
+                                       p->proceeding = 1;
+                                       ss7_rel(p->ss7);
+
+                               }
+                       }
+#endif
                        /* don't continue in ast_indicate */
                        res = 0;
                        break;
@@ -5243,7 +5432,7 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
                                i->dsp_features = features & ~DSP_PROGRESS_TALK;
 #ifdef HAVE_PRI
                                /* We cannot do progress detection until receives PROGRESS message */
-                               if (i->outgoing && (i->sig == SIG_PRI)) {
+                               if (i->outgoing && ((i->sig == SIG_PRI) || (i->sig == SIG_SS7))) {
                                        /* Remember requested DSP features, don't treat
                                           talking as ANSWER */
                                        features = 0;
@@ -5306,7 +5495,7 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
 #endif
        tmp->cid.cid_pres = i->callingpres;
        tmp->cid.cid_ton = i->cid_ton;
-#ifdef HAVE_PRI
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
        tmp->transfercapability = transfercapability;
        pbx_builtin_setvar_helper(tmp, "TRANSFERCAPABILITY", ast_transfercapability2str(transfercapability));
        if (transfercapability & PRI_TRANS_CAP_DIGITAL)
@@ -6664,6 +6853,7 @@ static int handle_init_event(struct zt_pvt *i, int event)
                        zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK);
                        break;
                case SIG_PRI:
+               case SIG_SS7:
                        zt_disable_ec(i);
                        res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
                        break;
@@ -7038,6 +7228,16 @@ static int pri_create_spanmap(int span, int trunkgroup, int logicalspan)
 
 #endif
 
+#ifdef HAVE_SS7
+static struct zt_ss7 * ss7_resolve_linkset(int linkset)
+{
+       if ((linkset < 0) || (linkset >= NUM_SPANS))
+               return NULL;
+       else
+               return &linksets[linkset - 1];
+}
+#endif /* HAVE_SS7 */
+
 static struct zt_pvt *mkintf(int channel, int signalling, int outsignalling, int radio, struct zt_pri *pri, int reloading)
 {
        /* Make a zt_pvt structure for this interface (or CRV if "pri" is specified) */
@@ -7131,6 +7331,35 @@ static struct zt_pvt *mkintf(int channel, int signalling, int outsignalling, int
                                        return NULL;
                                }
                        }
+#ifdef HAVE_SS7
+                       if (signalling == SIG_SS7) {
+                               struct zt_ss7 *ss7;
+                               int clear = 0;
+                               if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &clear)) {
+                                       ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno));
+                                       destroy_zt_pvt(&tmp);
+                                       return NULL;
+                               }
+
+                               ss7 = ss7_resolve_linkset(cur_linkset);
+                               if (!ss7) {
+                                       ast_log(LOG_ERROR, "Unable to find linkset %d\n", cur_linkset);
+                                       destroy_zt_pvt(&tmp);
+                                       return NULL;
+                               }
+                               if (cur_cicbeginswith < 0) {
+                                       ast_log(LOG_ERROR, "Need to set cicbeginswith for the channels!\n");
+                                       destroy_zt_pvt(&tmp);
+                                       return NULL;
+                               }
+
+                               tmp->cic = cur_cicbeginswith++;
+
+                               tmp->ss7 = ss7;
+                               tmp->ss7call = NULL;
+                               ss7->pvts[ss7->numchans++] = tmp;
+                       }
+#endif
 #ifdef HAVE_PRI
                        if ((signalling == SIG_PRI) || (signalling == SIG_GR303FXOKS) || (signalling == SIG_GR303FXSKS)) {
                                int offset;
@@ -7419,7 +7648,7 @@ static struct zt_pvt *mkintf(int channel, int signalling, int outsignalling, int
                                ast_dsp_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax);
                        update_conf(tmp);
                        if (!here) {
-                               if (signalling != SIG_PRI)
+                               if ((signalling != SIG_PRI) && (signalling != SIG_SS7))
                                        /* Hang it up to be sure it's good */
                                        zt_set_hook(tmp->subs[SUB_REAL].zfd, ZT_ONHOOK);
                        }
@@ -7533,6 +7762,15 @@ static inline int available(struct zt_pvt *p, int channelmatch, int groupmatch,
                                return 1;
                }
 #endif
+#ifdef HAVE_SS7
+               /* Trust PRI */
+               if (p->ss7) {
+                       if (p->ss7call || p->blocked)
+                               return 0;
+                       else
+                               return 1;
+               }
+#endif
                if (!(p->radio || (p->oprmode < 0)))
                {
                        if (!p->sig || (p->sig == SIG_FXSLS))
@@ -7880,6 +8118,454 @@ next:
        return tmp;
 }
 
+#ifdef HAVE_SS7
+static int zt_setlaw(int zfd, int law);
+
+static int ss7_find_cic(struct zt_ss7 *linkset, int cic)
+{
+       int i;
+       int winner = -1;
+       for (i = 0; i < linkset->numchans; i++) {
+               if (linkset->pvts[i] && (linkset->pvts[i]->cic == cic)) {
+                       winner = i;
+               }
+       }
+       return winner;
+}
+
+static inline void ss7_block_cics(struct zt_ss7 *linkset, int startcic, int endcic, unsigned char state[], int block)
+{
+       int i;
+
+       for (i = 0; i < linkset->numchans; i++) {
+               if (linkset->pvts[i] && ((linkset->pvts[i]->cic >= startcic) && (linkset->pvts[i]->cic <= endcic))) {
+                       if (state) {
+                               if (state[i])
+                                       linkset->pvts[i]->blocked = block;
+                       } else
+                               linkset->pvts[i]->blocked = block;
+               }
+       }
+}
+
+static int ss7_reset_linkset(struct zt_ss7 *linkset)
+{
+       int i, startcic = -1, endcic;
+
+       if (linkset->numchans <= 0)
+               return 0;
+
+       startcic = linkset->pvts[0]->cic;
+
+       for (i = 0; i < linkset->numchans; i++) {
+               if (linkset->pvts[i+1] && (linkset->pvts[i+1]->cic - linkset->pvts[i]->cic) == 1) {
+                       continue;
+               } else {
+                       endcic = linkset->pvts[i]->cic;
+                       ast_verbose(VERBOSE_PREFIX_3 "Resetting CICs %d to %d\n", startcic, endcic);
+                       isup_grs(linkset->ss7, startcic, endcic);
+
+                       if (linkset->pvts[i+1])
+                               startcic = linkset->pvts[i+1]->cic;
+               }
+       }
+}
+
+static void zt_loopback(struct zt_pvt *p, int enable)
+{
+       if (ioctl(p->subs[SUB_REAL].zfd, ZT_LOOPBACK, &enable)) {
+               ast_log(LOG_WARNING, "Unable to set loopback on channel %d\n", p->channel);
+               return;
+       }
+}
+
+static void ss7_start_call(struct zt_pvt *p, struct zt_ss7 *linkset)
+{
+       struct ss7 *ss7 = linkset->ss7;
+       int res;
+       int law = 1;
+       struct ast_channel *c;
+
+       if (ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1)
+               ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", p->channel, law);
+       
+       if (linkset->type == SS7_ITU)
+               law = ZT_LAW_ALAW;
+       else
+               law = ZT_LAW_MULAW;
+
+       res = zt_setlaw(p->subs[SUB_REAL].zfd, law);
+       if (res < 0) 
+               ast_log(LOG_WARNING, "Unable to set law on channel %d\n", p->channel);
+       
+       isup_acm(ss7, p->ss7call);
+
+       ast_mutex_unlock(&linkset->lock);
+       c = zt_new(p, AST_STATE_RING, 1, SUB_REAL, law, 0);
+       ast_mutex_lock(&linkset->lock);
+       if (c)
+               ast_verbose(VERBOSE_PREFIX_3 "Accepting call to '%s' on CIC %d\n", p->exten, p->cic);
+       else
+               ast_log(LOG_WARNING, "Unable to start PBX on CIC %d\n", p->cic);
+}
+
+static void *ss7_linkset(void *data)
+{
+       int res, i;
+       struct timeval *next = NULL, tv;
+       struct zt_ss7 *linkset = (struct zt_ss7 *) data;
+       struct ss7 *ss7 = linkset->ss7;
+       ss7_event *e = NULL;
+       struct zt_pvt *p;
+       int chanpos;
+       pthread_attr_t attr;
+       struct pollfd pollers[NUM_DCHANS];
+       int cic;
+       int nextms = 0;
+
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+       ss7_start(ss7);
+
+       while(1) {
+               ast_mutex_lock(&linkset->lock);
+               if ((next = ss7_schedule_next(ss7))) {
+                       gettimeofday(&tv, NULL);
+                       tv.tv_sec = next->tv_sec - tv.tv_sec;
+                       tv.tv_usec = next->tv_usec - tv.tv_usec;
+                       if (tv.tv_usec < 0) {
+                               tv.tv_usec += 1000000;
+                               tv.tv_sec -= 1;
+                       }
+                       if (tv.tv_sec < 0) {
+                               tv.tv_sec = 0;
+                               tv.tv_usec = 0;
+                       }
+                       nextms = tv.tv_sec * 1000;
+                       nextms += tv.tv_usec / 1000;
+               }
+               ast_mutex_unlock(&linkset->lock);
+
+               for (i = 0; i < linkset->numsigchans; i++) {
+                       pollers[i].fd = linkset->fds[i];
+                       pollers[i].events = POLLIN | POLLOUT | POLLPRI;
+                       pollers[i].revents = 0;
+               }
+
+               res = poll(pollers, linkset->numsigchans, nextms);
+               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) {
+                               int x;
+                               if (ioctl(pollers[i].fd, ZT_GETEVENT, &x)) {
+                                       ast_log(LOG_ERROR, "Error in exception retrieval!\n");
+                               }
+                               switch (x) {
+                               case ZT_EVENT_OVERRUN:
+                                       ast_log(LOG_ERROR, "Overrun detected!\n");
+                                       break;
+                               case ZT_EVENT_BADFCS:
+                                       ast_log(LOG_ERROR, "Bad FCS!\n");
+                                       break;
+                               case ZT_EVENT_ABORT:
+                                       ast_log(LOG_ERROR, "HDLC Abort!\n");
+                                       break;
+                               case ZT_EVENT_ALARM:
+                                       ast_log(LOG_ERROR, "Alarm on link!\n");
+                                       linkset->linkstate[i] |= (LINKSTATE_DOWN | LINKSTATE_INALARM);
+                                       linkset->linkstate[i] &= ~LINKSTATE_UP;
+                                       ss7_link_alarm(ss7, pollers[i].fd);
+                                       break;
+                               case ZT_EVENT_NOALARM:
+                                       ast_log(LOG_ERROR, "Alarm cleared on link\n");
+                                       linkset->linkstate[i] &= ~(LINKSTATE_INALARM | LINKSTATE_DOWN);
+                                       linkset->linkstate[i] |= LINKSTATE_STARTING;
+                                       ss7_link_noalarm(ss7, pollers[i].fd);
+                                       break;
+                               default:
+                                       ast_log(LOG_ERROR, "Got exception %d!\n", x);
+                                       break;
+                               }
+                       }
+
+                       if (pollers[i].revents & POLLIN)
+                               res = ss7_read(ss7, pollers[i].fd);
+                       if (pollers[i].revents & POLLOUT) {
+                               res = ss7_write(ss7, pollers[i].fd);
+                               if (res < 0) {
+                                       ast_log(LOG_ERROR, "Error in write %s", strerror(errno));
+                               }
+                       }
+               }
+
+#if 0
+               if (res < 0)
+                       exit(-1);
+#endif
+
+               while ((e = ss7_check_event(ss7))) {
+                       switch (e->e) {
+                       case SS7_EVENT_UP:
+                               ast_verbose("--- SS7 Up ---\n");
+                               ss7_reset_linkset(linkset);
+                               break;
+                       case MTP2_LINK_UP:
+                               ast_log(LOG_DEBUG, "MTP2 link up\n");
+                               break;
+                       case ISUP_EVENT_CPG:
+                               chanpos = ss7_find_cic(linkset, e->cpg.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "CPG on unconfigured CIC %d\n", e->cpg.cic);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               ast_mutex_lock(&p->lock);
+                               switch (e->cpg.event) {
+                               case CPG_EVENT_ALERTING:
+                                       p->alerting = 1;
+                                       p->subs[SUB_REAL].needringing = 1;
+                                       break;
+                               case CPG_EVENT_PROGRESS:
+                               case CPG_EVENT_INBANDINFO:
+                                       {
+                                               struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROGRESS, };
+                                               ast_log(LOG_DEBUG, "Queuing frame PROGRESS on CIC %d\n", p->cic);
+                                               zap_queue_frame(p, &f, ss7);
+                                               p->progress = 1;
+                                       }
+                                       break;
+                               default:
+                                       ast_log(LOG_DEBUG, "Do not handle CPG with event type 0x%x\n", e->cpg.event);
+                               }
+
+                               ast_mutex_unlock(&p->lock);
+                               break;
+                       case ISUP_EVENT_RSC:
+                               ast_verbose("Resetting CIC %d\n", e->rsc.cic);
+                               chanpos = ss7_find_cic(linkset, e->rsc.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "RSC on unconfigured CIC %d\n", e->rsc.cic);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               p->blocked = 0;
+                               isup_rlc(ss7, e->rsc.call);
+                               break;
+                       case ISUP_EVENT_GRS:
+                               ast_log(LOG_DEBUG, "Got Reset for CICs %d to %d: Acknowledging\n", e->grs.startcic, e->grs.endcic);
+                               isup_gra(ss7, e->grs.startcic, e->grs.endcic);
+                               ss7_block_cics(linkset, e->grs.startcic, e->grs.endcic, NULL, 0);
+                               break;
+                       case ISUP_EVENT_GRA:
+                               ast_log(LOG_DEBUG, "Got GRA from CIC %d to %d.\n", e->gra.startcic, e->gra.endcic);
+                               ss7_block_cics(linkset, e->gra.startcic, e->gra.endcic, e->gra.status, 1);
+                               break;
+                       case ISUP_EVENT_IAM:
+                               ast_log(LOG_DEBUG, "Got IAM for CIC %d and number %s\n", e->iam.cic, e->iam.called_party_num);
+                               chanpos = ss7_find_cic(linkset, e->iam.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "IAM on unconfigured CIC %d\n", e->iam.cic);
+                                       isup_rel(ss7, e->iam.call, -1);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               ast_mutex_lock(&p->lock);
+                               if (p->owner) {
+                                       if (p->ss7call == e->iam.call) {
+                                               ast_mutex_unlock(&p->lock);
+                                               ast_log(LOG_WARNING, "Duplicate IAM requested on CIC %d\n", e->iam.cic);
+                                               break;
+                                       } else {
+                                               ast_mutex_unlock(&p->lock);
+                                               ast_log(LOG_WARNING, "Ring requested on CIC %d already in use!\n", e->iam.cic);
+                                               break;
+                                       }
+                               }
+
+                               p->ss7call = e->iam.call;
+
+                               if (p->use_callerid)
+                                       ast_copy_string(p->cid_num, e->iam.calling_party_num, sizeof(p->cid_num));
+                               else
+                                       p->cid_num[0] = 0;
+
+                               if (p->immediate) {
+                                       p->exten[0] = 's';
+                                       p->exten[1] = '\0';
+                               } else if (!ast_strlen_zero(e->iam.called_party_num)) {
+                                       char *st;
+                                       ast_copy_string(p->exten, e->iam.called_party_num, sizeof(p->exten));
+                                       st = strchr(p->exten, '#');
+                                       if (st)
+                                               *st = '\0';
+                               } else
+                                       p->exten[0] = '\0';
+
+                               /* Need to fill these fields */
+                               p->cid_ani[0] = '\0';
+                               p->cid_name[0] = '\0';
+                               p->cid_ton = 0;
+                               /* Set DNID */
+                               if (!ast_strlen_zero(e->iam.called_party_num))
+                                       ast_copy_string(p->dnid, e->iam.called_party_num, sizeof(p->exten));
+
+                               if (ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
+
+                                       if (e->iam.cot_check_required) {
+                                               zt_loopback(p, 1);
+                                       } else
+                                               ss7_start_call(p, linkset);
+                               } else {
+                                       ast_log(LOG_DEBUG, "Call on CIC for unconfigured extension %s\n", p->exten);
+                                       isup_rel(ss7, e->iam.call, -1);
+                               }
+                               ast_mutex_unlock(&p->lock);
+                               break;
+                       case ISUP_EVENT_COT:
+                               chanpos = ss7_find_cic(linkset, e->cot.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "COT on unconfigured CIC %d\n", e->cot.cic);
+                                       isup_rel(ss7, e->cot.call, -1);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+
+                               zt_loopback(p, 0);
+                               
+                               isup_acm(ss7, p->ss7call);
+                               ss7_start_call(p, linkset);
+                               break;
+                       case ISUP_EVENT_REL:
+                               chanpos = ss7_find_cic(linkset, e->rel.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "REL on unconfigured CIC %d\n", e->rel.cic);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               ast_mutex_lock(&p->lock);
+                               if (p->owner)
+                                       ast_queue_hangup(p->owner);
+                               else
+                                       ast_log(LOG_WARNING, "REL on channel (CIC %d) without owner!\n", p->cic);
+
+                               isup_rlc(ss7, e->rel.call);
+                               p->ss7call = NULL;
+
+                               ast_mutex_unlock(&p->lock);
+                               break;
+                       case ISUP_EVENT_ACM:
+                               chanpos = ss7_find_cic(linkset, e->acm.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "ACM on unconfigured CIC %d\n", e->acm.cic);
+                                       isup_rel(ss7, e->acm.call, -1);
+                                       break;
+                               } else {
+                                       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, };
+
+                                       p = linkset->pvts[chanpos];
+
+                                       ast_log(LOG_DEBUG, "Queueing frame from SS7_EVENT_ACM on CIC %d\n", p->cic);
+
+                                       ast_mutex_lock(&p->lock);
+                                       zap_queue_frame(p, &f, linkset);
+                                       p->proceeding = 1;
+
+                                       ast_mutex_unlock(&p->lock);
+                               }
+                               break;
+                       case ISUP_EVENT_CGB:
+                               ss7_block_cics(linkset, e->cgb.startcic, e->cgb.endcic, e->cgb.status, 1);
+                               isup_cgba(linkset->ss7, e->cgb.startcic, e->cgb.endcic, e->cgb.status);
+                               break;
+                       case ISUP_EVENT_CGU:
+                               ss7_block_cics(linkset, e->cgu.startcic, e->cgu.endcic, e->cgb.status, 0);
+                               isup_cgua(linkset->ss7, e->cgu.startcic, e->cgu.endcic, e->cgb.status);
+                               break;
+                       case ISUP_EVENT_BLO:
+                               chanpos = ss7_find_cic(linkset, e->blo.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "BLO on unconfigured CIC %d\n", e->blo.cic);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               ast_log(LOG_DEBUG, "Blocking CIC %d\n", e->blo.cic);
+                               p->blocked = 1;
+                               isup_bla(linkset->ss7, e->blo.cic);
+                               break;
+                       case ISUP_EVENT_UBL:
+                               chanpos = ss7_find_cic(linkset, e->ubl.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "UBL on unconfigured CIC %d\n", e->ubl.cic);
+                                       break;
+                               }
+                               p = linkset->pvts[chanpos];
+                               ast_log(LOG_DEBUG, "Unblocking CIC %d\n", e->ubl.cic);
+                               p->blocked = 0;
+                               isup_uba(linkset->ss7, e->ubl.cic);
+                               break;
+                       case ISUP_EVENT_CON:
+                       case ISUP_EVENT_ANM:
+                               if (e->e == ISUP_EVENT_CON)
+                                       cic = e->con.cic;
+                               else
+                                       cic = e->anm.cic;
+
+                               chanpos = ss7_find_cic(linkset, cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "ANM/CON on unconfigured CIC %d\n", cic);
+                                       isup_rel(ss7, (e->e == ISUP_EVENT_ANM) ? e->anm.call : e->con.call, -1);
+                                       break;
+                               } else {
+                                       p = linkset->pvts[chanpos];
+                                       ast_mutex_lock(&p->lock);
+                                       p->subs[SUB_REAL].needanswer = 1;
+                                       zt_enable_ec(p);
+                                       ast_mutex_unlock(&p->lock);
+                               }
+                               break;
+                       case ISUP_EVENT_RLC:
+                               chanpos = ss7_find_cic(linkset, e->rlc.cic);
+                               if (chanpos < 0) {
+                                       ast_log(LOG_WARNING, "RLC on unconfigured CIC %d\n", e->rlc.cic);
+                                       break;
+                               } else {
+                                       p = linkset->pvts[chanpos];
+                                       ast_mutex_lock(&p->lock);
+                                       p->ss7call = NULL;
+                                       ast_mutex_unlock(&p->lock);
+                               }
+                               break;
+                       default:
+                               ast_log(LOG_DEBUG, "Unknown event %s\n", ss7_event2str(e->e));
+                               break;
+                       }
+               }
+               ast_mutex_unlock(&linkset->lock);
+       }
+
+       return 0;
+}
+
+static void zt_ss7_message(struct ss7 *ss7, char *s)
+{
+       ast_log(LOG_DEBUG, "%s", s);
+}
+
+static void zt_ss7_error(struct ss7 *ss7, char *s)
+{
+       ast_log(LOG_ERROR, "%s", s);
+}
+#endif /* HAVE_SS7 */
 
 #ifdef HAVE_PRI
 static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv)
@@ -9878,6 +10564,12 @@ static int zap_show_channel(int fd, int argc, char **argv)
                                if (tmp->slaves[x])
                                        ast_cli(fd, "Slave Channel: %d\n", tmp->slaves[x]->channel);
                        }
+#ifdef HAVE_SS7
+                       if (tmp->ss7) {
+                               ast_cli(fd, "CIC: %d\n", tmp->cic);
+                               ast_cli(fd, "Blocked: %s\n", tmp->blocked ? "yes" : "no");
+                       }
+#endif
 #ifdef HAVE_PRI
                        if (tmp->pri) {
                                ast_cli(fd, "PRI Flags: ");
@@ -10309,16 +11001,242 @@ static int __unload_module(void)
                zt_close(pris[i].fds[i]);
        }
 #endif
+#ifdef HAVE_SS7
+       for (i = 0; i < NUM_SPANS; i++) {
+               if (linksets[i].master && (linksets[i].master != AST_PTHREADT_NULL))
+                       pthread_join(linksets[i].master, NULL);
+               zt_close(linksets[i].fds[i]);
+       }
+#endif /* HAVE_SS7 */
        return 0;
 }
 
+#ifdef HAVE_SS7
+static int linkset_addsigchan(int sigchan)
+{
+       struct zt_ss7 *link;
+       int res;
+       int curfd;
+       ZT_PARAMS p;
+       ZT_BUFFERINFO bi;
+       struct zt_spaninfo si;
+
+
+       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 (cur_ss7type < 0) {
+               ast_log(LOG_ERROR, "Unspecified or invalid ss7type\n");
+               return -1;
+       }
+
+       if (!link->ss7)
+               link->ss7 = ss7_new(cur_ss7type);
+
+       if (!link->ss7) {
+               ast_log(LOG_ERROR, "Can't create new SS7!\n");
+               return -1;
+       }
+
+       link->type = cur_ss7type;
+
+       if (cur_pointcode < 0) {
+               ast_log(LOG_ERROR, "Unspecified pointcode!\n");
+               return -1;
+       } else
+               ss7_set_pc(link->ss7, cur_pointcode);
+
+       if (sigchan < 0) {
+               ast_log(LOG_ERROR, "Invalid sigchan!\n");
+               return -1;
+       } else {
+               if (link->numsigchans >= NUM_DCHANS) {
+                       ast_log(LOG_ERROR, "Too many sigchans on linkset %d\n", cur_linkset);
+                       return -1;
+               }
+               curfd = link->numsigchans;
+
+               link->fds[curfd] = open("/dev/zap/channel", O_RDWR, 0600);
+               if ((link->fds[curfd] < 0) || (ioctl(link->fds[curfd],ZT_SPECIFY,&sigchan) == -1)) {
+                       ast_log(LOG_ERROR, "Unable to open SS7 sigchan %d (%s)\n", sigchan, strerror(errno));
+                       return -1;
+               }
+               res = ioctl(link->fds[curfd], ZT_GET_PARAMS, &p);
+               if (res) {
+                       zt_close(link->fds[curfd]);
+                       link->fds[curfd] = -1;
+                       ast_log(LOG_ERROR, "Unable to get parameters for sigchan %d (%s)\n", sigchan, strerror(errno));
+                       return -1;
+               }
+               if ((p.sigtype != ZT_SIG_HDLCFCS) && (p.sigtype != ZT_SIG_HARDHDLC)) {
+                       zt_close(link->fds[curfd]);
+                       link->fds[curfd] = -1;
+                       ast_log(LOG_ERROR, "sigchan %d is not in HDLC/FCS mode.  See /etc/zaptel.conf\n", sigchan);
+                       return -1;
+               }
+
+               bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+               bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+               bi.numbufs = 32;
+               bi.bufsize = 512;
+
+               if (ioctl(link->fds[curfd], ZT_SET_BUFINFO, &bi)) {
+                       ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d\n", sigchan);
+                       zt_close(link->fds[curfd]);
+                       link->fds[curfd] = -1;
+                       return -1;
+               }
+
+               ss7_add_link(link->ss7, link->fds[curfd]);
+               link->numsigchans++;
+
+               memset(&si, 0, sizeof(si));
+               res = ioctl(link->fds[curfd], ZT_SPANSTAT, &si);
+               if (res) {
+                       zt_close(link->fds[curfd]);
+                       link->fds[curfd] = -1;
+                       ast_log(LOG_ERROR, "Unable to get span state for sigchan %d (%s)\n", sigchan, strerror(errno));
+               }
+
+               if (!si.alarms) {
+                       link->linkstate[curfd] = LINKSTATE_DOWN;
+                       ss7_link_noalarm(link->ss7, link->fds[curfd]);
+               } else {
+                       link->linkstate[curfd] = LINKSTATE_DOWN | LINKSTATE_INALARM;
+                       ss7_link_alarm(link->ss7, link->fds[curfd]);
+               }
+       }
+
+       if (cur_adjpointcode < 0) {
+               ast_log(LOG_ERROR, "Unspecified adjpointcode!\n");
+               return -1;
+       } else {
+               ss7_set_adjpc(link->ss7, link->fds[curfd], cur_adjpointcode);
+       }
+
+       if (cur_defaultdpc < 0) {
+               ast_log(LOG_ERROR, "Unspecified defaultdpc!\n");
+               return -1;
+       } else {
+               ss7_set_default_dpc(link->ss7, cur_defaultdpc);
+       }
+
+       if (cur_networkindicator < 0) {
+               ast_log(LOG_ERROR, "Invalid networkindicator!\n");
+               return -1;
+       } else
+               ss7_set_network_ind(link->ss7, cur_networkindicator);
+
+       return 0;
+}
+
+static int handle_ss7_no_debug(int fd, int argc, char *argv[])
+{
+       int span;
+       if (argc < 5)
+               return RESULT_SHOWUSAGE;
+       span = atoi(argv[4]);
+       if ((span < 1) || (span > NUM_SPANS)) {
+               ast_cli(fd, "Invalid linkset %s.  Should be a number %d to %d\n", argv[4], 1, NUM_SPANS);
+               return RESULT_SUCCESS;
+       }
+       if (!linksets[span-1].ss7) {
+               ast_cli(fd, "No SS7 running on linkset %d\n", span);
+               return RESULT_SUCCESS;
+       }
+       if (linksets[span-1].ss7)
+               ss7_set_debug(linksets[span-1].ss7, 0);
+
+       ast_cli(fd, "Disabled debugging on linkset %d\n", span);
+       return RESULT_SUCCESS;
+}
+
+static int handle_ss7_debug(int fd, int argc, char *argv[])
+{
+       int span;
+       if (argc < 4)
+               return RESULT_SHOWUSAGE;
+       span = atoi(argv[3]);
+       if ((span < 1) || (span > NUM_SPANS)) {
+               ast_cli(fd, "Invalid linkset %s.  Should be a number %d to %d\n", argv[4], 1, NUM_SPANS);
+               return RESULT_SUCCESS;
+       }
+       if (!linksets[span-1].ss7) {
+               ast_cli(fd, "No SS7 running on linkset %d\n", span);
+               return RESULT_SUCCESS;
+       }
+       if (linksets[span-1].ss7)
+               ss7_set_debug(linksets[span-1].ss7, SS7_DEBUG_MTP2 | SS7_DEBUG_MTP3 | SS7_DEBUG_ISUP);
+
+       ast_cli(fd, "Enabled debugging on linkset %d\n", span);
+       return RESULT_SUCCESS;
+}
+
+#if 0
+static int handle_ss7_show_linkset(int fd, int argc, char *argv[])
+{
+       int linkset;
+       struct zt_ss7 *ss7;
+       if (argc < 4)
+               return RESULT_SHOWUSAGE;
+       linkset = atoi(argv[3]);
+       if ((linkset < 1) || (linkset > NUM_SPANS)) {
+               ast_cli(fd, "Invalid linkset %s.  Should be a number %d to %d\n", argv[4], 1, NUM_SPANS);
+               return RESULT_SUCCESS;
+       }
+       if (!linksets[linkset-1].ss7) {
+               ast_cli(fd, "No SS7 running on linkset %d\n", linkset);
+               return RESULT_SUCCESS;
+       }
+       if (linksets[linkset-1].ss7)
+               ss7 = linksets[linkset-1];
+
+       if (
+
+       return RESULT_SUCCESS;
+}
+#endif
+
+static const char ss7_debug_help[] = 
+       "Usage: ss7 debug linkset <linkset>\n"
+       "       Enables debugging on a given SS7 linkset\n";
+       
+static const char ss7_no_debug_help[] = 
+       "Usage: ss7 no debug linkset <span>\n"
+       "       Disables debugging on a given SS7 linkset\n";
+
+#if 0
+static const char ss7_show_linkset_help[] = 
+       "Usage: ss7 show linkset <span>\n"
+       "       Disables debugging on a given SS7 linkset\n";
+#endif
+
+static struct ast_cli_entry zap_ss7_cli[] = {
+       { { "ss7", "debug", "linkset", NULL }, handle_ss7_debug,
+         "Enables SS7 debugging on a linkset", ss7_debug_help, NULL },
+       { { "ss7", "no", "debug", "linkset", NULL }, handle_ss7_no_debug,
+         "Disables SS7 debugging on a linkset", ss7_no_debug_help, NULL },
+#if 0
+       { { "ss7", "show", "linkset", NULL }, handle_ss7_show_linkset,
+         "Disables SS7 debugging on a linkset", ss7_show_linkset_help, NULL },
+#endif
+};
+#endif /* HAVE_SS7 */
+
 static int unload_module(void)
 {
-#ifdef HAVE_PRI                
        int y;
+#ifdef HAVE_PRI                
        for (y = 0; y < NUM_SPANS; y++)
                ast_mutex_destroy(&pris[y].lock);
 #endif
+#ifdef HAVE_SS7
+       for (y = 0; y < NUM_SPANS; y++)
+               ast_mutex_destroy(&linksets[y].lock);
+#endif /* HAVE_SS7 */
        return __unload_module();
 }
 
@@ -10736,6 +11654,11 @@ static int process_zap(struct ast_variable *v, int reload, int skipchannels)
                                        cur_radio = 0;
                                        pritype = PRI_CPE;
 #endif
+#ifdef HAVE_SS7
+                               } else if (!strcasecmp(v->value, "ss7")) {
+                                       cur_signalling = SIG_SS7;
+                                       cur_radio = 0;
+#endif
                                } else {
                                        ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
                                }
@@ -10903,6 +11826,42 @@ static int process_zap(struct ast_variable *v, int reload, int skipchannels)
                                facilityenable = ast_true(v->value);
 #endif /* PRI_GETSET_TIMERS */
 #endif /* HAVE_PRI */
+#ifdef HAVE_SS7
+                       } else if (!strcasecmp(v->name, "ss7type")) {
+                               if (!strcasecmp(v->value, "itu")) {
+                                       cur_ss7type = SS7_ITU;
+                               } else if (!strcasecmp(v->value, "ansi")) {
+                                       cur_ss7type = SS7_ANSI;
+                               } else
+                                       ast_log(LOG_WARNING, "'%s' is an unknown ss7 switch type!\n", v->value);
+                       } else if (!strcasecmp(v->name, "linkset")) {
+                               cur_linkset = atoi(v->value);
+                       } else if (!strcasecmp(v->name, "pointcode")) {
+                               cur_pointcode = atoi(v->value);
+                       } else if (!strcasecmp(v->name, "adjpointcode")) {
+                               cur_adjpointcode = atoi(v->value);
+                       } else if (!strcasecmp(v->name, "defaultdpc")) {
+                               cur_defaultdpc = atoi(v->value);
+                       } else if (!strcasecmp(v->name, "cicbeginswith")) {
+                               cur_cicbeginswith = atoi(v->value);
+                       } else if (!strcasecmp(v->name, "networkindicator")) {
+                               if (!strcasecmp(v->value, "national"))
+                                       cur_networkindicator = SS7_NI_NAT;
+                               else if (!strcasecmp(v->value, "national_spare"))
+                                       cur_networkindicator = SS7_NI_NAT_SPARE;
+                               else if (!strcasecmp(v->value, "international"))
+                                       cur_networkindicator = SS7_NI_INT;
+                               else if (!strcasecmp(v->value, "international_spare"))
+                                       cur_networkindicator = SS7_NI_INT_SPARE;
+                               else
+                                       cur_networkindicator = -1;
+                       } else if (!strcasecmp(v->name, "sigchan")) {
+                               int sigchan, res;
+                               sigchan = atoi(v->value);
+                               res = linkset_addsigchan(sigchan);
+                               if (res < 0)
+                                       return -1;
+#endif /* HAVE_SS7 */
                        } else if (!strcasecmp(v->name, "cadence")) {
                                /* setup to scan our argument */
                                int element_count, c[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
@@ -11186,6 +12145,19 @@ static int setup_zap(int reload)
                }
        }
 #endif
+#ifdef HAVE_SS7
+       if (!reload) {
+               for (x = 0; x < NUM_SPANS; x++) {
+                       if (linksets[x].ss7) {
+                               if (ast_pthread_create(&linksets[x].master, NULL, ss7_linkset, &linksets[x])) {
+                                       ast_log(LOG_ERROR, "Unable to start SS7 linkset on span %d\n", x + 1);
+                                       return -1;
+                               } else if (option_verbose > 1)
+                                       ast_verbose(VERBOSE_PREFIX_2 "Starting SS7 linkset on span %d\n", x + 1);
+                       }
+               }
+       }
+#endif
        /* And start the monitor for the first time */
        restart_monitor();
        return 0;
@@ -11194,9 +12166,9 @@ static int setup_zap(int reload)
 static int load_module(void)
 {
        int res;
+       int y,i;
 
 #ifdef HAVE_PRI
-       int y,i;
        memset(pris, 0, sizeof(pris));
        for (y = 0; y < NUM_SPANS; y++) {
                ast_mutex_init(&pris[y].lock);
@@ -11210,6 +12182,17 @@ static int load_module(void)
        ast_register_application(zap_send_keypad_facility_app, zap_send_keypad_facility_exec,
                        zap_send_keypad_facility_synopsis, zap_send_keypad_facility_descrip);
 #endif
+#ifdef HAVE_SS7
+       memset(linksets, 0, sizeof(linksets));
+       for (y = 0; y < NUM_SPANS; y++) {
+               ast_mutex_init(&linksets[y].lock);
+               linksets[y].master = AST_PTHREADT_NULL;
+               for (i = 0; i < NUM_DCHANS; i++)
+                       linksets[y].fds[i] = -1;
+       }
+       ss7_set_error(zt_ss7_error);
+       ss7_set_message(zt_ss7_message);
+#endif /* HAVE_SS7 */
        res = setup_zap(0);
        /* Make sure we can register our Zap channel type */
        if (res)
@@ -11224,6 +12207,10 @@ static int load_module(void)
        ast_string_field_set(&inuse, name, "GR-303InUse");
        ast_cli_register_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry));
 #endif 
+#ifdef HAVE_SS7
+       ast_cli_register_multiple(zap_ss7_cli, sizeof(zap_ss7_cli) / sizeof(zap_ss7_cli[0]));
+#endif
+
        ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
        
        memset(round_robin, 0, sizeof(round_robin));
index d870b8d..ee9bedf 100644 (file)
@@ -656,3 +656,37 @@ immediate=no
 ;context=default
 ;channel => 1 
 
+; Signaling type SS7
+signalling = ss7
+
+; Variant of SS7 signaling:
+; Options are itu and ansi
+ss7type = itu
+
+; All settings apply to linkset 1
+linkset = 1
+
+; Point code of the linkset
+pointcode = 1
+
+; Point code of node adjacent to this signaling link (Possibly the STP between you and
+; your destination)
+adjpointcode = 2
+
+; Default point code that you would like to assign to outgoing messages (in case of
+; routing through STPs, or using A links)
+defaultdpc = 3
+
+; Begin CIC (Circuit indication codes) count with this number
+cicbeginswith = 1
+
+; What the MTP3 network indicator bits should be set to.  Choices are
+; national, national_spare, international, international_spare
+networkindicator=international
+
+; First signaling channel
+sigchan = 48
+
+; Channels to associate with CICs on this linkset
+channel = 25-47
+
index 3218814..a9337fb 100644 (file)
@@ -194,6 +194,7 @@ AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss])
 AST_EXT_LIB_SETUP([POPT], [popt], [popt])
 AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
 AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
+AST_EXT_LIB_SETUP([SS7], [ISDN SS7], [ss7])
 AST_EXT_LIB_SETUP([PWLIB], [PWlib], [pwlib])
 AST_EXT_LIB_SETUP([OPENH323], [OpenH323], [h323])
 AST_EXT_LIB_SETUP([QT], [Qt], [qt])
@@ -702,6 +703,8 @@ AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
 
 AST_EXT_LIB_CHECK([PRI], [pri], [pri_call], [libpri.h])
 
+AST_EXT_LIB_CHECK([SS7], [ss7], [ss7_new], [libss7.h])
+
 if test "${USE_PWLIB}" != "no"; then
        if test ! -z "${PWLIB_DIR}"; then
                PWLIBDIR="${PWLIB_DIR}"
index d123905..4d1a86b 100644 (file)
 /* Define to 1 if you have the `sqrt' function. */
 #undef HAVE_SQRT
 
+/* Define to indicate the ${SS7_DESCRIP} library */
+#undef HAVE_SS7
+
 /* Define to 1 if `stat' has the bug that it succeeds when given the
    zero-length file name argument. */
 #undef HAVE_STAT_EMPTY_STRING_BUG
index 683484a..d0af459 100644 (file)
@@ -114,6 +114,9 @@ POPT_LIB=@POPT_LIB@
 PRI_INCLUDE=@PRI_INCLUDE@
 PRI_LIB=@PRI_LIB@
 
+PRI_INCLUDE=@SS7_INCLUDE@
+PRI_LIB=@SS7_LIB@
+
 PWLIB_INCLUDE=@PWLIB_INCLUDE@
 PWLIB_LIB=@PWLIB_LIB@