Version 0.1.8 from FTP
authorMark Spencer <markster@digium.com>
Mon, 7 May 2001 03:31:07 +0000 (03:31 +0000)
committerMark Spencer <markster@digium.com>
Mon, 7 May 2001 03:31:07 +0000 (03:31 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@317 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_tor.c

index 45ed420..65a7791 100755 (executable)
@@ -24,6 +24,7 @@
 #include <asterisk/file.h>
 #include <asterisk/ulaw.h>
 #include <asterisk/callerid.h>
+#include <asterisk/cli.h>
 #include <sys/signal.h>
 #include <sys/select.h>
 #include <errno.h>
 #include <zap.h>
 #include <math.h>
 #include <tonezone.h>
+#ifdef TORMENTA_PRI
+#include <libpri.h>
+#endif
+
+/* 
+   XXX 
+   XXX   We definitely need to lock the private structure in tor_read and such 
+   XXX  
+ */
 
+#ifdef TORMENTA_PRI
+static char *desc = "Tormenta (Zapata) Channelized T1/PRI Driver";
+static char *tdesc = "Tormenta T1//PRI Driver";
+#else
 static char *desc = "Tormenta (Zapata) Channelized T1 Driver";
-static char *type = "Tor";
 static char *tdesc = "Tormenta T1 Driver";
+#endif
+static char *type = "Tor";
 static char *config = "tormenta.conf";
 
 #define SIG_EM         0x1
@@ -49,8 +64,9 @@ static char *config = "tormenta.conf";
 #define SIG_FXOLS      0x5
 #define SIG_FXOGS      0x6
 #define SIG_FXOKS      0x7
+#define SIG_PRI                0x8
 
-#define tor_STATE_DOWN 0
+#define NUM_SPANS      2
 
 static char context[AST_MAX_EXTENSION] = "default";
 static char callerid[256] = "";
@@ -73,6 +89,22 @@ static int immediate = 0;
 
 static int stripmsd = 0;
 
+static int callwaiting = 0;
+
+static int callwaitingcallerid = 0;
+
+static int hidecallerid = 0;
+
+static int threewaycalling = 0;
+
+static int transfer = 0;
+
+static float rxgain = 0.0;
+
+static float txgain = 0.0;
+
+static int echocancel;
+
 /* Wait up to 16 seconds for first digit (FXO logic) */
 static int firstdigittimeout = 16000;
 
@@ -116,31 +148,184 @@ static inline int tor_wait_event(int fd)
 /* Chunk size to read -- we use the same size as the chunks that the zapata library uses.  */   
 #define READ_SIZE 204
 
+#define MASK_AVAIL             (1 << 0)                /* Channel available for PRI use */
+#define MASK_INUSE             (1 << 1)                /* Channel currently in use */
+
+#define CALLWAITING_SILENT_SAMPLES     ( (300 * 8) / READ_SIZE) /* 300 ms */
+#define CALLWAITING_REPEAT_SAMPLES     ( (10000 * 8) / READ_SIZE) /* 300 ms */
+
+struct tor_pvt;
+
+
+#ifdef TORMENTA_PRI
+struct tor_pri {
+       pthread_t master;                       /* Thread of master */
+       pthread_mutex_t lock;           /* Mutex */
+       int nodetype;                           /* Node type */
+       int switchtype;                         /* Type of switch to emulate */
+       struct pri *pri;
+       int debug;
+       int fd;
+       int up;
+       int offset;
+       int span;
+       int chanmask[24];                       /* Channel status */
+       struct tor_pvt *pvt[24];        /* Member channel pvt structs */
+       struct tor_channel *chan[24];   /* Channels on each line */
+};
+
+static struct tor_pri pris[NUM_SPANS];
+
+static int pritype = PRI_CPE;
+
+#if 0
+#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE)
+#else
+#define DEFAULT_PRI_DEBUG 0
+/*
+#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW)
+*/
+#endif
+
+static inline int pri_grab(struct tor_pri *pri)
+{
+       int res;
+       /* Grab the lock first */
+    res = ast_pthread_mutex_lock(&pri->lock);
+       if (res)
+               return res;
+       /* Then break the select */
+       pthread_kill(pri->master, SIGURG);
+       return 0;
+}
+
+static inline void pri_rel(struct tor_pri *pri)
+{
+       ast_pthread_mutex_unlock(&pri->lock);
+}
+
+static int switchtype = PRI_SWITCH_NI2;
+
+#endif
+
 static struct tor_pvt {
        ZAP *z;
+       pthread_mutex_t lock;
        struct ast_channel *owner;      /* Our owner (if applicable) */
+       struct ast_channel *owners[3];  
+               /* Up to three channels can be associated with this call */
+               
+       int callwaitindex;                      /* Call waiting index into owners */    
+       int thirdcallindex;                     /* Three-way calling index into owners */
+       int normalindex;                        /* "Normal" call index into owners */
+       
        int sig;                                        /* Signalling style */
+       float rxgain;
+       float txgain;
        struct tor_pvt *next;                   /* Next channel in list */
        char context[AST_MAX_EXTENSION];
+       char exten[AST_MAX_EXTENSION];
        char language[MAX_LANGUAGE];
        char callerid[AST_MAX_EXTENSION];
+       char callwaitcid[AST_MAX_EXTENSION];
        char dtmfq[AST_MAX_EXTENSION];
        struct ast_frame f;
        short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE];
        int group;
-       int state;                                              /* Perhaps useful state info */
        int immediate;                          /* Answer before getting digits? */
        int channel;                            /* Channel Number */
-       int ringgothangup;                              /* Have we received exactly one hangup after a ring */
+       int span;                                       /* Span number */
        int dialing;
        int use_callerid;                       /* Whether or not to use caller id on this channel */
+       int hidecallerid;
+       int permhidecallerid;           /* Whether to hide our outgoing caller ID or not */
+       int callwaitingrepeat;          /* How many samples to wait before repeating call waiting */
        unsigned char *cidspill;
        int cidpos;
        int cidlen;
        int stripmsd;
+       int needringing[3];
+       int needanswer[3];
+       int callwaiting;
+       int callwaitcas;
+       int callwaitrings;
+       int echocancel;
+       int permcallwaiting;
+       int callwaitingcallerid;
+       int threewaycalling;
+       int transfer;
+       int cref;                                       /* Call reference number */
        DIAL_OPERATION dop;
+       struct tor_confinfo conf;       /* Saved state of conference */
+       struct tor_confinfo conf2;      /* Saved state of alternate conference */
+       int confno;                                     /* Conference number */
+       ZAP *pseudo;                            /* Pseudo channel FD */
+       int pseudochan;                         /* Pseudo channel */
+#ifdef TORMENTA_PRI
+       struct tor_pri *pri;
+       q931_call *call;
+#endif 
 } *iflist = NULL;
 
+#define FIRST_PSEUDO 49
+
+#define INTHREEWAY(p) ((p->normalindex > -1) && (p->thirdcallindex > -1) && \
+               (p->owner == p->owners[p->normalindex]))
+
+static int alloc_pseudo(struct tor_pvt *p)
+{
+       int x;
+       ZAP *z;
+       int res;
+       BUFFER_INFO bi;
+       char fn[256];
+       if (p->pseudo || p->pseudochan){
+               ast_log(LOG_WARNING, "Already have a pseudo fd: %d, chan: %d\n",
+                       zap_fd(p->pseudo), p->pseudochan);
+               return -1;
+       }
+       for (x=FIRST_PSEUDO;;x++) {
+               snprintf(fn, sizeof(fn), "/dev/tor/%d", x);
+               z = zap_open(fn, 1);
+               if (!z) {
+                       if (errno != EBUSY) {
+                               ast_log(LOG_WARNING, "Unable to open %s: %s\n", fn, strerror(errno));
+                               return -1;
+                       }
+               } else {
+                       res = ioctl(zap_fd(z), TOR_GET_BUFINFO, &bi);
+                       if (!res) {
+                               bi.txbufpolicy = POLICY_IMMEDIATE;
+                               bi.rxbufpolicy = POLICY_IMMEDIATE;
+                               bi.numbufs = 4;
+                               res = ioctl(zap_fd(z), TOR_SET_BUFINFO, &bi);
+                               if (res < 0) {
+                                       ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d\n", x);
+                               }
+                       } else
+                               ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", x);
+                       p->pseudo = z;
+                       p->pseudochan = x;
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Allocated pseudo channel %d on FD %d\n", p->pseudochan, zap_fd(p->pseudo));
+                       return 0;
+               }
+       }
+       /* Never reached */
+       return 0;
+}
+
+static int unalloc_pseudo(struct tor_pvt *p)
+{
+       if (p->pseudo)
+               zap_close(p->pseudo);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Released pseudo channel %d\n", p->pseudochan);
+       p->pseudo = NULL;
+       p->pseudochan = 0;
+       return 0;
+}
+
 static int tor_digit(struct ast_channel *ast, char digit)
 {
        DIAL_OPERATION zo;
@@ -206,12 +391,147 @@ static char *sig2str(int sig)
                return "FXO Groundstart";
        case SIG_FXOKS:
                return "FXO Kewlstart";
+       case SIG_PRI:
+               return "PRI Signalling";
        default:
                snprintf(buf, sizeof(buf), "Unknown signalling %d\n", sig);
                return buf;
        }
 }
 
+static int conf_set(struct tor_pvt *p, int req, int force)
+{
+       /* Set channel to given conference, -1 to allocate one */
+       TOR_CONFINFO ci;
+       TOR_CONFINFO cip;
+       int res;
+       if ((p->confno > -1) && (p->confno != req) && (!force)) {
+               ast_log(LOG_WARNING, "Channel %d already has conference %d allocated\n", p->channel, p->confno);
+               return -1;
+       }
+       ci.chan = 0;
+       ci.confno = 0;
+       /* Check current conference stuff */
+       res = ioctl(zap_fd(p->z), TOR_GETCONF, &ci);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Failed to get conference info on channel %d: %s\n",
+                       p->channel, strerror(errno));
+               return -1;
+       }
+       if (!force && ci.confmode && (ci.confno != p->confno)) {
+               ast_log(LOG_WARNING, "Channel %d is already in a conference (%d, %d) we didn't create (req = %d)\n", p->channel, ci.confno, ci.confmode, req);
+               return -1;
+       }
+       ci.chan = 0;
+       ci.confno = req;
+       ci.confmode = TOR_CONF_REALANDPSEUDO | TOR_CONF_TALKER | TOR_CONF_LISTENER | TOR_CONF_PSEUDO_LISTENER | TOR_CONF_PSEUDO_TALKER;
+       res = ioctl(zap_fd(p->z), TOR_SETCONF, &ci);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Failed to set conference to %d on channel %d: %s\n",
+                       req, p->channel, strerror(errno));
+               return -1;
+       }
+       if (INTHREEWAY(p)) {
+                       /* We have a three way call active, be sure the third participant is included in
+                          our conference. */
+               cip.chan = 0;
+               cip.confno = ci.confno;
+               cip.confmode = TOR_CONF_CONF | TOR_CONF_TALKER | TOR_CONF_LISTENER;
+               
+               res = ioctl(zap_fd(p->pseudo), TOR_SETCONF, &cip);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "Failed to set conference info on pseudo channel %d: %s\n",
+                               p->pseudochan, strerror(errno));
+                       return -1;
+               }
+               ast_log(LOG_DEBUG, "Conferenced in third way call\n");
+       } else {
+               if (p->pseudo || (p->pseudochan)) {
+                       ast_log(LOG_DEBUG, "There's a pseudo something on %d (channel %d), but we're not conferencing it in at the moment?\n",
+                               zap_fd(p->pseudo), p->pseudochan);
+                       cip.chan = 0;
+                       cip.confno = ci.confno;
+                       cip.confmode = TOR_CONF_NORMAL;
+                       res = ioctl(zap_fd(p->pseudo), TOR_SETCONF, &cip);
+                       if (res < 0) {
+                               ast_log(LOG_WARNING, "Failed to set conference info on pseudo channel %d: %s\n",
+                                       p->pseudochan, strerror(errno));
+                               return -1;
+                       }
+               }
+       }
+       p->confno = ci.confno;
+       return 0;
+}
+
+static int three_way(struct tor_pvt *p)
+{
+       ast_log(LOG_DEBUG, "Setting up three way call\n");
+       return conf_set(p, p->confno, 0);
+}
+
+static int conf_clear(struct tor_pvt *p)
+{
+       TOR_CONFINFO ci;
+       int res;
+       ci.confmode = TOR_CONF_NORMAL;
+       ci.chan = 0;
+       ci.confno = 0;
+       res = ioctl(zap_fd(p->z), TOR_SETCONF, &ci);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Failed to clear conference info on channel %d: %s\n",
+                       p->channel, strerror(errno));
+               return -1;
+       }
+       p->confno = -1;
+       return 0;
+}
+
+static void tor_enable_ec(struct tor_pvt *p)
+{
+       int x;
+       int res;
+       if (p->echocancel) {
+               x = 1;
+               res = ioctl(zap_fd(p->z), TOR_ECHOCANCEL, &x);
+               if (res) 
+                       ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d\n", p->channel);
+               else
+                       ast_log(LOG_DEBUG, "Enabled echo cancellation on channel %d\n", p->channel);
+       }
+}
+
+static void tor_disable_ec(struct tor_pvt *p)
+{
+       int x;
+       int res;
+       if (p->echocancel) {
+               x = 0;
+               res = ioctl(zap_fd(p->z), TOR_ECHOCANCEL, &x);
+               if (res) 
+                       ast_log(LOG_WARNING, "Unable to disable echo cancellation on channel %d\n", p->channel);
+               else
+                       ast_log(LOG_DEBUG, "disabled echo cancellation on channel %d\n", p->channel);
+       }
+}
+
+static int tor_get_index(struct ast_channel *ast, struct tor_pvt *p, int nullok)
+{
+       int res;
+       if (p->owners[0] == ast)
+               res = 0;
+       else if (p->owners[1] == ast)
+               res = 1;
+       else if (p->owners[2] == ast)
+               res = 2;
+       else {
+               res = -1;
+               if (!nullok)
+                       ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
+       }
+       return res;
+}
+
 static int set_actual_gain(int fd, int chan, float rxgain, float txgain)
 {
        struct  tor_gains g;
@@ -237,6 +557,7 @@ static int set_actual_gain(int fd, int chan, float rxgain, float txgain)
          /* set 'em */
        return(ioctl(fd,TOR_SETGAINS,&g));
 }
