Initial, incomplete support for D-channel backup
authorMark Spencer <markster@digium.com>
Mon, 7 Jun 2004 03:39:18 +0000 (03:39 +0000)
committerMark Spencer <markster@digium.com>
Mon, 7 Jun 2004 03:39:18 +0000 (03:39 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3163 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_zap.c

index 78ec79d..dd21714 100755 (executable)
@@ -55,7 +55,7 @@
 #include <ctype.h>
 #ifdef ZAPATA_PRI
 #include <libpri.h>
-#ifndef PRI_GR303_SUPPORT
+#ifndef PRI_ENSLAVE_SUPPORT
 #error "You need newer libpri"
 #endif
 #endif
@@ -133,11 +133,18 @@ static char *config = "zapata.conf";
 #define SIG_GR303FXOKS   (0x100000 | ZT_SIG_FXOKS)
 
 #define NUM_SPANS      32
+#define NUM_DCHANS     4               /* No more than 4 d-channels */
 #define MAX_CHANNELS   672     /* No more than a DS3 per trunk group */
 #define RESET_INTERVAL 3600    /* How often (in seconds) to reset unused channels */
 
 #define CHAN_PSEUDO    -2
 
+#define DCHAN_PROVISIONED (1 << 0)
+#define DCHAN_NOTINALARM  (1 << 1)
+#define DCHAN_UP          (1 << 2)
+
+#define DCHAN_AVAILABLE        (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP)
+
 static char context[AST_MAX_EXTENSION] = "default";
 static char callerid[256] = "";
 
@@ -310,16 +317,17 @@ struct zt_pri {
        int nodetype;                           /* Node type */
        int switchtype;                         /* Type of switch to emulate */
        int dialplan;                   /* Dialing plan */
-       int dchannel;                   /* What channel the dchannel is on */
+       int dchannels[NUM_DCHANS];      /* What channel are the dchannels on */
        int trunkgroup;                 /* What our trunkgroup is */
        int mastertrunkgroup;   /* What trunk group is our master */
        int prilogicalspan;             /* Logical span number within trunk group */
        int numchans;                   /* Num of channels we represent */
        int overlapdial;                /* In overlap dialing mode */
-       struct pri *pri;
+       struct pri *dchans[NUM_DCHANS]; /* Actual d-channels */
+       int dchanavail[NUM_DCHANS];             /* Whether each channel is available */
+       struct pri *pri;                                /* Currently active D-channel */
        int debug;
-       int fd;
-       int up;
+       int fds[NUM_DCHANS];    /* FD's for d-channels */
        int offset;
        int span;
        int resetting;
@@ -1762,6 +1770,16 @@ static int destroy_channel(struct zt_pvt *prev, struct zt_pvt *cur, int now)
 }
 
 #ifdef ZAPATA_PRI
+int pri_is_up(struct zt_pri *pri)
+{
+       int x;
+       for (x=0;x<NUM_DCHANS;x++) {
+               if (pri->dchanavail[x] == DCHAN_AVAILABLE)
+                       return 1;
+       }
+       return 0;
+}
+
 int pri_assign_bearer(struct zt_pvt *crv, struct zt_pri *pri, struct zt_pvt *bearer)
 {
        bearer->owner = &inuse;
@@ -1772,6 +1790,48 @@ int pri_assign_bearer(struct zt_pvt *crv, struct zt_pri *pri, struct zt_pvt *bea
        crv->pri = pri;
        return 0;
 }
+
+static char *pri_order(int level)
+{
+       switch(level) {
+       case 0:
+               return "Primary";
+       case 1:
+               return "Secondary";
+       case 2:
+               return "Tertiary";
+       case 3:
+               return "Quaternary";
+       default:
+               return "<Unknown>";
+       }               
+}
+
+int pri_find_dchan(struct zt_pri *pri)
+{
+       int oldslot = -1;
+       struct pri *old;
+       int newslot = -1;
+       int x;
+       old = pri->pri;
+       for(x=0;x<NUM_DCHANS;x++) {
+               if ((pri->dchanavail[x] == DCHAN_AVAILABLE) && (newslot < 0))
+                       newslot = x;
+               if (pri->dchans[x] == old) {
+                       oldslot = x;
+               }
+       }
+       if (newslot < 0) {
+               ast_log(LOG_WARNING, "No D-channels available!  Using Primary on channel anyway %d!\n",
+                       pri->dchannels[newslot]);
+               newslot = 0;
+       }
+       if (old && (oldslot != newslot))
+               ast_log(LOG_NOTICE, "Switching from from d-channel %d to channel %d!\n",
+                       pri->dchannels[oldslot], pri->dchannels[newslot]);
+       pri->pri = pri->dchans[newslot];
+       return 0;
+}
 #endif
 
 static int zt_hangup(struct ast_channel *ast)
@@ -5478,10 +5538,11 @@ static int pri_resolve_span(int *span, int channel, int offset, struct zt_spanin
                        *span = -1;
                } else {
                        if (si->totalchans == 31) { /* if it's an E1 */
-                               pris[*span].dchannel = 16;
+                               pris[*span].dchannels[0] = 16 + offset;
                        } else {
-                               pris[*span].dchannel = 24;
+                               pris[*span].dchannels[0] = 24 + offset;
                        }
+                       pris[*span].dchanavail[0] |= DCHAN_PROVISIONED;
                        pris[*span].offset = offset;
                        pris[*span].span = *span + 1;
                }
@@ -5489,57 +5550,66 @@ static int pri_resolve_span(int *span, int channel, int offset, struct zt_spanin
        return 0;
 }
 
-static int pri_create_trunkgroup(int trunkgroup, int channel)
+static int pri_create_trunkgroup(int trunkgroup, int *channels)
 {
        struct zt_spaninfo si;
        ZT_PARAMS p;
        int fd;
        int span;
-       int x;
+       int ospan=0;
+       int x,y;
        for (x=0;x<NUM_SPANS;x++) {
                if (pris[x].trunkgroup == trunkgroup) {
-                       ast_log(LOG_WARNING, "Trunk group %d already exists on span %d, channel %d\n", trunkgroup, x + 1, pris[x].dchannel);
+                       ast_log(LOG_WARNING, "Trunk group %d already exists on span %d, Primary d-channel %d\n", trunkgroup, x + 1, pris[x].dchannels[0]);
                        return -1;
                }
        }
-       memset(&si, 0, sizeof(si));
-       memset(&p, 0, sizeof(p));
-       fd = open("/dev/zap/channel", O_RDWR);
-       if (fd < 0) {
-               ast_log(LOG_WARNING, "Failed to open channel: %s\n", strerror(errno));
-               return -1;
-       }
-       x = channel;
-       if (ioctl(fd, ZT_SPECIFY, &x)) {
-               ast_log(LOG_WARNING, "Failed to specify channel %d: %s\n", channel, strerror(errno));
-               close(fd);
-               return -1;
-       }
-       if (ioctl(fd, ZT_GET_PARAMS, &p)) {
-               ast_log(LOG_WARNING, "Failed to get channel parameters for channel %d: %s\n", channel, strerror(errno));
-               return -1;
-       }
-       if (ioctl(fd, ZT_SPANSTAT, &si)) {
-               ast_log(LOG_WARNING, "Failed go get span information on channel %d (span %d)\n", channel, p.spanno);
-               close(fd);
-               return -1;
-       }
-       span = p.spanno - 1;
-       if (pris[span].trunkgroup) {
-               ast_log(LOG_WARNING, "Span %d is already provisioned for trunk group %d\n", span + 1, pris[span].trunkgroup);
-               close(fd);
-               return -1;
-       }
-       if (pris[span].pvts[0]) {
-               ast_log(LOG_WARNING, "Span %d is already provisioned with channels (implicit PRI maybe?)\n", span + 1);
+       for (y=0;y<NUM_DCHANS;y++) {
+               if (!channels[y])       
+                       break;
+               memset(&si, 0, sizeof(si));
+               memset(&p, 0, sizeof(p));
+               fd = open("/dev/zap/channel", O_RDWR);
+               if (fd < 0) {
+                       ast_log(LOG_WARNING, "Failed to open channel: %s\n", strerror(errno));
+                       return -1;
+               }
+               x = channels[y];
+               if (ioctl(fd, ZT_SPECIFY, &x)) {
+                       ast_log(LOG_WARNING, "Failed to specify channel %d: %s\n", channels[y], strerror(errno));
+                       close(fd);
+                       return -1;
+               }
+               if (ioctl(fd, ZT_GET_PARAMS, &p)) {
+                       ast_log(LOG_WARNING, "Failed to get channel parameters for channel %d: %s\n", channels[y], strerror(errno));
+                       return -1;
+               }
+               if (ioctl(fd, ZT_SPANSTAT, &si)) {
+                       ast_log(LOG_WARNING, "Failed go get span information on channel %d (span %d)\n", channels[y], p.spanno);
+                       close(fd);
+                       return -1;
+               }
+               span = p.spanno - 1;
+               if (pris[span].trunkgroup) {
+                       ast_log(LOG_WARNING, "Span %d is already provisioned for trunk group %d\n", span + 1, pris[span].trunkgroup);
+                       close(fd);
+                       return -1;
+               }
+               if (pris[span].pvts[0]) {
+                       ast_log(LOG_WARNING, "Span %d is already provisioned with channels (implicit PRI maybe?)\n", span + 1);
+                       close(fd);
+                       return -1;
+               }
+               if (!y) {
+                       pris[span].trunkgroup = trunkgroup;
+                       pris[span].offset = channels[y] - p.chanpos;
+                       ospan = span;
+               }
+               pris[ospan].dchannels[y] = channels[y];
+               pris[ospan].dchanavail[y] |= DCHAN_PROVISIONED;
+               pris[span].span = span + 1;
                close(fd);
-               return -1;
        }
-       pris[span].trunkgroup = trunkgroup;
-       pris[span].offset = channel - p.chanpos;
-       pris[span].dchannel = p.chanpos;
-       pris[span].span = span + 1;
-       close(fd);
        return 0;       
 }
 
@@ -5702,6 +5772,8 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_p
                if ((signalling == SIG_PRI) || (signalling == SIG_GR303FXOKS)) {
                        int offset;
                        int myswitchtype;
+                       int matchesdchan;
+                       int x,y;
                        offset = 0;
                        if ((signalling == SIG_PRI) && ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &offset)) {
                                ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno));
@@ -5731,8 +5803,18 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_p
                                        myswitchtype = switchtype;
                                else
                                        myswitchtype = PRI_SWITCH_GR303_TMC;
+                               /* Make sure this isn't a d-channel */
+                               matchesdchan=0;
+                               for (x=0;x<NUM_SPANS;x++) {
+                                       for (y=0;y<NUM_DCHANS;y++) {
+                                               if (pris[x].dchannels[y] == tmp->channel) {
+                                                       matchesdchan = 1;
+                                                       break;
+                                               }
+                                       }
+                               }
                                offset = p.chanpos;
-                               if (offset != pris[span].dchannel) {
+                               if (!matchesdchan) {
                                        if (pris[span].nodetype && (pris[span].nodetype != pritype)) {
                                                ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype));
                                                free(tmp);
@@ -5949,7 +6031,7 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio, struct zt_p
                        ioctl(tmp->subs[SUB_REAL].zfd,ZT_SETTONEZONE,&tmp->tonezone);
 #ifdef ZAPATA_PRI
                        /* the dchannel is down so put the channel in alarm */
-                       if (tmp->pri && tmp->pri->up == 0)
+                       if (tmp->pri && !pri_is_up(tmp->pri))
                                tmp->inalarm = 1;
                        else
                                tmp->inalarm = 0;
@@ -6506,7 +6588,7 @@ static void *pri_dchannel(void *vpri)
 {
        struct zt_pri *pri = vpri;
        pri_event *e;
-       struct pollfd fds[1];
+       struct pollfd fds[NUM_DCHANS];
        int res;
        int chanpos = 0;
        int x;
@@ -6514,7 +6596,7 @@ static void *pri_dchannel(void *vpri)
        int activeidles;
        int nextidle = -1;
        struct ast_channel *c;
-       struct timeval tv, *next;
+       struct timeval tv, lowest, *next;
        struct timeval lastidle = { 0, 0 };
        int doidling=0;
        char *cc;
@@ -6522,7 +6604,8 @@ static void *pri_dchannel(void *vpri)
        struct ast_channel *idle;
        pthread_t p;
        time_t t;
-       int i;
+       int i, which=-1;
+       int numdchans;
        struct zt_pvt *crv;
        pthread_t threadid;
        pthread_attr_t attr;
@@ -6549,12 +6632,17 @@ static void *pri_dchannel(void *vpri)
                        ast_log(LOG_WARNING, "Idle dial string '%s' lacks '@context'\n", pri->idleext);
        }
        for(;;) {
-               fds[0].fd = pri->fd;
-               fds[0].events = POLLIN | POLLPRI;
+               for (i=0;i<NUM_DCHANS;i++) {
+                       if (!pri->dchannels[i])
+                               break;
+                       fds[i].fd = pri->fds[i];
+                       fds[i].events = POLLIN | POLLPRI;
+               }
+               numdchans = i;
                time(&t);
                ast_mutex_lock(&pri->lock);
                if (pri->switchtype != PRI_SWITCH_GR303_TMC) {
-                       if (pri->resetting && pri->up) {
+                       if (pri->resetting && pri_is_up(pri)) {
                                if (pri->resetpos < 0)
                                        pri_check_restart(pri);
                        } else {
@@ -6565,7 +6653,7 @@ static void *pri_dchannel(void *vpri)
                        }
                }
                /* Look for any idle channels if appropriate */
-               if (doidling && pri->up) {
+               if (doidling && pri_is_up(pri)) {
                        nextidle = -1;
                        haveidles = 0;
                        activeidles = 0;
@@ -6624,63 +6712,93 @@ static void *pri_dchannel(void *vpri)
                                }
                        }
                }
-               if ((next = pri_schedule_next(pri->pri))) {
-                       /* We need relative time here */
-                       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;
-                       }
-                       if (doidling || pri->resetting) {
-                               if (tv.tv_sec > 1) {
-                                       tv.tv_sec = 1;
-                                       tv.tv_usec = 0;
+               /* Start with reasonable max */
+               lowest.tv_sec = 60;
+               lowest.tv_usec = 0;
+               for (i=0; i<NUM_DCHANS; i++) {
+                       /* Find lowest available d-channel */
+                       if (!pri->dchannels[i])
+                               break;
+                       if ((next = pri_schedule_next(pri->pri))) {
+                               /* We need relative time here */
+                               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;
                                }
-                       } else {
-                               if (tv.tv_sec > 60) {
-                                       tv.tv_sec = 60;
+                               if (tv.tv_sec < 0) {
+                                       tv.tv_sec = 0;
                                        tv.tv_usec = 0;
                                }
+                               if (doidling || pri->resetting) {
+                                       if (tv.tv_sec > 1) {
+                                               tv.tv_sec = 1;
+                                               tv.tv_usec = 0;
+                                       }
+                               } else {
+                                       if (tv.tv_sec > 60) {
+                                               tv.tv_sec = 60;
+                                               tv.tv_usec = 0;
+                                       }
+                               }
+                       } else if (doidling || pri->resetting) {
+                               /* Make sure we stop at least once per second if we're
+                                  monitoring idle channels */
+                               tv.tv_sec = 1;
+                               tv.tv_usec = 0;
+                       } else {
+                               /* Don't poll for more than 60 seconds */
+                               tv.tv_sec = 60;
+                               tv.tv_usec = 0;
+                       }
+                       if (!i || (tv.tv_sec < lowest.tv_sec) || ((tv.tv_sec == lowest.tv_sec) && (tv.tv_usec < lowest.tv_usec))) {
+                               lowest.tv_sec = tv.tv_sec;
+                               lowest.tv_usec = tv.tv_usec;
                        }
-               } else if (doidling || pri->resetting) {
-                       /* Make sure we stop at least once per second if we're
-                          monitoring idle channels */
-                       tv.tv_sec = 1;
-                       tv.tv_usec = 0;
-               } else {
-                       /* Don't poll for more than 60 seconds */
-                       tv.tv_sec = 60;
-                       tv.tv_usec = 0;
                }
                ast_mutex_unlock(&pri->lock);
 
                e = NULL;
-               res = poll(fds, 1, tv.tv_sec * 1000 + tv.tv_usec / 1000);
+               res = poll(fds, numdchans, lowest.tv_sec * 1000 + lowest.tv_usec / 1000);
 
                ast_mutex_lock(&pri->lock);
                if (!res) {
-                       /* Just a timeout, run the scheduler */
-                       e = pri_schedule_run(pri->pri);
+                       for (which=0;which<NUM_DCHANS;which++) {
+                               if (!pri->dchans[which])
+                                       break;
+                               /* Just a timeout, run the scheduler */
+                               e = pri_schedule_run(pri->dchans[which]);
+                               if (e)
+                                       break;
+                       }
                } else if (res > -1) {
-                       e = pri_check_event(pri->pri);
+                       for (which=0;which<NUM_DCHANS;which++) {
+                               if (!pri->dchans[which])
+                                       break;
+                               if (fds[which].revents & (POLLIN | POLLPRI)) {
+                                       e = pri_check_event(pri->dchans[which]);
+                               }
+                               if (e)
+                                       break;
+                       }
                } else if (errno != EINTR)
                        ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno));
 
                if (e) {
                        if (pri->debug)
-                               pri_dump_event(pri->pri, e);
+                               pri_dump_event(pri->dchans[which], e);
                        switch(e->e) {
                        case PRI_EVENT_DCHAN_UP:
                                if (option_verbose > 1) 
-                                       ast_verbose(VERBOSE_PREFIX_2 "D-Channel on span %d up\n", pri->span);
-                               pri->up = 1;
+                                       ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span);
+                               pri->dchanavail[which] |= DCHAN_UP;
+                               pri_find_dchan(pri);
+
+                               /* Note presense of D-channel */
                                time(&pri->lastreset);
+
                                /* Restart in 5 seconds */
                                pri->lastreset -= RESET_INTERVAL;
                                pri->lastreset += 5;
@@ -6693,26 +6811,29 @@ static void *pri_dchannel(void *vpri)
                                break;
                        case PRI_EVENT_DCHAN_DOWN:
                                if (option_verbose > 1) 
-                                       ast_verbose(VERBOSE_PREFIX_2 "D-Channel on span %d down\n", pri->span);
-                               pri->up = 0;
-                               pri->resetting = 0;
-                               /* Hangup active channels and put them in alarm mode */
-                               for (i=0; i<pri->numchans; i++) {
-                                       struct zt_pvt *p = pri->pvts[i];
-                                       if (p) {
-                                               if (p->call) {
-                                                       if (p->pri && p->pri->pri) {
-                                                               pri_hangup(p->pri->pri, p->call, -1);
-                                                               pri_destroycall(p->pri->pri, p->call);
-                                                               p->call = NULL;
-                                                       } else
-                                                               ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n");
+                                       ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down\n", pri_order(which), pri->span);
+                               pri->dchanavail[which] &= ~DCHAN_UP;
+                               pri_find_dchan(pri);
+                               if (!pri_is_up(pri)) {
+                                       pri->resetting = 0;
+                                       /* Hangup active channels and put them in alarm mode */
+                                       for (i=0; i<pri->numchans; i++) {
+                                               struct zt_pvt *p = pri->pvts[i];
+                                               if (p) {
+                                                       if (p->call) {
+                                                               if (p->pri && p->pri->pri) {
+                                                                       pri_hangup(p->pri->pri, p->call, -1);
+                                                                       pri_destroycall(p->pri->pri, p->call);
+                                                                       p->call = NULL;
+                                                               } else
+                                                                       ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n");
+                                                       }
+                                                       if (p->master) {
+                                                               pri_hangup_all(p->master);
+                                                       } else if (p->owner)
+                                                               p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                       p->inalarm = 1;
                                                }
-                                               if (p->master) {
-                                                       pri_hangup_all(p->master);
-                                               } else if (p->owner)
-                                                       p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
-                                               p->inalarm = 1;
                                        }
                                }
                                break;
@@ -7231,14 +7352,28 @@ static void *pri_dchannel(void *vpri)
                                ast_log(LOG_DEBUG, "Event: %d\n", e->e);
                        }
                } else {
-                       /* Check for an event */
-                       x = 0;
-                       res = ioctl(pri->fd, ZT_GETEVENT, &x);
-                       if (x) 
-                               ast_log(LOG_NOTICE, "PRI got event: %d on span %d\n", x, pri->span);
-                       if (option_debug)
-                               ast_log(LOG_DEBUG, "Got event %s (%d) on D-channel for span %d\n", event2str(x), x, pri->span);
-               }
+                       for (i=0;i<NUM_DCHANS;i++) {
+                               if (!pri->dchannels[i])
+                                       break;
+                               /* Check for an event */
+                               x = 0;
+                               res = ioctl(pri->fds[i], ZT_GETEVENT, &x);
+                               if (x) 
+                                       ast_log(LOG_NOTICE, "PRI got event: %d on %s D-channel of span %d\n", x, pri_order(x), pri->span);
+
+                               /* Keep track of alarm state */ 
+                               if (x == ZT_EVENT_ALARM) {
+                                       pri->dchanavail[i] &= ~(DCHAN_NOTINALARM | DCHAN_UP);
+                                       pri_find_dchan(pri);
+                               } else if (x == ZT_EVENT_NOALARM) {
+                                       pri->dchanavail[i] |= DCHAN_NOTINALARM;
+                                       pri_find_dchan(pri);
+                               }
+                               
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Got event %s (%d) on D-channel for span %d\n", event2str(x), x, pri->span);
+                       }
+               }       
                ast_mutex_unlock(&pri->lock);
        }
        /* Never reached */
@@ -7250,53 +7385,78 @@ static int start_pri(struct zt_pri *pri)
        int res, x;
        ZT_PARAMS p;
        ZT_BUFFERINFO bi;
-
-       pri->fd = open("/dev/zap/channel", O_RDWR, 0600);
-       x = pri->offset + pri->dchannel;
-       if ((pri->fd < 0) || (ioctl(pri->fd,ZT_SPECIFY,&x) == -1)) {
-               ast_log(LOG_ERROR, "Unable to open D-channel %d (%d + %d) (%s)\n", x, pri->offset, pri->dchannel, strerror(errno));
-               return -1;
-       }
-
-       res = ioctl(pri->fd, ZT_GET_PARAMS, &p);
-       if (res) {
-               close(pri->fd);
-               pri->fd = -1;
-               ast_log(LOG_ERROR, "Unable to get parameters for D-channel %d (%s)\n", x, strerror(errno));
-               return -1;
-       }
-       if (p.sigtype != ZT_SIG_HDLCFCS) {
-               close(pri->fd);
-               pri->fd = -1;
-               ast_log(LOG_ERROR, "D-channel %d (%d + %d) is not in HDLC/FCS mode.  See /etc/zaptel.conf\n", x, pri->offset, pri->dchannel);
-               return -1;
-       }
-       bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
-       bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
-       bi.numbufs = 16;
-       bi.bufsize = 1024;
-       if (ioctl(pri->fd, ZT_SET_BUFINFO, &bi)) {
-               ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d\n", x);
-               close(pri->fd);
-               pri->fd = -1;
-               return -1;
-       }
-       pri->pri = pri_new(pri->fd, pri->nodetype, pri->switchtype);
-       if (!pri->pri) {
-               close(pri->fd);
-               pri->fd = -1;
-               ast_log(LOG_ERROR, "Unable to create PRI structure\n");
-               return -1;
+       struct zt_spaninfo si;
+       int i;
+       
+       for (i=0;i<NUM_DCHANS;i++) {
+               if (!pri->dchannels[i])
+                       break;
+               pri->fds[i] = open("/dev/zap/channel", O_RDWR, 0600);
+               x = pri->dchannels[i];
+               if ((pri->fds[i] < 0) || (ioctl(pri->fds[i],ZT_SPECIFY,&x) == -1)) {
+                       ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno));
+                       return -1;
+               }
+               res = ioctl(pri->fds[i], ZT_GET_PARAMS, &p);
+               if (res) {
+                       close(pri->fds[i]);
+                       pri->fds[i] = -1;
+                       ast_log(LOG_ERROR, "Unable to get parameters for D-channel %d (%s)\n", x, strerror(errno));
+                       return -1;
+               }
+               if (p.sigtype != ZT_SIG_HDLCFCS) {
+                       close(pri->fds[i]);
+                       pri->fds[i] = -1;
+                       ast_log(LOG_ERROR, "D-channel %d is not in HDLC/FCS mode.  See /etc/zaptel.conf\n", x);
+                       return -1;
+               }
+               memset(&si, 0, sizeof(si));
+               res = ioctl(pri->fds[i], ZT_SPANSTAT, &si);
+               if (res) {
+                       close(pri->fds[i]);
+                       pri->fds[i] = -1;
+                       ast_log(LOG_ERROR, "Unable to get span state for D-channel %d (%s)\n", x, strerror(errno));
+               }
+               if (!si.alarms)
+                       pri->dchanavail[i] |= DCHAN_NOTINALARM;
+               else
+                       pri->dchanavail[i] &= ~DCHAN_NOTINALARM;
+               bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+               bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+               bi.numbufs = 16;
+               bi.bufsize = 1024;
+               if (ioctl(pri->fds[i], ZT_SET_BUFINFO, &bi)) {
+                       ast_log(LOG_ERROR, "Unable to set appropriate buffering on channel %d\n", x);
+                       close(pri->fds[i]);
+                       pri->fds[i] = -1;
+                       return -1;
+               }
+               pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
+               /* Force overlap dial if we're doing GR-303! */
+               if (pri->switchtype == PRI_SWITCH_GR303_TMC)
+                       pri->overlapdial = 1;
+               pri_set_overlapdial(pri->dchans[i],pri->overlapdial);
+               /* Enslave to master if appropriate */
+               if (i)
+                       pri_enslave(pri->dchans[0], pri->dchans[i]);
+               if (!pri->dchans[i]) {
+                       close(pri->fds[i]);
+                       pri->fds[i] = -1;
+                       ast_log(LOG_ERROR, "Unable to create PRI structure\n");
+                       return -1;
+               }
+               pri_set_debug(pri->dchans[i], DEFAULT_PRI_DEBUG);
        }
+       /* Assume primary is the one we use */
+       pri->pri = pri->dchans[0];
        pri->resetpos = -1;
-       /* Force overlap dial if we're doing GR-303! */
-       if (pri->switchtype == PRI_SWITCH_GR303_TMC)
-               pri->overlapdial = 1;
-       pri_set_overlapdial(pri->pri,pri->overlapdial);
-       pri_set_debug(pri->pri, DEFAULT_PRI_DEBUG);
        if (pthread_create(&pri->master, NULL, pri_dchannel, pri)) {
-               close(pri->fd);
-               pri->fd = -1;
+               for (i=0;i<NUM_DCHANS;i++) {
+                       if (!pri->dchannels[i])
+                               break;
+                       close(pri->fds[i]);
+                       pri->fds[i] = -1;
+               }
                ast_log(LOG_ERROR, "Unable to spawn D-channel: %s\n", strerror(errno));
                return -1;
        }
@@ -7322,6 +7482,7 @@ static char *complete_span(char *line, char *word, int pos, int state)
 static int handle_pri_debug(int fd, int argc, char *argv[])
 {
        int span;
+       int x;
        if (argc < 4) {
                return RESULT_SHOWUSAGE;
        }
@@ -7334,7 +7495,10 @@ static int handle_pri_debug(int fd, int argc, char *argv[])
                ast_cli(fd, "No PRI running on span %d\n", span);
                return RESULT_SUCCESS;
        }
-       pri_set_debug(pris[span-1].pri, PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE);
+       for (x=0;x<NUM_DCHANS;x++) {
+               if (pris[span-1].dchans[x])
+                       pri_set_debug(pris[span-1].dchans[x], PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE);
+       }
        ast_cli(fd, "Enabled debugging on span %d\n", span);
        return RESULT_SUCCESS;
 }
@@ -7344,6 +7508,7 @@ static int handle_pri_debug(int fd, int argc, char *argv[])
 static int handle_pri_no_debug(int fd, int argc, char *argv[])
 {
        int span;
+       int x;
        if (argc < 5)
                return RESULT_SHOWUSAGE;
        span = atoi(argv[4]);
@@ -7355,7 +7520,10 @@ static int handle_pri_no_debug(int fd, int argc, char *argv[])
                ast_cli(fd, "No PRI running on span %d\n", span);
                return RESULT_SUCCESS;
        }
-       pri_set_debug(pris[span-1].pri, 0);
+       for (x=0;x<NUM_DCHANS;x++) {
+               if (pris[span-1].dchans[x])
+                       pri_set_debug(pris[span-1].dchans[x], 0);
+       }
        ast_cli(fd, "Disabled debugging on span %d\n", span);
        return RESULT_SUCCESS;
 }
@@ -7363,6 +7531,7 @@ static int handle_pri_no_debug(int fd, int argc, char *argv[])
 static int handle_pri_really_debug(int fd, int argc, char *argv[])
 {
        int span;
+       int x;
        if (argc < 5)
                return RESULT_SHOWUSAGE;
        span = atoi(argv[4]);
@@ -7374,14 +7543,36 @@ static int handle_pri_really_debug(int fd, int argc, char *argv[])
                ast_cli(fd, "No PRI running on span %d\n", span);
                return RESULT_SUCCESS;
        }
-       pri_set_debug(pris[span-1].pri, (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE));
+       for (x=0;x<NUM_DCHANS;x++) {
+               if (pris[span-1].dchans[x])
+                       pri_set_debug(pris[span-1].dchans[x], (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE));
+       }
        ast_cli(fd, "Enabled EXTENSIVE debugging on span %d\n", span);
        return RESULT_SUCCESS;
 }
 