+
 static inline int tor_set_hook(int fd, int hs)
 {
        int x, res;
@@ -247,6 +568,106 @@ static inline int tor_set_hook(int fd, int hs)
        return res;
 }
 
+static int save_conference(struct tor_pvt *p)
+{
+       struct tor_confinfo c;
+       int res;
+       if (p->conf.confmode) {
+               ast_log(LOG_WARNING, "Can't save conference -- already in use\n");
+               return -1;
+       }
+       p->conf.chan = 0;
+       res = ioctl(zap_fd(p->z), TOR_GETCONF, &p->conf);
+       if (res) {
+               ast_log(LOG_WARNING, "Unable to get conference info: %s\n", strerror(errno));
+               p->conf.confmode = 0;
+               return -1;
+       }
+       c.chan = 0;
+       c.confno = 0;
+       c.confmode = TOR_CONF_NORMAL;
+       res = ioctl(zap_fd(p->z), TOR_SETCONF, &c);
+       if (res) {
+               ast_log(LOG_WARNING, "Unable to set conference info: %s\n", strerror(errno));
+               return -1;
+       }
+       switch(p->conf.confmode) {
+       case TOR_CONF_NORMAL:
+               p->conf2.confmode = 0;
+               break;
+       case TOR_CONF_MONITOR:
+               /* Get the other size */
+               p->conf2.chan = p->conf.confno;
+               res = ioctl(zap_fd(p->z), TOR_GETCONF, &p->conf2);
+               if (res) {
+                       ast_log(LOG_WARNING, "Unable to get secondaryconference info: %s\n", strerror(errno));
+                       p->conf2.confmode = 0;
+                       return -1;
+               }
+               c.chan = p->conf.confno;
+               c.confno = 0;
+               c.confmode = TOR_CONF_NORMAL;
+               res = ioctl(zap_fd(p->z), TOR_SETCONF, &c);
+               if (res) {
+                       ast_log(LOG_WARNING, "Unable to set secondaryconference info: %s\n", strerror(errno));
+                       p->conf2.confmode = 0;
+                       return -1;
+               }
+               break;
+       case TOR_CONF_CONF | TOR_CONF_LISTENER | TOR_CONF_TALKER:
+               p->conf2.confmode = 0;
+               break;
+       default:
+               ast_log(LOG_WARNING, "Don't know how to save conference state for conf mode %d\n", p->conf.confmode);
+               return -1;
+       }
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Disabled conferencing\n");
+       return 0;
+}
+
+static int restore_conference(struct tor_pvt *p)
+{
+       int res;
+       if (p->conf.confmode) {
+               res = ioctl(zap_fd(p->z), TOR_SETCONF, &p->conf);
+               p->conf.confmode = 0;
+               if (res) {
+                       ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno));
+                       return -1;
+               }
+               if (p->conf2.confmode) {
+                       res = ioctl(zap_fd(p->z), TOR_SETCONF, &p->conf2);
+                       p->conf2.confmode = 0;
+                       if (res) {
+                               ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno));
+                               return -1;
+                       }
+               }
+       }
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Restored conferencing\n");
+       return 0;
+}
+
+static int send_callerid(struct tor_pvt *p);
+
+int send_cwcidspill(struct tor_pvt *p)
+{
+       p->callwaitcas = 0;
+       p->cidspill = malloc(MAX_CALLERID_SIZE);
+       if (p->cidspill) {
+               memset(p->cidspill, 0x7f, MAX_CALLERID_SIZE);
+               p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, p->callwaitcid);
+               /* Make sure we account for the end */
+               p->cidlen += READ_SIZE * 4;
+               p->cidpos = 0;
+               send_callerid(p);
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "CPE supports Call Waiting Caller*ID.  Sending '%s'\n", p->callwaitcid);
+       } else return -1;
+       return 0;
+}
 
 static int send_callerid(struct tor_pvt *p)
 {
@@ -268,13 +689,62 @@ static int send_callerid(struct tor_pvt *p)
        }
        free(p->cidspill);
        p->cidspill = 0;
+       if (p->callwaitcas) {
+               zap_clrdtmfn(p->z);
+               /* Check for a the ack on the CAS */
+               res = zap_getdtmf(p->z, 1, NULL, 0, 250, 250, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
+               if (res > 0) {
+                       char tmp[2];
+                       strncpy(tmp, zap_dtmfbuf(p->z), sizeof(tmp));
+                       zap_clrdtmfn(p->z);
+                       if ((tmp[0] == 'A') || (tmp[0] == 'D')) {
+                               send_cwcidspill(p);
+                       }
+               } else {
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "CPE does not support Call Waiting Caller*ID.\n");
+                       restore_conference(p);
+               }
+       } else
+               restore_conference(p);
        return 0;
 }
-                                                                                
+
+static int tor_callwait(struct ast_channel *ast)
+{
+       struct tor_pvt *p = ast->pvt->pvt;
+       p->callwaitingrepeat = CALLWAITING_REPEAT_SAMPLES;
+       if (p->cidspill) {
+               ast_log(LOG_WARNING, "Spill already exists?!?\n");
+               free(p->cidspill);
+       }
+       p->cidspill = malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4);
+       if (p->cidspill) {
+               save_conference(p);
+               /* Silence */
+               memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4);
+               if (!p->callwaitrings && p->callwaitingcallerid) {
+                       ast_callerid_gen_cas(p->cidspill, 2400 + 680);
+                       p->callwaitcas = 1;
+                       p->cidlen = 2400 + 680 + READ_SIZE * 4;
+               } else {
+                       ast_callerid_gen_cas(p->cidspill, 2400);
+                       p->callwaitcas = 0;
+                       p->cidlen = 2400 + READ_SIZE * 4;
+               }
+               p->cidpos = 0;
+               send_callerid(p);
+       } else {
+               ast_log(LOG_WARNING, "Unable to create SAS/CAS spill\n");
+               return -1;
+       }
+       return 0;
+}
+
 static int tor_call(struct ast_channel *ast, char *dest, int timeout)
 {
        struct tor_pvt *p = ast->pvt->pvt;
-       int x, res;
+       int x, res, index;
        char *c, *n, *l;
        char callerid[256];
        if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
@@ -285,26 +755,46 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
        case SIG_FXOLS:
        case SIG_FXOGS:
        case SIG_FXOKS:
-               if (p->use_callerid) {
-                       /* Generate the Caller-ID spill if desired */
-                       if (p->cidspill) {
-                               ast_log(LOG_WARNING, "cidspill already exists??\n");
-                               free(p->cidspill);
+               if (p->owner == ast) {
+                       /* Normal ring, on hook */
+                       if (p->use_callerid) {
+                               /* Generate the Caller-ID spill if desired */
+                               if (p->cidspill) {
+                                       ast_log(LOG_WARNING, "cidspill already exists??\n");
+                                       free(p->cidspill);
+                               }
+                               p->cidspill = malloc(MAX_CALLERID_SIZE);
+                               p->callwaitcas = 0;
+                               if (p->cidspill) {
+                                       p->cidlen = ast_callerid_generate(p->cidspill, ast->callerid);
+                                       p->cidpos = 0;
+                                       send_callerid(p);
+                               } else
+                                       ast_log(LOG_WARNING, "Unable to generate CallerID spill\n");
                        }
-                       p->cidspill = malloc(MAX_CALLERID_SIZE);
-                       if (p->cidspill) {
-                               p->cidlen = ast_callerid_generate(p->cidspill, ast->callerid);
-                               p->cidpos = 0;
-                               send_callerid(p);
-                       } else
-                               ast_log(LOG_WARNING, "Unable to generate CallerID spill\n");
-               }
-               x = TOR_RING;
-               if (ioctl(zap_fd(p->z), TOR_HOOK, &x) && (errno != EINPROGRESS)) {
-                       ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
-                       return -1;
+                       x = TOR_RING;
+                       if (ioctl(zap_fd(p->z), TOR_HOOK, &x) && (errno != EINPROGRESS)) {
+                               ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
+                               return -1;
+                       }
+                       p->dialing = 1;
+               } else {
+                       /* Call waiting call */
+                       p->callwaitrings = 0;
+                       if (ast->callerid)
+                               strncpy(p->callwaitcid, ast->callerid, sizeof(p->callwaitcid));
+                       else
+                               strcpy(p->callwaitcid, "");
+                       /* Call waiting tone instead */
+                       if (tor_callwait(ast))
+                               return -1;
+                               
                }
                ast->state = AST_STATE_RINGING;
+               index = tor_get_index(ast, p, 0);
+               if (index > -1) {
+                       p->needringing[index] = 1;
+               }
                break;
        case SIG_FXSLS:
        case SIG_FXSGS:
@@ -336,7 +826,6 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
                        if (ast->callerid) {
                                strncpy(callerid, ast->callerid, sizeof(callerid));
                                ast_callerid_parse(callerid, &n, &l);
-                               printf("Name: %s, number: %s\n", n, l);
                                if (l) {
                                        ast_shrink_phone_number(l);
                                        if (!ast_isphonenumber(l))
@@ -362,6 +851,36 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
                p->dialing = 1;
                ast->state = AST_STATE_DIALING;
                break;
+#ifdef TORMENTA_PRI
+       case SIG_PRI:
+               c = strchr(dest, '/');
+               if (c)
+                       c++;
+               else
+                       c = dest;
+               if (ast->callerid) {
+                       strncpy(callerid, ast->callerid, sizeof(callerid));
+                       ast_callerid_parse(callerid, &n, &l);
+                       if (l) {
+                               ast_shrink_phone_number(l);
+                               if (!ast_isphonenumber(l))
+                                       l = NULL;
+                       }
+               } else
+                       l = NULL;
+               if (strlen(c) < p->stripmsd) {
+                       ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+                       return -1;
+               }
+               if (pri_call(p->pri->pri, p->call, PRI_TRANS_CAP_SPEECH, 
+                       ((p->channel - 1) % 24) + 1, p->pri->nodetype == PRI_NETWORK ? 0 : 1, 1, l, PRI_NATIONAL_ISDN, 
+                       l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE,
+                       c + p->stripmsd, PRI_NATIONAL_ISDN)) {
+                       ast_log(LOG_WARNING, "Unable to setup call to %s\n", c + p->stripmsd);
+                       return -1;
+               }
+               break;
+#endif                         
        default:
                ast_log(LOG_DEBUG, "not yet implemented\n");
                return -1;
@@ -372,6 +891,7 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
 static int tor_hangup(struct ast_channel *ast)
 {
        int res;
+       int index;
        struct tor_pvt *p = ast->pvt->pvt;
        TOR_PARAMS par;
        if (option_debug)
@@ -380,47 +900,126 @@ static int tor_hangup(struct ast_channel *ast)
                ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
                return 0;
        }
-       res = tor_set_hook(zap_fd(p->z), TOR_ONHOOK);
-       if (res < 0) {
-               ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
-               return -1;
+       index = tor_get_index(ast, p, 1);
+
+       zap_digitmode(p->z,0);
+       ast->state = AST_STATE_DOWN;
+       ast_log(LOG_DEBUG, "Hangup: index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+               index, p->normalindex, p->callwaitindex, p->thirdcallindex);
+       
+       if (index > -1) {
+               /* Real channel, do some fixup */
+               p->owners[index] = NULL;
+               p->needanswer[index] = 0;
+               p->needringing[index] = 0;
+               if (index == p->normalindex) {
+                       p->normalindex = -1;
+                       if ((p->callwaitindex > -1) && (p->thirdcallindex > -1)) 
+                               ast_log(LOG_WARNING, "Normal call hung up with both three way call and a call waiting call in place?\n");
+                       if (p->callwaitindex > -1) {
+                               /* If we hung up the normal call, make the call wait call
+                                  be the normal call if there was one */
+                               p->normalindex = p->callwaitindex;
+                               p->callwaitindex = -1;
+                       } else if (p->thirdcallindex > -1) {
+                               /* This was part of a three way call */
+                               p->normalindex = p->thirdcallindex;
+                               p->owners[p->normalindex]->fds[0] = zap_fd(p->z);
+                               p->thirdcallindex = -1;
+                               unalloc_pseudo(p);
+                       }
+               } else if (index == p->callwaitindex) {
+                       /* If this was a call waiting call, mark the call wait
+                          index as -1, so we know it's available again */
+                       p->callwaitindex = -1;
+               } else if (index == p->thirdcallindex) {
+                       /* If this was part of a three way call index, let us make
+                          another three way call */
+                       p->thirdcallindex = -1;
+                       unalloc_pseudo(p);
+               } else {
+                       /* This wasn't any sort of call, but how are we an index? */
+                       ast_log(LOG_WARNING, "Index found but not any type of call?\n");
+               }
        }
-       switch(p->sig) {
-       case SIG_FXOGS:
-       case SIG_FXOLS:
-       case SIG_FXOKS:
-               res = ioctl(zap_fd(p->z), TOR_GET_PARAMS, &par);
-               if (!res) {
-                       /* If they're off hook, try playing congestion */
-                       if (par.rxisoffhook)
-                               tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+
+       if (!p->owners[0] && !p->owners[1] && !p->owners[2]) {
+               p->owner = NULL;
+               /* Perform low level hangup if no owner left */
+#ifdef TORMENTA_PRI
+               if (p->sig == SIG_PRI) {
+                       if (p->call) {
+                               if (!pri_grab(p->pri)) {
+                                       res = pri_disconnect(p->pri->pri, p->call, PRI_CAUSE_NORMAL_CLEARING);
+                                       p->call = NULL;
+                                       if (res < 0) 
+                                               ast_log(LOG_WARNING, "pri_disconnect failed\n");
+                                       pri_rel(p->pri);                        
+                               } else {
+                                       ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+                                       res = -1;
+                               }
+                       } else
+                               res = 0;
+               } else
+#endif
+                       res = tor_set_hook(zap_fd(p->z), TOR_ONHOOK);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+                       return -1;
                }
-               break;
-       default:
+               switch(p->sig) {
+               case SIG_FXOGS:
+               case SIG_FXOLS:
+               case SIG_FXOKS:
+                       res = ioctl(zap_fd(p->z), TOR_GET_PARAMS, &par);
+                       if (!res) {
+#if 0
+                               ast_log(LOG_DEBUG, "Hanging up channel %d, offhook = %d\n", p->channel, par.rxisoffhook);
+#endif
+                               /* If they're off hook, try playing congestion */
+                               if (par.rxisoffhook)
+                                       tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+                               else
+                                       tone_zone_play_tone(zap_fd(p->z), -1);
+                       }
+                       break;
+               default:
+               }
+               if (index > -1) {
+                       p->needringing[index] = 0;
+                       p->needanswer[index] = 0;
+               }
+               if (p->cidspill)
+                       free(p->cidspill);
+               tor_disable_ec(p);
+               p->cidspill = NULL;
+               p->callwaitcas = 0;
+               p->callwaiting = p->permcallwaiting;
+               p->hidecallerid = p->permhidecallerid;
+               p->dialing = 0;
+               conf_clear(p);
+               unalloc_pseudo(p);
+               restart_monitor();
        }
+       p->callwaitingrepeat = 0;
+       ast->pvt->pvt = NULL;
        ast->state = AST_STATE_DOWN;
-       p->owner = NULL;
-       p->ringgothangup = 0;
-       if (p->cidspill)
-               free(p->cidspill);
-       p->cidspill = NULL;
-       pthread_mutex_lock(&usecnt_lock);
+       ast_pthread_mutex_lock(&usecnt_lock);
        usecnt--;
        if (usecnt < 0) 
                ast_log(LOG_WARNING, "Usecnt < 0???\n");
-       pthread_mutex_unlock(&usecnt_lock);
+       ast_pthread_mutex_unlock(&usecnt_lock);
        ast_update_use_count();
        if (option_verbose > 2) 
                ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name);
-       ast->pvt->pvt = NULL;
-       ast->state = AST_STATE_DOWN;
-       restart_monitor();
        return 0;
 }
 
 static int tor_answer(struct ast_channel *ast)
 {
        struct tor_pvt *p = ast->pvt->pvt;
+       int res=0;
        ast->state = AST_STATE_UP;
        switch(p->sig) {
        case SIG_FXSLS:
@@ -434,41 +1033,62 @@ static int tor_answer(struct ast_channel *ast)
        case SIG_FXOKS:
                /* Pick up the line */
                ast_log(LOG_DEBUG, "Took %s off hook\n", ast->name);
-               return tor_set_hook(zap_fd(p->z), TOR_OFFHOOK);
+               res =  tor_set_hook(zap_fd(p->z), TOR_OFFHOOK);
+               tone_zone_play_tone(zap_fd(p->z), -1);
+               if (INTHREEWAY(p))
+                       tone_zone_play_tone(zap_fd(p->pseudo), -1);
+               p->dialing = 0;
                break;
-               /* Nothing */
+#ifdef TORMENTA_PRI
+       case SIG_PRI:
+               /* Send a pri acknowledge */
+               if (!pri_grab(p->pri)) {
+                       res = pri_answer(p->pri->pri, p->call, 0, 1);
+                       pri_rel(p->pri);
+               } else {
+                       ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+                       res= -1;
+               }
                break;
+#endif         
        default:
                ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel);
                return -1;
        }
-       return 0;
+       return res;
 }
 
-static int bridge_cleanup(struct tor_pvt *p0, struct tor_pvt *p1)
+static inline int bridge_cleanup(struct tor_pvt *p0, struct tor_pvt *p1)
 {
-       struct tor_confinfo c;
        int res;
-       c.chan = 0;
-       c.confno = 0;
-       c.confmode = TOR_CONF_NORMAL;
-       res = ioctl(zap_fd(p0->z), TOR_SETCONF, &c);
-       if (res) {
-               ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %d: %s\n", p0->channel, strerror(errno));
+       res = conf_clear(p0);
+       res |= conf_clear(p1);
+       return res;
+}
+
+static int tor_setoption(struct ast_channel *chan, int option, void *data, int datalen)
+{
+char   *cp;
+
+       struct tor_pvt *p = chan->pvt->pvt;
+
+       ast_log(LOG_DEBUG, "Set option %d, data %p, len %d\n", option, data, datalen);
+       if (option != AST_OPTION_TONE_VERIFY) 
+          {
+               errno = ENOSYS;
                return -1;
-       }
-       c.chan = 0;
-       c.confno = 0;
-       c.confmode = TOR_CONF_NORMAL;
-       res = ioctl(zap_fd(p1->z), TOR_SETCONF, &c);
-       if (res) {
-               ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %d: %s\n", p1->channel, strerror(errno));
+          }
+       cp = (char *)data;
+       if ((!cp) || (datalen < 1))
+          {
+               errno = EINVAL;
                return -1;
-       }
+          }
+       zap_digitmode(p->z,((*cp) ? ZAP_MUTECONF : 0));  /* set mute mode if desired */
+       errno = 0;
        return 0;
 }
 
-
 static int tor_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
 {
        /* Do a quickie conference between the two channels and wait for something to happen */
@@ -476,40 +1096,96 @@ static int tor_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
        struct tor_pvt *p1 = c1->pvt->pvt;
        struct ast_channel *who, *cs[3];
        struct ast_frame *f;
-       struct tor_confinfo c;
-       int res;
        int to = -1;
-       /* Put the first channel in a unique conference */
-       c.chan = 0;
-       c.confno = p1->channel;
-       c.confmode = TOR_CONF_MONITOR;
        
-       /* Stop any playing */
+       int confno = -1;
+       
+       /* Stop any playing tones */
        tone_zone_play_tone(zap_fd(p0->z),      -1);
-       res = ioctl(zap_fd(p0->z), TOR_SETCONF, &c);
-       if (res) {
-               ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %s: %s\n", c0->name, strerror(errno));
-               bridge_cleanup(p0, p1);
-               return -1;
-       }
-       if (option_debug)
-               ast_log(LOG_DEBUG, "Channel %d got put on conference %d\n", c.chan, c.confno);
 
-       /* Put the other channel on the same conference */
-       c.chan = 0;
-       c.confno = p0->channel;
-       res = ioctl(zap_fd(p1->z), TOR_SETCONF, &c);
-       if (res) {
-               ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %s: %s\n", c0->name, strerror(errno));
-               bridge_cleanup(p0, p1);
-               return -1;
-       }
-       if (option_debug)
-               ast_log(LOG_DEBUG, "Channel %d got put on conference %d\n", c.chan, c.confno);
+       tone_zone_play_tone(zap_fd(p1->z),      -1);
 
+       cs[0] = c0;
+       cs[1] = c1;
        for (;;) {
-               cs[0] = c0;
-               cs[1] = c1;
+               pthread_mutex_lock(&c0->lock);
+               pthread_mutex_lock(&c1->lock);
+               p0 = c0->pvt->pvt;
+               p1 = c1->pvt->pvt;
+
+               if (!p0 || !p1) {
+                       pthread_mutex_unlock(&c0->lock);
+                       pthread_mutex_unlock(&c1->lock);
+                       return -1;
+               }
+
+               if (INTHREEWAY(p0) && (c0 == p0->owners[p0->thirdcallindex]))
+                       tone_zone_play_tone(zap_fd(p0->pseudo), -1);
+               if (INTHREEWAY(p1) && (c1 == p1->owners[p1->thirdcallindex]))
+                       tone_zone_play_tone(zap_fd(p1->pseudo), -1);
+               if (INTHREEWAY(p0) && (INTHREEWAY(p1))) {
+                       ast_log(LOG_WARNING, "Too weird, can't bridge multiple three way calls\n");
+                       pthread_mutex_unlock(&c0->lock);
+                       pthread_mutex_unlock(&c1->lock);
+                       return -1;
+               }
+               if ((p0->owner == c0) && (p1->owner == c1)) {
+                       /* Okay, this call should actually be connected */
+                       if ((p0->confno > -1) && (p1->confno > -1) && (p0->confno != p1->confno)) {
+                               /* We have a conflict here.  Try to resolve it. */
+                               if ((INTHREEWAY(p0) && (c0 == p0->owners[p0->normalindex]))) {
+                                       ast_log(LOG_DEBUG, "Channel %s is in a three way call with us, moving to our conference %d\n",
+                                               c1->name, p0->confno);
+                                       conf_set(p1, p0->confno, 1);
+                               } else if (INTHREEWAY(p1) && (c1 == p1->owners[p1->normalindex])) {
+                                               ast_log(LOG_DEBUG, "Channel %s is in a three way call with us, moving to our conference %d\n",
+                                                       c0->name, p1->confno);
+                                               conf_set(p0, p1->confno, 1);
+                               } else {
+                                       ast_log(LOG_WARNING, "Can't bridge since %s is on conf %d and %s is on conf %d\n",
+                                               c0->name, p0->confno, c1->name, p1->confno);
+                                       pthread_mutex_unlock(&c0->lock);
+                                       pthread_mutex_unlock(&c1->lock);
+                                       return -1;
+                               }
+                       }
+                       if (p0->confno > -1)
+                               confno = p0->confno;
+                       else
+                               confno = p1->confno;
+                       if (confno < 0) {
+                               conf_set(p0, -1, 0);
+                               confno = p0->confno;
+                               ast_log(LOG_DEBUG, "Creating new conference %d for %s\n", confno, c0->name);
+                       }
+                       if (p0->confno != confno) {
+                               ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c0->name, confno);
+                               conf_set(p0, confno, 0);
+                       }
+                       if (p1->confno != confno) {
+                               ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c1->name, confno);
+                               conf_set(p1, confno, 0);
+                       }
+               } else if (INTHREEWAY(p0) && (c0 == p0->owners[p0->thirdcallindex])) {
+                       /* p0 is in a three way call and we're the third leg.  Join their
+                          conference, already in progress if there is one */
+                       if ((p0->confno > -1) && (p1->confno != p0->confno)) {
+                               confno = p0->confno;
+                               ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c1->name, confno);
+                               conf_set(p1, confno, 0);
+                       }
+               } else if (INTHREEWAY(p1) && (c1 == p1->owners[p1->thirdcallindex])) {
+                       /* p0 is in a three way call and we're the third leg.  Join their
+                          conference, already in progress if there is one */
+                       if ((p1->confno > -1) && (p1->confno != p0->confno)) {
+                               confno = p0->confno;
+                               ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c0->name, confno);
+                               conf_set(p0, confno, 0);
+                       }
+               }
+               pthread_mutex_unlock(&c0->lock);
+               pthread_mutex_unlock(&c1->lock);
+               
                who = ast_waitfor_n(cs, 2, &to);
                if (!who) {
                        ast_log(LOG_WARNING, "Nobody there??\n");
@@ -555,14 +1231,105 @@ static int tor_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
                cs[0] = cs[1];
                cs[1] = cs[2];
        }
-               
+       return 0;
+}
+
+static int tor_indicate(struct ast_channel *chan, int condition);
+
+static int tor_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+       struct tor_pvt *p = newchan->pvt->pvt;
+       int x;
+       ast_log(LOG_DEBUG, "New owner for channel %d is %s\n", p->channel, newchan->name);
+       p->owner = newchan;
+       for (x=0;x<3;x++)
+               if (p->owners[x] == oldchan)
+                       p->owners[x] = newchan;
+       if (newchan->state == AST_STATE_RINGING) 
+               tor_indicate(newchan, AST_CONTROL_RINGING);
+       return 0;
+}
+
+static int tor_ring_phone(struct tor_pvt *p)
+{
+       int x;
+       int res;
+       /* Make sure our transmit state is on hook */
+       x = 0;
+       x = TOR_ONHOOK;
+       res = ioctl(zap_fd(p->z), TOR_HOOK, &x);
+       do {
+               x = TOR_RING;
+               res = ioctl(zap_fd(p->z), TOR_HOOK, &x);
+#if 0
+               printf("Res: %d, error: %s\n", res, strerror(errno));
+#endif                                         
+               if (res) {
+                       switch(errno) {
+                       case EBUSY:
+                       case EINTR:
+                               /* Wait just in case */
+                               usleep(10000);
+                               continue;
+                       case EINPROGRESS:
+                               res = 0;
+                               break;
+                       default:
+                               ast_log(LOG_WARNING, "Couldn't ring the phone: %s\n", strerror(errno));
+                               res = 0;
+                       }
+               }
+       } while (res);
+       return res;
+}
+
+static void *ss_thread(void *data);
+
+static struct ast_channel *tor_new(struct tor_pvt *, int, int, int, int);
+
+static int attempt_transfer(struct tor_pvt *p)
+{
+       /* In order to transfer, we need at least one of the channels to
+          actually be in a call bridge.  We can't conference two applications
+          together (but then, why would we want to?) */
+       if (p->owners[p->normalindex]->bridge) {
+               if (ast_channel_masquerade(p->owners[p->thirdcallindex], p->owners[p->normalindex]->bridge)) {
+                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                                       p->owners[p->normalindex]->bridge->name, p->owners[p->thirdcallindex]->name);
+                       return -1;
+               }
+               /* Orphan the channel */
+               p->owners[p->thirdcallindex] = NULL;
+               p->thirdcallindex = -1;
+       } else if (p->owners[p->thirdcallindex]->bridge) {
+               if (ast_channel_masquerade(p->owners[p->normalindex], p->owners[p->thirdcallindex]->bridge)) {
+                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                               p->owners[p->thirdcallindex]->bridge->name, p->owners[p->normalindex]->name);
+                       return -1;
+               }
+               /* Orphan the normal channel */
+               p->owners[p->normalindex] = NULL;
+               p->normalindex = p->thirdcallindex;
+               p->thirdcallindex = -1;
+       } else {
+               ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+                                       p->owners[p->normalindex]->name, p->owners[p->thirdcallindex]->name);
+               p->owners[p->thirdcallindex]->softhangup=1;
+       }
        return 0;
 }
 
 struct ast_frame *tor_handle_event(struct ast_channel *ast)
 {
        int res;
+       int index;
        struct tor_pvt *p = ast->pvt->pvt;
+       pthread_t threadid;
+       pthread_attr_t attr;
+       struct ast_channel *chan;
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       index = tor_get_index(ast, p, 0);
        p->f.frametype = AST_FRAME_NULL;
        p->f.datalen = 0;
        p->f.timelen = 0;
@@ -570,10 +1337,13 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
        p->f.offset = 0;
        p->f.src = "tor_handle_event";
        p->f.data = NULL;
+       if (index < 0)
+               return &p->f;
        res = tor_get_event(zap_fd(p->z));
-       ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d\n", event2str(res), res, p->channel);
+       ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index);
        switch(res) {
                case TOR_EVENT_DIALCOMPLETE:
+                       tor_enable_ec(p);
                        p->dialing = 0;
                        if (ast->state == AST_STATE_DIALING) {
 #if 0
@@ -586,7 +1356,72 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
                        }
                        break;
                case TOR_EVENT_ONHOOK:
-                       return NULL;
+                       switch(p->sig) {
+                       case SIG_FXOLS:
+                       case SIG_FXOGS:
+                       case SIG_FXOKS:
+                               /* Check for some special conditions regarding call waiting */
+                               if (index == p->normalindex) {
+                                       /* The normal line was hung up */
+                                       if (p->callwaitindex > -1) {
+                                               /* There's a call waiting call, so ring the phone */
+                                               p->owner = p->owners[p->callwaitindex];
+                                               if (option_verbose > 2) 
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has (callwait) call, ringing phone\n", p->owner);
+                                               p->needanswer[index] = 0;
+                                               p->needringing[index] = 0;
+                                               p->callwaitingrepeat = 0;
+                                               tor_ring_phone(p);
+                                       } else if (p->thirdcallindex > -1) {
+                                               if (p->transfer) {
+                                                       if (attempt_transfer(p))
+                                                               p->owners[p->thirdcallindex]->softhangup = 1;
+                                               } else
+                                                       p->owners[p->thirdcallindex]->softhangup=1;
+                                       }
+                               } else if (index == p->callwaitindex) {
+                                       /* Check to see if there is a normal call */
+                                       if (p->normalindex > -1) {
+                                               /* There's a call waiting call, so ring the phone */
+                                               p->owner = p->owners[p->normalindex];
+                                               if (option_verbose > 2) 
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has (normal) call, ringing phone\n", p->owner);
+                                               p->needanswer[index] = 0;
+                                               p->needringing[index] = 0;
+                                               p->callwaitingrepeat = 0;
+                                               tor_ring_phone(p);
+                                       }
+                               } else if (index == p->thirdcallindex) {
+                                       if ((ast->state != AST_STATE_UP) && (ast->state != AST_STATE_RINGING) &&
+                                                       (ast->state != AST_STATE_RING)) {
+                                               /* According to the LSSGR, we should kill everything now, and we 
+                                                  do, instead of ringing the phone */
+                                               if (p->normalindex > -1) 
+                                                       p->owners[p->normalindex]->softhangup=1;
+                                               if (p->callwaitindex > -1) {
+                                                       ast_log(LOG_WARNING, "Somehow there was a call wait\n");
+                                                       p->owners[p->callwaitindex]->softhangup = 1;
+                                               }
+                                               
+                                       } else {
+                                               if (p->transfer) {
+                                                       if (attempt_transfer(p))
+                                                               p->owners[p->normalindex]->softhangup = 1;
+                                                       else {
+                                                               /* Don't actually hangup.  We're going to get transferred */
+                                                               tor_disable_ec(p);
+                                                               break;
+                                                       }
+                                               } else 
+                                                       p->owners[p->normalindex]->softhangup = 1;
+                                       }
+                               }
+                               /* Fall through */
+                       default:
+                               tor_disable_ec(p);
+                               return NULL;
+                       }
+                       break;
                case TOR_EVENT_RINGOFFHOOK:
                        switch(p->sig) {
                        case SIG_FXOLS:
@@ -594,6 +1429,7 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
                        case SIG_FXOKS:
                                switch(ast->state) {
                                case AST_STATE_RINGING:
+                                       tor_enable_ec(p);
                                        ast->state = AST_STATE_UP;
                                        p->f.frametype = AST_FRAME_CONTROL;
                                        p->f.subclass = AST_CONTROL_ANSWER;
@@ -605,6 +1441,7 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
                                                free(p->cidspill);
                                                p->cidspill = NULL;
                                        }
+                                       p->dialing = 0;
                                        return &p->f;
                                case AST_STATE_DOWN:
                                        ast->state = AST_STATE_RING;
@@ -613,6 +1450,9 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
                                        p->f.subclass = AST_CONTROL_OFFHOOK;
                                        ast_log(LOG_DEBUG, "channel %d picked up\n", p->channel);
                                        return &p->f;
+                               case AST_STATE_UP:
+                                       /* Okay -- probably call waiting*/
+                                       break;
                                default:
                                        ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->state);
                                }