+static void build_status(char *s, int status, int active)
+{
+       strcpy(s, "");
+       if (status & DCHAN_PROVISIONED)
+               strcat(s, "Provisioned, ");
+       if (!(status & DCHAN_NOTINALARM))
+               strcat(s, "In Alarm, ");
+       if (status & DCHAN_UP)
+               strcat(s, "Up");
+       else
+               strcat(s, "Down");
+       if (active)
+               strcat(s, ", Active");
+       else
+               strcat(s, ", Standby");
+}
+
 static int handle_pri_show_span(int fd, int argc, char *argv[])
 {
        int span;
+       int x;
+       char status[256];
        if (argc < 4)
                return RESULT_SHOWUSAGE;
        span = atoi(argv[3]);
@@ -7393,7 +7584,15 @@ static int handle_pri_show_span(int fd, int argc, char *argv[])
                ast_cli(fd, "No PRI running on span %d\n", span);
                return RESULT_SUCCESS;
        }
-       pri_dump_info(pris[span-1].pri);
+       for(x=0;x<NUM_DCHANS;x++) {
+               if (pris[span-1].dchannels[x]) {
+                       ast_cli(fd, "%s D-channel: %d\n", pri_order(x), pris[span-1].dchannels[x]);
+                       build_status(status, pris[span-1].dchanavail[x], pris[span-1].dchans[x] == pris[span-1].pri);
+                       ast_cli(fd, "Status: %s\n", status);
+                       pri_dump_info(pris[span-1].pri);
+                       ast_cli(fd, "\n");
+               }
+       }
        return RESULT_SUCCESS;
 }
 