@@ -647,6 +1487,7 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
                                ast_log(LOG_WARNING, "Didn't finish Caller-ID spill.  Cancelling.\n");
                                free(p->cidspill);
                                p->cidspill = NULL;
+                               p->callwaitcas = 0;
                        }
                        p->f.frametype = AST_FRAME_CONTROL;
                        p->f.subclass = AST_CONTROL_RINGING;
@@ -659,8 +1500,88 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
                        case SIG_FXOLS:
                        case SIG_FXOGS:
                        case SIG_FXOKS:
-                               /* XXX For now, treat as a hang up */
-                               return NULL;
+                               ast_log(LOG_DEBUG, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
+                                       index, p->normalindex, p->callwaitindex, p->thirdcallindex);
+                               if (index == p->normalindex) {
+                                       if (p->callwaitindex > -1) {
+                                               tone_zone_play_tone(zap_fd(p->z), -1);
+                                               p->owner = p->owners[p->callwaitindex];
+                                               if (p->owner->state == AST_STATE_RINGING) {
+                                                       p->owner->state = AST_STATE_UP;
+                                                       p->needanswer[p->callwaitindex] = 1;
+                                               }
+                                               p->callwaitingrepeat = 0;
+                                               conf_clear(p);
+                                       } else if (p->thirdcallindex == -1) {
+                                               if (p->threewaycalling) {
+                                                       if ((ast->state == AST_STATE_RINGING) ||
+                                                                       (ast->state == AST_STATE_UP) ||
+                                                                       (ast->state == AST_STATE_RING)) {
+                                                               if (!alloc_pseudo(p)) {
+                                                                       /* Start three way call */
+                                                                       res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+                                                                       if (res)
+                                                                               ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
+                                                                       chan = tor_new(p, AST_STATE_RESERVED,0,0,1);
+                                                                       p->owner = chan;
+                                                                       if (pthread_create(&threadid, &attr, ss_thread, chan)) {
+                                                                               ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
+                                                                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+                                                                               ast_hangup(chan);
+                                                                       } else {
+                                                                               if (option_verbose > 2) 
+                                                                                       ast_verbose(VERBOSE_PREFIX_3 "Started three way call on channel %d (index %d)\n", p->channel, p->thirdcallindex);
+                                                                               conf_clear(p);
+                                                                       }               
+                                                               } else
+                                                                       ast_log(LOG_WARNING, "Unable to allocate pseudo channel\n");
+                                                       } else 
+                                                               ast_log(LOG_DEBUG, "Flash when call not up or ringing\n");
+                                               }
+                                       } else {
+                                               if (option_debug)
+                                                       ast_log(LOG_DEBUG, "Got flash with three way call up, dropping last call %d\n",
+                                                               p->thirdcallindex);
+                                               /* Drop the last call and stop the conference */
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Dropping three-way call on %s\n", p->owners[p->thirdcallindex]->name);
+                                               p->owners[p->thirdcallindex]->softhangup=1;
+                                               conf_clear(p);
+                                       }
+                               } else if (index == p->callwaitindex) {
+                                       if (p->normalindex > -1) {
+                                               p->owner = p->owners[p->normalindex];
+                                               p->callwaitingrepeat = 0;
+                                               conf_clear(p);
+                                       } else
+                                               ast_log(LOG_WARNING, "Wink/Flash on call wait, with no normal channel to flash to on channel %d?\n", p->channel);
+                               } else if (index == p->thirdcallindex) {
+                                       if (p->normalindex > -1) {
+                                               if ((ast->state != AST_STATE_RINGING) && (ast->state != AST_STATE_UP) && (ast->state != AST_STATE_RING)) {
+                                                       tone_zone_play_tone(zap_fd(p->z), -1);
+                                                       p->owner = p->owners[p->normalindex];
+                                                       ast_log(LOG_DEBUG, "Dumping incomplete three way call in state %d\n", ast->state);
+                                                       return NULL;
+                                               }
+                                               p->owner = p->owners[p->normalindex];
+                                               p->owners[p->thirdcallindex]->fds[0] = zap_fd(p->pseudo);
+                                               p->callwaitingrepeat = 0;
+                                               if (p->owners[p->thirdcallindex]->state == AST_STATE_RINGING) {
+                                                       /* If we were ringing, stop the ringing on the main line and start it on
+                                                          the pseudo */
+                                                       tone_zone_play_tone(zap_fd(p->z), -1);
+                                                       tone_zone_play_tone(zap_fd(p->pseudo), TOR_TONE_RINGTONE);
+                                               }
+                                               three_way(p);
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Established 3-way conference between %s and %s\n", 
+                                                                               p->owners[p->normalindex]->name, p->owners[p->thirdcallindex]->name);
+                                       } else {
+                                               ast_log(LOG_WARNING, "Wink/Flash on threeway call, with no normal channel to flash to on channel %d?\n", p->channel);
+                                               return NULL;
+                                       }
+                               }
+                               break;
                        case SIG_EM:
                        case SIG_EMWINK:
                        case SIG_FEATD:
@@ -693,6 +1614,73 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
 
 struct ast_frame *tor_exception(struct ast_channel *ast)
 {
+       struct tor_pvt *p = ast->pvt->pvt;
+       int res;
+       int usedindex=-1;
+       p->f.frametype = AST_FRAME_NULL;
+       p->f.datalen = 0;
+       p->f.timelen = 0;
+       p->f.mallocd = 0;
+       p->f.offset = 0;
+       p->f.subclass = 0;
+       p->f.src = "tor_exception";
+       p->f.data = NULL;
+       if ((p->owner != p->owners[0]) && 
+           (p->owner != p->owners[1]) &&
+               (p->owner != p->owners[2])) {
+               /* If nobody owns us, absorb the event appropriately, otherwise
+                  we loop indefinitely.  This occurs when, during call waiting, the
+                  other end hangs up our channel so that it no longer exists, but we
+                  have neither FLASH'd nor ONHOOK'd to signify our desire to
+                  change to the other channel. */
+               res = tor_get_event(zap_fd(p->z));
+               if ((p->callwaitindex > -1) && (p->normalindex > -1)) 
+                       ast_log(LOG_WARNING, "Absorbing exception on unowned channel, but there is both a normal and call waiting call still here?\n");
+               if (p->callwaitindex > -1) {
+                       tone_zone_play_tone(zap_fd(p->z), -1);
+                       p->owner = p->owners[p->callwaitindex];
+                       usedindex = p->callwaitindex;
+               } else if (p->normalindex > -1) {
+                       tone_zone_play_tone(zap_fd(p->z), -1);
+                       p->owner = p->owners[p->normalindex];
+                       usedindex = p->normalindex;
+               } else {
+                       ast_log(LOG_WARNING, "No call wait call, no normal call, what do I do?\n");
+                       return NULL;
+               }
+               switch(res) {
+               case TOR_EVENT_ONHOOK:
+                       tor_disable_ec(p);
+                       if (p->owner) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has call, ringing phone\n", p->owner->name);
+                               tor_ring_phone(p);
+                               p->callwaitingrepeat = 0;
+                       } else
+                               ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+                       break;
+               case TOR_EVENT_WINKFLASH:
+                       if (p->owner) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %d flashed to other channel %s\n", p->channel, p->owner->name);
+                               if ((usedindex == p->callwaitindex) && (p->owner->state == AST_STATE_RINGING)) {
+                                       /* Answer the call wait if necessary */
+                                       p->needanswer[usedindex] = 1;
+                                       p->owner->state = AST_STATE_UP;
+                               }
+                               p->callwaitingrepeat = 0;
+                       } else
+                               ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
+               }
+               return &p->f;
+       }
+       /* If it's not us, return NULL immediately */
+       if (ast != p->owner)
+               return &p->f;
+               
        return tor_handle_event(ast);
 }
 
@@ -700,21 +1688,78 @@ struct ast_frame  *tor_read(struct ast_channel *ast)
 {
        struct tor_pvt *p = ast->pvt->pvt;
        int res,x;
+       int index;
        unsigned char ireadbuf[READ_SIZE];
        unsigned char *readbuf;
+       ZAP *z = NULL;
        
-       p->f.frametype = AST_FRAME_DTMF;
+       pthread_mutex_lock(&p->lock);
+       
+       p->f.frametype = AST_FRAME_NULL;
        p->f.datalen = 0;
        p->f.timelen = 0;
        p->f.mallocd = 0;
        p->f.offset = 0;
+       p->f.subclass = 0;
        p->f.src = "tor_read";
        p->f.data = NULL;
        
+       index = tor_get_index(ast, p, 0);
+       
+       /* Hang up if we don't really exist */
+       if (index < 0)  {
+               ast_log(LOG_WARNING, "We dont exist?\n");
+               pthread_mutex_unlock(&p->lock);
+               return NULL;
+       }
+       
+       if (p->needringing[index]) {
+               /* Send ringing frame if requested */
+               p->needringing[index] = 0;
+               p->f.frametype = AST_FRAME_CONTROL;
+               p->f.subclass = AST_CONTROL_RINGING;
+               pthread_mutex_unlock(&p->lock);
+               return &p->f;
+       }       
+       
+       if (p->needanswer[index]) {
+               /* Send ringing frame if requested */
+               p->needanswer[index] = 0;
+               p->f.frametype = AST_FRAME_CONTROL;
+               p->f.subclass = AST_CONTROL_ANSWER;
+               pthread_mutex_unlock(&p->lock);
+               return &p->f;
+       }       
+       
+       if (ast != p->owner) {
+               /* If it's not us.  If this isn't a three way call, return immediately */
+               if (!INTHREEWAY(p)) {
+                       pthread_mutex_unlock(&p->lock);
+                       return &p->f;
+               }
+               /* If it's not the third call, return immediately */
+               if (ast != p->owners[p->thirdcallindex]) {
+                       pthread_mutex_unlock(&p->lock);
+                       return &p->f;
+               }
+               if (!p->pseudo) 
+                       ast_log(LOG_ERROR, "No pseudo channel\n");
+               z = p->pseudo;          
+       } else
+               z = p->z;
+
+       if (!z) {
+               ast_log(LOG_WARNING, "No zap structure?!?\n");
+               pthread_mutex_unlock(&p->lock);
+               return NULL;
+       }
+       
        /* Check first for any outstanding DTMF characters */
        if (strlen(p->dtmfq)) {
                p->f.subclass = p->dtmfq[0];
                memmove(p->dtmfq, p->dtmfq + 1, sizeof(p->dtmfq) - 1);
+               p->f.frametype = AST_FRAME_DTMF;
+               pthread_mutex_unlock(&p->lock);
                return &p->f;
        }
        
@@ -726,34 +1771,66 @@ struct ast_frame  *tor_read(struct ast_channel *ast)
                readbuf = ((unsigned char *)p->buffer) + AST_FRIENDLY_OFFSET;
        } else {
                ast_log(LOG_WARNING, "Don't know how to read frames in format %d\n", ast->pvt->rawreadformat);
+               pthread_mutex_unlock(&p->lock);
                return NULL;
        }
        CHECK_BLOCKING(ast);
-       res = zap_recchunk(p->z, readbuf, READ_SIZE, ZAP_DTMFINT);
+       if ((z != p->z) && (z != p->pseudo)) {
+               pthread_mutex_unlock(&p->lock);
+               return NULL;
+       }
+       res = zap_recchunk(z, readbuf, READ_SIZE, ZAP_DTMFINT);
        ast->blocking = 0;
        /* Check for hangup */
        if (res < 0) {
                if (res == -1) 
                        ast_log(LOG_WARNING, "tor_rec: %s\n", strerror(errno));
+               pthread_mutex_unlock(&p->lock);
                return NULL;
        }
        if (res != READ_SIZE) {
                if (option_debug)
                        ast_log(LOG_DEBUG, "Short read, must be DTMF or something...\n");
                /* XXX UGLY!!  Zapata's DTMF handling is a bit ugly XXX */
-               if (zap_dtmfwaiting(p->z) && !strlen(zap_dtmfbuf(p->z))) {
-                       zap_getdtmf(p->z, 1, NULL, 0, 1, 1, 0);
+               if (zap_dtmfwaiting(z) && !strlen(zap_dtmfbuf(z))) {
+                       zap_getdtmf(z, 1, NULL, 0, 1, 1, 0);
                }
-               if (strlen(zap_dtmfbuf(p->z))) {
-                       ast_log(LOG_DEBUG, "Got some dtmf ('%s')... on channel %s\n", zap_dtmfbuf(p->z), ast->name);
+               if (strlen(zap_dtmfbuf(z))) {
+                       ast_log(LOG_DEBUG, "Got some dtmf ('%s')... on channel %s\n", zap_dtmfbuf(z), ast->name);
                        /* DTMF tone detected.  Queue and erturn */
-                       strncpy(p->dtmfq + strlen(p->dtmfq), zap_dtmfbuf(p->z), sizeof(p->dtmfq) - strlen(p->dtmfq));
-                       zap_clrdtmfn(p->z);
+                       if (p->callwaitcas) {
+                               if (!strcmp(zap_dtmfbuf(z), "A") || !strcmp(zap_dtmfbuf(z), "D")) {
+                                       ast_log(LOG_DEBUG, "Got some DTMF, but it's for the CAS\n");
+                                       if (p->cidspill)
+                                               free(p->cidspill);
+                                       send_cwcidspill(p);
+                               }
+                               /* Return NULL */
+                               pthread_mutex_unlock(&p->lock);
+                               return &p->f;
+                       } else {
+                               strncpy(p->dtmfq + strlen(p->dtmfq), zap_dtmfbuf(z), sizeof(p->dtmfq) - strlen(p->dtmfq));
+                               zap_clrdtmfn(z);
+                       }
                } else {
+                       pthread_mutex_unlock(&p->lock);
                        return tor_handle_event(ast);
                }
+               if (strlen(p->dtmfq)) {
+                       p->f.subclass = p->dtmfq[0];
+                       memmove(p->dtmfq, p->dtmfq + 1, sizeof(p->dtmfq) - 1);
+                       p->f.frametype = AST_FRAME_DTMF;
+               }
+               pthread_mutex_unlock(&p->lock);
                return &p->f;
        }
+       if (p->callwaitingrepeat)
+               p->callwaitingrepeat--;
+       /* Repeat callwaiting */
+       if (p->callwaitingrepeat == 1) {
+               p->callwaitrings++;
+               tor_callwait(ast);
+       }
        if (ast->pvt->rawreadformat == AST_FORMAT_SLINEAR) {
                for (x=0;x<READ_SIZE;x++) {
                        p->buffer[x + AST_FRIENDLY_OFFSET/2] = ast_mulaw[readbuf[x]];
@@ -763,7 +1840,7 @@ struct ast_frame  *tor_read(struct ast_channel *ast)
                p->f.datalen = READ_SIZE;
 
        /* Handle CallerID Transmission */
-       if ((ast->rings == 1) && (p->cidspill))
+       if (p->cidspill &&((ast->state == AST_STATE_UP) || (ast->rings == 1)))
                send_callerid(p);
 
        p->f.frametype = AST_FRAME_VOICE;
@@ -775,19 +1852,35 @@ struct ast_frame  *tor_read(struct ast_channel *ast)
 #if 0
        ast_log(LOG_DEBUG, "Read %d of voice on %s\n", p->f.datalen, ast->name);
 #endif 
+       if (p->dialing) {
+               /* Whoops, we're still dialing, don't send anything */
+               p->f.frametype = AST_FRAME_NULL;
+               p->f.subclass = 0;
+               p->f.timelen = 0;
+               p->f.mallocd = 0;
+               p->f.offset = 0;
+               p->f.data = NULL;
+               p->f.datalen= 0;
+       }
+       pthread_mutex_unlock(&p->lock);
        return &p->f;
 }
 
-static int my_tor_write(struct tor_pvt *p, unsigned char *buf, int len)
+static int my_tor_write(struct tor_pvt *p, unsigned char *buf, int len, int threeway)
 {
        int sent=0;
        int size;
        int res;
+       int fd;
+       if (threeway) 
+               fd = zap_fd(p->pseudo);
+       else
+               fd = zap_fd(p->z);
        while(len) {
                size = len;
                if (size > READ_SIZE)
                        size = READ_SIZE;
-               res = write(zap_fd(p->z), buf, size);
+               res = write(fd, buf, size);
                if (res != size) {
                        ast_log(LOG_DEBUG, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel);
                        return sent;
@@ -805,6 +1898,18 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
        int res;
        unsigned char outbuf[4096];
        short *inbuf;
+       
+       if (ast != p->owner) {
+               /* If it's not us.  If this isn't a three way call, return immediately */
+               if (!INTHREEWAY(p)) {
+                       return 0;
+               }
+               /* If it's not the third call, return immediately */
+               if (ast != p->owners[p->thirdcallindex]) {
+                       return 0;
+               }
+       }
+       
        /* Write a frame of (presumably voice) data */
        if (frame->frametype != AST_FRAME_VOICE) {
                ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
@@ -815,11 +1920,17 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
                return -1;
        }
        if (p->dialing) {
-               ast_log(LOG_DEBUG, "Dropping frame since I'm still dialing...\n");
+#if 0
+               if (option_debug)
+#endif         
+                       ast_log(LOG_DEBUG, "Dropping frame since I'm still dialing...\n");
                return 0;
        }
        if (p->cidspill) {
-               ast_log(LOG_DEBUG, "Dropping frame since I've still got a callerid spill\n");
+#if 0
+               if (option_debug)
+#endif         
+                       ast_log(LOG_DEBUG, "Dropping frame since I've still got a callerid spill\n");
                return 0;
        }
        /* Return if it's not valid data */
@@ -833,10 +1944,10 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
                inbuf = frame->data;
                for (x=0;x<frame->datalen/2;x++)
                        outbuf[x] = ast_lin2mu[inbuf[x]+32768];
-               res = my_tor_write(p, outbuf, frame->datalen/2);
+               res = my_tor_write(p, outbuf, frame->datalen/2, (ast != p->owner));
        } else {
                /* uLaw already */
-               res = my_tor_write(p, (unsigned char *)frame->data, frame->datalen);
+               res = my_tor_write(p, (unsigned char *)frame->data, frame->datalen, (ast != p->owner));
        }
        if (res < 0) {
                ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
@@ -848,14 +1959,44 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
        return 0;
 }
 
-static struct ast_channel *tor_new(struct tor_pvt *i, int state, int startpbx)
+static int tor_indicate(struct ast_channel *chan, int condition)
+{
+       struct tor_pvt *p = chan->pvt->pvt;
+       int res=-1;
+       switch(condition) {
+       case AST_CONTROL_BUSY:
+               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_BUSY);
+               break;
+       case AST_CONTROL_RINGING:
+               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
+               if (chan->state != AST_STATE_UP)
+                       chan->state = AST_STATE_RINGING;
+               break;
+       case AST_CONTROL_CONGESTION:
+               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+               break;
+       default:
+               ast_log(LOG_WARNING, "Don't know how to set condition %d on channel %s\n", condition, chan->name);
+       }
+       return res;
+}
+
+static struct ast_channel *tor_new(struct tor_pvt *i, int state, int startpbx, int callwaiting, int thirdcall)
 {
        struct ast_channel *tmp;
+       int x;
+       for (x=0;x<3;x++)
+               if (!i->owners[x])
+                       break;
+       if (x > 2) {
+               ast_log(LOG_WARNING, "No available owner slots\n");
+               return NULL;
+       }
        tmp = ast_channel_alloc();
        if (tmp) {
-               snprintf(tmp->name, sizeof(tmp->name), "Tor/%d", i->channel);
+               snprintf(tmp->name, sizeof(tmp->name), "Tor/%d-%d", i->channel, x + 1);
                tmp->type = type;
-               tmp->fd = zap_fd(i->z);
+               tmp->fds[0] = zap_fd(i->z);
                tmp->nativeformats = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW;
                /* Start out assuming ulaw since it's smaller :) */
                tmp->pvt->rawreadformat = AST_FORMAT_ULAW;
@@ -875,15 +2016,38 @@ static struct ast_channel *tor_new(struct tor_pvt *i, int state, int startpbx)
                tmp->pvt->write = tor_write;
                tmp->pvt->bridge = tor_bridge;
                tmp->pvt->exception = tor_exception;
+               tmp->pvt->indicate = tor_indicate;
+               tmp->pvt->fixup = tor_fixup;
+               tmp->pvt->setoption = tor_setoption;
                if (strlen(i->language))
                        strncpy(tmp->language, i->language, sizeof(tmp->language));
-               i->owner = tmp;
-               pthread_mutex_lock(&usecnt_lock);
+               /* Keep track of who owns it */
+               i->owners[x] = tmp;
+               if (!i->owner)
+                       i->owner = tmp;
+               if (callwaiting) {
+                       if (i->callwaitindex > -1)
+                               ast_log(LOG_WARNING, "channel %d already has a call wait call\n", i->channel);
+                       i->callwaitindex = x;
+               } else if (thirdcall) {
+                       if (i->thirdcallindex > -1)
+                               ast_log(LOG_WARNING, "channel %d already has a third call\n", i->channel);
+                       i->thirdcallindex = x;
+               } else {
+                       if (i->normalindex > -1) 
+                               ast_log(LOG_WARNING, "channel %d already has a normal call\n", i->channel);
+                       i->normalindex = x;
+               }
+               ast_pthread_mutex_lock(&usecnt_lock);
                usecnt++;
-               pthread_mutex_unlock(&usecnt_lock);
+               ast_pthread_mutex_unlock(&usecnt_lock);
                ast_update_use_count();
                strncpy(tmp->context, i->context, sizeof(tmp->context));
+               if (strlen(i->exten))
+                       strncpy(tmp->exten, i->exten, sizeof(tmp->exten));
                if (startpbx) {
+                       if (strlen(i->callerid))
+                               tmp->callerid = strdup(i->callerid);
                        if (ast_pbx_start(tmp)) {
                                ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
                                ast_hangup(tmp);
@@ -900,7 +2064,7 @@ static int ignore_pat(char *s)
 {
        int x;
        for (x=0;x<dialpats;x++)
-               if (!strcmp(s, keepdialpat[x]))
+               if (ast_extension_match(keepdialpat[x], s))
                        return 1;
        return 0;
 }
@@ -909,7 +2073,7 @@ static int bump_gains(struct tor_pvt *p)
 {
        int res;
        /* Bump receive gain by 9.0db */
-       res = set_actual_gain(zap_fd(p->z), 0, 9, 0);
+       res = set_actual_gain(zap_fd(p->z), 0, p->rxgain + 5.0, p->txgain);
        if (res) {
                ast_log(LOG_WARNING, "Unable to bump gain\n");
                return -1;
@@ -921,7 +2085,7 @@ static int restore_gains(struct tor_pvt *p)
 {
        int res;
        /* Bump receive gain by 9.0db */
-       res = set_actual_gain(zap_fd(p->z), 0, 0, 0);
+       res = set_actual_gain(zap_fd(p->z), 0, p->rxgain, p->txgain);
        if (res) {
                ast_log(LOG_WARNING, "Unable to restore gain\n");
                return -1;
@@ -941,6 +2105,7 @@ static void *ss_thread(void *data)
        char *name, *number;
        int flags;
        int i;
+       int timeout;
        char *s1, *s2;
        int len = 0;
        int res;
@@ -991,16 +2156,15 @@ static void *ss_thread(void *data)
                        } else
                                ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d.  Assuming E&M Wink instead\n", p->channel);
                }
-               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
-               if (res < 0)
-                       ast_log(LOG_WARNING, "Unable to start ringback tone on channel %d\n", p->channel);
+               tor_enable_ec(p);
                if (ast_exists_extension(chan, chan->context, exten, 1)) {
                        strncpy(chan->exten, exten, sizeof(chan->exten));
                        zap_clrdtmf(p->z);
                        res = ast_pbx_run(chan);
-                       if (res) 
+                       if (res) {
                                ast_log(LOG_WARNING, "PBX exited non-zero\n");
-                       res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+                       }
                        return NULL;
                } else {
                        if (option_verbose > 2)
@@ -1023,74 +2187,96 @@ static void *ss_thread(void *data)
        case SIG_FXOGS:
        case SIG_FXOKS:
                /* Read the first digit */
-               res = zap_getdtmf(p->z, 1, NULL, 0, firstdigittimeout, firstdigittimeout, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
-               if (res < 0) {
-                       if (option_debug)
-                               ast_log(LOG_DEBUG, "getdtmf returned %d (%s)...\n", res, strerror(errno));
-                       /* Got hung up on apparently.  Stop any playing tones.  We're done */
-                       res = tone_zone_play_tone(zap_fd(p->z), -1);
-                       ast_hangup(chan);
-                       return NULL;
-               }
-               if (res) {
-                       strncpy(exten + len, zap_dtmfbuf(p->z), sizeof(exten) - len);
-                       len++;
-                       if (ast_exists_extension(chan, chan->context, exten, 1)) {
-                               if (!ignore_pat(exten)) {
-                                       res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
-                                       if (res < 0)
-                                               ast_log(LOG_WARNING, "Unable to start ringback tone on channel %d\n", p->channel);
-                               }
-                               /* Check for a single digit extension */
-                               strncpy(chan->exten, exten, sizeof(chan->exten));
-                               zap_clrdtmf(p->z);
-                               res = ast_pbx_run(chan);
-                               if (res) 
-                                       ast_log(LOG_WARNING, "PBX exited non-zero\n");
+               timeout = firstdigittimeout;
+               while(len < AST_MAX_EXTENSION-1) {
+                       res = ast_waitfordigit(chan, timeout);
+                       if (res < 0) {
+                               ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n");
+                               res = tone_zone_play_tone(zap_fd(p->z), -1);
+                               ast_hangup(chan);
+                               return NULL;
+                       } else if (res == 0) {
+                               ast_log(LOG_DEBUG, "not enough digits...\n");
                                res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+                               tor_wait_event(zap_fd(p->z));
+                               ast_hangup(chan);
                                return NULL;
+                       } else {
+                               exten[len++]=res;
+            exten[len] = '\0';
                        }
-                       
                        if (!ignore_pat(exten))
                                tone_zone_play_tone(zap_fd(p->z), -1);
-                       while(len < AST_MAX_EXTENSION-1) {
-                               zap_clrdtmf(p->z);
-                               res = zap_getdtmf(p->z, 1, NULL, 0, gendigittimeout, gendigittimeout, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
-                               if (res < 0) {
-                                       ast_log(LOG_DEBUG, "getdtmf returned < 0...\n");
-                                       res = tone_zone_play_tone(zap_fd(p->z), -1);
-                                       ast_hangup(chan);
-                                       return NULL;
-                               } else if (res == 0) {
-                                       ast_log(LOG_DEBUG, "not enough digits...\n");
+                       if (ast_exists_extension(chan, chan->context, exten, 1)) {
+                               res = tone_zone_play_tone(zap_fd(p->z), -1);
+                               strncpy(chan->exten, exten, sizeof(chan->exten));
+                               if (strlen(p->callerid) && !p->hidecallerid)
+                                       chan->callerid = strdup(p->callerid);
+                               chan->state = AST_STATE_RING;
+                               tor_enable_ec(p);
+                               res = ast_pbx_run(chan);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "PBX exited non-zero\n");
                                        res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
-                                       tor_wait_event(zap_fd(p->z));
-                                       ast_hangup(chan);
-                                       return NULL;
-                               } else {
-                                       strncpy(exten + len, zap_dtmfbuf(p->z), sizeof(exten) - len);
-                                       len++;
-                                       if (!ignore_pat(exten))
-                                               tone_zone_play_tone(zap_fd(p->z), -1);
-                                       if (ast_exists_extension(chan, chan->context, exten, 1)) {
-                                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
-                                               if (res < 0)
-                                                       ast_log(LOG_WARNING, "Unable to start ringback tone on channel %d\n", p->channel);
-                                               strncpy(chan->exten, exten, sizeof(chan->exten));
-                                               if (strlen(p->callerid))
-                                                       chan->callerid = strdup(p->callerid);
-                                               zap_clrdtmf(p->z);
-                                               res = ast_pbx_run(chan);
-                                               if (res) 
-                                                       ast_log(LOG_WARNING, "PBX exited non-zero\n");
-                                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
-                                               return NULL;
-                                       } else if (!ast_canmatch_extension(chan, chan->context, exten, 1)) {
-                                               printf("Can't match %s is context %s\n", exten, chan->context);
-                                               break;
-                                       }
+                               }                                               
+                               return NULL;
+                       } else if (p->callwaiting && !strcmp(exten, "*70")) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name);
+                               /* Disable call waiting if enabled */
+                               p->callwaiting = 0;
+                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
+                                               chan->name, strerror(errno));
+                               }
+                               len = 0;
+                               memset(exten, 0, sizeof(exten));
+                               timeout = firstdigittimeout;
+                                       
+                       } else if (!p->hidecallerid && !strcmp(exten, "*67")) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
+                               /* Disable Caller*ID if enabled */
+                               p->hidecallerid = 1;
+                               if (chan->callerid)
+                                       free(chan->callerid);
+                               chan->callerid = NULL;
+                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
+                                               chan->name, strerror(errno));
+                               }
+                               len = 0;
+                               memset(exten, 0, sizeof(exten));
+                               timeout = firstdigittimeout;
+                                       
+                       } else if (p->hidecallerid && !strcmp(exten, "*82")) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
+                               /* Enable Caller*ID if enabled */
+                               p->hidecallerid = 0;
+                               if (chan->callerid)
+                                       free(chan->callerid);
+                               chan->callerid = NULL;
+                               res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
+                                               chan->name, strerror(errno));
                                }
+                               len = 0;
+                               memset(exten, 0, sizeof(exten));
+                               timeout = firstdigittimeout;
+                                       
+                       } else if (!ast_canmatch_extension(chan, chan->context, exten, 1) &&
+                                                       ((exten[0] != '*') || (strlen(exten) > 2))) {
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Can't match %s from '%s' in context %s\n", exten, chan->callerid ? chan->callerid : "<Unknown Caller>", chan->context);
+                               break;
                        }
+                       timeout = gendigittimeout;
+                       if (len && !ignore_pat(exten))
+                               tone_zone_play_tone(zap_fd(p->z), -1);
                }
                break;
        case SIG_FXSLS:
@@ -1162,6 +2348,7 @@ static void *ss_thread(void *data)
                        chan->callerid = strdup(cid);
                chan->state = AST_STATE_RING;
                chan->rings = 1;
+               tor_enable_ec(p);
                res = ast_pbx_run(chan);
                if (res) {
                        ast_hangup(chan);
@@ -1185,7 +2372,10 @@ static int handle_init_event(struct tor_pvt *i, int event)
 {
        int res;
        pthread_t threadid;
+       pthread_attr_t attr;
        struct ast_channel *chan;
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        /* Handle an event on a given channel for the monitor thread. */
        switch(event) {
        case TOR_EVENT_RINGOFFHOOK:
@@ -1195,8 +2385,10 @@ static int handle_init_event(struct tor_pvt *i, int event)
                case SIG_FXOGS:
                case SIG_FXOKS:
                        if (i->immediate) {
+                               tor_enable_ec(i);
                                /* The channel is immediately up.  Start right away */
-                               chan = tor_new(i, AST_STATE_UP, 1);
+                               res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_RINGTONE);
+                               chan = tor_new(i, AST_STATE_RING, 1, 0, 0);
                                if (!chan)  {
                                        ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
                                        res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_CONGESTION);
@@ -1208,14 +2400,17 @@ static int handle_init_event(struct tor_pvt *i, int event)
                                if (res < 0) 
                                        ast_log(LOG_WARNING, "Unable to play dialtone on channel %d\n", i->channel);
                                /* Check for callerid, digits, etc */
-                               chan = tor_new(i, AST_STATE_DOWN, 0);
-                               if (pthread_create(&threadid, NULL, ss_thread, chan)) {
+                               chan = tor_new(i, AST_STATE_DOWN, 0, 0, 0);
+                               if (pthread_create(&threadid, &attr, ss_thread, chan)) {
                                        ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
                                        res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_CONGESTION);
                                        if (res < 0)
                                                ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
                                        ast_hangup(chan);
                                }
+#if 0
+                               printf("Created thread %ld detached in switch\n", threadid);
+#endif
                        }
                        break;
                case SIG_EMWINK:
@@ -1225,14 +2420,17 @@ static int handle_init_event(struct tor_pvt *i, int event)
                case SIG_FXSGS:
                case SIG_FXSKS:
                                /* Check for callerid, digits, etc */
-                               chan = tor_new(i, AST_STATE_RING, 0);
-                               if (pthread_create(&threadid, NULL, ss_thread, chan)) {
+                               chan = tor_new(i, AST_STATE_RING, 0, 0, 0);
+                               if (pthread_create(&threadid, &attr, ss_thread, chan)) {
                                        ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
                                        res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_CONGESTION);
                                        if (res < 0)
                                                ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
                                        ast_hangup(chan);
                                }
+#if 0
+                               printf("Created thread %ld detached in switch(2)\n", threadid);
+#endif
                                break;
                default:
                        ast_log(LOG_WARNING, "Don't know how to handle ring/answer with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
@@ -1255,6 +2453,7 @@ static int handle_init_event(struct tor_pvt *i, int event)
                case SIG_FXSLS:
                case SIG_FXSGS:
                case SIG_FXSKS:
+                       tor_disable_ec(i);
                        res = tone_zone_play_tone(zap_fd(i->z), -1);
                        tor_set_hook(zap_fd(i->z), TOR_ONHOOK);
                        break;
@@ -1270,7 +2469,6 @@ static int handle_init_event(struct tor_pvt *i, int event)
 
 static void *do_monitor(void *data)
 {
-       fd_set rfds;
        fd_set efds;
        int n, res;
        struct tor_pvt *i;
@@ -1285,16 +2483,9 @@ static void *do_monitor(void *data)
        ast_log(LOG_DEBUG, "Monitor starting...\n");
 #endif
        for(;;) {
-               /* Don't let anybody kill us right away.  Nobody should lock the interface list
-                  and wait for the monitor list, but the other way around is okay. */
-               if (pthread_mutex_lock(&monlock)) {
-                       ast_log(LOG_ERROR, "Unable to grab monitor lock\n");
-                       return NULL;
-               }
                /* Lock the interface list */
-               if (pthread_mutex_lock(&iflock)) {
+               if (ast_pthread_mutex_lock(&iflock)) {
                        ast_log(LOG_ERROR, "Unable to grab interface lock\n");
-                       pthread_mutex_unlock(&monlock);
                        return NULL;
                }
                /* Build the stuff we're going to select on, that is the socket of every
@@ -1314,13 +2505,11 @@ static void *do_monitor(void *data)
                        i = i->next;
                }
                /* Okay, now that we know what to do, release the interface lock */
-               pthread_mutex_unlock(&iflock);
+               ast_pthread_mutex_unlock(&iflock);
                
-               /* And from now on, we're okay to be killed, so release the monitor lock as well */
-               pthread_mutex_unlock(&monlock);
                pthread_testcancel();
                /* Wait indefinitely for something to happen */
-               res = select(n + 1, &rfds, NULL, &efds, NULL);
+               res = select(n + 1, NULL, NULL, &efds, NULL);
                pthread_testcancel();
                /* Okay, select has finished.  Let's see what happened.  */
                if (res < 0) {
@@ -1330,7 +2519,7 @@ static void *do_monitor(void *data)
                }
                /* Alright, lock the interface list again, and let's look and see what has
                   happened */
-               if (pthread_mutex_lock(&iflock)) {
+               if (ast_pthread_mutex_lock(&iflock)) {
                        ast_log(LOG_WARNING, "Unable to lock the interface list\n");
                        continue;
                }
@@ -1343,12 +2532,13 @@ static void *do_monitor(void *data)
                                        continue;
                                }
                                res = tor_get_event(zap_fd(i->z));
-                               ast_log(LOG_DEBUG, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel);
                                handle_init_event(i, res);
                        }
                        i=i->next;
                }
-               pthread_mutex_unlock(&iflock);
+               ast_pthread_mutex_unlock(&iflock);
        }
        /* Never reached */
        return NULL;
@@ -1357,34 +2547,36 @@ static void *do_monitor(void *data)
 
 static int restart_monitor(void)
 {
+       pthread_attr_t attr;
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        /* If we're supposed to be stopped -- stay stopped */
        if (monitor_thread == -2)
                return 0;
-       if (pthread_mutex_lock(&monlock)) {
+       if (ast_pthread_mutex_lock(&monlock)) {
                ast_log(LOG_WARNING, "Unable to lock monitor\n");
                return -1;
        }
        if (monitor_thread == pthread_self()) {
-               pthread_mutex_unlock(&monlock);
+               ast_pthread_mutex_unlock(&monlock);
                ast_log(LOG_WARNING, "Cannot kill myself\n");
                return -1;
        }
        if (monitor_thread) {
-#if 1
                pthread_cancel(monitor_thread);
-#endif
                pthread_kill(monitor_thread, SIGURG);
-#if 0
                pthread_join(monitor_thread, NULL);
-#endif
        }
        /* Start a new monitor */
-       if (pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
-               pthread_mutex_unlock(&monlock);
+       if (pthread_create(&monitor_thread, &attr, do_monitor, NULL) < 0) {
+               ast_pthread_mutex_unlock(&monlock);
                ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
                return -1;
        }
-       pthread_mutex_unlock(&monlock);
+#if 0
+       printf("Created thread %ld detached in restart monitor\n", monitor_thread);
+#endif
+       ast_pthread_mutex_unlock(&monlock);
        return 0;
 }
 
@@ -1397,6 +2589,7 @@ static struct tor_pvt *mkif(int channel, int signalling)
        struct tor_bufferinfo bi;
 #endif 
        int res;
+       int span;
        TOR_PARAMS p;
 
        tmp = malloc(sizeof(struct tor_pvt));
@@ -1419,8 +2612,50 @@ static struct tor_pvt *mkif(int channel, int signalling)
                }
                if (p.sigtype != (signalling & 0xf)) {
                        ast_log(LOG_ERROR, "Signalling requested is %s but line is in %s signalling\n", sig2str(signalling), sig2str(p.sigtype));
+                       free(tmp);
                        return NULL;
                }
+               span = (channel - 1)/24;
+               tmp->span = span + 1;
+#ifdef TORMENTA_PRI
+               if (signalling == SIG_PRI) {
+                       int offset;
+                       offset = 1;
+                       if (ioctl(zap_fd(tmp->z), TOR_AUDIOMODE, &offset)) {
+                               ast_log(LOG_ERROR, "Unable to set audio mode on clear channel %d of span %d: %s\n", channel, span, strerror(errno));
+                               return NULL;
+                       }
+                       if (span >= NUM_SPANS) {
+                               ast_log(LOG_ERROR, "Channel %d does not lie on a span I know of (%d)\n", channel, span);
+                               free(tmp);
+                               return NULL;
+                       } else {
+                               offset = (channel -1) % 24 + 1;
+                               if (offset < 24) {
+                                       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);
+                                               return NULL;
+                                       }
+                                       if (pris[span].switchtype && (pris[span].switchtype != switchtype)) {
+                                               ast_log(LOG_ERROR, "Span %d is already a %s switch\n", span + 1, pri_switch2str(pris[span].switchtype));
+                                               free(tmp);
+                                               return NULL;
+                                       }
+                                       pris[span].nodetype = pritype;
+                                       pris[span].switchtype = switchtype;
+                                       pris[span].chanmask[offset] |= MASK_AVAIL;
+                                       pris[span].pvt[offset] = tmp;
+                                       tmp->pri = &pris[span];
+                                       tmp->call = NULL;
+                               } else {
+                                       ast_log(LOG_ERROR, "Channel 24 is reserved for D-channel.\n");
+                                       free(tmp);
+                                       return NULL;
+                               }
+                       }
+               }
+#endif         
                /* Adjust starttime on loopstart and kewlstart trunks to reasonable values */
                if ((signalling == SIG_FXSKS) || (signalling == SIG_FXSLS)) {
                        p.starttime = 250;
@@ -1454,24 +2689,82 @@ static struct tor_pvt *mkif(int channel, int signalling)
                        ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", channel);
 #endif
                tmp->immediate = immediate;
-               tmp->state = tor_STATE_DOWN;
                tmp->sig = signalling;
-               tmp->use_callerid = use_callerid;
+               if ((signalling == SIG_FXOKS) || (signalling == SIG_FXOLS) || (signalling == SIG_FXOGS))
+                       tmp->permcallwaiting = callwaiting;
+               else
+                       tmp->permcallwaiting = 0;
+               tmp->callwaitingcallerid = callwaitingcallerid;
+               tmp->threewaycalling = threewaycalling;
+               tmp->permhidecallerid = hidecallerid;
+               tmp->echocancel = echocancel;
+               tmp->callwaiting = tmp->permcallwaiting;
+               tmp->hidecallerid = tmp->permhidecallerid;
                tmp->channel = channel;
                tmp->stripmsd = stripmsd;
+               tmp->use_callerid = use_callerid;
+               tmp->callwaitindex = -1;
+               tmp->normalindex = -1;
+               tmp->thirdcallindex = -1;
+               tmp->normalindex = -1;
+               tmp->confno = -1;
+               tmp->pseudo = NULL;
+               tmp->pseudochan = 0;
+               tmp->transfer = transfer;
+               pthread_mutex_init(&tmp->lock, NULL);
                strncpy(tmp->language, language, sizeof(tmp->language));
                strncpy(tmp->context, context, sizeof(tmp->context));
                strncpy(tmp->callerid, callerid, sizeof(tmp->callerid));
                tmp->group = cur_group;
                tmp->next = NULL;
-               tmp->ringgothangup = 0;
-               /* Hang it up to be sure it's good */
-               tor_set_hook(zap_fd(tmp->z), TOR_ONHOOK);
-               
+               tmp->rxgain = rxgain;
+               tmp->txgain = txgain;
+               set_actual_gain(zap_fd(tmp->z), 0, tmp->rxgain, tmp->txgain);
+               zap_digitmode(tmp->z, ZAP_DTMF /* | ZAP_MUTECONF */);
+               conf_clear(tmp);
+               if (signalling != SIG_PRI) 
+                       /* Hang it up to be sure it's good */
+                       tor_set_hook(zap_fd(tmp->z), TOR_ONHOOK);
        }
        return tmp;
 }
 
+static inline int available(struct tor_pvt *p, int channelmatch, int groupmatch)
+{
+       /* First, check group matching */
+       if ((p->group & groupmatch) != groupmatch)
+               return 0;
+       /* Check to see if we have a channel match */
+       if ((channelmatch > 0) && (p->channel != channelmatch))
+               return 0;
+               
+       /* If no owner definitely available */
+       if (!p->owner)
+               return 1;
+
+       if (!p->callwaiting) {
+               /* If they don't have call waiting enabled, then for sure they're unavailable at this point */
+               return 0;
+       }
+
+       if (p->callwaitindex > -1) {
+               /* If there is already a call waiting call, then we can't take a second one */
+               return 0;
+       }
+       
+       if ((p->owner->state != AST_STATE_UP) &&
+               (p->owner->state != AST_STATE_RINGING)) {
+               /* If the current call is not up, then don't allow the call */
+               return 0;
+       }
+       if ((p->thirdcallindex > -1) && (p->owner == p->owners[p->thirdcallindex])) {
+               /* Can't take a call wait when the three way calling hasn't been merged yet. */
+               return 0;
+       }
+       /* We're cool */
+       return 1;
+}
+
 static struct ast_channel *tor_request(char *type, int format, void *data)
 {
        int oldformat;
@@ -1482,6 +2775,7 @@ static struct ast_channel *tor_request(char *type, int format, void *data)
        char *dest=NULL;
        int x;
        char *s;
+       int callwait;
        
        /* We do signed linear */
        oldformat = format;
@@ -1515,24 +2809,32 @@ static struct ast_channel *tor_request(char *type, int format, void *data)
                channelmatch = x;
        }
        /* Search for an unowned channel */
-       if (pthread_mutex_lock(&iflock)) {
+       if (ast_pthread_mutex_lock(&iflock)) {
                ast_log(LOG_ERROR, "Unable to lock interface list???\n");
                return NULL;
        }
        p = iflist;
        while(p && !tmp) {
-               if (!p->owner &&        /* No current owner */
-                   ((channelmatch < 0) || (p->channel == channelmatch)) && /* Right channel */
-                       (((p->group & groupmatch) == groupmatch))                               /* Right group */
-               ) {
+               if (available(p, channelmatch, groupmatch)) {
                        if (option_debug)
                                ast_log(LOG_DEBUG, "Using channel %d\n", p->channel);
-                       tmp = tor_new(p, AST_STATE_RESERVED, 0);
+#ifdef TORMENTA_PRI
+               if (p->pri) 
+                               if (!(p->call = pri_new_call(p->pri->pri))) {
+                                       ast_log(LOG_WARNING, "Unable to create call on channel %d\n", p->channel);
+                                       break;
+                               }
+#endif
+                       callwait = (p->owner != NULL);
+                       tmp = tor_new(p, AST_STATE_RESERVED, 0, p->owner ? 1 : 0, 0);
+                       /* Note if the call is a call waiting call */
+                       if (callwait)
+                               tmp->cdrflags |= AST_CDR_CALLWAIT;
                        break;
                }
                p = p->next;
        }
-       pthread_mutex_unlock(&iflock);
+       ast_pthread_mutex_unlock(&iflock);
        restart_monitor();
        return tmp;
 }
@@ -1572,6 +2874,358 @@ static int get_group(char *s)
        return group;
 }
 
+#ifdef TORMENTA_PRI
+
+static int pri_find_empty_chan(struct tor_pri *pri)
+{
+       int x;
+       for (x=23;x>0;x--) {
+               if (pri->pvt[x] && !pri->pvt[x]->owner)
+                       return x;
+       }
+       return 0;
+}
+
+static int pri_fixup(struct tor_pri *pri, int channel, q931_call *c)
+{
+       int x;
+       for (x=1;x<24;x++) {
+               if (!pri->pvt[x]) continue;
+               if (pri->pvt[x]->call == c) {
+                       /* Found our call */
+                       if (channel != x) {
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "Moving call from channel %d to channel %d\n",
+                                               x, channel);
+                               if (pri->pvt[channel]->owner) {
+                                       ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n",
+                                               x, channel, channel);
+                                       return 0;
+                               }
+                               /* Fix it all up now */
+                               pri->pvt[channel]->owner = pri->pvt[x]->owner;
+                               pri->pvt[channel]->owner->pvt->pvt = pri->pvt[channel];
+                               pri->pvt[channel]->owner->fds[0] = zap_fd(pri->pvt[channel]->z);
+                               pri->pvt[channel]->call = pri->pvt[x]->call;
+                               
+                               /* Free up the old channel, now not in use */
+                               pri->pvt[x]->owner = NULL;
+                               pri->pvt[x]->call = NULL;
+                       }
+                       return channel;
+               }
+       }
+       return 0;
+}
+
+static void *pri_dchannel(void *vpri)
+{
+       struct tor_pri *pri = vpri;
+       pri_event *e;
+       fd_set efds;
+       fd_set rfds;
+       int res;
+       int chan;
+       int x;
+       struct ast_channel *c;
+       for(;;) {
+               FD_ZERO(&rfds);
+               FD_ZERO(&efds);
+               FD_SET(pri->fd, &rfds);
+               FD_SET(pri->fd, &efds);
+               res = select(pri->fd + 1, &rfds, NULL, &efds, pri_schedule_next(pri->pri));
+               pthread_mutex_lock(&pri->lock);
+               if (!res) {
+                       /* Just a timeout, run the scheduler */
+                       pri_schedule_run(pri->pri);
+               } else if (res > -1) {
+                       e = pri_check_event(pri->pri);
+                       if (e) {
+                               if (pri->debug)
+                                       pri_dump_event(pri->pri, 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;
+                                       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;
+                                       break;
+                               case PRI_EVENT_RESTART:
+                                       chan = e->restart.channel;
+                                       if (chan > -1) {
+                                               if ((chan < 1) || (chan > 23) )
+                                                       ast_log(LOG_WARNING, "Restart requested on odd channel number %d on span %d\n", chan, pri->span);
+                                               else if (!pri->pvt[chan])
+                                                       ast_log(LOG_WARNING, "Restart requested on unconfigured channel %d on span %d\n", chan, pri->span);
+                                               else {
+                                                       if (option_verbose > 2)
+                                                               ast_verbose(VERBOSE_PREFIX_3 "B-channel %d restarted on span %d\n", 
+                                                                       chan, pri->span);
+                                                       /* Force soft hangup if appropriate */
+                                                       if (pri->pvt[chan]->owner)
+                                                               pri->pvt[chan]->owner->softhangup = 1;
+                                               }
+                                       } else {
+                                               if (option_verbose > 2)
+                                                       ast_verbose("Restart on requested on entire span %d\n", pri->span);
+                                               for (x=1;x<24;x++)
+                                                       if (pri->pvt[chan]->owner)
+                                                               pri->pvt[chan]->owner->softhangup = 1;
+                                       }
+                                       break;
+                               case PRI_EVENT_RING:
+                                       chan = e->ring.channel;
+                                       if ((chan < 1) || (chan > 23)) {
+                                               ast_log(LOG_WARNING, "Ring requested on odd channel number %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       } else if (!pri->pvt[chan]) {
+                                               ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       } else if (pri->pvt[chan]->owner) {
+                                               ast_log(LOG_WARNING, "Ring requested on channel %d already in use on span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       }
+                                       if (!chan && (e->ring.flexible))
+                                               chan = pri_find_empty_chan(pri);
+                                       if (chan) {
+                                               /* Get caller ID */
+                                               if (pri->pvt[chan]->use_callerid) 
+                                                       strncpy(pri->pvt[chan]->callerid, e->ring.callingnum, sizeof(pri->pvt[chan]->callerid));
+                                               else
+                                                       strcpy(pri->pvt[chan]->callerid, "");
+                                               /* Get called number */
+                                               if (strlen(e->ring.callednum)) {
+                                                       strncpy(pri->pvt[chan]->exten, e->ring.callednum, sizeof(pri->pvt[chan]->exten));
+                                               } else
+                                                       strcpy(pri->pvt[chan]->exten, "s");
+                                               /* Make sure extension exists */
+                                               if (ast_exists_extension(NULL, pri->pvt[chan]->context, pri->pvt[chan]->exten, 1)) {
+                                                       /* Start PBX */
+                                                       pri->pvt[chan]->call = e->ring.call;
+                                                       c = tor_new(pri->pvt[chan], AST_STATE_RING, 1, 0, 0);
+                                                       if (c) {
+                                                               if (option_verbose > 2)
+                                                                       ast_verbose(VERBOSE_PREFIX_3 "Accepting call from '%s' to '%s' on channel %d, span %d\n",
+                                                                               e->ring.callingnum, pri->pvt[chan]->exten, chan, pri->span);
+                                                               pri_acknowledge(pri->pri, e->ring.call, chan, 0);
+                                                       } else {
+                                                               ast_log(LOG_WARNING, "Unable to start PBX on channel %d, span %d\n", chan, pri->span);
+                                                               pri_release(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
+                                                               pri->pvt[chan]->call = NULL;
+                                                       }
+                                               } else {
+                                                       if (option_verbose > 2) 
+                                                               ast_verbose(VERBOSE_PREFIX_3 "Extension '%s' in context '%s' does not exist.  Rejecting call on channel %d, span %d\n", 
+                                                                       pri->pvt[chan]->exten, pri->pvt[chan]->context, chan, pri->span);
+                                                       pri_release(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED);
+                                               }
+                                       } else 
+                                               pri_release(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
+                                       break;
+                               case PRI_EVENT_RINGING:
+                                       chan = e->ringing.channel;
+                                       if ((chan < 1) || (chan > 23)) {
+                                               ast_log(LOG_WARNING, "Ringing requested on odd channel number %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       } else if (!pri->pvt[chan]) {
+                                               ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       }
+                                       if (chan) {
+                                               chan = pri_fixup(pri, chan, e->ringing.call);
+                                               if (!chan) {
+                                                       ast_log(LOG_WARNING, "Ringing requested on channel %d not in use on span %d\n", e->ringing.channel, pri->span);
+                                                       chan = 0;
+                                               } else
+                                                       pri->pvt[chan]->needringing[0] =1;
+                                       }
+                                       break;                          
+                               case PRI_EVENT_ANSWER:
+                                       chan = e->answer.channel;
+                                       if ((chan < 1) || (chan > 23)) {
+                                               ast_log(LOG_WARNING, "Answer on odd channel number %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       } else if (!pri->pvt[chan]) {
+                                               ast_log(LOG_WARNING, "Answer on unconfigured channel %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       }
+                                       if (chan) {
+                                               chan = pri_fixup(pri, chan, e->ringing.call);
+                                               if (!chan) {
+                                                       ast_log(LOG_WARNING, "Ring requested on channel %d not in use on span %d\n", chan, pri->span);
+                                                       chan = 0;
+                                               } else
+                                                       pri->pvt[chan]->needanswer[0] =1;
+                                       }
+                                       break;                          
+                               case PRI_EVENT_HANGUP:
+                                       chan = e->hangup.channel;
+                                       if ((chan < 1) || (chan > 23)) {
+                                               ast_log(LOG_WARNING, "Hangup requested on odd channel number %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       } else if (!pri->pvt[chan]) {
+                                               ast_log(LOG_WARNING, "Hanngup requested on unconfigured channel %d span %d\n", chan, pri->span);
+                                               chan = 0;
+                                       }
+                                       if (chan) {
+                                               chan = pri_fixup(pri, chan, e->hangup.call);
+                                               if (chan) {
+                                                       if (pri->pvt[chan]->owner) {
+                                                               if (option_verbose > 3) 
+                                                                       ast_verbose(VERBOSE_PREFIX_3, "Channel %d, span %d got hangup\n", chan, pri->span);
+                                                               pri->pvt[chan]->owner->softhangup = 1;
+                                                               pri->pvt[chan]->call = NULL;
+                                                       }
+                                               }
+                                       }
+                                       break;
+                               case PRI_EVENT_CONFIG_ERR:
+                                       ast_log(LOG_WARNING, "PRI Error: %s\n", e->err.err);
+                                       break;
+                               default:
+                                       ast_log(LOG_DEBUG, "Event: %d\n", e->e);
+                               }
+                       } else {
+                               /* Check for an event */
+                               x = 0;
+                               res = ioctl(pri->fd, TOR_GETEVENT, &x);
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Got event %s (%d) on D-channel for span %d\n", event2str(x), x, pri->span);
+                       }
+               } else {
+                       if (errno != EINTR) 
+                               ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno));
+               }
+               pthread_mutex_unlock(&pri->lock);
+       }
+       /* Never reached */
+       return NULL;
+}
+
+static int start_pri(struct tor_pri *pri)
+{
+       char filename[80];
+       int res;
+       TOR_PARAMS p;
+       BUFFER_INFO bi;
+       snprintf(filename, sizeof(filename), "/dev/tor/%d", pri->offset + 24);
+       pri->fd = open(filename, O_RDWR, 0600);
+       if (pri->fd < 0) {
+               ast_log(LOG_ERROR, "Unable to open D-channel %s (%s)\n", filename, strerror(errno));
+               return -1;
+       }
+       res = ioctl(pri->fd, TOR_GET_PARAMS, &p);
+       if (res) {
+               close(pri->fd);
+               pri->fd = -1;
+               ast_log(LOG_ERROR, "Unable to get parameters for D-channel %s (%s)\n", filename, strerror(errno));
+               return -1;
+       }
+       if (p.sigtype != TOR_HDLCFCS) {
+               close(pri->fd);
+               pri->fd = -1;
+               ast_log(LOG_ERROR, "D-channel %s is not in HDLC/FCS mode.  See /etc/tormenta.conf\n", filename);
+               return -1;
+       }
+       bi.txbufpolicy = POLICY_IMMEDIATE;
+       bi.rxbufpolicy = POLICY_IMMEDIATE;
+       bi.numbufs = 4;
+       bi.bufsize = 1024;
+       if (ioctl(pri->fd, TOR_SET_BUFINFO, &bi)) {
+               ast_log(LOG_ERROR, "Unable to set appropriate buffering on %s\n", filename);
+               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;
+       }
+       pri_set_debug(pri->pri, DEFAULT_PRI_DEBUG);
+       if (pthread_create(&pri->master, NULL, pri_dchannel, pri)) {
+               close(pri->fd);
+               pri->fd = -1;
+               ast_log(LOG_ERROR, "Unable to spawn D-channel: %s\n", strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+static char *complete_span(char *line, char *word, int pos, int state)
+{
+       int span=1;
+       char tmp[50];
+       while(span <= NUM_SPANS) {
+               if (span > state)
+                       break;
+               span++;
+       }
+       if (span <= NUM_SPANS) {
+               snprintf(tmp, sizeof(tmp), "%d", span);
+               return strdup(tmp);
+       } else
+               return NULL;
+}
+
+static int handle_pri_debug(int fd, int argc, char *argv[])
+{
+       int span;
+       span = atoi(argv[3]);
+       if ((span < 1) || (span > NUM_SPANS)) {
+               ast_cli(fd, "Invalid span %s.  Should be a number %d to %d\n", argv[3], 1, NUM_SPANS);
+               return RESULT_SUCCESS;
+       }
+       if (!pris[span-1].pri) {
+               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);
+       ast_cli(fd, "Enabled debugging on span %d\n", span);
+       return RESULT_SUCCESS;
+}
+
+static int handle_pri_no_debug(int fd, int argc, char *argv[])
+{
+       int span;
+       span = atoi(argv[4]);
+       if ((span < 1) || (span > NUM_SPANS)) {
+               ast_cli(fd, "Invalid span %s.  Should be a number %d to %d\n", argv[4], 1, NUM_SPANS);
+               return RESULT_SUCCESS;
+       }
+       if (!pris[span-1].pri) {
+               ast_cli(fd, "No PRI running on span %d\n", span);
+               return RESULT_SUCCESS;
+       }
+       pri_set_debug(pris[span-1].pri, 0);
+       ast_cli(fd, "Disabled debugging on span %d\n", span);
+       return RESULT_SUCCESS;
+}
+
+
+static char pri_debug_help[] = 
+       "Usage: pri debug span <span>\n"
+       "       Enables debugging on a given PRI span\n";
+       
+static char pri_no_debug_help[] = 
+       "Usage: pri no debug span <span>\n"
+       "       Disables debugging on a given PRI span\n";
+
+static struct ast_cli_entry pri_debug = {
+       { "pri", "debug", "span", NULL }, handle_pri_debug, "Enables PRI debugging on a span", pri_debug_help, complete_span 
+};
+
+static struct ast_cli_entry pri_no_debug = {
+       { "pri", "no", "debug", "span", NULL }, handle_pri_no_debug, "Enables PRI debugging on a span", pri_no_debug_help, complete_span };
+
+#endif
+
 int load_module()
 {
        struct ast_config *cfg;
@@ -1579,6 +3233,17 @@ int load_module()
        struct tor_pvt *tmp;
        char *chan;
        int start, finish,x;
+#ifdef TORMENTA_PRI
+       int y;
+#endif
+
+       
+#ifdef TORMENTA_PRI
+       memset(pris, 0, sizeof(pris));
+       for (y=0;y<NUM_SPANS;y++)
+               pris[y].fd = -1;
+#endif
+       
        cfg = ast_load(config);
 
        /* We *must* have a config file otherwise stop immediately */
@@ -1588,7 +3253,7 @@ int load_module()
        }
        
 
-       if (pthread_mutex_lock(&iflock)) {
+       if (ast_pthread_mutex_lock(&iflock)) {
                /* It's a little silly to lock it, but we mind as well just to be sure */
                ast_log(LOG_ERROR, "Unable to lock interface list???\n");
                return -1;
@@ -1600,7 +3265,7 @@ int load_module()
                        if (cur_signalling < 0) {
                                ast_log(LOG_ERROR, "Signalling must be specified before any channels are.\n");
                                ast_destroy(cfg);
-                               pthread_mutex_unlock(&iflock);
+                               ast_pthread_mutex_unlock(&iflock);
                                unload_module();
                                return -1;
                        }
@@ -1614,7 +3279,7 @@ int load_module()
                                } else {
                                        ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", v->value, chan);
                                        ast_destroy(cfg);
-                                       pthread_mutex_unlock(&iflock);
+                                       ast_pthread_mutex_unlock(&iflock);
                                        unload_module();
                                        return -1;
                                }
@@ -1634,13 +3299,27 @@ int load_module()
                                        } else {
                                                ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
                                                ast_destroy(cfg);
-                                               pthread_mutex_unlock(&iflock);
+                                               ast_pthread_mutex_unlock(&iflock);
                                                unload_module();
                                                return -1;
                                        }
                                }
                                chan = strtok(NULL, ",");
                        }
+               } else if (!strcasecmp(v->name, "usecallerid")) {
+                       use_callerid = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "threewaycalling")) {
+                       threewaycalling = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "transfer")) {
+                       transfer = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "echocancel")) {
+                       echocancel = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "hidecallerid")) {
+                       hidecallerid = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "callwaiting")) {
+                       callwaiting = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "callwaitingcallerid")) {
+                       callwaitingcallerid = ast_true(v->value);
                } else if (!strcasecmp(v->name, "context")) {
                        strncpy(context, v->value, sizeof(context));
                } else if (!strcasecmp(v->name, "language")) {
@@ -1651,6 +3330,14 @@ int load_module()
                        cur_group = get_group(v->value);
                } else if (!strcasecmp(v->name, "immediate")) {
                        immediate = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "rxgain")) {
+                       if (sscanf(v->value, "%f", &rxgain) != 1) {
+                               ast_log(LOG_WARNING, "Invalid rxgain: %s\n", v->value);
+                       }
+               } else if (!strcasecmp(v->name, "txgain")) {
+                       if (sscanf(v->value, "%f", &txgain) != 1) {
+                               ast_log(LOG_WARNING, "Invalid txgain: %s\n", v->value);
+                       }
                } else if (!strcasecmp(v->name, "callerid")) {
                        if (!strcasecmp(v->value, "asreceived"))
                                strcpy(callerid,"");
@@ -1681,18 +3368,40 @@ int load_module()
                                cur_signalling = SIG_FXOKS;
                        } else if (!strcasecmp(v->value, "featd")) {
                                cur_signalling = SIG_FEATD;
+#ifdef TORMENTA_PRI
+                       } else if (!strcasecmp(v->value, "pri_net")) {
+                               cur_signalling = SIG_PRI;
+                               pritype = PRI_NETWORK;
+                       } else if (!strcasecmp(v->value, "pri_cpe")) {
+                               cur_signalling = SIG_PRI;
+                               pritype = PRI_CPE;
+#endif
                        } else {
                                ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
+                       }
+#ifdef TORMENTA_PRI
+               } else if (!strcasecmp(v->name, "switchtype")) {
+                       if (!strcasecmp(v->value, "national")) 
+                               switchtype = PRI_SWITCH_NI2;
+                       else if (!strcasecmp(v->value, "dms100"))
+                               switchtype = PRI_SWITCH_DMS100;
+                       else if (!strcasecmp(v->value, "4ess"))
+                               switchtype = PRI_SWITCH_ATT4ESS;
+                       else if (!strcasecmp(v->value, "5ess"))
+                               switchtype = PRI_SWITCH_LUCENT5E;
+                       else {
+                               ast_log(LOG_ERROR, "Unknown switchtype '%s'\n", v->value);
                                ast_destroy(cfg);
-                               pthread_mutex_unlock(&iflock);
+                               ast_pthread_mutex_unlock(&iflock);
                                unload_module();
                                return -1;
                        }
+#endif         
                } else
                        ast_log(LOG_DEBUG, "Ignoring %s\n", v->name);
                v = v->next;
        }
-       pthread_mutex_unlock(&iflock);
+       ast_pthread_mutex_unlock(&iflock);
        /* Make sure we can register our Tor channel type */
        if (ast_channel_register(type, tdesc, AST_FORMAT_SLINEAR |  AST_FORMAT_ULAW, tor_request)) {
                ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
@@ -1701,6 +3410,24 @@ int load_module()
                return -1;
        }
        ast_destroy(cfg);
+#ifdef TORMENTA_PRI
+       for (x=0;x<NUM_SPANS;x++) {
+               for (y=1;y<23;y++) {
+                       if (pris[x].chanmask[y]) {
+                               pris[x].offset = x * 24;
+                               pris[x].span = x + 1;
+                               if (start_pri(pris + x)) {
+                                       ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);
+                                       return -1;
+                               } else if (option_verbose > 1) 
+                                       ast_verbose(VERBOSE_PREFIX_2 "Starting D-Channel on span %d\n", x + 1);
+                               break;
+                       }
+               }
+       }
+       ast_cli_register(&pri_debug);
+       ast_cli_register(&pri_no_debug);
+#endif 
        /* And start the monitor for the first time */
        restart_monitor();
        return 0;
@@ -1711,7 +3438,7 @@ int unload_module()
        struct tor_pvt *p, *pl;
        /* First, take us out of the channel loop */
        ast_channel_unregister(type);
-       if (!pthread_mutex_lock(&iflock)) {
+       if (!ast_pthread_mutex_lock(&iflock)) {
                /* Hangup all interfaces if they have an owner */
                p = iflist;
                while(p) {
@@ -1720,25 +3447,25 @@ int unload_module()
                        p = p->next;
                }
                iflist = NULL;
-               pthread_mutex_unlock(&iflock);
+               ast_pthread_mutex_unlock(&iflock);
        } else {
                ast_log(LOG_WARNING, "Unable to lock the monitor\n");
                return -1;
        }
-       if (!pthread_mutex_lock(&monlock)) {
+       if (!ast_pthread_mutex_lock(&monlock)) {
                if (monitor_thread) {
                        pthread_cancel(monitor_thread);
                        pthread_kill(monitor_thread, SIGURG);
                        pthread_join(monitor_thread, NULL);
                }
                monitor_thread = -2;
-               pthread_mutex_unlock(&monlock);
+               ast_pthread_mutex_unlock(&monlock);
        } else {
                ast_log(LOG_WARNING, "Unable to lock the monitor\n");
                return -1;
        }
 
-       if (!pthread_mutex_lock(&iflock)) {
+       if (!ast_pthread_mutex_lock(&iflock)) {
                /* Destroy all the interfaces and free their memory */
                p = iflist;
                while(p) {
@@ -1754,7 +3481,7 @@ int unload_module()
                        free(pl);
                }
                iflist = NULL;
-               pthread_mutex_unlock(&iflock);
+               ast_pthread_mutex_unlock(&iflock);
        } else {
                ast_log(LOG_WARNING, "Unable to lock the monitor\n");
                return -1;
@@ -1765,9 +3492,9 @@ int unload_module()
 int usecount()
 {
        int res;
-       pthread_mutex_lock(&usecnt_lock);
+       ast_pthread_mutex_lock(&usecnt_lock);
        res = usecnt;
-       pthread_mutex_unlock(&usecnt_lock);
+       ast_pthread_mutex_unlock(&usecnt_lock);
        return res;
 }