@@ -7971,7 +8170,7 @@ static int __unload_module(void)
 #ifdef ZAPATA_PRI              
        for(i=0;i<NUM_SPANS;i++) {
                pthread_join(pris[i].master, NULL);
-               zt_close(pris[i].fd);
+               zt_close(pris[i].fds[i]);
        }
 #endif
        return 0;
@@ -7996,9 +8195,10 @@ static int setup_zap(void)
        int cur_radio = 0;
 #ifdef ZAPATA_PRI
        int spanno;
+       int i;
        int logicalspan;
        int trunkgroup;
-       int dchannel;
+       int dchannels[NUM_DCHANS];
        struct zt_pri *pri;
 #endif
 
@@ -8024,14 +8224,23 @@ static int setup_zap(void)
                        trunkgroup = atoi(v->value);
                        if (trunkgroup > 0) {
                                if ((c = strchr(v->value, ','))) {
-                                       dchannel = atoi(c + 1);
-                                       if (dchannel > 0) {
-                                               if (pri_create_trunkgroup(trunkgroup, dchannel)) {
-                                                       ast_log(LOG_WARNING, "Unable to create trunk group %d with D-channel %d at line %d of zapata.conf\n", trunkgroup, dchannel, v->lineno);
+                                       i = 0;
+                                       memset(dchannels, 0, sizeof(dchannels));
+                                       while(c && (i < NUM_DCHANS)) {
+                                               dchannels[i] = atoi(c + 1);
+                                               if (dchannels[i] < 0) {
+                                                       ast_log(LOG_WARNING, "D-channel for trunk group %d must be a postiive number at line %d of zapata.conf\n", trunkgroup, v->lineno);
+                                               } else
+                                                       i++;
+                                               c = strchr(c + 1, ',');
+                                       }
+                                       if (i) {
+                                               if (pri_create_trunkgroup(trunkgroup, dchannels)) {
+                                                       ast_log(LOG_WARNING, "Unable to create trunk group %d with Primary D-channel %d at line %d of zapata.conf\n", trunkgroup, dchannels[0], v->lineno);
                                                } else if (option_verbose > 1)
-                                                       ast_verbose(VERBOSE_PREFIX_2 "Created trunk group %d with D-channel %d\n", trunkgroup, dchannel);
+                                                       ast_verbose(VERBOSE_PREFIX_2 "Created trunk group %d with Primary D-channel %d and %d backup%s\n", trunkgroup, dchannels[0], i - 1, (i == 1) ? "" : "s");
                                        } else
-                                               ast_log(LOG_WARNING, "D-channel for trunk group %d must be a postiive number at line %d of zapata.conf\n", trunkgroup, v->lineno);
+                                               ast_log(LOG_WARNING, "Trunk group %d lacks any valid D-channels at line %d of zapata.conf\n", trunkgroup, v->lineno);
                                } else
                                        ast_log(LOG_WARNING, "Trunk group %d lacks a primary D-channel at line %d of zapata.conf\n", trunkgroup, v->lineno);
                        } else
@@ -8572,11 +8781,12 @@ int load_module(void)
        int res;
 
 #ifdef ZAPATA_PRI
-       int y;
+       int y,i;
        memset(pris, 0, sizeof(pris));
        for (y=0;y<NUM_SPANS;y++) {
                pris[y].offset = -1;
-               pris[y].fd = -1;
+               for (i=0;i<NUM_DCHANS;i++)
+                       pris[y].fds[i] = -1;
        }
        pri_set_error(zt_pri_error);
        pri_set_message(zt_pri_message);