Version 0.3.0 from FTP
authorMark Spencer <markster@digium.com>
Fri, 7 Feb 2003 19:23:19 +0000 (19:23 +0000)
committerMark Spencer <markster@digium.com>
Fri, 7 Feb 2003 19:23:19 +0000 (19:23 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@610 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/Makefile
channels/chan_zap.c
channels/chan_zap_old.c [new file with mode: 0755]

diff --git a/CHANGES b/CHANGES
index a4ecfb0..2088156 100755 (executable)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,39 @@
+Asterisk 0.3.0
+ -- Add fastfoward, rewind, seek, and truncate functions to streams
+ -- Support registration
+ -- Add G729 format
+ -- Permit applications to return a digit indicating new extension
+ -- Change "SHUTDOWN" to "STOP" in commands
+ -- SIP "Hold" fixes and VXML URI support
+ -- New chan_zap with 160 sample chunk size
+ -- Add DTMF, MF, and Fax tone detector to dsp routines
+ -- Allow overlap dialing (inbound) on PRI
+ -- Enable tone detection with PRI
+ -- Add special information tone detection
+ -- Add Asterisk DB support
+ -- Add pulse dialing
+ -- Re-record all system prompts
+ -- Change "timelen" to samples for better accuracy
+ -- Move to editline, eliminating readline dependency
+ -- Add peer "poke" support to SIP and IAX
+ -- Add experimental call progress detection
+ -- Add SIP authentication (digest)
+ -- Add RDNIS
+ -- Reroute faxes to "fax" extension
+ -- Create ISDN/modem group concept
+ -- Centralize indication
+ -- Add initial MGCP support
+ -- SIP debugging cleanup
+ -- SIP reload
+ -- SIP commands (show channels, etc)
+ -- Add optional busy detection
+ -- Add Visual Message Waiting Indicator (MDMF and SDMF)
+ -- Add ambiguous extension matching
+ -- Add *69
+ -- Major SIP enhancements from SIPit
+ -- Rewrite of ZAP CLASS features using subchannels
+ -- Enhanced call parking
+ -- Add extended outgoing spool support (pbx_spool)
 Asterisk 0.2.0
  -- Outbound origination API
  -- Call management improvements
index 7255f0e..7669373 100755 (executable)
@@ -14,7 +14,7 @@
 CHANNEL_LIBS=chan_modem.so chan_iax.so chan_sip.so \
             chan_modem_aopen.so chan_oss.so \
              chan_modem_bestdata.so chan_modem_i4l.so \
-             chan_agent.so
+             chan_agent.so chan_mgcp.so
 
 #
 # If you really want VoFR you can have it :-P
@@ -27,8 +27,12 @@ CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations
 CFLAGS+=$(shell [ ! -f /usr/include/linux/if_wanpipe.h ] && echo " -DOLD_SANGOMA_API")
 CHANNEL_LIBS+=$(shell [ -f /usr/include/alsa/asoundlib.h ] && echo "chan_alsa.so")
 CFLAGS+=$(shell [ -f /usr/lib/libpri.so.1 ] && echo " -DZAPATA_PRI")
+CFLAGS+=$(shell [ -f /usr/lib/libmfcr2.so.1 ] && echo " -DZAPATA_R2")
 CFLAGS+=$(shell [ -f alsa-monitor.h ] && echo " -DALSA_MONITOR")
 ZAPPRI=$(shell [ -f /usr/lib/libpri.so.1 ] && echo "-lpri")
+ZAPR2=$(shell [ -f /usr/lib/libmfcr2.so.1 ] && echo "-lmfcr2")
+CHANZAP=$(shell if [ -f .oldzap ]; then echo "chan_zap_old.c"; else echo "chan_zap.c"; fi)
+ZAPLIB=$(shell if ! [ -f .newzap ]; then echo "-lzap"; fi)
 
 ALSA_SRC=chan_alsa.c
 ALSA_SRC+=$(shell [ -f alsa-monitor.h ] && echo "alsa-monitor.h")
@@ -39,9 +43,11 @@ CFLAGS+=#-DVOFRDUMPER
 
 ZAPDIR=/usr/lib
 
-CHANNEL_LIBS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "chan_zap.so")
+CHANNEL_LIBS+=$(shell [ -f /usr/include/linux/zaptel.h ] && echo "chan_zap.so")
 
-CFLAGS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "-I$(ZAPDIR)")
+CHANNEL_LIBS+=$(shell [ -f /usr/include/nbs.h ] && echo "chan_nbs.so" )
+
+#CFLAGS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "-I$(ZAPDIR)")
 
 all: $(CHANNEL_LIBS) 
 
@@ -66,14 +72,20 @@ ringtone.h: gentone
 
 chan_oss.o: chan_oss.c  busy.h ringtone.h
 
+chan_zap.o: $(CHANZAP)
+       $(CC) -c $(CFLAGS) -o chan_zap.o $(CHANZAP)
+
 chan_zap.so: chan_zap.o
-       $(CC) -shared -Xlinker -x -o $@ $<  $(ZAPPRI) -lzap -ltonezone
+       $(CC) -shared -Xlinker -x -o $@ $<  $(ZAPPRI) $(ZAPR2) -ltonezone $(ZAPLIB)
 
 chan_alsa.o: $(ALSA_SRC)
 
 chan_alsa.so: chan_alsa.o
        $(CC) -shared -Xlinker -x -o $@ $< -lasound -lm -ldl
 
+chan_nbs.so: chan_nbs.o
+       $(CC) -shared -Xlinker -x -o $@ $< -lnbs
+
 #chan_modem.so : chan_modem.o
 #      $(CC) -rdynamic -shared -Xlinker -x -o $@ $<
 
index 2a3dfcc..342038e 100755 (executable)
 #include <asterisk/options.h>
 #include <asterisk/file.h>
 #include <asterisk/ulaw.h>
+#include <asterisk/alaw.h>
 #include <asterisk/callerid.h>
 #include <asterisk/adsi.h>
 #include <asterisk/cli.h>
 #include <asterisk/cdr.h>
 #include <asterisk/parking.h>
 #include <asterisk/musiconhold.h>
+#include <asterisk/say.h>
 #include <asterisk/tdd.h>
+#include <asterisk/app.h>
+#include <asterisk/dsp.h>
 #include <sys/signal.h>
 #include <sys/select.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <stdint.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <linux/zaptel.h>
-#include <zap.h>
 #include <math.h>
 #include <tonezone.h>
-#include <dirent.h>
+#include <ctype.h>
 #ifdef ZAPATA_PRI
 #include <libpri.h>
 #endif
+#ifdef ZAPATA_R2
+#include <libmfcr2.h>
+#endif
 
 #include "../asterisk.h"
 
@@ -53,7 +60,6 @@
    XXX  
  */
 
-#define RINGT 274
 
 /*
  * Define ZHONE_HACK to cause us to go off hook and then back on hook when
  * even though their web site says they support their products for life.
  */
 
-#define ZHONE_HACK
+/* #define ZHONE_HACK */
 
 #define CHANNEL_PSEUDO -12
 
+#define AST_LAW(p) (((p)->law == ZT_LAW_ALAW) ? AST_FORMAT_ALAW : AST_FORMAT_ULAW)
+
+static char *desc = "Zapata Telephony"
 #ifdef ZAPATA_PRI
-static char *desc = "Zapata Telephony (PRI) Driver";
-static char *tdesc = "Zapata Telephony + PRI Interface Driver";
-#else
-static char *desc = "Zapata Telphony Driver";
-static char *tdesc = "Zapata Telephony Interface Driver";
+               " w/PRI"
+#endif
+#ifdef ZAPATA_R2
+               " w/R2"
+#endif
+;
+
+static char *tdesc = "Zapata Telephony Driver"
+#ifdef ZAPATA_PRI
+               " w/PRI"
+#endif
+#ifdef ZAPATA_R2
+               " w/R2"
 #endif
+;
+
 static char *type = "Zap";
 static char *typecompat = "Tor";       /* Retain compatibility with chan_tor */
 static char *config = "zapata.conf";
@@ -92,6 +111,7 @@ static char *config = "zapata.conf";
 #define SIG_FXOGS      ZT_SIG_FXOGS
 #define SIG_FXOKS      ZT_SIG_FXOKS
 #define SIG_PRI                ZT_SIG_CLEAR
+#define SIG_R2         ZT_SIG_CAS
 
 #define NUM_SPANS      32
 #define RESET_INTERVAL 3600    /* How often (in seconds) to reset unused channels */
@@ -111,6 +131,7 @@ static int cur_signalling = -1;
 static int cur_group = 0;
 static int cur_callergroup = 0;
 static int cur_pickupgroup = 0;
+static int relaxdtmf = 0;
 
 static int immediate = 0;
 
@@ -122,6 +143,8 @@ static int callwaitingcallerid = 0;
 
 static int hidecallerid = 0;
 
+static int callreturn = 0;
+
 static int threewaycalling = 0;
 
 static int transfer = 0;
@@ -136,6 +159,10 @@ static int echocancel;
 
 static int echocanbridged = 0;
 
+static int busydetect = 0;
+
+static int callprogress = 0;
+
 static char accountcode[20] = "";
 
 static char mailbox[AST_MAX_EXTENSION];
@@ -157,6 +184,9 @@ static int firstdigittimeout = 16000;
 /* How long to wait for following digits (FXO logic) */
 static int gendigittimeout = 8000;
 
+/* How long to wait for an extra digit, if there is an ambiguous match */
+static int matchdigittimeout = 3000;
+
 static int usecnt =0;
 static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
 
@@ -195,18 +225,25 @@ static inline int zt_wait_event(int fd)
        return j;
 }
 
-/* Chunk size to read -- we use the same size as the chunks that the zapata library uses.  */   
-#define READ_SIZE 204
+/* Chunk size to read -- we use 20ms chunks to make things happy.  */   
+#define READ_SIZE 160
 
 #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 */
+#define CIDCW_EXPIRE_SAMPLES           ( (500 * 8) / READ_SIZE) /* 500 ms */
+#define RINGT                                          ( (8000 * 8) / READ_SIZE)
 
 struct zt_pvt;
 
 
+#ifdef ZAPATA_R2
+static int r2prot = -1;
+#endif
+
+
 #ifdef ZAPATA_PRI
 struct zt_pri {
        pthread_t master;                       /* Thread of master */
@@ -218,6 +255,7 @@ struct zt_pri {
        int minidle;                            /* Min # of "idling" calls to keep active */
        int nodetype;                           /* Node type */
        int switchtype;                         /* Type of switch to emulate */
+       int dialplan;                   /* Dialing plan */
        int dchannel;                   /* What channel the dchannel is on */
        int channels;                   /* Num of chans in span (31 or 24) */
        struct pri *pri;
@@ -226,12 +264,12 @@ struct zt_pri {
        int up;
        int offset;
        int span;
-       int chanmask[31];                       /* Channel status */
+       int chanmask[32];                       /* Channel status */
        int resetting;
        int resetchannel;
        time_t lastreset;
-       struct zt_pvt *pvt[31]; /* Member channel pvt structs */
-       struct zt_channel *chan[31];    /* Channels on each line */
+       struct zt_pvt *pvt[32]; /* Member channel pvt structs */
+       struct zt_channel *chan[32];    /* Channels on each line */
 };
 
 
@@ -263,21 +301,55 @@ static inline void pri_rel(struct zt_pri *pri)
 }
 
 static int switchtype = PRI_SWITCH_NI2;
+static int dialplan = PRI_NATIONAL_ISDN + 1;
 
 #endif
 
+#define SUB_REAL               0                       /* Active call */
+#define SUB_CALLWAIT   1                       /* Call-Waiting call on hold */
+#define SUB_THREEWAY   2                       /* Three-way call */
+
+static char *subnames[] = {
+       "Real",
+       "Callwait",
+       "Threeway"
+};
+
+struct zt_subchannel {
+       int zfd;
+       struct ast_channel *owner;
+       int chan;
+       short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE];
+       struct ast_frame f;             /* One frame for each channel.  How did this ever work before? */
+       int needringing;
+       int needcallerid;
+       int needanswer;
+       int linear;
+       int inthreeway;
+       int curconfno;                  /* What conference we're currently in */
+};
+
+#define CONF_USER_REAL         (1 << 0)
+#define CONF_USER_THIRDCALL    (1 << 1)
+
+#define MAX_SLAVES     4
+
 static struct zt_pvt {
-       ZAP *z;
        pthread_mutex_t lock;
-       struct ast_channel *owner;      /* Our owner (if applicable) */
-       struct ast_channel *owners[3];  
+       struct ast_channel *owner;      /* Our current active owner (if applicable) */
                /* 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 */
+       struct zt_subchannel sub_unused;        /* Just a safety precaution */
+       struct zt_subchannel subs[3];   /* Sub-channels */
+       struct zt_confinfo saveconf;    /* Saved conference info */
+
+       struct zt_pvt *slaves[MAX_SLAVES];      /* Slave to us (follows our conferencing) */
+       struct zt_pvt *master;  /* Master to us (we follow their conferencing) */
+       int inconference;               /* If our real should be in the conference */
        
        int sig;                                        /* Signalling style */
+       int radio;                              /* radio type */
+       int firstradio;                         /* first radio flag */
        float rxgain;
        float txgain;
        struct zt_pvt *next;                    /* Next channel in list */
@@ -286,13 +358,14 @@ static struct zt_pvt {
        char language[MAX_LANGUAGE];
        char musicclass[MAX_LANGUAGE];
        char callerid[AST_MAX_EXTENSION];
+       char lastcallerid[AST_MAX_EXTENSION];
        char callwaitcid[AST_MAX_EXTENSION];
-       char dtmfq[AST_MAX_EXTENSION];
-       struct ast_frame f_unused;      /* Usually unused, but could in rare cases be needed */
-       struct ast_frame f[3];          /* One frame for each channel.  How did this ever work before? */
-       short buffer[3][AST_FRIENDLY_OFFSET/2 + READ_SIZE];
+       char rdnis[AST_MAX_EXTENSION];
        int group;
        int law;
+       int confno;                                     /* Our conference */
+       int confusers;                          /* Who is using our conference */
+       int propconfno;                         /* Propagated conference number */
        int callgroup;
        int pickupgroup;
        int immediate;                          /* Answer before getting digits? */
@@ -302,32 +375,33 @@ static struct zt_pvt {
        int dialednone;
        int use_callerid;                       /* Whether or not to use caller id on this channel */
        int hidecallerid;
+       int callreturn;
        int permhidecallerid;           /* Whether to hide our outgoing caller ID or not */
        int callwaitingrepeat;          /* How many samples to wait before repeating call waiting */
+       int cidcwexpire;                        /* When to expire our muting for CID/CW */
        unsigned char *cidspill;
        int cidpos;
        int cidlen;
        int ringt;
        int stripmsd;
-       int needringing[3];
-       int needanswer[3];
        int callwaiting;
        int callwaitcas;
        int callwaitrings;
        int echocancel;
        int echocanbridged;
+       int echocanon;
        int permcallwaiting;
        int callwaitingcallerid;
        int threewaycalling;
        int transfer;
+       int digital;
+       int outgoing;
        int dnd;
+       int busydetect;
+       int callprogress;
+       struct ast_dsp *dsp;
        int cref;                                       /* Call reference number */
        ZT_DIAL_OPERATION dop;
-       struct zt_confinfo conf;        /* Saved state of conference */
-       struct zt_confinfo conf2;       /* Saved state of alternate conference */
-       int confno;                                     /* Conference number */
-       ZAP *pseudo;                            /* Pseudo channel FD */
-       int pseudochan;                         /* Pseudo channel */
        int destroy;
        int ignoredtmf;                         
        int inalarm;
@@ -335,23 +409,36 @@ static struct zt_pvt {
        int amaflags;                           /* AMA Flags */
        char didtdd;                    /* flag to say its done it once */
        struct tdd_state *tdd;          /* TDD flag */
-       int reallinear;
-       int pseudolinear;
        int adsi;
        int cancallforward;
        char call_forward[AST_MAX_EXTENSION];
        char mailbox[AST_MAX_EXTENSION];
+       int onhooktime;
+       int msgstate;
        
        int confirmanswer;              /* Wait for '#' to confirm answer */
        int distinctivering;    /* Which distinctivering to use */
        int cidrings;                   /* Which ring to deliver CID on */
        
+       int faxhandled;                 /* Has a fax tone already been handled? */
+       
        char mate;                      /* flag to say its in MATE mode */
+       int pulsedial;          /* whether a pulse dial phone is detected */
+       int dtmfrelax;          /* whether to run in relaxed DTMF mode */
 #ifdef ZAPATA_PRI
        struct zt_pri *pri;
        q931_call *call;
        int isidlecall;
        int resetting;
+       int prioffset;
+       int alreadyhungup;
+#endif 
+#ifdef ZAPATA_R2
+       int r2prot;
+       mfcr2_t *r2;
+       int hasr2call;
+       int r2blocked;
+       int sigchecked;
 #endif 
 } *iflist = NULL;
 
@@ -371,13 +458,13 @@ static int cidrings[] = {
 
 #define NUM_CADENCE (sizeof(cadences) / sizeof(cadences[0]))
 
-#define INTHREEWAY(p) ((p->normalindex > -1) && (p->thirdcallindex > -1) && \
-               (p->owner == p->owners[p->normalindex]))
-
 #define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \
-                       (p->sig == SIG_FXSGS))
+                       (p->sig == SIG_FXSGS) || (p->sig == SIG_PRI))
 
+#define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & SIG_EM) /* || (p->sig & __ZT_SIG_FXO) */)
+#define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & SIG_EM) /* || (p->sig & __ZT_SIG_FXO) */)
 
+#if 0
 /* return non-zero if clear dtmf is appropriate */
 static int CLEARDTMF(struct ast_channel *chan) {
 struct zt_pvt *p = chan->pvt->pvt,*themp;
@@ -401,57 +488,168 @@ struct ast_channel *them;
        if (them->bridge->pvt->bridge != zt_bridge) return 0;
        return 1; /* okay, I guess we are okay to be clear */
 }
+#endif
 
-static int alloc_pseudo(struct zt_pvt *p)
+static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok)
 {
-       int x;
-       ZAP *z;
        int res;
-       ZT_BUFFERINFO bi;
-       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;
+       if (p->subs[0].owner == ast)
+               res = 0;
+       else if (p->subs[1].owner == ast)
+               res = 1;
+       else if (p->subs[2].owner == 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 void swap_subs(struct zt_pvt *p, int a, int b)
+{
+       int tchan;
+       int tinthreeway;
+       struct ast_channel *towner;
+
+       ast_log(LOG_DEBUG, "Swapping %d and %d\n", a, b);
+
+       tchan = p->subs[a].chan;
+       towner = p->subs[a].owner;
+       tinthreeway = p->subs[a].inthreeway;
+
+       p->subs[a].chan = p->subs[b].chan;
+       p->subs[a].owner = p->subs[b].owner;
+       p->subs[a].inthreeway = p->subs[b].inthreeway;
+
+       p->subs[b].chan = tchan;
+       p->subs[b].owner = towner;
+       p->subs[b].inthreeway = tinthreeway;
+
+       if (p->subs[a].owner)
+               p->subs[a].owner->fds[0] = p->subs[a].zfd;
+       if (p->subs[b].owner)
+               p->subs[b].owner->fds[0] = p->subs[b].zfd;
+       
+}
+
+static int zt_open(char *fn)
+{
+       int fd;
+       int isnum;
+       int chan = 0;
+       int bs;
+       int x;
+       isnum = 1;
+       for (x=0;x<strlen(fn);x++) {
+               if (!isdigit(fn[x])) {
+                       isnum = 0;
+                       break;
+               }
+       }
+       if (isnum) {
+               chan = atoi(fn);
+               if (chan < 1) {
+                       ast_log(LOG_WARNING, "Invalid channel number '%s'\n", fn);
+                       return -1;
+               }
+               fn = "/dev/zap/channel";
        }
-       z = zap_open("/dev/zap/pseudo", 1);
-       if (!z) {
-               ast_log(LOG_WARNING, "Unable to open /dev/zap/pseudo: %s\n", strerror(errno));
+       fd = open(fn, O_RDWR | O_NONBLOCK);
+       if (fd < 0) {
+               ast_log(LOG_WARNING, "Unable to open '%s': %s\n", fn, strerror(errno));
                return -1;
-       } else {
-               res = ioctl(zap_fd(z), ZT_GET_BUFINFO, &bi);
-               if (!res) {
-                       bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
-                       bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
-                       bi.numbufs = 4;
-                       res = ioctl(zap_fd(z), ZT_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;
-               if (ioctl(zap_fd(z), ZT_CHANNO, &x) == 1) {
-                       ast_log(LOG_WARNING,"Unable to get channel number for pseudo channel on FD %d\n",zap_fd(z));
+       }
+       if (chan) {
+               if (ioctl(fd, ZT_SPECIFY, &chan)) {
+                       x = errno;
+                       close(fd);
+                       errno = x;
+                       ast_log(LOG_WARNING, "Unable to specify channel %d: %s\n", chan, strerror(errno));
                        return -1;
                }
-               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 */
+       bs = READ_SIZE;
+       if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs) == -1) return -1;
+       return fd;
+}
+
+static void zt_close(int fd)
+{
+       close(fd);
+}
+
+int zt_setlinear(int zfd, int linear)
+{
+       int res;
+       res = ioctl(zfd, ZT_SETLINEAR, &linear);
+       if (res)
+               return res;
        return 0;
 }
 
-static int unalloc_pseudo(struct zt_pvt *p)
+
+int zt_setlaw(int zfd, int law)
 {
-       if (p->pseudo)
-               zap_close(p->pseudo);
-       if (option_debug)
-               ast_log(LOG_DEBUG, "Released pseudo channel %d\n", p->pseudochan);
-       p->pseudolinear = 0;
-       p->pseudo = NULL;
-       p->pseudochan = 0;
+       int res;
+       res = ioctl(zfd, ZT_SETLAW, &law);
+       if (res)
+               return res;
+       return 0;
+}
+
+static int alloc_sub(struct zt_pvt *p, int x)
+{
+       ZT_BUFFERINFO bi;
+       int res;
+       if (p->subs[x].zfd < 0) {
+               p->subs[x].zfd = zt_open("/dev/zap/pseudo");
+               if (p->subs[x].zfd > -1) {
+                       res = ioctl(p->subs[x].zfd, ZT_GET_BUFINFO, &bi);
+                       if (!res) {
+                               bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+                               bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+                               bi.numbufs = 4;
+                               res = ioctl(p->subs[x].zfd, ZT_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);
+                       if (ioctl(p->subs[x].zfd, ZT_CHANNO, &p->subs[x].chan) == 1) {
+                               ast_log(LOG_WARNING,"Unable to get channel number for pseudo channel on FD %d\n",p->subs[x].zfd);
+                               zt_close(p->subs[x].zfd);
+                               p->subs[x].zfd = -1;
+                               return -1;
+                       }
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Allocated %s subchannel on FD %d channel %d\n", subnames[x], p->subs[x].zfd, p->subs[x].chan);
+                       return 0;
+               } else
+                       ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+               return -1;
+       }
+       ast_log(LOG_WARNING, "%s subchannel of %d already in use\n", subnames[x], p->channel);
+       return -1;
+}
+
+static int unalloc_sub(struct zt_pvt *p, int x)
+{
+       if (!x) {
+               ast_log(LOG_WARNING, "Trying to unalloc the real channel %d?!?\n", p->channel);
+               return -1;
+       }
+       ast_log(LOG_DEBUG, "Released sub %d of channel %d\n", x, p->channel);
+       if (p->subs[x].zfd > -1) {
+               zt_close(p->subs[x].zfd);
+       }
+       p->subs[x].zfd = -1;
+       p->subs[x].linear = 0;
+       p->subs[x].chan = 0;
+       p->subs[x].owner = NULL;
+       p->subs[x].inthreeway = 0;
+       p->subs[x].curconfno = -1;
        return 0;
 }
 
@@ -459,16 +657,21 @@ static int zt_digit(struct ast_channel *ast, char digit)
 {
        ZT_DIAL_OPERATION zo;
        struct zt_pvt *p;
-       int res;
-       zo.op = ZT_DIAL_OP_APPEND;
-       zo.dialstr[0] = 'T';
-       zo.dialstr[1] = digit;
-       zo.dialstr[2] = 0;
+       int res = 0;
+       int index;
        p = ast->pvt->pvt;
-       if ((res = ioctl(zap_fd(p->z), ZT_DIAL, &zo)))
-               ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit);
-       else
-               p->dialing = 1;
+
+       index = zt_get_index(ast, p, 0);
+       if (index == SUB_REAL) {
+               zo.op = ZT_DIAL_OP_APPEND;
+               zo.dialstr[0] = 'T';
+               zo.dialstr[1] = digit;
+               zo.dialstr[2] = 0;
+               if ((res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &zo)))
+                       ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit);
+               else
+                       p->dialing = 1;
+       }
        
        return res;
 }
@@ -486,18 +689,36 @@ static char *events[] = {
                "Dial Complete",
                "Ringer On",
                "Ringer Off",
-               "Hook Transition Complete"
+               "Hook Transition Complete",
+               "Bits Changed",
+               "Pulse Start"
 };
  
 static char *event2str(int event)
 {
         static char buf[256];
-        if ((event < 13) && (event > -1))
+        if ((event < 15) && (event > -1))
                 return events[event];
         sprintf(buf, "Event %d", event);
         return buf;
 }
 
+#ifdef ZAPATA_R2
+static int str2r2prot(char *swtype)
+{
+    if (!strcasecmp(swtype, "ar"))
+        return MFCR2_PROT_ARGENTINA;
+    /*endif*/
+    if (!strcasecmp(swtype, "cn"))
+        return MFCR2_PROT_CHINA;
+    /*endif*/
+    if (!strcasecmp(swtype, "kr"))
+        return MFCR2_PROT_KOREA;
+    /*endif*/
+    return -1;
+}
+#endif
+
 static char *sig2str(int sig)
 {
        static char buf[256];
@@ -526,6 +747,8 @@ static char *sig2str(int sig)
                return "FXO Kewlstart";
        case SIG_PRI:
                return "PRI Signalling";
+       case SIG_R2:
+               return "R2 Signalling";
        case 0:
                return "Pseudo Signalling";
        default:
@@ -534,92 +757,96 @@ static char *sig2str(int sig)
        }
 }
 
-static int conf_set(struct zt_pvt *p, int req, int force)
+static int conf_add(int *confno, struct zt_subchannel *c, int index)
 {
-
-       /* Set channel to given conference, -1 to allocate one */
-       ZT_CONFINFO ci;
-       ZT_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), ZT_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, %x) we didn't create (though we did make %d) (req = %d)\n", p->channel, ci.confno, ci.confmode, p->confno, req);
-               return -1;
-       }
-       ci.chan = 0;
-       ci.confno = req;
-       ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER | ZT_CONF_PSEUDO_LISTENER | ZT_CONF_PSEUDO_TALKER; 
-       res = ioctl(zap_fd(p->z), ZT_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));
+       /* If the conference already exists, and we're already in it
+          don't bother doing anything */
+       ZT_CONFINFO zi;
+       if ((*confno > 0) && (c->curconfno == *confno))
+               return 0; 
+       if (c->curconfno > 0) {
+               ast_log(LOG_WARNING, "Subchannel %d is already in conference %d, moving to %d\n", c->zfd, c->curconfno, *confno);
+       }
+       if (c->zfd < 0)
+               return 0;
+       memset(&zi, 0, sizeof(zi));
+       zi.chan = 0;
+       zi.confno = *confno;
+       if (!index) {
+               /* Real-side and pseudo-side both participate in conference */
+               zi.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER |
+                                                       ZT_CONF_PSEUDO_TALKER | ZT_CONF_PSEUDO_LISTENER;
+       } else
+               zi.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
+       if (ioctl(c->zfd, ZT_SETCONF, &zi)) {
+               ast_log(LOG_WARNING, "Failed to add %d to conference %d\n", c->zfd, *confno);
                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 = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
-               
-               res = ioctl(zap_fd(p->pseudo), ZT_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 = 0;
-                       cip.confmode = ZT_CONF_NORMAL;
-                       res = ioctl(zap_fd(p->pseudo), ZT_SETCONF, &cip);
-                       if (res < 0) {
-                               ast_log(LOG_WARNING, "Failed to set conference info on pseudo channel %d (mode %08x, conf %d): %s\n",
-                                       p->pseudochan, cip.confno, cip.confmode, strerror(errno));
-                               return -1;
-                       }
-               }
-       }
-       p->confno = ci.confno;
+       c->curconfno = zi.confno;
+       *confno = zi.confno;
+       ast_log(LOG_DEBUG, "Added %d to conference %d\n", c->zfd, *confno);
        return 0;
 }
 
-static int three_way(struct zt_pvt *p)
+static int conf_del(int *confno, struct zt_subchannel *c, int index)
 {
-       ast_log(LOG_DEBUG, "Setting up three way call\n");
-       return conf_set(p, p->confno, 0);
+       ZT_CONFINFO zi;
+               /* Can't delete from this conference if it's not 0 */
+       if ((*confno < 1) ||
+               /* Can't delete if there's no zfd */
+               (c->zfd < 0) ||
+               /* Don't delete from the conference if it's not our conference */
+               (*confno != c->curconfno) 
+               /* Don't delete if we don't think it's conferenced at all (implied) */
+               ) return 0;
+       memset(&zi, 0, sizeof(zi));
+       zi.chan = 0;
+       zi.confno = 0;
+       zi.confmode = 0;
+       if (ioctl(c->zfd, ZT_SETCONF, &zi)) {
+               ast_log(LOG_WARNING, "Failed to drop %d from conference %d\n", c->zfd, *confno);
+               return -1;
+       }
+       c->curconfno = -1;
+       ast_log(LOG_DEBUG, "Removed %d from conference %d\n", c->zfd, *confno);
+       return 0;
 }
 
-static int conf_clear(struct zt_pvt *p)
+static int update_conf(struct zt_pvt *p)
 {
-       ZT_CONFINFO ci;
-       int res;
-       ci.confmode = ZT_CONF_NORMAL;
-       ci.chan = 0;
-       ci.confno = 0;
-       res = ioctl(zap_fd(p->z), ZT_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;
+       int needconf = 0;
+       int x;
+       /* Update conference state in a stateless fashion */
+       /* Start with the obvious, general stuff */
+       for (x=0;x<3;x++) {
+               if ((p->subs[x].zfd > -1) && p->subs[x].inthreeway) {
+                       conf_add(&p->confno, &p->subs[x], x);
+                       needconf++;
+               } else {
+                       conf_del(&p->confno, &p->subs[x], x);
+               }
        }
-       p->confno = -1;
+       /* If we have a slave, add him to our conference now */
+       for (x=0;x<MAX_SLAVES;x++) {
+               if (p->slaves[x]) {
+                       conf_add(&p->confno, &p->slaves[x]->subs[SUB_REAL], SUB_REAL);
+                       needconf++;
+               }
+       }
+       /* If we're supposed to be in there, do so now */
+       if (p->inconference && !p->subs[SUB_REAL].inthreeway) {
+               conf_add(&p->confno, &p->subs[SUB_REAL], SUB_REAL);
+               needconf++;
+       }
+       /* If we have a master, add ourselves to his conference */
+       if (p->master) 
+               conf_add(&p->master->confno, &p->subs[SUB_REAL], SUB_REAL);
+       if (!needconf) {
+               /* Nobody is left (or should be left) in our conference.  
+                  Kill it.  */
+               p->confno = -1;
+       }
+       ast_log(LOG_DEBUG, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
        return 0;
 }
 
@@ -627,13 +854,19 @@ static void zt_enable_ec(struct zt_pvt *p)
 {
        int x;
        int res;
+       if (p->echocanon) {
+               ast_log(LOG_DEBUG, "Echo cancellation already on\n");
+               return;
+       }
        if (p && p->echocancel) {
                x = p->echocancel;
-               res = ioctl(zap_fd(p->z), ZT_ECHOCANCEL, &x);
+               res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOCANCEL, &x);
                if (res) 
                        ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d\n", p->channel);
-               else
+               else {
+                       p->echocanon = 1;
                        ast_log(LOG_DEBUG, "Enabled echo cancellation on channel %d\n", p->channel);
+               }
        } else
                ast_log(LOG_DEBUG, "No echocancellation requested\n");
 }
@@ -644,32 +877,16 @@ static void zt_disable_ec(struct zt_pvt *p)
        int res;
        if (p->echocancel) {
                x = 0;
-               res = ioctl(zap_fd(p->z), ZT_ECHOCANCEL, &x);
+               res = ioctl(p->subs[SUB_REAL].zfd, ZT_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);
        }
+       p->echocanon = 0;
 }
 
-static int zt_get_index(struct ast_channel *ast, struct zt_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;
-}
-
-int set_actual_gain(int fd, int chan, float rxgain, float txgain)
+int set_actual_gain(int fd, int chan, float rxgain, float txgain, int law)
 {
        struct  zt_gains g;
        float ltxgain;
@@ -680,18 +897,29 @@ int set_actual_gain(int fd, int chan, float rxgain, float txgain)
        ltxgain = pow(10.0,txgain / 20.0);
          /* caluculate linear value of rx gain */
        lrxgain = pow(10.0,rxgain / 20.0);
-       for (j=0;j<256;j++) {
-               /* XXX Fix for A-law XXX */
-               k = (int)(((float)AST_MULAW(j)) * lrxgain);
-               if (k > 32767) k = 32767;
-               if (k < -32767) k = -32767;
-               g.rxgain[j] = AST_LIN2MU(k);
-               k = (int)(((float)AST_MULAW(j)) * ltxgain);
-               if (k > 32767) k = 32767;
-               if (k < -32767) k = -32767;
-               g.txgain[j] = AST_LIN2MU(k);
+       if (law == ZT_LAW_ALAW) {
+               for (j=0;j<256;j++) {
+                       k = (int)(((float)AST_ALAW(j)) * lrxgain);
+                       if (k > 32767) k = 32767;
+                       if (k < -32767) k = -32767;
+                       g.rxgain[j] = AST_LIN2A(k);
+                       k = (int)(((float)AST_ALAW(j)) * ltxgain);
+                       if (k > 32767) k = 32767;
+                       if (k < -32767) k = -32767;
+                       g.txgain[j] = AST_LIN2A(k);
+               }
+       } else {
+               for (j=0;j<256;j++) {
+                       k = (int)(((float)AST_MULAW(j)) * lrxgain);
+                       if (k > 32767) k = 32767;
+                       if (k < -32767) k = -32767;
+                       g.rxgain[j] = AST_LIN2MU(k);
+                       k = (int)(((float)AST_MULAW(j)) * ltxgain);
+                       if (k > 32767) k = 32767;
+                       if (k < -32767) k = -32767;
+                       g.txgain[j] = AST_LIN2MU(k);
+               }
        }
-               
          /* set 'em */
        return(ioctl(fd,ZT_SETGAINS,&g));
 }
@@ -702,7 +930,20 @@ static inline int zt_set_hook(int fd, int hs)
        x = hs;
        res = ioctl(fd, ZT_HOOK, &x);
        if (res < 0) 
+       {
+               if (errno == EINPROGRESS) return 0;
                ast_log(LOG_WARNING, "zt hook failed: %s\n", strerror(errno));
+       }
+       return res;
+}
+
+static inline int zt_confmute(struct zt_pvt *p, int muted)
+{
+       int x, res;
+       x = muted;
+       res = ioctl(p->subs[SUB_REAL].zfd, ZT_CONFMUTE, &x);
+       if (res < 0) 
+               ast_log(LOG_WARNING, "zt confmute(%d) failed on channel %d: %s\n", muted, p->channel, strerror(errno));
        return res;
 }
 
@@ -710,55 +951,25 @@ static int save_conference(struct zt_pvt *p)
 {
        struct zt_confinfo c;
        int res;
-       if (p->conf.confmode) {
+       if (p->saveconf.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), ZT_GETCONF, &p->conf);
+       p->saveconf.chan = 0;
+       res = ioctl(p->subs[SUB_REAL].zfd, ZT_GETCONF, &p->saveconf);
        if (res) {
                ast_log(LOG_WARNING, "Unable to get conference info: %s\n", strerror(errno));
-               p->conf.confmode = 0;
+               p->saveconf.confmode = 0;
                return -1;
        }
        c.chan = 0;
        c.confno = 0;
        c.confmode = ZT_CONF_NORMAL;
-       res = ioctl(zap_fd(p->z), ZT_SETCONF, &c);
+       res = ioctl(p->subs[SUB_REAL].zfd, ZT_SETCONF, &c);
        if (res) {
                ast_log(LOG_WARNING, "Unable to set conference info: %s\n", strerror(errno));
                return -1;
        }
-       switch(p->conf.confmode) {
-       case ZT_CONF_NORMAL:
-               p->conf2.confmode = 0;
-               break;
-       case ZT_CONF_MONITOR:
-               /* Get the other size */
-               p->conf2.chan = p->conf.confno;
-               res = ioctl(zap_fd(p->z), ZT_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 = ZT_CONF_NORMAL;
-               res = ioctl(zap_fd(p->z), ZT_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 ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER:
-               p->conf2.confmode = 0;
-               break;
-       default:
-               ast_log(LOG_WARNING, "Don't know how to save conference state for conf mode %08x\n", p->conf.confmode);
-               return -1;
-       }
        if (option_debug)
                ast_log(LOG_DEBUG, "Disabled conferencing\n");
        return 0;
@@ -767,21 +978,13 @@ static int save_conference(struct zt_pvt *p)
 static int restore_conference(struct zt_pvt *p)
 {
        int res;
-       if (p->conf.confmode) {
-               res = ioctl(zap_fd(p->z), ZT_SETCONF, &p->conf);
-               p->conf.confmode = 0;
+       if (p->saveconf.confmode) {
+               res = ioctl(p->subs[SUB_REAL].zfd, ZT_SETCONF, &p->saveconf);
+               p->saveconf.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), ZT_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");
@@ -793,10 +996,11 @@ static int send_callerid(struct zt_pvt *p);
 int send_cwcidspill(struct zt_pvt *p)
 {
        p->callwaitcas = 0;
+       p->cidcwexpire = 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);
+               p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, p->callwaitcid, AST_LAW(p));
                /* Make sure we account for the end */
                p->cidlen += READ_SIZE * 4;
                p->cidpos = 0;
@@ -809,33 +1013,21 @@ int send_cwcidspill(struct zt_pvt *p)
 
 static int has_voicemail(struct zt_pvt *p)
 {
-       DIR *dir;
-       struct dirent *de;
-       char fn[256];
 
-       /* If no mailbox, return immediately */
-       if (!strlen(p->mailbox))
-               return 0;
-       snprintf(fn, sizeof(fn), "%s/vm/%s/INBOX", AST_SPOOL_DIR, p->mailbox);
-       dir = opendir(fn);
-       if (!dir)
-               return 0;
-       while ((de = readdir(dir))) {
-               if (!strncasecmp(de->d_name, "msg", 3))
-                       break;
-       }
-       closedir(dir);
-       if (de)
-               return 1;
-       return 0;
+       return ast_app_has_voicemail(p->mailbox);
 }
 
 static int send_callerid(struct zt_pvt *p)
 {
        /* Assumes spill in p->cidspill, p->cidlen in length and we're p->cidpos into it */
        int res;
+       /* Take out of linear mode if necessary */
+       if (p->subs[SUB_REAL].linear) {
+               p->subs[SUB_REAL].linear = 0;
+               zt_setlinear(p->subs[SUB_REAL].zfd, 0);
+       }
        while(p->cidpos < p->cidlen) {
-               res = write(zap_fd(p->z), p->cidspill + p->cidpos, p->cidlen - p->cidpos);
+               res = write(p->subs[SUB_REAL].zfd, p->cidspill + p->cidpos, p->cidlen - p->cidpos);
                if (res < 0) {
                        if (errno == EAGAIN)
                                return 0;
@@ -849,23 +1041,10 @@ static int send_callerid(struct zt_pvt *p)
                p->cidpos += res;
        }
        free(p->cidspill);
-       p->cidspill = 0;
+       p->cidspill = NULL;
        if (p->callwaitcas) {
-               zap_clrdtmfn(p->z);
-               /* Check for a the ack on the CAS (up to 500 ms) */
-               res = zap_getdtmf(p->z, 1, NULL, 0, 500, 500, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
-               if (res > 0) {
-                       char tmp[2];
-                       strncpy(tmp, zap_dtmfbuf(p->z), sizeof(tmp)-1);
-                       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);
-               }
+               /* Wait for CID/CW to expire */
+               p->cidcwexpire = CIDCW_EXPIRE_SAMPLES;
        } else
                restore_conference(p);
        return 0;
@@ -885,11 +1064,11 @@ static int zt_callwait(struct ast_channel *ast)
                /* Silence */
                memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4);
                if (!p->callwaitrings && p->callwaitingcallerid) {
-                       ast_gen_cas(p->cidspill, 1, 2400 + 680);
+                       ast_gen_cas(p->cidspill, 1, 2400 + 680, AST_LAW(p));
                        p->callwaitcas = 1;
                        p->cidlen = 2400 + 680 + READ_SIZE * 4;
                } else {
-                       ast_gen_cas(p->cidspill, 1, 2400);
+                       ast_gen_cas(p->cidspill, 1, 2400, AST_LAW(p));
                        p->callwaitcas = 0;
                        p->cidlen = 2400 + READ_SIZE * 4;
                }
@@ -902,17 +1081,32 @@ static int zt_callwait(struct ast_channel *ast)
        return 0;
 }
 
-static int zt_call(struct ast_channel *ast, char *dest, int timeout)
+static int zt_call(struct ast_channel *ast, char *rdest, int timeout)
 {
        struct zt_pvt *p = ast->pvt->pvt;
        int x, res, index;
        char *c, *n, *l;
+       char *s;
        char callerid[256];
+       char dest[256];
+       strncpy(dest, rdest, sizeof(dest) - 1);
        if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
                ast_log(LOG_WARNING, "zt_call called on %s, neither down nor reserved\n", ast->name);
                return -1;
        }
        p->dialednone = 0;
+       if (p->radio)  /* if a radio channel, up immediately */
+       {
+               /* Special pseudo -- automatically up */
+               ast_setstate(ast, AST_STATE_UP); 
+               return 0;
+       }
+       x = ZT_FLUSH_READ | ZT_FLUSH_WRITE;
+       res = ioctl(p->subs[SUB_REAL].zfd, ZT_FLUSH, &x);
+       if (res)
+               ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", p->channel);
+       p->outgoing = 1;
+
        switch(p->sig) {
        case SIG_FXOLS:
        case SIG_FXOGS:
@@ -931,7 +1125,7 @@ static int zt_call(struct ast_channel *ast, char *dest, int timeout)
                                p->cidspill = malloc(MAX_CALLERID_SIZE);
                                p->callwaitcas = 0;
                                if (p->cidspill) {
-                                       p->cidlen = ast_callerid_generate(p->cidspill, ast->callerid);
+                                       p->cidlen = ast_callerid_generate(p->cidspill, ast->callerid, AST_LAW(p));
                                        p->cidpos = 0;
                                        send_callerid(p);
                                } else
@@ -939,16 +1133,16 @@ static int zt_call(struct ast_channel *ast, char *dest, int timeout)
                        }
                        /* Select proper cadence */
                        if ((p->distinctivering > 0) && (p->distinctivering <= NUM_CADENCE)) {
-                               if (ioctl(zap_fd(p->z), ZT_SETCADENCE, &cadences[p->distinctivering-1]))
+                               if (ioctl(p->subs[SUB_REAL].zfd, ZT_SETCADENCE, &cadences[p->distinctivering-1]))
                                        ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s'\n", p->distinctivering, ast->name);
                                p->cidrings = cidrings[p->distinctivering - 1];
                        } else {
-                               if (ioctl(zap_fd(p->z), ZT_SETCADENCE, NULL))
+                               if (ioctl(p->subs[SUB_REAL].zfd, ZT_SETCADENCE, NULL))
                                        ast_log(LOG_WARNING, "Unable to reset default ring on '%s'\n", ast->name);
                                p->cidrings = 1;
                        }
                        x = ZT_RING;
-                       if (ioctl(zap_fd(p->z), ZT_HOOK, &x) && (errno != EINPROGRESS)) {
+                       if (ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x) && (errno != EINPROGRESS)) {
                                ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
                                return -1;
                        }
@@ -963,12 +1157,29 @@ static int zt_call(struct ast_channel *ast, char *dest, int timeout)
                        /* Call waiting tone instead */
                        if (zt_callwait(ast))
                                return -1;
+                       /* Make ring-back */
+                       if (tone_zone_play_tone(p->subs[SUB_CALLWAIT].zfd, ZT_TONE_RINGTONE))
+                               ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name);
                                
                }
+               if (ast->callerid) 
+                       strncpy(callerid, ast->callerid, sizeof(callerid)-1);
+               else
+                       strcpy(callerid, "");
+               ast_callerid_parse(callerid, &n, &l);
+               if (l) {
+                       ast_shrink_phone_number(l);
+                       if (!ast_isphonenumber(l))
+                               l = NULL;
+               }
+               if (l)
+                       strcpy(p->lastcallerid, l);
+               else
+                       strcpy(p->lastcallerid, "");
                ast_setstate(ast, AST_STATE_RINGING);
                index = zt_get_index(ast, p, 0);
                if (index > -1) {
-                       p->needringing[index] = 1;
+                       p->subs[index].needringing = 1;
                }
                break;
        case SIG_FXSLS:
@@ -990,7 +1201,7 @@ static int zt_call(struct ast_channel *ast, char *dest, int timeout)
                }
                x = ZT_START;
                /* Start the trunk */
-               res = ioctl(zap_fd(p->z), ZT_HOOK, &x);
+               res = ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
                if (res < 0) {
                        if (errno != EINPROGRESS) {
                                ast_log(LOG_WARNING, "Unable to start channel: %s\n", strerror(errno));
@@ -1036,9 +1247,9 @@ static int zt_call(struct ast_channel *ast, char *dest, int timeout)
                } else 
                        snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%s", c + p->stripmsd);
                if (!res) {
-                       if (ioctl(zap_fd(p->z), ZT_DIAL, &p->dop)) {
+                       if (ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop)) {
                                x = ZT_ONHOOK;
-                               ioctl(zap_fd(p->z), ZT_HOOK, &x);
+                               ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
                                ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(errno));
                                return -1;
                        }
@@ -1069,14 +1280,23 @@ static int zt_call(struct ast_channel *ast, char *dest, int timeout)
                        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) % p->pri->channels) + 1, p->pri->nodetype == PRI_NETWORK ? 0 : 1, 1, l, PRI_NATIONAL_ISDN, 
+               p->dop.op = ZT_DIAL_OP_REPLACE;
+               s = strchr(c + p->stripmsd, 'w');
+               if (s) {
+                       snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%s", s);
+                       *s = '\0';
+               } else {
+                       strcpy(p->dop.dialstr, "");
+               }
+               if (pri_call(p->pri->pri, p->call, p->digital ? PRI_TRANS_CAP_DIGITAL : PRI_TRANS_CAP_SPEECH, 
+                       p->prioffset, p->pri->nodetype == PRI_NETWORK ? 0 : 1, 1, l, p->pri->dialplan - 1, n,
                        l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE,
-                       c + p->stripmsd, PRI_NATIONAL_ISDN, 
+                       c + p->stripmsd, p->pri->dialplan - 1, 
                        ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW))) {
                        ast_log(LOG_WARNING, "Unable to setup call to %s\n", c + p->stripmsd);
                        return -1;
                }
+               ast_setstate(ast, AST_STATE_DIALING);
                break;
 #endif         
        case 0:
@@ -1094,7 +1314,6 @@ static int destroy_channel(struct zt_pvt *prev, struct zt_pvt *cur, int now)
 {
        int owned = 0;
        int i = 0;
-       int res = 0;
 
        if (!now) {
                if (cur->owner) {
@@ -1102,7 +1321,7 @@ static int destroy_channel(struct zt_pvt *prev, struct zt_pvt *cur, int now)
                }
 
                for (i = 0; i < 3; i++) {
-                       if (cur->owners[i]) {
+                       if (cur->subs[i].owner) {
                                owned = 1;
                        }
                }
@@ -1112,11 +1331,8 @@ static int destroy_channel(struct zt_pvt *prev, struct zt_pvt *cur, int now)
                        } else {
                                iflist = cur->next;
                        }
-                       res = zap_close(cur->z);
-                       if (res) {
-                               ast_log(LOG_ERROR, "Unable to close device on channel %d\n", cur->channel);
-                               free(cur);
-                               return -1;
+                       if (cur->subs[SUB_REAL].zfd > -1) {
+                               zt_close(cur->subs[SUB_REAL].zfd);
                        }
                        free(cur);
                }
@@ -1126,11 +1342,8 @@ static int destroy_channel(struct zt_pvt *prev, struct zt_pvt *cur, int now)
                } else {
                        iflist = cur->next;
                }
-               res = zap_close(cur->z);
-               if (res) {
-                       ast_log(LOG_ERROR, "Unable to close device on channel %d\n", cur->channel);
-                       free(cur);
-                       return -1;
+               if (cur->subs[SUB_REAL].zfd > -1) {
+                       zt_close(cur->subs[SUB_REAL].zfd);
                }
                free(cur);
        }
@@ -1157,44 +1370,99 @@ static int zt_hangup(struct ast_channel *ast)
        index = zt_get_index(ast, p, 1);
 
        restore_gains(p);
-       zap_digitmode(p->z,0);
+       
+       if (p->dsp)
+               ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);
 
-       ast_setstate(ast, AST_STATE_DOWN);
-       ast_log(LOG_DEBUG, "Hangup: index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
-               index, p->normalindex, p->callwaitindex, p->thirdcallindex);
+       x = 0;
+       zt_confmute(p, 0);
+
+       ast_log(LOG_DEBUG, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+               p->channel, index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd);
        p->ignoredtmf = 0;
        
        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->owner = p->owners[p->normalindex];
-                               p->thirdcallindex = -1;
-                               unalloc_pseudo(p);
+               p->subs[index].owner = NULL;
+               p->subs[index].needanswer = 0;
+               p->subs[index].needringing = 0;
+               p->subs[index].linear = 0;
+               p->subs[index].needcallerid = 0;
+               zt_setlinear(p->subs[index].zfd, 0);
+               if (index == SUB_REAL) {
+                       if ((p->subs[SUB_CALLWAIT].zfd > -1) && (p->subs[SUB_THREEWAY].zfd > -1)) {
+                               ast_log(LOG_DEBUG, "Normal call hung up with both three way call and a call waiting call in place?\n");
+                               if (p->subs[SUB_CALLWAIT].inthreeway) {
+                                       /* We had flipped over to answer a callwait and now it's gone */
+                                       ast_log(LOG_DEBUG, "We were flipped over to the callwait, moving back and unowning.\n");
+                                       /* Move to the call-wait, but un-own us until they flip back. */
+                                       swap_subs(p, SUB_CALLWAIT, SUB_REAL);
+                                       unalloc_sub(p, SUB_CALLWAIT);
+                                       p->owner = NULL;
+                               } else {
+                                       /* The three way hung up, but we still have a call wait */
+                                       ast_log(LOG_DEBUG, "We were in the threeway and have a callwait still.  Ditching the threeway.\n");
+                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                       unalloc_sub(p, SUB_THREEWAY);
+                                       if (p->subs[SUB_REAL].inthreeway) {
+                                               /* This was part of a three way call.  Immediately make way for
+                                                  another call */
+                                               ast_log(LOG_DEBUG, "Call was complete, setting owner to former third call\n");
+                                               p->owner = p->subs[SUB_REAL].owner;
+                                       } else {
+                                               /* This call hasn't been completed yet...  Set owner to NULL */
+                                               ast_log(LOG_DEBUG, "Call was incomplete, setting owner to NULL\n");
+                                               p->owner = NULL;
+                                       }
+                                       p->subs[SUB_REAL].inthreeway = 0;
+                               }
+                       } else if (p->subs[SUB_CALLWAIT].zfd > -1) {
+                               /* Move to the call-wait and switch back to them. */
+                               swap_subs(p, SUB_CALLWAIT, SUB_REAL);
+                               unalloc_sub(p, SUB_CALLWAIT);
+                               p->owner = p->subs[SUB_REAL].owner;
+                               if (p->subs[SUB_REAL].owner->bridge)
+                                       ast_moh_stop(p->subs[SUB_REAL].owner->bridge);
+                       } else if (p->subs[SUB_THREEWAY].zfd > -1) {
+                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                               unalloc_sub(p, SUB_THREEWAY);
+                               if (p->subs[SUB_REAL].inthreeway) {
+                                       /* This was part of a three way call.  Immediately make way for
+                                          another call */
+                                       ast_log(LOG_DEBUG, "Call was complete, setting owner to former third call\n");
+                                       p->owner = p->subs[SUB_REAL].owner;
+                               } else {
+                                       /* This call hasn't been completed yet...  Set owner to NULL */
+                                       ast_log(LOG_DEBUG, "Call was incomplete, setting owner to NULL\n");
+                                       p->owner = NULL;
+                               }
+                               p->subs[SUB_REAL].inthreeway = 0;
+                       }
+               } else if (index == SUB_CALLWAIT) {
+                       /* Ditch the holding callwait call, and immediately make it availabe */
+                       if (p->subs[SUB_CALLWAIT].inthreeway) {
+                               /* This is actually part of a three way, placed on hold.  Place the third part
+                                  on music on hold now */
+                               if (p->subs[SUB_THREEWAY].owner && p->subs[SUB_THREEWAY].owner->bridge)
+                                       ast_moh_start(p->subs[SUB_THREEWAY].owner->bridge, NULL);
+                               p->subs[SUB_THREEWAY].inthreeway = 0;
+                               /* Make it the call wait now */
+                               swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
+                               unalloc_sub(p, SUB_THREEWAY);
+                       } else
+                               unalloc_sub(p, SUB_CALLWAIT);
+               } else if (index == SUB_THREEWAY) {
+                       if (p->subs[SUB_CALLWAIT].inthreeway) {
+                               /* The other party of the three way call is currently in a call-wait state.
+                                  Start music on hold for them, and take the main guy out of the third call */
+                               if (p->subs[SUB_CALLWAIT].owner && p->subs[SUB_CALLWAIT].owner->bridge)
+                                       ast_moh_start(p->subs[SUB_CALLWAIT].owner->bridge, NULL);
+                               p->subs[SUB_CALLWAIT].inthreeway = 0;
                        }
-               } else if (index == p->callwaitindex) {
-                       /* If this was a call waiting call, mark the call wait
-                          index as -1, so we know iavailable again */
-                       p->callwaitindex = -1;
-               } else if (index == p->thirdcallindex) {
+                       p->subs[SUB_REAL].inthreeway = 0;
                        /* If this was part of a three way call index, let us make
                           another three way call */
-                       p->thirdcallindex = -1;
-                       unalloc_pseudo(p);
+                       unalloc_sub(p, SUB_THREEWAY);
                } 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");
@@ -1202,16 +1470,23 @@ static int zt_hangup(struct ast_channel *ast)
        }
 
 
-       if (!p->owners[0] && !p->owners[1] && !p->owners[2]) {
+       if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) {
                p->owner = NULL;
                p->ringt = 0;
                p->distinctivering = 0;
                p->confirmanswer = 0;
                p->cidrings = 1;
+               p->outgoing = 0;
+               p->digital = 0;
+               p->faxhandled = 0;
+               p->pulsedial = 0;
+               p->onhooktime = time(NULL);
+               if (p->dsp) {
+                       ast_dsp_free(p->dsp);
+                       p->dsp = NULL;
+               }
                law = ZT_LAW_DEFAULT;
-               res = ioctl(zap_fd(p->z), ZT_SETLAW, &law);
-               p->reallinear = 0;
-               zap_setlinear(p->z, 0);
+               res = ioctl(p->subs[SUB_REAL].zfd, ZT_SETLAW, &law);
                if (res < 0) 
                        ast_log(LOG_WARNING, "Unable to set law on channel %d to default\n", p->channel);
                /* Perform low level hangup if no owner left */
@@ -1220,7 +1495,10 @@ static int zt_hangup(struct ast_channel *ast)
                        if (p->call) {
                                if (!pri_grab(p->pri)) {
                                        res = pri_disconnect(p->pri->pri, p->call, PRI_CAUSE_NORMAL_CLEARING);
-                                       p->call = NULL;
+                                       if (p->alreadyhungup) {
+                                               p->call = NULL;
+                                               p->alreadyhungup = 0;
+                                       }
                                        if (res < 0) 
                                                ast_log(LOG_WARNING, "pri_disconnect failed\n");
                                        pri_rel(p->pri);                        
@@ -1233,8 +1511,19 @@ static int zt_hangup(struct ast_channel *ast)
 
                } else 
 #endif
-               if (p->sig)
-                       res = zt_set_hook(zap_fd(p->z), ZT_ONHOOK);
+#ifdef ZAPATA_R2
+               if (p->sig == SIG_R2) {
+                       if (p->hasr2call) {
+                               mfcr2_DropCall(p->r2, NULL, UC_NORMAL_CLEARING);
+                               p->hasr2call = 0;
+                               res = 0;
+                       } else
+                               res = 0;
+
+               } else 
+#endif
+               if (p->sig)
+                       res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
                if (res < 0) {
                        ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
                        return -1;
@@ -1243,24 +1532,20 @@ static int zt_hangup(struct ast_channel *ast)
                case SIG_FXOGS:
                case SIG_FXOLS:
                case SIG_FXOKS:
-                       res = ioctl(zap_fd(p->z), ZT_GET_PARAMS, &par);
+                       res = ioctl(p->subs[SUB_REAL].zfd, ZT_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), ZT_TONE_CONGESTION);
+                                       tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
                                else
-                                       tone_zone_play_tone(zap_fd(p->z), -1);
+                                       tone_zone_play_tone(p->subs[SUB_REAL].zfd, -1);
                        }
                        break;
                default:
-                       tone_zone_play_tone(zap_fd(p->z), -1);
-               }
-               if (index > -1) {
-                       p->needringing[index] = 0;
-                       p->needanswer[index] = 0;
+                       tone_zone_play_tone(p->subs[SUB_REAL].zfd, -1);
                }
                if (p->cidspill)
                        free(p->cidspill);
@@ -1275,13 +1560,14 @@ static int zt_hangup(struct ast_channel *ast)
                p->callwaiting = p->permcallwaiting;
                p->hidecallerid = p->permhidecallerid;
                p->dialing = 0;
-               conf_clear(p);
-               unalloc_pseudo(p);
+               strcpy(p->rdnis, "");
+               update_conf(p);
                restart_monitor();
        }
 
 
        p->callwaitingrepeat = 0;
+       p->cidcwexpire = 0;
        ast->pvt->pvt = NULL;
        ast_setstate(ast, AST_STATE_DOWN);
        ast_pthread_mutex_lock(&usecnt_lock);
@@ -1315,7 +1601,15 @@ static int zt_answer(struct ast_channel *ast)
 {
        struct zt_pvt *p = ast->pvt->pvt;
        int res=0;
+       int index;
+       int oldstate = ast->_state;
        ast_setstate(ast, AST_STATE_UP);
+       index = zt_get_index(ast, p, 0);
+       if (index < 0)
+               index = SUB_REAL;
+       /* nothing to do if a radio channel */
+       if (p->radio)
+               return 0;
        switch(p->sig) {
        case SIG_FXSLS:
        case SIG_FXSGS:
@@ -1332,11 +1626,17 @@ static int zt_answer(struct ast_channel *ast)
        case SIG_FXOKS:
                /* Pick up the line */
                ast_log(LOG_DEBUG, "Took %s off hook\n", ast->name);
-               res =  zt_set_hook(zap_fd(p->z), ZT_OFFHOOK);
-               tone_zone_play_tone(zap_fd(p->z), -1);
-               if (INTHREEWAY(p))
-                       tone_zone_play_tone(zap_fd(p->pseudo), -1);
+               res =  zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+               tone_zone_play_tone(p->subs[index].zfd, -1);
                p->dialing = 0;
+               if ((index == SUB_REAL) && p->subs[SUB_THREEWAY].inthreeway) {
+                       if (oldstate == AST_STATE_RINGING) {
+                               ast_log(LOG_DEBUG, "Finally swapping real and threeway\n");
+                               tone_zone_play_tone(p->subs[SUB_THREEWAY].zfd, -1);
+                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                               p->owner = p->subs[SUB_REAL].owner;
+                       }
+               }
                break;
 #ifdef ZAPATA_PRI
        case SIG_PRI:
@@ -1349,7 +1649,16 @@ static int zt_answer(struct ast_channel *ast)
                        res= -1;
                }
                break;
-#endif         
+#endif
+#ifdef ZAPATA_R2
+       case SIG_R2:
+               res = mfcr2_AnswerCall(p->r2, NULL);
+               if (res)
+                       ast_log(LOG_WARNING, "R2 Answer call failed :( on %s\n", ast->name);
+               break;
+#endif                 
+       case 0:
+               return 0;
        default:
                ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel);
                return -1;
@@ -1357,31 +1666,16 @@ static int zt_answer(struct ast_channel *ast)
        return res;
 }
 
-static inline int bridge_cleanup(struct zt_pvt *p0, struct zt_pvt *p1)
-{
-       int res = 0;
-       if (p0) {
-               res = conf_clear(p0);
-               if (!p0->echocanbridged)
-                       zt_enable_ec(p0);
-       }
-       if (p1) {
-               res |= conf_clear(p1);
-               if (!p1->echocanbridged)
-                       zt_enable_ec(p1);
-       }       
-       return res;
-}
-
 static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen)
 {
 char   *cp;
+int    x;
 
        struct zt_pvt *p = chan->pvt->pvt;
 
        
        if ((option != AST_OPTION_TONE_VERIFY) &&
-               (option != AST_OPTION_TDD))
+               (option != AST_OPTION_TDD) && (option != AST_OPTION_RELAXDTMF))
           {
                errno = ENOSYS;
                return -1;
@@ -1394,18 +1688,20 @@ char    *cp;
           }
        switch(option) {
            case AST_OPTION_TONE_VERIFY:
+               if (!p->dsp)
+                       break;
                switch(*cp) {
                    case 1:
-                       ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF(1) on %s\n",chan->name);
-                       zap_digitmode(p->z,ZAP_MUTECONF);  /* set mute mode if desired */
+                               ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF(1) on %s\n",chan->name);
+                               ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | p->dtmfrelax);  /* set mute mode if desired */
                        break;
                    case 2:
-                       ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF/MAX(2) on %s\n",chan->name);
-                       zap_digitmode(p->z,ZAP_MUTECONF | ZAP_MUTEMAX);  /* set mute mode if desired */
+                               ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: MUTECONF/MAX(2) on %s\n",chan->name);
+                               ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX | p->dtmfrelax);  /* set mute mode if desired */
                        break;
                    default:
-                       ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: OFF(0) on %s\n",chan->name);
-                       zap_digitmode(p->z,0);  /* set mute mode if desired */
+                               ast_log(LOG_DEBUG, "Set option TONE VERIFY, mode: OFF(0) on %s\n",chan->name);
+                               ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);  /* set mute mode if desired */
                        break;
                }
                break;
@@ -1426,15 +1722,18 @@ char    *cp;
                if (!p->didtdd) { /* if havent done it yet */
                        unsigned char mybuf[41000],*buf;
                        int size,res,fd,len;
+                       int index;
                        fd_set wfds,efds;
                        buf = mybuf;
                        memset(buf,0x7f,sizeof(mybuf)); /* set to silence */
                        ast_tdd_gen_ecdisa(buf + 16000,16000);  /* put in tone */
                        len = 40000;
-                       if (chan != p->owner)   /* if in three-way */
-                               fd = zap_fd(p->pseudo);
-                       else
-                               fd = zap_fd(p->z);
+                       index = zt_get_index(chan, p, 0);
+                       if (index < 0) {
+                               ast_log(LOG_WARNING, "No index in TDD?\n");
+                               return -1;
+                       }
+                       fd = p->subs[index].zfd;
                        while(len) {
                                if (ast_check_hangup(chan)) return -1;
                                size = len;
@@ -1476,218 +1775,332 @@ char  *cp;
                        p->tdd = tdd_new(); /* allocate one */
                }               
                break;
+           case AST_OPTION_RELAXDTMF:  /* Relax DTMF decoding (or not) */
+               if (!*cp)
+               {               
+                       ast_log(LOG_DEBUG, "Set option RELAX DTMF, value: OFF(0) on %s\n",chan->name);
+                       x = 0;
+               }
+               else
+               {               
+                       ast_log(LOG_DEBUG, "Set option RELAX DTMF, value: ON(1) on %s\n",chan->name);
+                       x = 1;
+               }
+               ast_dsp_digitmode(p->dsp,x ? DSP_DIGITMODE_RELAXDTMF : DSP_DIGITMODE_DTMF | p->dtmfrelax);
+               break;
        }
        errno = 0;
        return 0;
 }
 
+static void zt_unlink(struct zt_pvt *slave, struct zt_pvt *master)
+{
+       /* Unlink a specific slave or all slaves/masters from a given master */
+       int x;
+       int hasslaves;
+       if (!master)
+               return;
+       hasslaves = 0;
+       for (x=0;x<MAX_SLAVES;x++) {
+               if (master->slaves[x]) {
+                       if (!slave || (master->slaves[x] == slave)) {
+                               /* Take slave out of the conference */
+                               ast_log(LOG_DEBUG, "Unlinking slave %d from %d\n", master->slaves[x]->channel, master->channel);
+                               conf_del(&master->confno, &master->slaves[x]->subs[SUB_REAL], SUB_REAL);
+                               master->slaves[x]->master = NULL;
+                               master->slaves[x] = NULL;
+                       } else
+                               hasslaves = 1;
+               }
+               if (!hasslaves)
+                       master->inconference = 0;
+       }
+       if (!slave) {
+               if (master->master) {
+                       /* Take master out of the conference */
+                       conf_del(&master->master->confno, &master->subs[SUB_REAL], SUB_REAL);
+                       hasslaves = 0;
+                       for (x=0;x<MAX_SLAVES;x++) {
+                               if (master->master->slaves[x] == master)
+                                       master->master->slaves[x] = NULL;
+                               else if (master->master->slaves[x])
+                                       hasslaves = 1;
+                       }
+                       if (!hasslaves)
+                               master->master->inconference = 0;
+               }
+               master->master = NULL;
+       }
+       update_conf(master);
+}
+
+static void zt_link(struct zt_pvt *slave, struct zt_pvt *master) {
+       int x;
+       if (!slave || !master) {
+               ast_log(LOG_WARNING, "Tried to link to/from NULL??\n");
+               return;
+       }
+       for (x=0;x<MAX_SLAVES;x++) {
+               if (!master->slaves[x]) {
+                       master->slaves[x] = slave;
+                       break;
+               }
+       }
+       if (x >= MAX_SLAVES) {
+               ast_log(LOG_WARNING, "Replacing slave %d with new slave, %d\n", master->slaves[MAX_SLAVES - 1]->channel, slave->channel);
+               master->slaves[MAX_SLAVES - 1] = slave;
+       }
+       if (slave->master) 
+               ast_log(LOG_WARNING, "Replacing master %d with new master, %d\n", slave->master->channel, master->channel);
+       slave->master = master;
+       
+       ast_log(LOG_DEBUG, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x);
+}
+
 static int zt_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 */
-       struct zt_pvt *p0 = c0->pvt->pvt;
-       struct zt_pvt *p1 = c1->pvt->pvt;
        struct ast_channel *who = NULL, *cs[3];
+       struct zt_pvt *p0, *p1, *op0, *op1;
+       struct zt_pvt *master=NULL, *slave=NULL;
        struct ast_frame *f;
-       int to = -1;
-       int firstpass = 1;
-       
-       int confno = -1;
-       
-         /* if need DTMF, cant native bridge */
+       int to;
+       int inconf = 0;
+       int nothingok = 0;
+       int ofd1, ofd2;
+       int oi1, oi2, i1 = -1, i2 = -1, t1, t2;
+       int os1 = -1, os2 = -1;
+       struct ast_channel *oc1, *oc2;
+
+       /* if need DTMF, cant native bridge */
        if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
                return -2;
-         /* if cant run clear DTMF, cant native bridge */
+       p0 = c0->pvt->pvt;
+       p1 = c1->pvt->pvt;
+       /* cant do pseudo-channels here */
+       if ((!p0->sig) || (!p1->sig)) return -2;
+
        ast_pthread_mutex_lock(&c0->lock);
        ast_pthread_mutex_lock(&c1->lock);
-       if (!CLEARDTMF(c0) || !CLEARDTMF(c1)) {
-               pthread_mutex_unlock(&c1->lock);
-               pthread_mutex_unlock(&c0->lock);
+       op0 = p0 = c0->pvt->pvt;
+       op1 = p1 = c1->pvt->pvt;
+       ofd1 = c0->fds[0];
+       ofd2 = c1->fds[0];
+       oi1 = zt_get_index(c0, p0, 0);
+       oi2 = zt_get_index(c1, p1, 0);
+       oc1 = p0->owner;
+       oc2 = p1->owner;
+       if ((oi1 < 0) || (oi2 < 0))
+               return -1;
+
+
+
+       ast_pthread_mutex_lock(&p0->lock);
+       if (pthread_mutex_trylock(&p1->lock)) {
+               /* Don't block, due to potential for deadlock */
+               ast_pthread_mutex_unlock(&p0->lock);
+               ast_pthread_mutex_unlock(&c0->lock);
+               ast_pthread_mutex_unlock(&c1->lock);
+               ast_log(LOG_NOTICE, "Avoiding deadlock...\n");
                return -3;
        }
-       p0 = c0->pvt->pvt;
-       p1 = c1->pvt->pvt;
-       if (c0->type == type)
-               /* Stop any playing tones */
-               tone_zone_play_tone(zap_fd(p0->z),      -1);
-       if (c1->type == type)
-               tone_zone_play_tone(zap_fd(p1->z),      -1);
-       pthread_mutex_unlock(&c1->lock);
-       pthread_mutex_unlock(&c0->lock);
+       if ((oi1 == SUB_REAL) && (oi2 == SUB_REAL)) {
+               if (!p0->owner || !p1->owner) {
+                       /* Currently unowned -- Do nothing.  */
+                       nothingok = 1;
+               } else {
+                       /* If we don't have a call-wait in a 3-way, and we aren't in a 3-way, we can be master */
+                       if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) {
+                               master = p0;
+                               slave = p1;
+                               inconf = 1;
+                       } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) {
+                               master = p1;
+                               slave = p0;
+                               inconf = 1;
+                       } else {
+                               ast_log(LOG_WARNING, "Huh?  Both calls are callwaits or 3-ways?  That's clever...?\n");
+                               ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n", p0->channel, oi1, (p0->subs[SUB_CALLWAIT].zfd > -1) ? 1 : 0, p0->subs[SUB_REAL].inthreeway,
+                                               p0->channel, oi1, (p1->subs[SUB_CALLWAIT].zfd > -1) ? 1 : 0, p1->subs[SUB_REAL].inthreeway);
+                       }
+               }
+       } else if ((oi1 == SUB_REAL) && (oi2 == SUB_THREEWAY)) {
+               if (p1->subs[SUB_THREEWAY].inthreeway) {
+                       master = p1;
+                       slave = p0;
+               } else {
+                       nothingok = 1;
+               }
+       } else if ((oi1 == SUB_THREEWAY) && (oi2 == SUB_REAL)) {
+               if (p0->subs[SUB_THREEWAY].inthreeway) {
+                       master = p0;
+                       slave = p1;
+               } else {
+                       nothingok  = 1;
+               }
+       } else if ((oi1 == SUB_REAL) && (oi2 == SUB_CALLWAIT)) {
+               /* We have a real and a call wait.  If we're in a three way call, put us in it, otherwise, 
+                  don't put us in anything */
+               if (p1->subs[SUB_CALLWAIT].inthreeway) {
+                       master = p1;
+                       slave = p0;
+               } else {
+                       nothingok = 1;
+               }
+       } else if ((oi1 == SUB_CALLWAIT) && (oi2 == SUB_REAL)) {
+               /* Same as previous */
+               if (p0->subs[SUB_CALLWAIT].inthreeway) {
+                       master = p0;
+                       slave = p1;
+               } else {
+                       nothingok = 1;
+               }
+       }
+       ast_log(LOG_DEBUG, "master: %d, slave: %d, nothingok: %d\n",
+               master ? master->channel : 0, slave ? slave->channel : 0, nothingok);
+       if (master && slave) {
+               /* Stop any tones, or play ringtone as appropriate.  If they're bridged
+                  in an active threeway call with a channel that is ringing, we should
+                  indicate ringing. */
+               if ((oi2 == SUB_THREEWAY) && 
+                       p1->subs[SUB_THREEWAY].inthreeway && 
+                       p1->subs[SUB_REAL].owner && 
+                       p1->subs[SUB_REAL].inthreeway && 
+                       (p1->subs[SUB_REAL].owner->_state == AST_STATE_RINGING)) {
+                               ast_log(LOG_DEBUG, "Playing ringback on %s since %s is in a ringing three-way\n", c0->name, c1->name);
+                               tone_zone_play_tone(p0->subs[oi1].zfd, ZT_TONE_RINGTONE);
+                               os2 = p1->subs[SUB_REAL].owner->_state;
+               } else {
+                               ast_log(LOG_DEBUG, "Stoping tones on %d/%d talking to %d/%d\n", p0->channel, oi1, p1->channel, oi2);
+                               tone_zone_play_tone(p0->subs[oi1].zfd, -1);
+               }
+               if ((oi1 == SUB_THREEWAY) && 
+                       p0->subs[SUB_THREEWAY].inthreeway && 
+                       p0->subs[SUB_REAL].owner && 
+                       p0->subs[SUB_REAL].inthreeway && 
+                       (p0->subs[SUB_REAL].owner->_state == AST_STATE_RINGING)) {
+                               ast_log(LOG_DEBUG, "Playing ringback on %s since %s is in a ringing three-way\n", c1->name, c0->name);
+                               tone_zone_play_tone(p1->subs[oi2].zfd, ZT_TONE_RINGTONE);
+                               os1 = p0->subs[SUB_REAL].owner->_state;
+               } else {
+                               ast_log(LOG_DEBUG, "Stoping tones on %d/%d talking to %d/%d\n", p1->channel, oi2, p0->channel, oi1);
+                               tone_zone_play_tone(p1->subs[oi1].zfd, -1);
+               }
+               if ((oi1 == SUB_REAL) && (oi2 == SUB_REAL)) {
+                       if (!p0->echocanbridged || !p1->echocanbridged) {
+                               /* Disable echo cancellation if appropriate */
+                               zt_disable_ec(p0);
+                               zt_disable_ec(p1);
+                       }
+               }
+               zt_link(slave, master);
+               master->inconference = inconf;
+       } else if (!nothingok)
+               ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n", p0->channel, subnames[oi1], p1->channel, subnames[oi2]);
+
+       update_conf(p0);
+       update_conf(p1);
+       t1 = p0->subs[SUB_REAL].inthreeway;
+       t2 = p1->subs[SUB_REAL].inthreeway;
+
+       ast_pthread_mutex_unlock(&p0->lock);
+       ast_pthread_mutex_unlock(&p1->lock);
 
+       ast_pthread_mutex_unlock(&c0->lock);
+       ast_pthread_mutex_unlock(&c1->lock);
+
+       /* Native bridge failed */
+       if ((!master || !slave) && !nothingok) {
+               if (op0 == p0)
+                       zt_enable_ec(p0);
+               if (op1 == p1)
+                       zt_enable_ec(p1);
+               return -1;
+       }
+       
        cs[0] = c0;
        cs[1] = c1;
+       cs[2] = NULL;
        for (;;) {
+               /* Here's our main loop...  Start by locking things, looking for private parts, 
+                  and then balking if anything is wrong */
                ast_pthread_mutex_lock(&c0->lock);
                ast_pthread_mutex_lock(&c1->lock);
                p0 = c0->pvt->pvt;
                p1 = c1->pvt->pvt;
-
-               /* Stop if we're a zombie or need a soft hangup */
-               if (c0->zombie || ast_check_hangup(c0) || c1->zombie || ast_check_hangup(c1)) {
-                       *fo = NULL;
-                       if (who) *rc = who;
-                       bridge_cleanup(p0, p1);
-                       pthread_mutex_unlock(&c0->lock);
-                       pthread_mutex_unlock(&c1->lock);
-                       return 0;
-               }
-               if (!p0 || !p1 || (c0->type != type) || (c1->type != type)) {
-                       if (!firstpass) {
-                               if ((c0->type == type) && !p0->echocanbridged)
-                                       zt_enable_ec(p0);
-                               if ((c1->type == type) && !p1->echocanbridged)
-                                       zt_enable_ec(p1);
-                       }
-                       pthread_mutex_unlock(&c0->lock);
-                       pthread_mutex_unlock(&c1->lock);
-                       return -2;
-               }
-
-               if (firstpass) {
-                       /* Only do this once, turning off echo cancellation if this is a native bridge
-                          with bridged echo cancellation turned off */
-                       if (!p0->echocanbridged)
-                               zt_disable_ec(p0);
-                       if (!p1->echocanbridged)
-                               zt_disable_ec(p1);
-                       firstpass = 0;
-               }
-               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");
-                       if (!p0->echocanbridged)
+               if (op0 == p0)
+                       i1 = zt_get_index(c0, p0, 1);
+               if (op1 == p1)
+                       i2 = zt_get_index(c1, p1, 1);
+               ast_pthread_mutex_unlock(&c0->lock);
+               ast_pthread_mutex_unlock(&c1->lock);
+               if ((op0 != p0) || (op1 != p1) || 
+                   (ofd1 != c0->fds[0]) || 
+                       (ofd2 != c1->fds[0]) ||
+                   (p0->subs[SUB_REAL].owner && (os1 > -1) && (os1 != p0->subs[SUB_REAL].owner->_state)) || 
+                   (p1->subs[SUB_REAL].owner && (os2 > -1) && (os2 != p1->subs[SUB_REAL].owner->_state)) || 
+                   (oc1 != p0->owner) || 
+                       (oc2 != p1->owner) ||
+                       (t1 != p0->subs[SUB_REAL].inthreeway) ||
+                       (t2 != p1->subs[SUB_REAL].inthreeway) ||
+                       (oi1 != i1) ||
+                       (oi2 != i2)) {
+                       if (slave && master)
+                               zt_unlink(slave, master);
+                       ast_log(LOG_DEBUG, "Something changed out on %d/%d to %d/%d, returning -3 to restart\n",
+                                                                       op0->channel, oi1, op1->channel, oi2);
+                       if (op0 == p0)
                                zt_enable_ec(p0);
-                       if (!p1->echocanbridged)
+                       if (op1 == p1)
                                zt_enable_ec(p1);
-                       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);
-                                       if (!p0->echocanbridged)
-                                               zt_enable_ec(p0);
-                                       if (!p1->echocanbridged)
-                                               zt_enable_ec(p1);
-                                       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);
-                       }
+                       return -3;
                }
-               pthread_mutex_unlock(&c0->lock);
-               pthread_mutex_unlock(&c1->lock);
-               
+               to = -1;
                who = ast_waitfor_n(cs, 2, &to);
                if (!who) {
-                       ast_log(LOG_WARNING, "Nobody there??\n");
+                       ast_log(LOG_DEBUG, "Ooh, empty read...\n");
                        continue;
                }
-                 /* if gone out of CLEAR DTMF mode */
-               if (!CLEARDTMF(c0) || !CLEARDTMF(c1)) {
-                       *fo = NULL;
-                       *rc = who;
-                       bridge_cleanup(p0, p1);
-                       return -3;
-               }
-               if (who == c0)
-                       p0->ignoredtmf = 1;
-               else
-                       p1->ignoredtmf = 1;
+               if (who->pvt->pvt == op0) 
+                       op0->ignoredtmf = 1;
+               else if (who->pvt->pvt == op1)
+                       op1->ignoredtmf = 1;
                f = ast_read(who);
-               if (who == c0)
-                       p0->ignoredtmf = 0;
-               else
-                       p1->ignoredtmf = 0;
+               if (who->pvt->pvt == op0) 
+                       op0->ignoredtmf = 0;
+               else if (who->pvt->pvt == op1)
+                       op1->ignoredtmf = 0;
                if (!f) {
                        *fo = NULL;
                        *rc = who;
-                       bridge_cleanup(p0, p1);
-                       return 0;
-               }
-               if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
-                       *fo = f;
-                       *rc = who;
-                       bridge_cleanup(p0, p1);
+                       if (slave && master)
+                               zt_unlink(slave, master);
+                       if (op0 == p0)
+                               zt_enable_ec(p0);
+                       if (op1 == p1)
+                               zt_enable_ec(p1);
                        return 0;
                }
-               if ((f->frametype == AST_FRAME_VOICE) ||
-                       (f->frametype == AST_FRAME_TEXT) ||
-                       (f->frametype == AST_FRAME_VIDEO) || 
-                       (f->frametype == AST_FRAME_IMAGE) ||
-                       (f->frametype == AST_FRAME_DTMF)) {
-                       if ((f->frametype == AST_FRAME_DTMF) && (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
-                               if ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
-                                       *rc = c0;
-                                       *fo = f;
-                                       bridge_cleanup(p0, p1);
-                                       return 0;
-                               } else
-                               if ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) {
-                                       *rc = c1;
-                                       *fo = f;
-                                       bridge_cleanup(p0, p1);
-                                       return 0;
-                               }
+               if (f->frametype == AST_FRAME_DTMF) {
+                       if (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || 
+                           ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))) {
+                               *fo = f;
+                               *rc = who;
+                               if (slave && master)
+                                       zt_unlink(slave, master);
+                               return 0;
+                       } else if ((who == c0) && p0->pulsedial) {
+                               ast_write(c1, f);
+                       } else if ((who == c1) && p1->pulsedial) {
+                               ast_write(c0, f);
                        }
-                       ast_frfree(f);
-               } else
-                       ast_frfree(f);
+               }
+               ast_frfree(f);
+
                /* Swap who gets priority */
                cs[2] = cs[0];
                cs[0] = cs[1];
                cs[1] = cs[2];
        }
-       return 0;
 }
 
 static int zt_indicate(struct ast_channel *chan, int condition);
@@ -1697,12 +2110,17 @@ static int zt_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
        struct zt_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;
+       if (p->owner == oldchan)
+               p->owner = newchan;
        for (x=0;x<3;x++)
-               if (p->owners[x] == oldchan)
-                       p->owners[x] = newchan;
+               if (p->subs[x].owner == oldchan) {
+                       if (!x)
+                               zt_unlink(NULL, p);
+                       p->subs[x].owner = newchan;
+               }
        if (newchan->_state == AST_STATE_RINGING) 
                zt_indicate(newchan, AST_CONTROL_RINGING);
+       update_conf(p);
        return 0;
 }
 
@@ -1713,10 +2131,10 @@ static int zt_ring_phone(struct zt_pvt *p)
        /* Make sure our transmit state is on hook */
        x = 0;
        x = ZT_ONHOOK;
-       res = ioctl(zap_fd(p->z), ZT_HOOK, &x);
+       res = ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
        do {
                x = ZT_RING;
-               res = ioctl(zap_fd(p->z), ZT_HOOK, &x);
+               res = ioctl(p->subs[SUB_REAL].zfd, ZT_HOOK, &x);
 #if 0
                printf("Res: %d, error: %s\n", res, strerror(errno));
 #endif                                         
@@ -1748,34 +2166,116 @@ static int attempt_transfer(struct zt_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)) {
+       if (p->subs[SUB_REAL].owner->bridge) {
+               /* The three-way person we're about to transfer to could still be in MOH, so
+                  stop if now if appropriate */
+               if (p->subs[SUB_THREEWAY].owner->bridge)
+                       ast_moh_stop(p->subs[SUB_THREEWAY].owner->bridge);
+               if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RINGING) {
+                       ast_indicate(p->subs[SUB_REAL].owner->bridge, AST_CONTROL_RINGING);
+               }
+               if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, p->subs[SUB_REAL].owner->bridge)) {
                        ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       p->owners[p->normalindex]->bridge->name, p->owners[p->thirdcallindex]->name);
+                                       p->subs[SUB_REAL].owner->bridge->name, p->subs[SUB_THREEWAY].owner->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)) {
+               unalloc_sub(p, SUB_THREEWAY);
+       } else if (p->subs[SUB_THREEWAY].owner->bridge) {
+               if (p->subs[SUB_REAL].owner->_state == AST_STATE_RINGING) {
+                       ast_indicate(p->subs[SUB_THREEWAY].owner->bridge, AST_CONTROL_RINGING);
+               }
+               ast_moh_stop(p->subs[SUB_THREEWAY].owner->bridge);
+               if (ast_channel_masquerade(p->subs[SUB_REAL].owner, p->subs[SUB_THREEWAY].owner->bridge)) {
                        ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                               p->owners[p->thirdcallindex]->bridge->name, p->owners[p->normalindex]->name);
+                                       p->subs[SUB_THREEWAY].owner->bridge->name, p->subs[SUB_REAL].owner->name);
                        return -1;
                }
-               /* Orphan the normal channel */
-               p->owners[p->normalindex] = NULL;
-               p->normalindex = p->thirdcallindex;
-               p->thirdcallindex = -1;
+               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+               unalloc_sub(p, SUB_THREEWAY);
+               /* Tell the caller not to hangup */
+               return 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 |= AST_SOFTHANGUP_DEV;
+                                       p->subs[SUB_REAL].owner->name, p->subs[SUB_THREEWAY].owner->name);
+               p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
        }
        return 0;
 }
 
-struct ast_frame *zt_handle_event(struct ast_channel *ast)
+#ifdef ZAPATA_R2
+static struct ast_frame *handle_r2_event(struct zt_pvt *p, mfcr2_event_t *e, int index)
+{
+       struct ast_frame *f;
+       f = &p->subs[index].f;
+       if (!p->r2) {
+               ast_log(LOG_WARNING, "Huh?  No R2 structure :(\n");
+               return NULL;
+       }
+       switch(e->e) {
+       case MFCR2_EVENT_BLOCKED:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Channel %d blocked\n", p->channel);
+               break;
+       case MFCR2_EVENT_UNBLOCKED:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Channel %d unblocked\n", p->channel);
+               break;
+       case MFCR2_EVENT_CONFIG_ERR:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Config error on channel %d\n", p->channel);
+               break;
+       case MFCR2_EVENT_RING:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Ring on channel %d\n", p->channel);
+               break;
+       case MFCR2_EVENT_HANGUP:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Hangup on channel %d\n", p->channel);
+               break;
+       case MFCR2_EVENT_RINGING:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Ringing on channel %d\n", p->channel);
+               break;
+       case MFCR2_EVENT_ANSWER:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Answer on channel %d\n", p->channel);
+               break;
+       case MFCR2_EVENT_HANGUP_ACK:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Hangup ACK on channel %d\n", p->channel);
+               break;
+       case MFCR2_EVENT_IDLE:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Idle on channel %d\n", p->channel);
+               break;
+       default:
+               ast_log(LOG_WARNING, "Unknown MFC/R2 event %d\n", e->e);
+               break;
+       }
+       return f;
+}
+
+static mfcr2_event_t *r2_get_event_bits(struct zt_pvt *p)
+{
+       int x;
+       int res;
+       mfcr2_event_t *e;
+       res = ioctl(p->subs[SUB_REAL].zfd, ZT_GETRXBITS, &x);
+       if (res) {
+               ast_log(LOG_WARNING, "Unable to check received bits\n");
+               return NULL;
+       }
+       if (!p->r2) {
+               ast_log(LOG_WARNING, "Odd, no R2 structure on channel %d\n", p->channel);
+               return NULL;
+       }
+       e = mfcr2_cas_signaling_event(p->r2, x);
+       return e;
+}
+#endif
+
+static struct ast_frame *zt_handle_event(struct ast_channel *ast)
 {
        int res,x;
        int index;
@@ -1786,21 +2286,52 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        index = zt_get_index(ast, p, 0);
-       p->f[index].frametype = AST_FRAME_NULL;
-       p->f[index].datalen = 0;
-       p->f[index].timelen = 0;
-       p->f[index].mallocd = 0;
-       p->f[index].offset = 0;
-       p->f[index].src = "zt_handle_event";
-       p->f[index].data = NULL;
+       p->subs[index].f.frametype = AST_FRAME_NULL;
+       p->subs[index].f.datalen = 0;
+       p->subs[index].f.samples = 0;
+       p->subs[index].f.mallocd = 0;
+       p->subs[index].f.offset = 0;
+       p->subs[index].f.src = "zt_handle_event";
+       p->subs[index].f.data = NULL;
        if (index < 0)
-               return &p->f[index];
-       res = zt_get_event(zap_fd(p->z));
+               return &p->subs[index].f;
+       res = zt_get_event(p->subs[index].zfd);
        ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index);
+       if (res & (ZT_EVENT_PULSEDIGIT | ZT_EVENT_DTMFDIGIT)) {
+               if (res & ZT_EVENT_PULSEDIGIT)
+                       p->pulsedial = 1;
+               else
+                       p->pulsedial = 0;
+               ast_log(LOG_DEBUG, "Pulse dial '%c'\n", res & 0xff);
+               p->subs[index].f.frametype = AST_FRAME_DTMF;
+               p->subs[index].f.subclass = res & 0xff;
+               /* Return the captured digit */
+               return &p->subs[index].f;
+       }
        switch(res) {
+               case ZT_EVENT_BITSCHANGED:
+                       if (p->sig == SIG_R2) {
+#ifdef ZAPATA_R2
+                               struct ast_frame  *f = &p->subs[index].f;
+                               mfcr2_event_t *e;
+                               e = r2_get_event_bits(p);
+                               if (e)
+                                       f = handle_r2_event(p, e, index);
+                               return f;
+#else                          
+                               break;
+#endif
+                       }
+                       ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
+               case ZT_EVENT_PULSE_START:
+                       /* Stop tone if there's a pulse start and the PBX isn't started */
+                       if (!ast->pbx)
+                               tone_zone_play_tone(p->subs[index].zfd, -1);
+                       break;  
                case ZT_EVENT_DIALCOMPLETE:
                        if (p->inalarm) break;
-                       if (ioctl(zap_fd(p->z),ZT_DIALING,&x) == -1) {
+                       if (p->radio) break;
+                       if (ioctl(p->subs[index].zfd,ZT_DIALING,&x) == -1) {
                                ast_log(LOG_DEBUG, "ZT_DIALING ioctl failed on %s\n",ast->name);
                                return NULL;
                        }
@@ -1808,12 +2339,14 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                zt_enable_ec(p);
                                p->dialing = 0;
                                if (ast->_state == AST_STATE_DIALING) {
-                                       if (p->confirmanswer || (!p->dialednone && ((p->sig == SIG_EM) || (p->sig == SIG_EMWINK) || (p->sig == SIG_FEATD) || (p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATB)))) {
+                                       if (p->callprogress && CANPROGRESSDETECT(p) && p->dsp) {
+                                               ast_log(LOG_DEBUG, "Done dialing, but waiting for progress detection before doing more...\n");
+                                       } else if (p->confirmanswer || (!p->dialednone && ((p->sig == SIG_EM) || (p->sig == SIG_EMWINK) || (p->sig == SIG_FEATD) || (p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATB)))) {
                                                ast_setstate(ast, AST_STATE_RINGING);
                                        } else {
                                                ast_setstate(ast, AST_STATE_UP);
-                                               p->f[index].frametype = AST_FRAME_CONTROL;
-                                               p->f[index].subclass = AST_CONTROL_ANSWER;
+                                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                               p->subs[index].f.subclass = AST_CONTROL_ANSWER;
                                        }
                                }
                        }
@@ -1822,69 +2355,60 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        p->inalarm = 1;
                        /* fall through intentionally */
                case ZT_EVENT_ONHOOK:
+                       if (p->radio)
+                       {
+                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY;
+                               break;
+                       }
                        switch(p->sig) {
                        case SIG_FXOLS:
                        case SIG_FXOGS:
                        case SIG_FXOKS:
+                               p->onhooktime = time(NULL);
+                               p->msgstate = -1;
                                /* Check for some special conditions regarding call waiting */
-                               if (index == p->normalindex) {
+                               if (index == SUB_REAL) {
                                        /* The normal line was hung up */
-                                       if (p->callwaitindex > -1) {
-                                               bridge_cleanup(p->owners[p->normalindex]->pvt->pvt,
-                                                   p->owners[p->callwaitindex]->pvt->pvt);
-                                               /* There's a call waiting call, so ring the phone */
-                                               p->owner = p->owners[p->callwaitindex];
+                                       if (p->subs[SUB_CALLWAIT].owner) {
+                                               /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
+                                               swap_subs(p, SUB_CALLWAIT, SUB_REAL);
                                                if (option_verbose > 2) 
-                                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has (callwait) call, ringing phone\n", p->owner->name);
-                                               p->needanswer[index] = 0;
-                                               p->needringing[index] = 0;
-                                               p->callwaitingrepeat = 0;
-                                               zt_ring_phone(p);
-                                       } else if (p->thirdcallindex > -1) {
-                                               if (p->transfer) {
-                                                       if (attempt_transfer(p))
-                                                               p->owners[p->thirdcallindex]->_softhangup |= AST_SOFTHANGUP_DEV;
-                                               } else
-                                                       p->owners[p->thirdcallindex]->_softhangup |= AST_SOFTHANGUP_DEV;
-                                       }
-                               } else if (index == p->callwaitindex) {
-                                       /* Check to see if there is a normal call */
-                                       if (p->normalindex > -1) {
-                                               bridge_cleanup(p->owners[p->normalindex]->pvt->pvt,
-                                                   p->owners[p->callwaitindex]->pvt->pvt);
-                                               /* 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->name);
-                                               p->needanswer[index] = 0;
-                                               p->needringing[index] = 0;
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %d still has (callwait) call, ringing phone\n", p->channel);
+                                               unalloc_sub(p, SUB_CALLWAIT);   
+#if 0
+                                               p->subs[index].needanswer = 0;
+                                               p->subs[index].needringing = 0;
+#endif                                         
                                                p->callwaitingrepeat = 0;
+                                               p->cidcwexpire = 0;
+                                               p->owner = NULL;
                                                zt_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 |= AST_SOFTHANGUP_DEV;
-                                               if (p->callwaitindex > -1) {
-                                                       ast_log(LOG_WARNING, "Somehow there was a call wait\n");
-                                                       p->owners[p->callwaitindex]->_softhangup |= AST_SOFTHANGUP_DEV;
+                                       } else if (p->subs[SUB_THREEWAY].owner) {
+                                               if ((ast->pbx) ||
+                                                       (ast->_state == AST_STATE_UP)) {
+                                                       if (p->transfer) {
+                                                               /* In any case this isn't a threeway call anymore */
+                                                               p->subs[SUB_REAL].inthreeway = 0;
+                                                               p->subs[SUB_THREEWAY].inthreeway = 0;
+                                                               if ((res = attempt_transfer(p)) < 0)
+                                                                       p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                               else if (res) {
+                                                                       /* Don't actually hang up at this point */
+                                                                       break;
+                                                               }
+                                                       } else
+                                                               p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                               } else {
+                                                       /* Swap subs and dis-own channel */
+                                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                                       p->owner = NULL;
+                                                       /* Ring the phone */
+                                                       zt_ring_phone(p);
                                                }
-                                               
-                                       } else {
-                                               if (p->transfer) {
-                                                       if (attempt_transfer(p))
-                                                               p->owners[p->normalindex]->_softhangup |= AST_SOFTHANGUP_DEV;
-                                                       else {
-                                                               /* Don't actually hangup.  We're going to get transferred */
-                                                               zt_disable_ec(p);
-                                                               break;
-                                                       }
-                                               } else 
-                                                       p->owners[p->normalindex]->_softhangup |= AST_SOFTHANGUP_DEV;
                                        }
+                               } else {
+                                       ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", index);
                                }
                                /* Fall through */
                        default:
@@ -1894,6 +2418,12 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        break;
                case ZT_EVENT_RINGOFFHOOK:
                        if (p->inalarm) break;
+                       if (p->radio)
+                       {
+                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY;
+                               break;
+                       }
                        switch(p->sig) {
                        case SIG_FXOLS:
                        case SIG_FXOGS:
@@ -1901,10 +2431,10 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                switch(ast->_state) {
                                case AST_STATE_RINGING:
                                        zt_enable_ec(p);
-                                       p->f[index].frametype = AST_FRAME_CONTROL;
-                                       p->f[index].subclass = AST_CONTROL_ANSWER;
+                                       p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                       p->subs[index].f.subclass = AST_CONTROL_ANSWER;
                                        /* Make sure it stops ringing */
-                                       zt_set_hook(zap_fd(p->z), ZT_OFFHOOK);
+                                       zt_set_hook(p->subs[index].zfd, ZT_OFFHOOK);
                                        ast_log(LOG_DEBUG, "channel %d answered\n", p->channel);
                                        if (p->cidspill) {
                                                /* Cancel any running CallerID spill */
@@ -1912,23 +2442,24 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                                p->cidspill = NULL;
                                        }
                                        p->dialing = 0;
+                                       p->callwaitcas = 0;
                                        if (p->confirmanswer) {
                                                /* Ignore answer if "confirm answer" is selected */
-                                               p->f[index].frametype = AST_FRAME_NULL;
-                                               p->f[index].subclass = 0;
+                                               p->subs[index].f.frametype = AST_FRAME_NULL;
+                                               p->subs[index].f.subclass = 0;
                                        } else 
                                                ast_setstate(ast, AST_STATE_UP);
-                                       return &p->f[index];
+                                       return &p->subs[index].f;
                                case AST_STATE_DOWN:
                                        ast_setstate(ast, AST_STATE_RING);
                                        ast->rings = 1;
-                                       p->f[index].frametype = AST_FRAME_CONTROL;
-                                       p->f[index].subclass = AST_CONTROL_OFFHOOK;
+                                       p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                       p->subs[index].f.subclass = AST_CONTROL_OFFHOOK;
                                        ast_log(LOG_DEBUG, "channel %d picked up\n", p->channel);
-                                       return &p->f[index];
+                                       return &p->subs[index].f;
                                case AST_STATE_UP:
                                        /* Make sure it stops ringing */
-                                       zt_set_hook(zap_fd(p->z), ZT_OFFHOOK);
+                                       zt_set_hook(p->subs[index].zfd, ZT_OFFHOOK);
                                        /* Okay -- probably call waiting*/
                                        if (p->owner->bridge)
                                                        ast_moh_stop(p->owner->bridge);
@@ -1952,17 +2483,17 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                if (ast->_state == AST_STATE_DOWN) {
                                        if (option_debug)
                                                ast_log(LOG_DEBUG, "Ring detected\n");
-                                       p->f[index].frametype = AST_FRAME_CONTROL;
-                                       p->f[index].subclass = AST_CONTROL_RING;
+                                       p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                       p->subs[index].f.subclass = AST_CONTROL_RING;
                                } else if (ast->_state == AST_STATE_RINGING) {
                                        if (option_debug)
                                                ast_log(LOG_DEBUG, "Line answered\n");
                                        if (p->confirmanswer) {
-                                               p->f[index].frametype = AST_FRAME_NULL;
-                                               p->f[index].subclass = 0;
+                                               p->subs[index].f.frametype = AST_FRAME_NULL;
+                                               p->subs[index].f.subclass = 0;
                                        } else {
-                                               p->f[index].frametype = AST_FRAME_CONTROL;
-                                               p->f[index].subclass = AST_CONTROL_ANSWER;
+                                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                               p->subs[index].f.subclass = AST_CONTROL_ANSWER;
                                                ast_setstate(ast, AST_STATE_UP);
                                        }
                                } else if (ast->_state != AST_STATE_RING)
@@ -1974,6 +2505,7 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        break;
                case ZT_EVENT_RINGEROFF:
                        if (p->inalarm) break;
+                       if (p->radio) break;
                        ast->rings++;
                        if ((ast->rings > p->cidrings) && (p->cidspill)) {
                                ast_log(LOG_WARNING, "Didn't finish Caller-ID spill.  Cancelling.\n");
@@ -1981,8 +2513,8 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                p->cidspill = NULL;
                                p->callwaitcas = 0;
                        }
-                       p->f[index].frametype = AST_FRAME_CONTROL;
-                       p->f[index].subclass = AST_CONTROL_RINGING;
+                       p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                       p->subs[index].f.subclass = AST_CONTROL_RINGING;
                        break;
                case ZT_EVENT_RINGERON:
                        break;
@@ -1991,113 +2523,123 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        break;
                case ZT_EVENT_WINKFLASH:
                        if (p->inalarm) break;
+                       if (p->radio) break;
                        switch(p->sig) {
                        case SIG_FXOLS:
                        case SIG_FXOGS:
                        case SIG_FXOKS:
                                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];
+                                       index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd);
+                               p->callwaitcas = 0;
+                               if (index == SUB_REAL) {
+                                       if (p->subs[SUB_CALLWAIT].owner) {
+                                               /* Swap to call-wait */
+                                               swap_subs(p, SUB_REAL, SUB_CALLWAIT);
+                                               tone_zone_play_tone(p->subs[SUB_REAL].zfd, -1);
+                                               p->owner = p->subs[SUB_REAL].owner;
+                                               ast_log(LOG_DEBUG, "Making %s the new owner\n", p->owner->name);
                                                if (p->owner->_state == AST_STATE_RINGING) {
                                                        ast_setstate(p->owner, AST_STATE_UP);
-                                                       p->needanswer[p->callwaitindex] = 1;
+                                                       p->subs[SUB_REAL].needanswer = 1;
                                                }
                                                p->callwaitingrepeat = 0;
-                                               conf_clear(p);
+                                               p->cidcwexpire = 0;
                                                /* Start music on hold if appropriate */
-                                               if (p->owners[p->normalindex]->bridge)
-                                                               ast_moh_start(p->owners[p->normalindex]->bridge, NULL);
-                                               if (p->owners[p->callwaitindex]->bridge)
-                                                               ast_moh_stop(p->owners[p->callwaitindex]->bridge);
-                                       } else if (p->thirdcallindex == -1) {
+                                               if (!p->subs[SUB_CALLWAIT].inthreeway && p->subs[SUB_CALLWAIT].owner->bridge)
+                                                               ast_moh_start(p->subs[SUB_CALLWAIT].owner->bridge, NULL);
+                                               if (p->subs[SUB_REAL].owner->bridge)
+                                                               ast_moh_stop(p->subs[SUB_REAL].owner->bridge);
+                                       } else if (!p->subs[SUB_THREEWAY].owner) {
                                                if (p->threewaycalling) {
-                                                       if ((ast->_state == AST_STATE_RINGING) ||
+                                                       /* XXX This section needs much more error checking!!! XXX */
+                                                       /* Start a 3-way call if feasible */
+                                                       if ((ast->pbx) ||
                                                                        (ast->_state == AST_STATE_UP) ||
                                                                        (ast->_state == AST_STATE_RING)) {
-                                                               if (!alloc_pseudo(p)) {
-                                                                       /* Start three way call */
+                                                               if (!alloc_sub(p, SUB_THREEWAY)) {
+                                                                       /* Make new channel */
+                                                                       chan = zt_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0);
+                                                                       /* Swap things around between the three-way and real call */
+                                                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
                                                                        /* Disable echo canceller for better dialing */
                                                                        zt_disable_ec(p);
-                                                                       res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                                                                       res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_DIALRECALL);
                                                                        if (res)
                                                                                ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
-                                                                       chan = zt_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), ZT_TONE_CONGESTION);
+                                                                               res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
                                                                                zt_enable_ec(p);
                                                                                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);
+                                                                                       ast_verbose(VERBOSE_PREFIX_3 "Started three way call on channel %d\n", p->channel);
                                                                                /* Start music on hold if appropriate */
-                                                                               if (p->owners[p->normalindex]->bridge)
-                                                                                               ast_moh_start(p->owners[p->normalindex]->bridge, NULL);
+                                                                               if (p->subs[SUB_THREEWAY].owner->bridge)
+                                                                                       ast_moh_start(p->subs[SUB_THREEWAY].owner->bridge, NULL);
                                                                        }               
                                                                } else
-                                                                       ast_log(LOG_WARNING, "Unable to allocate pseudo channel\n");
+                                                                       ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\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 |= AST_SOFTHANGUP_DEV;
-                                               conf_clear(p);
-                                       }
-                               } else if (index == p->callwaitindex) {
-                                       if (p->normalindex > -1) {
-                                               p->owner = p->owners[p->normalindex];
-                                               p->callwaitingrepeat = 0;
-                                               conf_clear(p);
-                                               /* Start music on hold if appropriate */
-                                               if (p->owners[p->callwaitindex]->bridge)
-                                                               ast_moh_start(p->owners[p->callwaitindex]->bridge, NULL);
-                                               if (p->owners[p->normalindex]->bridge)
-                                                               ast_moh_stop(p->owners[p->normalindex]->bridge);
-                                       } 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) {
-                                               /* One way or another, cancel music on hold */
-                                               if (p->owners[p->normalindex]->bridge)
-                                                               ast_moh_stop(p->owners[p->normalindex]->bridge);
-                                               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);
-                                                       zt_enable_ec(p);
-                                                       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), ZT_TONE_RINGTONE);
+                                               /* Already have a 3 way call */
+                                               if (p->subs[SUB_THREEWAY].inthreeway) {
+                                                       /* Call is already up, drop the last person */
+                                                       if (option_debug)
+                                                               ast_log(LOG_DEBUG, "Got flash with three way call up, dropping last call on %d\n", p->channel);
+                                                       /* If the primary call isn't answered yet, use it */
+                                                       if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) {
+                                                               /* Swap back -- we're droppign the real 3-way that isn't finished yet*/
+                                                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                                               p->owner = p->subs[SUB_REAL].owner;
+                                                       }
+                                                       /* 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->subs[SUB_THREEWAY].owner->name);
+                                                       p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                       p->subs[SUB_REAL].inthreeway = 0;
+                                                       p->subs[SUB_THREEWAY].inthreeway = 0;
+                                               } else {
+                                                       /* Lets see what we're up to */
+                                                       if ((ast->pbx) ||
+                                                                       (ast->_state == AST_STATE_UP)) {
+                                                               int otherindex = SUB_THREEWAY;
+                                                               if (option_verbose > 2)
+                                                                       ast_verbose(VERBOSE_PREFIX_3 "Building conference on call on %s and %s\n", p->subs[SUB_THREEWAY].owner->name, p->subs[SUB_REAL].owner->name);
+                                                               /* Put them in the threeway, and flip */
+                                                               p->subs[SUB_THREEWAY].inthreeway = 1;
+                                                               p->subs[SUB_REAL].inthreeway = 1;
+                                                               if (ast->_state == AST_STATE_UP) {
+                                                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                                                       otherindex = SUB_REAL;
+                                                               }
+                                                               if (p->subs[otherindex].owner && p->subs[otherindex].owner->bridge)
+                                                                       ast_moh_stop(p->subs[otherindex].owner->bridge);
+                                                               p->owner = p->subs[SUB_REAL].owner;
+                                                               if (ast->_state == AST_STATE_RINGING) {
+                                                                       ast_log(LOG_DEBUG, "Enabling ringtone on real and threeway\n");
+                                                                       res = tone_zone_play_tone(p->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+                                                                       res = tone_zone_play_tone(p->subs[SUB_THREEWAY].zfd, ZT_TONE_RINGTONE);
+                                                               }
+                                                       } else {
+                                                               if (option_verbose > 2)
+                                                                       ast_verbose(VERBOSE_PREFIX_3 "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name);
+                                                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                                               p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                               p->owner = p->subs[SUB_REAL].owner;
+                                                               if (p->subs[SUB_REAL].owner && p->subs[SUB_REAL].owner->bridge)
+                                                                       ast_moh_stop(p->subs[SUB_REAL].owner->bridge);
+                                                       }
+                                                       
                                                }
-                                               three_way(p);
-                                               /* Restart the echo canceller */
-                                               zt_enable_ec(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;
                                        }
+                               } else {
+                                       ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", index, p->channel);
                                }
+                               update_conf(p);
                                break;
                        case SIG_EM:
                        case SIG_EMWINK:
@@ -2112,7 +2654,7 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        case SIG_FEATDMF:
                        case SIG_FEATB:
                                /* FGD MF *Must* wait for wink */
-                               res = ioctl(zap_fd(p->z), ZT_DIAL, &p->dop);
+                               res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop);
                                if (res < 0) {
                                        ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d\n", p->channel);
                                        p->dop.dialstr[0] = '\0';
@@ -2127,6 +2669,7 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        break;
                case ZT_EVENT_HOOKCOMPLETE:
                        if (p->inalarm) break;
+                       if (p->radio) break;
                        switch(p->sig) {
                        case SIG_FXSLS:  /* only interesting for FXS */
                        case SIG_FXSGS:
@@ -2134,7 +2677,7 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        case SIG_EM:
                        case SIG_EMWINK:
                        case SIG_FEATD:
-                               res = ioctl(zap_fd(p->z), ZT_DIAL, &p->dop);
+                               res = ioctl(p->subs[SUB_REAL].zfd, ZT_DIAL, &p->dop);
                                if (res < 0) {
                                        ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d\n", p->channel);
                                        p->dop.dialstr[0] = '\0';
@@ -2154,7 +2697,7 @@ struct ast_frame *zt_handle_event(struct ast_channel *ast)
                default:
                        ast_log(LOG_DEBUG, "Dunno what to do with event %d on channel %d\n", res, p->channel);
        }
-       return &p->f[index];
+       return &p->subs[index].f;
  }
 
 struct ast_frame *zt_exception(struct ast_channel *ast)
@@ -2163,40 +2706,33 @@ struct ast_frame *zt_exception(struct ast_channel *ast)
        int res;
        int usedindex=-1;
        int index;
-       
+
        index = zt_get_index(ast, p, 1);
        
-       p->f[index].frametype = AST_FRAME_NULL;
-       p->f[index].datalen = 0;
-       p->f[index].timelen = 0;
-       p->f[index].mallocd = 0;
-       p->f[index].offset = 0;
-       p->f[index].subclass = 0;
-       p->f[index].src = "zt_exception";
-       p->f[index].data = NULL;
-               
-       if ((p->owner != p->owners[0]) && 
-           (p->owner != p->owners[1]) &&
-               (p->owner != p->owners[2])) {
+       p->subs[index].f.frametype = AST_FRAME_NULL;
+       p->subs[index].f.datalen = 0;
+       p->subs[index].f.samples = 0;
+       p->subs[index].f.mallocd = 0;
+       p->subs[index].f.offset = 0;
+       p->subs[index].f.subclass = 0;
+       p->subs[index].f.src = "zt_exception";
+       p->subs[index].f.data = NULL;
+       
+       
+       if ((!p->owner) && (!p->radio)) {
                /* 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 = zt_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;
+               res = zt_get_event(p->subs[SUB_REAL].zfd);
+               /* Switch to real if there is one and this isn't something really silly... */
+               if ((res != ZT_EVENT_RINGEROFF) && (res != ZT_EVENT_RINGERON) &&
+                       (res != ZT_EVENT_HOOKCOMPLETE)) {
+                       ast_log(LOG_DEBUG, "Restoring owner of channel %d on event %d\n", p->channel, res);
+                       p->owner = p->subs[SUB_REAL].owner;
+                       if (p->owner && p->owner->bridge)
+                               ast_moh_stop(p->owner->bridge);
                }
                switch(res) {
                case ZT_EVENT_ONHOOK:
@@ -2206,33 +2742,53 @@ struct ast_frame *zt_exception(struct ast_channel *ast)
                                        ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has call, ringing phone\n", p->owner->name);
                                zt_ring_phone(p);
                                p->callwaitingrepeat = 0;
+                               p->cidcwexpire = 0;
                        } else
                                ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+                       update_conf(p);
+                       break;
+               case ZT_EVENT_RINGOFFHOOK:
+                       zt_set_hook(p->subs[SUB_REAL].zfd, ZT_OFFHOOK);
+                       if (p->owner && (p->owner->_state == AST_STATE_RINGING)) {
+                               p->subs[SUB_REAL].needanswer = 1;
+                       }
+                       break;
+               case ZT_EVENT_HOOKCOMPLETE:
+               case ZT_EVENT_RINGERON:
+               case ZT_EVENT_RINGEROFF:
+                       /* Do nothing */
                        break;
                case ZT_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;
+                               if (p->owner->_state != AST_STATE_UP) {
+                                       /* Answer if necessary */
+                                       usedindex = zt_get_index(p->owner, p, 0);
+                                       if (usedindex > -1) {
+                                               p->subs[usedindex].needanswer = 1;
+                                       }
                                        ast_setstate(p->owner, AST_STATE_UP);
                                }
                                p->callwaitingrepeat = 0;
+                               p->cidcwexpire = 0;
                                if (p->owner->bridge)
-                                               ast_moh_stop(p->owner->bridge);
+                                       ast_moh_stop(p->owner->bridge);
                        } else
                                ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+                       update_conf(p);
                        break;
                default:
                        ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
                }
-               return &p->f[index];
+               return &p->subs[index].f;
        }
+       if (!p->radio) ast_log(LOG_DEBUG, "Exception on %d, channel %d\n", ast->fds[0],p->channel);
        /* If it's not us, return NULL immediately */
-       if (ast != p->owner)
-               return &p->f[index];
-
+       if (ast != p->owner) {
+               ast_log(LOG_WARNING, "We're %s, not %s\n", ast->name, p->owner->name);
+               return &p->subs[index].f;
+       }
        return zt_handle_event(ast);
 }
 
@@ -2241,22 +2797,22 @@ struct ast_frame  *zt_read(struct ast_channel *ast)
        struct zt_pvt *p = ast->pvt->pvt;
        int res;
        int index;
-       int *linear;
-       ZAP *z = NULL;
        void *readbuf;
+       struct ast_frame *f;
        
+
        ast_pthread_mutex_lock(&p->lock);
        
        index = zt_get_index(ast, p, 0);
        
-       p->f[index].frametype = AST_FRAME_NULL;
-       p->f[index].datalen = 0;
-       p->f[index].timelen = 0;
-       p->f[index].mallocd = 0;
-       p->f[index].offset = 0;
-       p->f[index].subclass = 0;
-       p->f[index].src = "zt_read";
-       p->f[index].data = NULL;
+       p->subs[index].f.frametype = AST_FRAME_NULL;
+       p->subs[index].f.datalen = 0;
+       p->subs[index].f.samples = 0;
+       p->subs[index].f.mallocd = 0;
+       p->subs[index].f.offset = 0;
+       p->subs[index].f.subclass = 0;
+       p->subs[index].f.src = "zt_read";
+       p->subs[index].f.data = NULL;
        
        /* Hang up if we don't really exist */
        if (index < 0)  {
@@ -2265,6 +2821,27 @@ struct ast_frame  *zt_read(struct ast_channel *ast)
                return NULL;
        }
        
+       /* make sure it sends initial key state as first frame */
+       if (p->radio && (!p->firstradio))
+       {
+               ZT_PARAMS ps;
+
+               ps.channo = p->channel;
+               if (ioctl(p->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &ps) < 0)
+                       return NULL;
+               p->firstradio = 1;
+               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+               if (ps.rxisoffhook)
+               {
+                       p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY;
+               }
+               else
+               {
+                       p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY;
+               }
+               pthread_mutex_unlock(&p->lock);
+               return &p->subs[index].f;
+       }
        if (p->ringt == 1) {
                pthread_mutex_unlock(&p->lock);
                return NULL;
@@ -2272,148 +2849,72 @@ struct ast_frame  *zt_read(struct ast_channel *ast)
        else if (p->ringt > 0) 
                p->ringt--;
 
-       if (p->needringing[index]) {
+       if (p->subs[index].needringing) {
                /* Send ringing frame if requested */
-               p->needringing[index] = 0;
-               p->f[index].frametype = AST_FRAME_CONTROL;
-               p->f[index].subclass = AST_CONTROL_RINGING;
+               p->subs[index].needringing = 0;
+               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+               p->subs[index].f.subclass = AST_CONTROL_RINGING;
                ast_setstate(ast, AST_STATE_RINGING);
                pthread_mutex_unlock(&p->lock);
-               return &p->f[index];
-       }       
-       
-       if (p->needanswer[index]) {
-               /* Send ringing frame if requested */
-               p->needanswer[index] = 0;
-               p->f[index].frametype = AST_FRAME_CONTROL;
-               p->f[index].subclass = AST_CONTROL_ANSWER;
-               ast_setstate(ast, AST_STATE_UP);
-               pthread_mutex_unlock(&p->lock);
-               return &p->f[index];
-       }       
-       
-       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[index];
-               }
-               /* If it's not the third call, return immediately */
-               if (ast != p->owners[p->thirdcallindex]) {
-                       pthread_mutex_unlock(&p->lock);
-                       return &p->f[index];
-               }
-               if (!p->pseudo) 
-                       ast_log(LOG_ERROR, "No pseudo channel\n");
-               z = p->pseudo;  
-               linear = &p->pseudolinear;
-       } else {
-               z = p->z;
-               linear = &p->reallinear;
+               return &p->subs[index].f;
        }
 
-       if (!z) {
-               ast_log(LOG_WARNING, "No zap structure?!?\n");
-               pthread_mutex_unlock(&p->lock);
-               return NULL;
+       if (p->subs[index].needcallerid) {
+               ast_set_callerid(ast, strlen(p->lastcallerid) ? p->lastcallerid : NULL, 1);
+               p->subs[index].needcallerid = 0;
        }
        
-       /* Check first for any outstanding DTMF characters */
-       if (strlen(p->dtmfq)) {
-               p->f[index].subclass = p->dtmfq[0];
-               memmove(p->dtmfq, p->dtmfq + 1, sizeof(p->dtmfq) - 1);
-               p->f[index].frametype = AST_FRAME_DTMF;
-               if (p->confirmanswer) {
-                       printf("Confirm answer!\n");
-                       /* Upon receiving a DTMF digit, consider this an answer confirmation instead
-                          of a DTMF digit */
-                       p->f[index].frametype = AST_FRAME_CONTROL;
-                       p->f[index].subclass = AST_CONTROL_ANSWER;
-                       ast_setstate(ast, AST_STATE_UP);
-               }
+       if (p->subs[index].needanswer) {
+               /* Send ringing frame if requested */
+               p->subs[index].needanswer = 0;
+               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+               p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+               ast_setstate(ast, AST_STATE_UP);
                pthread_mutex_unlock(&p->lock);
-               return &p->f[index];
-       }
+               return &p->subs[index].f;
+       }       
        
        if (ast->pvt->rawreadformat == AST_FORMAT_SLINEAR) {
-               if (!*linear) {
-                       *linear = 1;
-                       res = zap_setlinear(p->z, *linear);
+               if (!p->subs[index].linear) {
+                       p->subs[index].linear = 1;
+                       res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
                        if (res) 
-                               ast_log(LOG_WARNING, "Unable to set channel %d to linear mode.\n", p->channel);
+                               ast_log(LOG_WARNING, "Unable to set channel %d (index %d) to linear mode.\n", p->channel, index);
                }
        } else if ((ast->pvt->rawreadformat == AST_FORMAT_ULAW) ||
                   (ast->pvt->rawreadformat == AST_FORMAT_ALAW)) {
-               if (*linear) {
-                       *linear = 0;
-                       res = zap_setlinear(p->z, *linear);
+               if (p->subs[index].linear) {
+                       p->subs[index].linear = 0;
+                       res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
                        if (res) 
-                               ast_log(LOG_WARNING, "Unable to set channel %d to linear mode.\n", p->channel);
+                               ast_log(LOG_WARNING, "Unable to set channel %d (index %d) to campanded mode.\n", p->channel, index);
                }
        } 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;
        }
-       readbuf = ((unsigned char *)p->buffer[index]) + AST_FRIENDLY_OFFSET;
+       readbuf = ((unsigned char *)p->subs[index].buffer) + AST_FRIENDLY_OFFSET;
        CHECK_BLOCKING(ast);
-       if ((z != p->z) && (z != p->pseudo)) {
-               pthread_mutex_unlock(&p->lock);
-               return NULL;
-       }
-       res = zap_recchunk(z, readbuf, READ_SIZE, ((p->ignoredtmf) ? 0 : ZAP_DTMFINT));
+       res = read(p->subs[index].zfd, readbuf, p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE);
        ast->blocking = 0;
        /* Check for hangup */
        if (res < 0) {
-               if (res == -1) 
-                       ast_log(LOG_WARNING, "zt_rec: %s\n", strerror(errno));
+               if (res == -1)  {
+                       if (errno == EAGAIN) {
+                               /* Return "NULL" frame if there is nobody there */
+                               pthread_mutex_unlock(&p->lock);
+                               return &p->subs[index].f;
+                       } else
+                               ast_log(LOG_WARNING, "zt_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(z) && !strlen(zap_dtmfbuf(z))) {
-                       zap_getdtmf(z, 1, NULL, 0, 1, 1, 0);
-               }
-               if (strlen(zap_dtmfbuf(z))) {
-                       if (p->confirmanswer) {
-                               printf("Confirm answer!\n");
-                               /* Upon receiving a DTMF digit, consider this an answer confirmation instead
-                                  of a DTMF digit */
-                               p->f[index].frametype = AST_FRAME_CONTROL;
-                               p->f[index].subclass = AST_CONTROL_ANSWER;
-                               ast_setstate(ast, AST_STATE_UP);
-                       } else {
-                               ast_log(LOG_DEBUG, "Got some dtmf ('%s')... on channel %s\n", zap_dtmfbuf(z), ast->name);
-                               /* DTMF tone detected.  Queue and erturn */
-                               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[index];
-                               } else {
-                                       strncpy(p->dtmfq + strlen(p->dtmfq), zap_dtmfbuf(z), sizeof(p->dtmfq) - strlen(p->dtmfq)-1);
-                                       zap_clrdtmfn(z);
-                               }
-                       }
-               } else {
-                       pthread_mutex_unlock(&p->lock);
-                       return zt_handle_event(ast);
-               }
-               if (strlen(p->dtmfq)) {
-                       p->f[index].subclass = p->dtmfq[0];
-                       memmove(p->dtmfq, p->dtmfq + 1, sizeof(p->dtmfq) - 1);
-                       p->f[index].frametype = AST_FRAME_DTMF;
-               }
+       if (res != (p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE)) {
+               ast_log(LOG_DEBUG, "Short read (%d/%d), must be an event...\n", res, p->subs[index].linear ? READ_SIZE * 2 : READ_SIZE);
                pthread_mutex_unlock(&p->lock);
-               return &p->f[index];
+               return zt_handle_event(ast);
        }
        if (p->tdd) { /* if in TDD mode, see if we receive that */
                int c;
@@ -2424,71 +2925,160 @@ struct ast_frame  *zt_read(struct ast_channel *ast)
                        return NULL;
                }
                if (c) { /* if a char to return */
-                       p->f[index].subclass = 0;
-                       p->f[index].frametype = AST_FRAME_TEXT;
-                       p->f[index].mallocd = 0;
-                       p->f[index].offset = AST_FRIENDLY_OFFSET;
-                       p->f[index].data = p->buffer[index] + AST_FRIENDLY_OFFSET;
-                       p->f[index].datalen = 1;
-                       *((char *) p->f[index].data) = c;
+                       p->subs[index].f.subclass = 0;
+                       p->subs[index].f.frametype = AST_FRAME_TEXT;
+                       p->subs[index].f.mallocd = 0;
+                       p->subs[index].f.offset = AST_FRIENDLY_OFFSET;
+                       p->subs[index].f.data = p->subs[index].buffer + AST_FRIENDLY_OFFSET;
+                       p->subs[index].f.datalen = 1;
+                       *((char *) p->subs[index].f.data) = c;
                        pthread_mutex_unlock(&p->lock);
-                       return &p->f[index];
+                       return &p->subs[index].f;
                }
        }
        if (p->callwaitingrepeat)
                p->callwaitingrepeat--;
+       if (p->cidcwexpire)
+               p->cidcwexpire--;
        /* Repeat callwaiting */
        if (p->callwaitingrepeat == 1) {
                p->callwaitrings++;
                zt_callwait(ast);
        }
-       if (ast->pvt->rawreadformat == AST_FORMAT_SLINEAR) {
-               p->f[index].datalen = READ_SIZE * 2;
+       /* Expire CID/CW */
+       if (p->cidcwexpire == 1) {
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "CPE does not support Call Waiting Caller*ID.\n");
+               restore_conference(p);
+       }
+       if (p->subs[index].linear) {
+               p->subs[index].f.datalen = READ_SIZE * 2;
        } else 
-               p->f[index].datalen = READ_SIZE;
+               p->subs[index].f.datalen = READ_SIZE;
 
        /* Handle CallerID Transmission */
-       if (p->cidspill &&((ast->_state == AST_STATE_UP) || (ast->rings == p->cidrings))) {
+       if ((p->owner == ast) && p->cidspill &&((ast->_state == AST_STATE_UP) || (ast->rings == p->cidrings))) {
                send_callerid(p);
        }
 
-       p->f[index].frametype = AST_FRAME_VOICE;
-       p->f[index].subclass = ast->pvt->rawreadformat;
-       p->f[index].timelen = READ_SIZE/8;
-       p->f[index].mallocd = 0;
-       p->f[index].offset = AST_FRIENDLY_OFFSET;
-       p->f[index].data = p->buffer[index] + AST_FRIENDLY_OFFSET/2;
+       p->subs[index].f.frametype = AST_FRAME_VOICE;
+       p->subs[index].f.subclass = ast->pvt->rawreadformat;
+       p->subs[index].f.samples = READ_SIZE;
+       p->subs[index].f.mallocd = 0;
+       p->subs[index].f.offset = AST_FRIENDLY_OFFSET;
+       p->subs[index].f.data = p->subs[index].buffer + AST_FRIENDLY_OFFSET/2;
 #if 0
-       ast_log(LOG_DEBUG, "Read %d of voice on %s\n", p->f[index].datalen, ast->name);
+       ast_log(LOG_DEBUG, "Read %d of voice on %s\n", p->subs[index].f.datalen, ast->name);
 #endif 
-       if (p->dialing) {
-               /* Whoops, we're still dialing, don't send anything */
-               p->f[index].frametype = AST_FRAME_NULL;
-               p->f[index].subclass = 0;
-               p->f[index].timelen = 0;
-               p->f[index].mallocd = 0;
-               p->f[index].offset = 0;
-               p->f[index].data = NULL;
-               p->f[index].datalen= 0;
+       if (p->dialing || /* Transmitting something */
+          (index && (ast->_state != AST_STATE_UP)) || /* Three-way or callwait that isn't up */
+          ((index == SUB_CALLWAIT) && !p->subs[SUB_CALLWAIT].inthreeway) /* Inactive and non-confed call-wait */
+          ) {
+               /* Whoops, we're still dialing, or in a state where we shouldn't transmit....
+                  don't send anything */
+               p->subs[index].f.frametype = AST_FRAME_NULL;
+               p->subs[index].f.subclass = 0;
+               p->subs[index].f.samples = 0;
+               p->subs[index].f.mallocd = 0;
+               p->subs[index].f.offset = 0;
+               p->subs[index].f.data = NULL;
+               p->subs[index].f.datalen= 0;
+       }
+       if (p->dsp && !p->ignoredtmf && !index) {
+               /* Perform busy detection. etc on the zap line */
+               f = ast_dsp_process(ast, p->dsp, &p->subs[index].f, 0);
+               if (f) {
+                       if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_BUSY)) {
+                               if ((ast->_state == AST_STATE_UP) && !p->outgoing) {
+                                       /* Treat this as a "hangup" instead of a "busy" on the assumption that
+                                          a busy  */
+                                       f = NULL;
+                               }
+                       } else if (f->frametype == AST_FRAME_DTMF) {
+                               /* DSP clears us of being pulse */
+                               p->pulsedial = 0;
+                       }
+               }
+       } else 
+               f = &p->subs[index].f; 
+       if (f && (f->frametype == AST_FRAME_DTMF)) {
+               ast_log(LOG_DEBUG, "DTMF digit: %c on %s\n", f->subclass, ast->name);
+               if (p->confirmanswer) {
+                       ast_log(LOG_DEBUG, "Confirm answer on %s!\n", ast->name);
+                       /* Upon receiving a DTMF digit, consider this an answer confirmation instead
+                          of a DTMF digit */
+                       p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                       p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+                       ast_setstate(ast, AST_STATE_UP);
+                       f = &p->subs[index].f;
+               } else if (p->callwaitcas) {
+                       if ((f->subclass == 'A') || (f->subclass == 'D')) {
+                               ast_log(LOG_DEBUG, "Got some DTMF, but it's for the CAS\n");
+                               if (p->cidspill)
+                                       free(p->cidspill);
+                               send_cwcidspill(p);
+                       }
+                       p->callwaitcas = 0;
+                       p->subs[index].f.frametype = AST_FRAME_NULL;
+                       p->subs[index].f.subclass = 0;
+                       f = &p->subs[index].f;
+               } else if (f->subclass == 'f') {
+                       /* Fax tone -- Handle and return NULL */
+                       if (!p->faxhandled) {
+                               p->faxhandled++;
+                               if (strcmp(ast->exten, "fax")) {
+                                       if (ast_exists_extension(ast, ast->context, "fax", 1, ast->callerid)) {
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", ast->name);
+                                               if (ast_async_goto(ast, ast->context, "fax", 1, 0))
+                                                       ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, ast->context);
+                                       } else
+                                               ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
+                               } else
+                                       ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
+                       } else
+                                       ast_log(LOG_DEBUG, "Fax already handled\n");
+                       zt_confmute(p, 0);
+                       p->subs[index].f.frametype = AST_FRAME_NULL;
+                       p->subs[index].f.subclass = 0;
+                       f = &p->subs[index].f;
+               } else if (f->subclass == 'm') {
+                       /* Confmute request */
+                       zt_confmute(p, 1);
+                       p->subs[index].f.frametype = AST_FRAME_NULL;
+                       p->subs[index].f.subclass = 0;
+                       f = &p->subs[index].f;          
+               } else if (f->subclass == 'u') {
+                       /* Unmute */
+                       zt_confmute(p, 0);
+                       p->subs[index].f.frametype = AST_FRAME_NULL;
+                       p->subs[index].f.subclass = 0;
+                       f = &p->subs[index].f;          
+               } else
+                       zt_confmute(p, 0);
+       }
+#if 0
+       if (f->frametype == AST_FRAME_VOICE && (ast->_state == AST_STATE_UP)) {
+               p->subs[index].f.frametype = AST_FRAME_NULL;
+               p->subs[index].f.subclass = 0;
+               f = &p->subs[index].f;
        }
+#endif 
        pthread_mutex_unlock(&p->lock);
-       return &p->f[index];
+       return f;
 }
 
-static int my_zt_write(struct zt_pvt *p, unsigned char *buf, int len, int threeway)
+static int my_zt_write(struct zt_pvt *p, unsigned char *buf, int len, int index, int linear)
 {
        int sent=0;
        int size;
        int res;
        int fd;
-       if (threeway) 
-               fd = zap_fd(p->pseudo);
-       else
-               fd = zap_fd(p->z);
+       fd = p->subs[index].zfd;
        while(len) {
                size = len;
-               if (size > READ_SIZE)
-                       size = READ_SIZE;
+               if (size > (linear ? READ_SIZE * 2 : READ_SIZE))
+                       size = (linear ? READ_SIZE * 2 : READ_SIZE);
                res = write(fd, buf, size);
                if (res != size) {
                        if (option_debug)
@@ -2506,16 +3096,12 @@ static int zt_write(struct ast_channel *ast, struct ast_frame *frame)
        struct zt_pvt *p = ast->pvt->pvt;
        int res;
        unsigned char outbuf[4096];
+       int index;
        
-       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;
-               }
+       index = zt_get_index(ast, p, 0);
+       if (index < 0) {
+               ast_log(LOG_WARNING, "%s doesn't really exist?\n", ast->name);
+               return -1;
        }
        
        /* Write a frame of (presumably voice) data */
@@ -2547,41 +3133,24 @@ static int zt_write(struct ast_channel *ast, struct ast_frame *frame)
                ast_log(LOG_WARNING, "Frame too large\n");
                return 0;
        }
+
        if (frame->subclass == AST_FORMAT_SLINEAR) {
-               if (ast == p->owner) {
-                       if (!p->reallinear) {
-                               p->reallinear = 1;
-                               res = zap_setlinear(p->z, p->reallinear);
-                               if (res)
-                                       ast_log(LOG_WARNING, "Unable to set linear mode on channel %d\n", p->channel);
-                       }
-               } else {
-                       if (!p->pseudolinear) {
-                               p->pseudolinear = 1;
-                               res = zap_setlinear(p->pseudo, p->pseudolinear);
-                               if (res)
-                                       ast_log(LOG_WARNING, "Unable to set linear mode on channel %d (pseudo)\n", p->channel);
-                       }
+               if (!p->subs[index].linear) {
+                       p->subs[index].linear = 1;
+                       res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+                       if (res)
+                               ast_log(LOG_WARNING, "Unable to set linear mode on channel %d\n", p->channel);
                }
-               res = my_zt_write(p, (unsigned char *)frame->data, frame->datalen, (ast != p->owner));
+               res = my_zt_write(p, (unsigned char *)frame->data, frame->datalen, index, 1);
        } else {
                /* x-law already */
-               if (ast == p->owner) {
-                       if (p->reallinear) {
-                               p->reallinear = 0;
-                               res = zap_setlinear(p->z, p->reallinear);
-                               if (res)
-                                       ast_log(LOG_WARNING, "Unable to set linear mode on channel %d\n", p->channel);
-                       }
-               } else {
-                       if (p->pseudolinear) {
-                               p->pseudolinear = 0;
-                               res = zap_setlinear(p->pseudo, p->pseudolinear);
-                               if (res)
-                                       ast_log(LOG_WARNING, "Unable to set linear mode on channel %d (pseudo)\n", p->channel);
-                       }
+               if (p->subs[index].linear) {
+                       p->subs[index].linear = 0;
+                       res = zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
+                       if (res)
+                               ast_log(LOG_WARNING, "Unable to set companded mode on channel %d\n", p->channel);
                }
-               res = my_zt_write(p, (unsigned char *)frame->data, frame->datalen, (ast != p->owner));
+               res = my_zt_write(p, (unsigned char *)frame->data, frame->datalen, index, 0);
        }
        if (res < 0) {
                ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
@@ -2594,50 +3163,58 @@ static int zt_indicate(struct ast_channel *chan, int condition)
 {
        struct zt_pvt *p = chan->pvt->pvt;
        int res=-1;
-       switch(condition) {
-       case AST_CONTROL_BUSY:
-               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_BUSY);
-               break;
-       case AST_CONTROL_RINGING:
-               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_RINGTONE);
-               if (chan->_state != AST_STATE_UP) {
-                       if ((chan->_state != AST_STATE_RING) ||
-                               ((p->sig != SIG_FXSKS) &&
-                                (p->sig != SIG_FXSLS) &&
-                                (p->sig != SIG_FXSGS)))
-                               ast_setstate(chan, AST_STATE_RINGING);
+       int index = zt_get_index(chan, p, 0);
+       if (index == SUB_REAL) {
+               switch(condition) {
+               case AST_CONTROL_BUSY:
+                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_BUSY);
+                       break;
+               case AST_CONTROL_RINGING:
+                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_RINGTONE);
+                       if (chan->_state != AST_STATE_UP) {
+                               if ((chan->_state != AST_STATE_RING) ||
+                                       ((p->sig != SIG_FXSKS) &&
+                                        (p->sig != SIG_FXSLS) &&
+                                        (p->sig != SIG_FXSGS)))
+                                       ast_setstate(chan, AST_STATE_RINGING);
+                       }
+                       break;
+               case AST_CONTROL_CONGESTION:
+                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+                       break;
+               case AST_CONTROL_RADIO_KEY:
+                       if (p->radio) 
+                           res =  zt_set_hook(p->subs[index].zfd, ZT_OFFHOOK);
+                       res = 0;
+                       break;
+               case AST_CONTROL_RADIO_UNKEY:
+                       if (p->radio)
+                           res =  zt_set_hook(p->subs[index].zfd, ZT_RINGOFF);
+                       res = 0;
+                       break;
+               case -1:
+                       res = tone_zone_play_tone(p->subs[index].zfd, -1);
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to set condition %d on channel %s\n", condition, chan->name);
                }
-               break;
-       case AST_CONTROL_CONGESTION:
-               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_CONGESTION);
-               break;
-       case -1:
-               res = tone_zone_play_tone(zap_fd(p->z), -1);
-               break;
-       default:
-               ast_log(LOG_WARNING, "Don't know how to set condition %d on channel %s\n", condition, chan->name);
-       }
+       } else
+               res = 0;
        return res;
 }
 
-static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int callwaiting, int thirdcall)
+static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int index, int law)
 {
        struct ast_channel *tmp;
-       int x;
        int deflaw;
        int res;
+       int x,y;
+       int features;
        ZT_PARAMS ps;
-       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(0);
        if (tmp) {
                ps.channo = i->channel;
-               res = ioctl(zap_fd(i->z), ZT_GET_PARAMS, &ps);
+               res = ioctl(i->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &ps);
                if (res) {
                        ast_log(LOG_WARNING, "Unable to get parameters, assuming MULAW\n");
                        ps.curlaw = ZT_LAW_MULAW;
@@ -2646,17 +3223,50 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
                        deflaw = AST_FORMAT_ALAW;
                else
                        deflaw = AST_FORMAT_ULAW;
-               snprintf(tmp->name, sizeof(tmp->name), "Zap/%d-%d", i->channel, x + 1);
+               if (law) {
+                       if (law == ZT_LAW_ALAW)
+                               deflaw = AST_FORMAT_ALAW;
+                       else
+                               deflaw = AST_FORMAT_ULAW;
+               }
+               y = 1;
+               do {
+                       snprintf(tmp->name, sizeof(tmp->name), "Zap/%d-%d", i->channel, y);
+                       for (x=0;x<3;x++) {
+                               if ((index != x) && i->subs[x].owner && !strcasecmp(tmp->name, i->subs[x].owner->name))
+                                       break;
+                       }
+                       y++;
+               } while (x < 3);
                tmp->type = type;
-               tmp->fds[0] = zap_fd(i->z);
-               
+               tmp->fds[0] = i->subs[index].zfd;
                tmp->nativeformats = AST_FORMAT_SLINEAR | deflaw;
-               
                /* Start out assuming ulaw since it's smaller :) */
                tmp->pvt->rawreadformat = deflaw;
                tmp->readformat = deflaw;
                tmp->pvt->rawwriteformat = deflaw;
                tmp->writeformat = deflaw;
+               i->subs[index].linear = 0;
+               zt_setlinear(i->subs[index].zfd, i->subs[index].linear);
+               features = 0;
+               if (i->busydetect && CANBUSYDETECT(i)) {
+                       features |= DSP_FEATURE_BUSY_DETECT;
+               }
+               if (i->callprogress && CANPROGRESSDETECT(i)) {
+                       features |= DSP_FEATURE_CALL_PROGRESS;
+               }
+               features |= DSP_FEATURE_DTMF_DETECT;
+               if (features) {
+                       if (i->dsp) {
+                               ast_log(LOG_DEBUG, "Already have a dsp on %s?\n", tmp->name);
+                       } else {
+                               i->dsp = ast_dsp_new();
+                               if (i->dsp) {
+                                       ast_dsp_set_features(i->dsp, features);
+                                       ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_DTMF | i->dtmfrelax);
+                               }
+                       }
+               }
                
                if (state == AST_STATE_RING)
                        tmp->rings = 1;
@@ -2677,27 +3287,16 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
                        strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
                if (strlen(i->musicclass))
                        strncpy(tmp->musicclass, i->musicclass, sizeof(tmp->musicclass)-1);
-               /* Keep track of who owns it */
-               i->owners[x] = tmp;
                if (!i->owner)
                        i->owner = tmp;
                if (strlen(i->accountcode))
                        strncpy(tmp->accountcode, i->accountcode, sizeof(tmp->accountcode)-1);
                if (i->amaflags)
                        tmp->amaflags = i->amaflags;
-               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;
+               if (i->subs[index].owner) {
+                       ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[index]);
                }
+               i->subs[index].owner = tmp;
                ast_setstate(tmp, state);
                ast_pthread_mutex_lock(&usecnt_lock);
                usecnt++;
@@ -2711,6 +3310,8 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
                        tmp->adsicpe = AST_ADSI_UNAVAILABLE;
                if (strlen(i->exten))
                        strncpy(tmp->exten, i->exten, sizeof(tmp->exten)-1);
+               if (strlen(i->rdnis))
+                       tmp->rdnis = strdup(i->rdnis);
                if (strlen(i->callerid)) {
                        tmp->callerid = strdup(i->callerid);
                        tmp->ani = strdup(i->callerid);
@@ -2736,7 +3337,7 @@ static int bump_gains(struct zt_pvt *p)
 {
        int res;
        /* Bump receive gain by 9.0db */
-       res = set_actual_gain(zap_fd(p->z), 0, p->rxgain + 5.0, p->txgain);
+       res = set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain + 5.0, p->txgain, p->law);
        if (res) {
                ast_log(LOG_WARNING, "Unable to bump gain\n");
                return -1;
@@ -2748,7 +3349,7 @@ static int restore_gains(struct zt_pvt *p)
 {
        int res;
        /* Bump receive gain by 9.0db */
-       res = set_actual_gain(zap_fd(p->z), 0, p->rxgain, p->txgain);
+       res = set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law);
        if (res) {
                ast_log(LOG_WARNING, "Unable to restore gains: %s\n", strerror(errno));
                return -1;
@@ -2756,6 +3357,23 @@ static int restore_gains(struct zt_pvt *p)
        return 0;
 }
 
+static int my_getsigstr(struct ast_channel *chan, char *str, char term, int ms)
+{
+char c;
+
+       *str = 0; /* start with empty output buffer */
+       for(;;)
+       {
+               /* Wait for the first digit (up to specified ms). */
+               c = ast_waitfordigit(chan,ms);
+               /* if timeout, hangup or error, return as such */
+               if (c < 1) return(c);
+               *str++ = c;
+               *str = 0;
+               if (c == term) return(1);
+       }
+}
+
 static void *ss_thread(void *data)
 {
        struct ast_channel *chan = data;
@@ -2764,57 +3382,82 @@ static void *ss_thread(void *data)
        char exten2[AST_MAX_EXTENSION];
        unsigned char buf[256];
        char cid[256];
+       char dtmfbuf[300];
        struct callerid_state *cs;
        char *name=NULL, *number=NULL;
        int flags;
-       int i;
+       int i,j;
        int timeout;
        int getforward=0;
        char *s1, *s2;
        int len = 0;
        int res;
+       int index;
        if (option_verbose > 2) 
                ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s'\n", chan->name);
-       zap_clrdtmf(p->z);
+       index = zt_get_index(chan, p, 1);
+       if (index < 0) {
+               ast_log(LOG_WARNING, "Huh?\n");
+               ast_hangup(chan);
+               return NULL;
+       }
+       if (p->dsp)
+               ast_dsp_digitreset(p->dsp);
        switch(p->sig) {
        case SIG_FEATD:
        case SIG_FEATDMF:
        case SIG_FEATB:
        case SIG_EMWINK:
-               zap_wink(p->z);
+               zt_set_hook(p->subs[index].zfd, ZT_WINK);
+               for(;;)
+               {
+                          /* set bits of interest */
+                       j = ZT_IOMUX_SIGEVENT;
+                           /* wait for some happening */
+                       if (ioctl(p->subs[index].zfd,ZT_IOMUX,&j) == -1) return(NULL);
+                          /* exit loop if we have it */
+                       if (j & ZT_IOMUX_SIGEVENT) break;
+               }
+                 /* get the event info */
+               if (ioctl(p->subs[index].zfd,ZT_GETEVENT,&j) == -1) return(NULL);
                /* Fall through */
        case SIG_EM:
-               res = tone_zone_play_tone(zap_fd(p->z), -1);
-               zap_clrdtmf(p->z);
+               res = tone_zone_play_tone(p->subs[index].zfd, -1);
+               if (p->dsp)
+                       ast_dsp_digitreset(p->dsp);
                /* set digit mode appropriately */
-               if ((p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATB)) zap_digitmode(p->z,ZAP_MF); 
-               else zap_digitmode(p->z,ZAP_DTMF);
+               if (p->dsp) {
+                       if ((p->sig == SIG_FEATDMF) || (p->sig == SIG_FEATB)) 
+                               ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_MF | p->dtmfrelax); 
+                       else 
+                               ast_dsp_digitmode(p->dsp,DSP_DIGITMODE_DTMF | p->dtmfrelax);
+               }
                /* Wait for the first digit (up to 5 seconds). */
-               res = zap_getdtmf(p->z, 1, NULL, 0, 5000, 5000, ZAP_TIMEOUTOK | ZAP_HOOKEXIT);
-
-               if (res == 1) {
+               res = ast_waitfordigit(chan,5000);
+               if (res > 0) {
+                       /* save first char */
+                       dtmfbuf[0] = res;
                        switch(p->sig)
                        {
                            case SIG_FEATD:
-                               res = zap_getdtmf(p->z, 50, "*", 0, 3000, 15000, ZAP_HOOKEXIT);
+                               res = my_getsigstr(chan,dtmfbuf + 1,'*',3000);
                                if (res > 0)
-                                       res = zap_getdtmf(p->z, 50, "*", 0, 3000, 15000, ZAP_HOOKEXIT);
-                               if (res < 1) zap_clrdtmf(p->z);
+                                       res = my_getsigstr(chan,dtmfbuf + strlen(dtmfbuf),'*',3000);
+                               if (res < 1) ast_dsp_digitreset(p->dsp);
                                break;
                            case SIG_FEATDMF:
-                               res = zap_getdtmf(p->z, 50, "#", 0, 3000, 15000, ZAP_HOOKEXIT);
-                               if (res > 0) {
-                                       res = zap_getdtmf(p->z, 50, "#", 0, 3000, 15000, ZAP_HOOKEXIT);
-                               }
-                               if (res < 1) zap_clrdtmf(p->z);
+                               res = my_getsigstr(chan,dtmfbuf + 1,'#',3000);
+                               if (res > 0)
+                                       res = my_getsigstr(chan,dtmfbuf + strlen(dtmfbuf),'#',3000);
+                               if (res < 1) ast_dsp_digitreset(p->dsp);
                                break;
                            case SIG_FEATB:
-                               res = zap_getdtmf(p->z, 50, "#", 0, 3000, 15000, ZAP_HOOKEXIT);
-                               if (res < 1) zap_clrdtmf(p->z);
+                               res = my_getsigstr(chan,dtmfbuf + 1,'#',3000);
+                               if (res < 1) ast_dsp_digitreset(p->dsp);
                                break;
                            default:
                                /* If we got it, get the rest */
-                               res = zap_getdtmf(p->z, 50, NULL, 0, 250, 15000, ZAP_TIMEOUTOK | ZAP_HOOKEXIT);
+                               res = my_getsigstr(chan,dtmfbuf + 1,' ',250);
                                break;
                        }
                }
@@ -2827,15 +3470,17 @@ static void *ss_thread(void *data)
                        ast_hangup(chan);
                        return NULL;
                }
-               strncpy(exten, zap_dtmfbuf(p->z), sizeof(exten)-1);
+               strncpy(exten, dtmfbuf, sizeof(exten)-1);
                if (!strlen(exten))
                        strncpy(exten, "s", sizeof(exten)-1);
                if (p->sig == SIG_FEATD) {
                        if (exten[0] == '*') {
+                               char *stringp=NULL;
                                strncpy(exten2, exten, sizeof(exten2)-1);
                                /* Parse out extension and callerid */
-                               s1 = strtok(exten2 + 1, "*");
-                               s2 = strtok(NULL, "*");
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "*");
+                               s2 = strsep(&stringp, "*");
                                if (s2) {
                                        if (strlen(p->callerid))
                                                chan->callerid = strdup(p->callerid);
@@ -2851,10 +3496,12 @@ static void *ss_thread(void *data)
                }
                if (p->sig == SIG_FEATDMF) {
                        if (exten[0] == '*') {
+                               char *stringp=NULL;
                                strncpy(exten2, exten, sizeof(exten2)-1);
                                /* Parse out extension and callerid */
-                               s1 = strtok(exten2 + 1, "#");
-                               s2 = strtok(NULL, "#");
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "#");
+                               s2 = strsep(&stringp, "#");
                                if (s2) {
                                        if (strlen(p->callerid))
                                                chan->callerid = strdup(p->callerid);
@@ -2870,9 +3517,11 @@ static void *ss_thread(void *data)
                }
                if (p->sig == SIG_FEATB) {
                        if (exten[0] == '*') {
+                               char *stringp=NULL;
                                strncpy(exten2, exten, sizeof(exten2)-1);
                                /* Parse out extension and callerid */
-                               s1 = strtok(exten2 + 1, "#");
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "#");
                                strncpy(exten, exten2 + 1, sizeof(exten)-1);
                        } else
                                ast_log(LOG_WARNING, "Got a non-Feature Group B input on channel %d.  Assuming E&M Wink instead\n", p->channel);
@@ -2880,18 +3529,18 @@ static void *ss_thread(void *data)
                zt_enable_ec(p);
                if (ast_exists_extension(chan, chan->context, exten, 1, chan->callerid)) {
                        strncpy(chan->exten, exten, sizeof(chan->exten)-1);
-                       zap_clrdtmf(p->z);
+                       ast_dsp_digitreset(p->dsp);
                        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), ZT_TONE_CONGESTION);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
                        }
                        return NULL;
                } else {
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_2 "Unknown extension '%s' in context '%s' requested\n", exten, chan->context);
                        sleep(2);
-                       res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_INFO);
+                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_INFO);
                        if (res < 0)
                                ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel);
                        else
@@ -2899,7 +3548,7 @@ static void *ss_thread(void *data)
                        res = ast_streamfile(chan, "ss-noservice", chan->language);
                        if (res >= 0)
                                ast_waitstream(chan, "");
-                       res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_CONGESTION);
+                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
                        ast_hangup(chan);
                        return NULL;
                }
@@ -2911,68 +3560,77 @@ static void *ss_thread(void *data)
                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), ZT_TONE_CONGESTION);
-                               zt_wait_event(zap_fd(p->z));
+                       timeout = 0;
+                       if (res < 0) {
+                               ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n");
+                               res = tone_zone_play_tone(p->subs[index].zfd, -1);
                                ast_hangup(chan);
                                return NULL;
-                       } else {
+                       } else if (res)  {
                                exten[len++]=res;
                                exten[len] = '\0';
                        }
                        if (!ast_ignore_pattern(chan->context, exten))
-                               tone_zone_play_tone(zap_fd(p->z), -1);
+                               tone_zone_play_tone(p->subs[index].zfd, -1);
+                       else
+                               tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
                        if (ast_exists_extension(chan, chan->context, exten, 1, p->callerid)) {
-                               if (getforward) {
-                                       /* Record this as the forwarding extension */
-                                       strncpy(p->call_forward, exten, sizeof(p->call_forward)); 
-                                       if (option_verbose > 2)
-                                               ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel);
-                                       res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
-                                       if (res)
-                                               break;
-                                       usleep(500000);
-                                       res = tone_zone_play_tone(zap_fd(p->z), -1);
-                                       sleep(1);
-                                       memset(exten, 0, sizeof(exten));
-                                       res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALTONE);
-                                       len = 0;
-                                       getforward = 0;
-                               } else  {
-                                       res = tone_zone_play_tone(zap_fd(p->z), -1);
-                                       strncpy(chan->exten, exten, sizeof(chan->exten)-1);
-                                       if (strlen(p->callerid)) {
-                                               if (!p->hidecallerid)
-                                                       chan->callerid = strdup(p->callerid);
-                                               chan->ani = strdup(p->callerid);
-                                       }
-                                       ast_setstate(chan, AST_STATE_RING);
-                                       zt_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), ZT_TONE_CONGESTION);
+                               if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->callerid)) {
+                                       if (getforward) {
+                                               /* Record this as the forwarding extension */
+                                               strncpy(p->call_forward, exten, sizeof(p->call_forward)); 
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel);
+                                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+                                               if (res)
+                                                       break;
+                                               usleep(500000);
+                                               res = tone_zone_play_tone(p->subs[index].zfd, -1);
+                                               sleep(1);
+                                               memset(exten, 0, sizeof(exten));
+                                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
+                                               len = 0;
+                                               getforward = 0;
+                                       } else  {
+                                               res = tone_zone_play_tone(p->subs[index].zfd, -1);
+                                               strncpy(chan->exten, exten, sizeof(chan->exten)-1);
+                                               if (strlen(p->callerid)) {
+                                                       if (!p->hidecallerid)
+                                                               chan->callerid = strdup(p->callerid);
+                                                       chan->ani = strdup(p->callerid);
+                                               }
+                                               ast_setstate(chan, AST_STATE_RING);
+                                               zt_enable_ec(p);
+                                               res = ast_pbx_run(chan);
+                                               if (res) {
+                                                       ast_log(LOG_WARNING, "PBX exited non-zero\n");
+                                                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+                                               }
+                                               return NULL;
                                        }
-                                       return NULL;
+                               } else {
+                                       /* It's a match, but they just typed a digit, and there is an ambiguous match,
+                                          so just set the timeout to matchdigittimeout and wait some more */
+                                       timeout = matchdigittimeout;
                                }
+                       } else if (res == 0) {
+                               ast_log(LOG_DEBUG, "not enough digits (and no ambiguous match)...\n");
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+                               zt_wait_event(p->subs[index].zfd);
+                               ast_hangup(chan);
+                               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), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                if (res) {
                                        ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
                                                chan->name, strerror(errno));
                                }
                                len = 0;
-                               ioctl(zap_fd(p->z),ZT_CONFDIAG,&len);
+                               ioctl(p->subs[index].zfd,ZT_CONFDIAG,&len);
                                memset(exten, 0, sizeof(exten));
                                timeout = firstdigittimeout;
                                        
@@ -2988,22 +3646,32 @@ static void *ss_thread(void *data)
                                          (chan_pvt->owner && (chan_pvt->owner->_state==AST_STATE_RING || chan_pvt->owner->_state == AST_STATE_RINGING)) &&
                                          chan_pvt->dialing
                                          ){
-                                               /* Switch us from Third call to Call Wait */
-                                               p->callwaitindex = p->thirdcallindex;
-                                               p->thirdcallindex = -1;
-                                               ast_log(LOG_DEBUG, "Call pickup on chan %s\n",chan_pvt->owner->name);
-                                               p->needanswer[zt_get_index(chan, p, 1)]=1;
-                                               zt_enable_ec(p);
-                                               if(ast_channel_masquerade(chan_pvt->owner,p->owner))
-                                                       printf("Error Masquerade failed on call-pickup\n");
-                                               /* Do not hang up masqueraded channel */
+                                               if (index == SUB_REAL) {
+                                                       if (p->subs[SUB_THREEWAY].owner) {
+                                                               /* If you make a threeway call and the *8# a call, it should actually 
+                                                                  look like a callwait */
+                                                               alloc_sub(p, SUB_CALLWAIT);
+                                                               swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
+                                                               unalloc_sub(p, SUB_THREEWAY);
+                                                       }
+                                                       /* Switch us from Third call to Call Wait */
+                                                       ast_log(LOG_DEBUG, "Call pickup on chan %s\n",chan_pvt->owner->name);
+                                                       p->subs[index].needanswer=1;
+                                                       zt_enable_ec(p);
+                                                       if(ast_channel_masquerade(chan_pvt->owner,p->owner))
+                                                               printf("Error Masquerade failed on call-pickup\n");
+                                                       ast_hangup(p->owner);
+                                               } else {
+                                                       ast_log(LOG_WARNING, "Huh?  Got *8# on call not on real\n");
+                                                       ast_hangup(p->owner);
+                                               }
                                                return NULL;
                                        }
                                        chan_pvt=chan_pvt->next;
                                }
                                ast_log(LOG_DEBUG, "No call pickup possible...\n");
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_CONGESTION);
-                               zt_wait_event(zap_fd(p->z));
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+                               zt_wait_event(p->subs[index].zfd);
                                ast_hangup(chan);
                                return NULL;
                        } else if (!p->hidecallerid && !strcmp(exten, "*67")) {
@@ -3014,7 +3682,7 @@ static void *ss_thread(void *data)
                                if (chan->callerid)
                                        free(chan->callerid);
                                chan->callerid = NULL;
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                if (res) {
                                        ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
                                                chan->name, strerror(errno));
@@ -3022,11 +3690,19 @@ static void *ss_thread(void *data)
                                len = 0;
                                memset(exten, 0, sizeof(exten));
                                timeout = firstdigittimeout;
+                       } else if (p->callreturn && !strcmp(exten, "*69")) {
+                               res = 0;
+                               if (strlen(p->lastcallerid)) {
+                                       res = ast_say_digit_str(chan, p->lastcallerid, "", chan->language);
+                               }
+                               if (!res)
+                                       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
+                               break;
                        } else if (!strcmp(exten, "*78")) {
                                /* Do not disturb */
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Enabled DND on channel %d\n", p->channel);
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                p->dnd = 1;
                                getforward = 0;
                                memset(exten, 0, sizeof(exten));
@@ -3035,30 +3711,30 @@ static void *ss_thread(void *data)
                                /* Do not disturb */
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %d\n", p->channel);
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                p->dnd = 0;
                                getforward = 0;
                                memset(exten, 0, sizeof(exten));
                                len = 0;
                        } else if (p->cancallforward && !strcmp(exten, "*72")) {
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                getforward = 1;
                                memset(exten, 0, sizeof(exten));
                                len = 0;
                        } else if (p->cancallforward && !strcmp(exten, "*73")) {
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Cancelling call forwarding on channel %d\n", p->channel);
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                memset(p->call_forward, 0, sizeof(p->call_forward));
                                getforward = 0;
                                memset(exten, 0, sizeof(exten));
                                len = 0;
                        } else if (p->transfer && !strcmp(exten, ast_parking_ext()) && 
-                                               (zt_get_index(chan, p, 1) == p->thirdcallindex) &&
-                                               p->owners[p->normalindex]->bridge) {
+                                               p->subs[SUB_THREEWAY].owner &&
+                                               p->subs[SUB_THREEWAY].owner->bridge) {
                                /* This is a three way call, the main call being a real channel, 
                                        and we're parking the first call. */
-                               ast_masq_park_call(p->owners[p->normalindex]->bridge, chan);
+                               ast_masq_park_call(p->subs[SUB_THREEWAY].owner->bridge, chan, 0, NULL);
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name);
                                break;
@@ -3071,7 +3747,7 @@ static void *ss_thread(void *data)
                                        free(chan->callerid);
                                if (strlen(p->callerid))
                                        chan->callerid = strdup(p->callerid);
-                               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_DIALRECALL);
+                               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALRECALL);
                                if (res) {
                                        ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
                                                chan->name, strerror(errno));
@@ -3080,31 +3756,32 @@ static void *ss_thread(void *data)
                                memset(exten, 0, sizeof(exten));
                                timeout = firstdigittimeout;
                        } else if (!strcmp(exten, "*0")) {
-                               int index = zt_get_index(chan, p, 0);
                                struct ast_channel *nbridge = 
-                                       p->owners[p->normalindex]->bridge;
+                                       p->subs[SUB_THREEWAY].owner;
                                struct zt_pvt *pbridge = NULL;
                                  /* set up the private struct of the bridged one, if any */
-                               if (nbridge) pbridge = nbridge->pvt->pvt;
-                               if ((p->thirdcallindex > -1) &&
-                                   (index == p->thirdcallindex) &&
-                                   nbridge && 
+                               if (nbridge && nbridge->bridge) pbridge = nbridge->bridge->pvt->pvt;
+                               if (nbridge && 
                                    (!strcmp(nbridge->type,"Zap")) &&
                                    ISTRUNK(pbridge)) {
                                        int func = ZT_FLASH;
                                        /* flash hookswitch */
-                                       if ((ioctl(zap_fd(pbridge->z),ZT_HOOK,&func) == -1) && (errno != EINPROGRESS)) {
+                                       if ((ioctl(pbridge->subs[SUB_REAL].zfd,ZT_HOOK,&func) == -1) && (errno != EINPROGRESS)) {
                                                ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n", 
                                                        nbridge->name, strerror(errno));
-                                       }                               
-                                       p->owner = p->owners[p->normalindex];
+                                       }
+                                       swap_subs(p, SUB_REAL, SUB_THREEWAY);
+                                       unalloc_sub(p, SUB_THREEWAY);
+                                       p->owner = p->subs[SUB_REAL].owner;
                                        ast_hangup(chan);
                                        return NULL;
                                } else {
-                                       tone_zone_play_tone(zap_fd(p->z), ZT_TONE_CONGESTION);
-                                       zt_wait_event(zap_fd(p->z));
-                                       tone_zone_play_tone(zap_fd(p->z), -1);
-                                       p->owner = p->owners[p->normalindex];
+                                       tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
+                                       zt_wait_event(p->subs[index].zfd);
+                                       tone_zone_play_tone(p->subs[index].zfd, -1);
+                                       swap_subs(p, SUB_REAL, SUB_THREEWAY);
+                                       unalloc_sub(p, SUB_THREEWAY);
+                                       p->owner = p->subs[SUB_REAL].owner;
                                        ast_hangup(chan);
                                        return NULL;
                                }                                       
@@ -3114,9 +3791,10 @@ static void *ss_thread(void *data)
                                        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 (!timeout)
+                               timeout = gendigittimeout;
                        if (len && !ast_ignore_pattern(chan->context, exten))
-                               tone_zone_play_tone(zap_fd(p->z), -1);
+                               tone_zone_play_tone(p->subs[index].zfd, -1);
                }
                break;
        case SIG_FXSLS:
@@ -3129,21 +3807,23 @@ static void *ss_thread(void *data)
                                bump_gains(p);
 #endif                         
                                len = 0;
+                               /* Take out of linear mode for Caller*ID processing */
+                               zt_setlinear(p->subs[index].zfd, 0);
                                for(;;) {       
                                        i = ZT_IOMUX_READ | ZT_IOMUX_SIGEVENT;
-                                       if ((res = ioctl(zap_fd(p->z), ZT_IOMUX, &i)))  {
+                                       if ((res = ioctl(p->subs[index].zfd, ZT_IOMUX, &i)))    {
                                                ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
                                                callerid_free(cs);
                                                ast_hangup(chan);
                                                return NULL;
                                        }
                                        if (i & ZT_IOMUX_SIGEVENT) {
-                                               res = zt_get_event(zap_fd(p->z));
+                                               res = zt_get_event(p->subs[index].zfd);
                                                ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
                                                res = 0;
                                                break;
                                        } else if (i & ZT_IOMUX_READ) {
-                                               res = read(zap_fd(p->z), buf, sizeof(buf));
+                                               res = read(p->subs[index].zfd, buf, sizeof(buf));
                                                if (res < 0) {
                                                        if (errno != ELAST) {
                                                                ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
@@ -3159,7 +3839,7 @@ static void *ss_thread(void *data)
                                                        res = -1;
                                                        break;
                                                }
-                                               res = callerid_feed(cs, buf, res);
+                                               res = callerid_feed(cs, buf, res, AST_LAW(p));
                                                if (res < 0) {
                                                        ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno));
                                                        break;
@@ -3172,6 +3852,8 @@ static void *ss_thread(void *data)
                                        if (option_debug)
                                                ast_log(LOG_DEBUG, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags);
                                }
+                               /* Restore linear mode (if appropriate) for Caller*ID processing */
+                               zt_setlinear(p->subs[index].zfd, p->subs[index].linear);
 #if 1
                                restore_gains(p);
 #endif                         
@@ -3206,17 +3888,61 @@ static void *ss_thread(void *data)
                return NULL;
        default:
                ast_log(LOG_WARNING, "Don't know how to handle simple switch with signalling %s on channel %d\n", sig2str(p->sig), p->channel);
-               res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_CONGESTION);
+               res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
                if (res < 0)
                                ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
        }
-       res = tone_zone_play_tone(zap_fd(p->z), ZT_TONE_CONGESTION);
+       res = tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_CONGESTION);
        if (res < 0)
                        ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
        ast_hangup(chan);
        return NULL;
 }
 
+#ifdef ZAPATA_R2
+static int handle_init_r2_event(struct zt_pvt *i, mfcr2_event_t *e)
+{
+       struct ast_channel *chan;
+       
+       switch(e->e) {
+       case MFCR2_EVENT_UNBLOCKED:
+               i->r2blocked = 0;
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "R2 Channel %d unblocked\n", i->channel);
+               break;
+       case MFCR2_EVENT_BLOCKED:
+               i->r2blocked = 1;
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "R2 Channel %d unblocked\n", i->channel);
+               break;
+       case MFCR2_EVENT_IDLE:
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "R2 Channel %d idle\n", i->channel);
+               break;
+       case MFCR2_EVENT_RINGING:
+                       /* This is what Asterisk refers to as a "RING" event. For some reason they're reversed in
+                          Steve's code */
+                       /* Check for callerid, digits, etc */
+                       i->hasr2call = 1;
+                       chan = zt_new(i, AST_STATE_RING, 0, SUB_REAL, 0);
+                       if (!chan) {
+                               ast_log(LOG_WARNING, "Unable to create channel for channel %d\n", i->channel);
+                               mfcr2_DropCall(i->r2, NULL, UC_NETWORK_CONGESTION);
+                               i->hasr2call = 0;
+                       }
+                       if (ast_pbx_start(chan)) {
+                               ast_log(LOG_WARNING, "Unable to start PBX on channel %s\n", chan->name);
+                               ast_hangup(chan);
+                       }
+                       break;
+       default:
+               ast_log(LOG_WARNING, "Don't know how to handle initial R2 event %s on channel %d\n", mfcr2_event2str(e->e), i->channel);        
+               return -1;
+       }
+       return 0;
+}
+#endif
+
 static int handle_init_event(struct zt_pvt *i, int event)
 {
        int res;
@@ -3225,8 +3951,22 @@ static int handle_init_event(struct zt_pvt *i, int event)
        struct ast_channel *chan;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       if (i->radio) return 0;
        /* Handle an event on a given channel for the monitor thread. */
        switch(event) {
+       case ZT_EVENT_NONE:
+       case ZT_EVENT_BITSCHANGED:
+#ifdef ZAPATA_R2
+               if (i->r2) {
+                       mfcr2_event_t *e;
+                       e = r2_get_event_bits(i);
+                       i->sigchecked = 1;
+                       if (e)
+                               handle_init_r2_event(i, e);
+               }
+#endif         
+               break;
+       case ZT_EVENT_WINKFLASH:
        case ZT_EVENT_RINGOFFHOOK:
                if (i->inalarm) break;
                /* Got a ring/answer.  What kind of channel are we? */
@@ -3234,30 +3974,35 @@ static int handle_init_event(struct zt_pvt *i, int event)
                case SIG_FXOLS:
                case SIG_FXOGS:
                case SIG_FXOKS:
+                       if (i->cidspill) {
+                               /* Cancel VMWI spill */
+                               free(i->cidspill);
+                               i->cidspill = NULL;
+                       }
                        if (i->immediate) {
                                zt_enable_ec(i);
                                /* The channel is immediately up.  Start right away */
-                               res = tone_zone_play_tone(zap_fd(i->z), ZT_TONE_RINGTONE);
-                               chan = zt_new(i, AST_STATE_RING, 1, 0, 0);
+                               res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_RINGTONE);
+                               chan = zt_new(i, AST_STATE_RING, 1, SUB_REAL, 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), ZT_TONE_CONGESTION);
+                                       res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
                                        if (res < 0)
                                                ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
                                }
                        } else {
                                /* Check for callerid, digits, etc */
-                               chan = zt_new(i, AST_STATE_DOWN, 0, 0, 0);
+                               chan = zt_new(i, AST_STATE_DOWN, 0, SUB_REAL, 0);
                                if (chan) {
                                        if (has_voicemail(i))
-                                               res = tone_zone_play_tone(zap_fd(i->z), ZT_TONE_DIALRECALL);
+                                               res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_DIALRECALL);
                                        else
-                                               res = tone_zone_play_tone(zap_fd(i->z), ZT_TONE_DIALTONE);
+                                               res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
                                        if (res < 0) 
                                                ast_log(LOG_WARNING, "Unable to play dialtone on channel %d\n", i->channel);
                                        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), ZT_TONE_CONGESTION);
+                                               res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
                                                if (res < 0)
                                                        ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
                                                ast_hangup(chan);
@@ -3280,10 +4025,10 @@ static int handle_init_event(struct zt_pvt *i, int event)
                case SIG_FEATB:
                case SIG_EM:
                                /* Check for callerid, digits, etc */
-                               chan = zt_new(i, AST_STATE_RING, 0, 0, 0);
+                               chan = zt_new(i, AST_STATE_RING, 0, SUB_REAL, 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), ZT_TONE_CONGESTION);
+                                       res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
                                        if (res < 0)
                                                ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
                                        ast_hangup(chan);
@@ -3294,7 +4039,7 @@ static int handle_init_event(struct zt_pvt *i, int event)
                                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);
-                       res = tone_zone_play_tone(zap_fd(i->z), ZT_TONE_CONGESTION);
+                       res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, ZT_TONE_CONGESTION);
                        if (res < 0)
                                        ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
                        return -1;
@@ -3306,7 +4051,6 @@ static int handle_init_event(struct zt_pvt *i, int event)
        case ZT_EVENT_ALARM:
                i->inalarm = 1;
                /* fall thru intentionally */
-       case ZT_EVENT_WINKFLASH:
        case ZT_EVENT_ONHOOK:
                /* Back on hook.  Hang up. */
                switch(i->sig) {
@@ -3321,22 +4065,22 @@ static int handle_init_event(struct zt_pvt *i, int event)
                case SIG_FXSGS:
                case SIG_FXSKS:
                        zt_disable_ec(i);
-                       res = tone_zone_play_tone(zap_fd(i->z), -1);
-                       zt_set_hook(zap_fd(i->z), ZT_ONHOOK);
+                       res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
+                       zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK);
                        break;
                case SIG_FXOKS:
                        zt_disable_ec(i);
                        /* Diddle the battery for the zhone */
 #ifdef ZHONE_HACK
-                       zt_set_hook(zap_fd(i->z), ZT_OFFHOOK);
+                       zt_set_hook(i->subs[SUB_REAL].zfd, ZT_OFFHOOK);
                        usleep(1);
 #endif                 
-                       res = tone_zone_play_tone(zap_fd(i->z), -1);
-                       zt_set_hook(zap_fd(i->z), ZT_ONHOOK);
+                       res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
+                       zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK);
                        break;
                default:
-                       ast_log(LOG_WARNING, "Don't know hwo to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
-                       res = tone_zone_play_tone(zap_fd(i->z), -1);
+                       ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
+                       res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
                        return -1;
                }
                break;
@@ -3347,8 +4091,14 @@ static int handle_init_event(struct zt_pvt *i, int event)
 static void *do_monitor(void *data)
 {
        fd_set efds;
-       int n, res;
+       fd_set rfds;
+       int n, res, res2;
        struct zt_pvt *i;
+       struct zt_pvt *last = NULL;
+       struct timeval tv;
+       time_t thispass = 0, lastpass = 0;
+       int found;
+       char buf[1024];
        /* This thread monitors all the frame relay interfaces which are not yet in use
           (and thus do not have a separate thread) indefinitely */
        /* From here on out, we die whenever asked */
@@ -3369,16 +4119,24 @@ static void *do_monitor(void *data)
                   zt_pvt that does not have an associated owner channel */
                n = -1;
                FD_ZERO(&efds);
+               FD_ZERO(&rfds);
                i = iflist;
                while(i) {
-                       if (i->z && i->sig) {
-                               if (FD_ISSET(zap_fd(i->z), &efds)) 
-                                       ast_log(LOG_WARNING, "Descriptor %d appears twice?\n", zap_fd(i->z));
-                               if (!i->owner) {
+                       if ((i->subs[SUB_REAL].zfd > -1) && i->sig && (!i->radio)) {
+                               if (FD_ISSET(i->subs[SUB_REAL].zfd, &efds)) 
+                                       ast_log(LOG_WARNING, "Descriptor %d appears twice?\n", i->subs[SUB_REAL].zfd);
+                               if (!i->owner && !i->subs[SUB_REAL].owner) {
                                        /* This needs to be watched, as it lacks an owner */
-                                       FD_SET(zap_fd(i->z), &efds);
-                                       if (zap_fd(i->z) > n)
-                                               n = zap_fd(i->z);
+                                       FD_SET(i->subs[SUB_REAL].zfd, &efds);
+                                       /* Message waiting or r2 channels also get watched for reading */
+#ifdef ZAPATA_R2
+                                       if (i->cidspill || i->r2)
+#else                                  
+                                       if (i->cidspill)
+#endif                                 
+                                               FD_SET(i->subs[SUB_REAL].zfd, &rfds);
+                                       if (i->subs[SUB_REAL].zfd > n)
+                                               n = i->subs[SUB_REAL].zfd;
                                }
                        }
                        i = i->next;
@@ -3387,8 +4145,10 @@ static void *do_monitor(void *data)
                ast_pthread_mutex_unlock(&iflock);
                
                pthread_testcancel();
-               /* Wait indefinitely for something to happen */
-               res = select(n + 1, NULL, NULL, &efds, NULL);
+               /* Wait at least a second for something to happen */
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               res = select(n + 1, &rfds, NULL, &efds, &tv);
                pthread_testcancel();
                /* Okay, select has finished.  Let's see what happened.  */
                if (res < 0) {
@@ -3402,16 +4162,113 @@ static void *do_monitor(void *data)
                        ast_log(LOG_WARNING, "Unable to lock the interface list\n");
                        continue;
                }
+               found = 0;
+               lastpass = thispass;
+               thispass = time(NULL);
                i = iflist;
                while(i) {
-                       if (i->z && i->sig) {
-                               if (FD_ISSET(zap_fd(i->z), &efds)) {
-                                       if (i->owner) {
-                                               ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d)...\n", zap_fd(i->z));
+                       if (thispass != lastpass) {
+                               if (!found && ((i == last) || ((i == iflist) && !last))) {
+                                       last = i;
+                                       if (last) {
+#if 0
+                                               printf("Checking channel %d\n", last->channel);
+#endif                                         
+                                               if (!last->cidspill && !last->owner && strlen(last->mailbox) && (thispass - last->onhooktime > 3) &&
+                                                       (last->sig & __ZT_SIG_FXO)) {
+#if 0
+                                                       printf("Channel %d has mailbox %s\n", last->channel, last->mailbox);
+#endif                                                 
+                                                       res = ast_app_has_voicemail(last->mailbox);
+                                                       if (last->msgstate != res) {
+                                                               int x;
+                                                               ast_log(LOG_DEBUG, "Message status for %s changed from %d to %d on %d\n", last->mailbox, last->msgstate, res, last->channel);
+                                                               x = ZT_FLUSH_WRITE;
+                                                               res2 = ioctl(last->subs[SUB_REAL].zfd, ZT_FLUSH, &x);
+                                                               if (res2)
+                                                                       ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", last->channel);
+                                                               last->cidspill = malloc(8192);
+                                                               if (last->cidspill) {
+                                                                       last->cidlen = vmwi_generate(last->cidspill, res, 1, AST_LAW(last));
+                                                                       last->cidpos = 0;
+#if 0
+                                                                       printf("Made %d bytes of message waiting for %d\n", last->cidlen, res);
+#endif                                                                 
+                                                                       last->msgstate = res;
+                                                                       last->onhooktime = thispass;
+                                                               }
+                                                               found ++;
+                                                       }
+                                               }
+                                               last = last->next;
+                                       }
+                               }
+                       }
+                       if ((i->subs[SUB_REAL].zfd > -1) && i->sig && (!i->radio)) {
+                               if (FD_ISSET(i->subs[SUB_REAL].zfd, &rfds)) {
+                                       if (i->owner || i->subs[SUB_REAL].owner) {
+                                               ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d) in read...\n", i->subs[SUB_REAL].zfd);
+                                               i = i->next;
+                                               continue;
+                                       }
+#ifdef ZAPATA_R2
+                                       if (i->r2) {
+                                               /* If it's R2 signalled, we always have to check for events */
+                                               mfcr2_event_t *e;
+                                               e = mfcr2_check_event(i->r2);
+                                               if (e)
+                                                       handle_init_r2_event(i, e);
+                                               else {
+                                                       e = mfcr2_schedule_run(i->r2);
+                                                       if (e)
+                                                               handle_init_r2_event(i, e);
+                                               }
+                                               i = i->next;
+                                               continue;
+                                       }
+#endif
+                                       if (!i->cidspill) {
+                                               ast_log(LOG_WARNING, "Whoa....  I'm reading but have no cidspill (%d)...\n", i->subs[SUB_REAL].zfd);
+                                               i = i->next;
+                                               continue;
+                                       }
+                                       res = read(i->subs[SUB_REAL].zfd, buf, sizeof(buf));
+                                       if (res > 0) {
+                                               /* We read some number of bytes.  Write an equal amount of data */
+                                               if (res > i->cidlen - i->cidpos) 
+                                                       res = i->cidlen - i->cidpos;
+                                               res2 = write(i->subs[SUB_REAL].zfd, i->cidspill + i->cidpos, res);
+                                               if (res2 > 0) {
+                                                       i->cidpos += res2;
+                                                       if (i->cidpos >= i->cidlen) {
+                                                               free(i->cidspill);
+                                                               i->cidspill = 0;
+                                                               i->cidpos = 0;
+                                                               i->cidlen = 0;
+                                                       }
+                                               } else {
+                                                       ast_log(LOG_WARNING, "Write failed: %s\n", strerror(errno));
+                                                       i->msgstate = -1;
+                                               }
+                                       } else {
+                                               ast_log(LOG_WARNING, "Read failed with %d: %s\n", res, strerror(errno));
+                                       }
+                                       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);
+                               }
+#ifdef ZAPATA_R2
+                               if (FD_ISSET(i->subs[SUB_REAL].zfd, &efds) || (i->r2 && !i->sigchecked)) 
+#else                          
+                               if (FD_ISSET(i->subs[SUB_REAL].zfd, &efds)) 
+#endif                         
+                               {
+                                       if (i->owner || i->subs[SUB_REAL].owner) {
+                                               ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d)...\n", i->subs[SUB_REAL].zfd);
                                                i = i->next;
                                                continue;
                                        }
-                                       res = zt_get_event(zap_fd(i->z));
+                                       res = zt_get_event(i->subs[SUB_REAL].zfd);
                                        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);
@@ -3479,13 +4336,13 @@ static int reset_channel(struct zt_pvt *p)
                p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
        }
        for (i = 0; i < 3; i++) {
-               if (p->owners[i]) {
+               if (p->subs[i].owner) {
                        ioctlflag = 0;
-                       p->owners[i]->_softhangup |= AST_SOFTHANGUP_DEV;
+                       p->subs[i].owner->_softhangup |= AST_SOFTHANGUP_DEV;
                }
        }
        if (ioctlflag) {
-               res = zt_set_hook(zap_fd(p->z), ZT_ONHOOK);
+               res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
                if (res < 0) {
                        ast_log(LOG_ERROR, "Unable to hangup chan_zap channel %d (ioctl)\n", p->channel);
                        return -1;
@@ -3496,7 +4353,7 @@ static int reset_channel(struct zt_pvt *p)
 }
 
 
-static struct zt_pvt *mkintf(int channel, int signalling)
+static struct zt_pvt *mkintf(int channel, int signalling, int radio)
 {
        /* Make a zt_pvt structure for this interface */
        struct zt_pvt *tmp = NULL, *tmp2,  *prev = NULL;
@@ -3508,6 +4365,7 @@ static struct zt_pvt *mkintf(int channel, int signalling)
        int res;
        int span=0;
        int here = 0;
+       int x;
        ZT_PARAMS p;
 
        tmp2 = iflist;
@@ -3533,6 +4391,8 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                        return NULL;
                }
                memset(tmp, 0, sizeof(struct zt_pvt));
+               for (x=0;x<3;x++)
+                       tmp->subs[x].zfd = -1;
                tmp->next = tmp2;
                if (!prev) {
                        iflist = tmp;
@@ -3546,15 +4406,15 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                        snprintf(fn, sizeof(fn), "%d", channel);
                        /* Open non-blocking */
                        if (!here)
-                               tmp->z = zap_open(fn, 1);
+                               tmp->subs[SUB_REAL].zfd = zt_open(fn);
                        /* Allocate a zapata structure */
-                       if (!tmp->z) {
+                       if (tmp->subs[SUB_REAL].zfd < 0) {
                                ast_log(LOG_ERROR, "Unable to open channel %d: %s\nhere = %d, tmp->channel = %d, channel = %d\n", channel, strerror(errno), here, tmp->channel, channel);
                                free(tmp);
                                return NULL;
                        }
                        memset(&p, 0, sizeof(p));
-                       res = ioctl(zap_fd(tmp->z), ZT_GET_PARAMS, &p);
+                       res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_GET_PARAMS, &p);
                        if (res < 0) {
                                ast_log(LOG_ERROR, "Unable to get parameters\n");
                                free(tmp);
@@ -3585,7 +4445,7 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                        int numchans;
                        int dchannel;
                        offset = 1;
-                       if (ioctl(zap_fd(tmp->z), ZT_AUDIOMODE, &offset)) {
+                       if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &offset)) {
                                ast_log(LOG_ERROR, "Unable to set audio mode on clear channel %d of span %d: %s\n", channel, p.spanno, strerror(errno));
                                return NULL;
                        }
@@ -3595,7 +4455,7 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                                return NULL;
                        } else {
                                si.spanno = 0;
-                               if (ioctl(zap_fd(tmp->z),ZT_SPANSTAT,&si) == -1) {
+                               if (ioctl(tmp->subs[SUB_REAL].zfd,ZT_SPANSTAT,&si) == -1) {
                                        ast_log(LOG_ERROR, "Unable to get span status: %s\n", strerror(errno));
                                        free(tmp);
                                        return NULL;
@@ -3605,7 +4465,7 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                                        numchans = 31;
                                } else {
                                        dchannel = 24;
-                                       numchans = 24;
+                                       numchans = 23;
                                }
                                offset = p.chanpos;
                                if (offset != dchannel) {
@@ -3619,6 +4479,11 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                                                free(tmp);
                                                return NULL;
                                        }
+                                       if ((pris[span].dialplan) && (pris[span].dialplan != dialplan)) {
+                                               ast_log(LOG_ERROR, "Span %d is already a %s dialing plan\n", span + 1, pri_plan2str(pris[span].dialplan));
+                                               free(tmp);
+                                               return NULL;
+                                       }
                                        if (strlen(pris[span].idledial) && strcmp(pris[span].idledial, idledial)) {
                                                ast_log(LOG_ERROR, "Span %d already has idledial '%s'.\n", span + 1, idledial);
                                                free(tmp);
@@ -3641,6 +4506,7 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                                        }
                                        pris[span].nodetype = pritype;
                                        pris[span].switchtype = switchtype;
+                                       pris[span].dialplan = dialplan;
                                        pris[span].chanmask[offset] |= MASK_AVAIL;
                                        pris[span].pvt[offset] = tmp;
                                        pris[span].channels = numchans;
@@ -3651,6 +4517,7 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                                        strncpy(pris[span].idleext, idleext, sizeof(pris[span].idleext) - 1);
                                        
                                        tmp->pri = &pris[span];
+                                       tmp->prioffset = offset;
                                        tmp->call = NULL;
                                } else {
                                        ast_log(LOG_ERROR, "Channel %d is reserved for D-channel.\n", offset);
@@ -3658,6 +4525,28 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                                        return NULL;
                                }
                        }
+               } else {
+                       tmp->prioffset = 0;
+               }
+#endif
+#ifdef ZAPATA_R2
+               if (signalling == SIG_R2) {
+                       if (r2prot < 0) {
+                               ast_log(LOG_WARNING, "R2 Country not specified for channel %d -- Assuming China\n", tmp->channel);
+                               tmp->r2prot = MFCR2_PROT_CHINA;
+                       } else
+                               tmp->r2prot = r2prot;
+                       tmp->r2 = mfcr2_new(tmp->subs[SUB_REAL].zfd, tmp->r2prot, 1);
+                       if (!tmp->r2) {
+                               ast_log(LOG_WARNING, "Unable to create r2 call :(\n");
+                               zt_close(tmp->subs[SUB_REAL].zfd);
+                               free(tmp);
+                               return NULL;
+                       }
+               } else {
+                       if (tmp->r2) 
+                               mfcr2_free(tmp->r2);
+                       tmp->r2 = NULL;
                }
 #endif
                /* Adjust starttime on loopstart and kewlstart trunks to reasonable values */
@@ -3666,30 +4555,36 @@ static struct zt_pvt *mkintf(int channel, int signalling)
                        (signalling == SIG_FEATD) || (signalling == SIG_FEATDMF) ||
                          (signalling == SIG_FEATB)) {
                        p.starttime = 250;
-                       res = ioctl(zap_fd(tmp->z), ZT_SET_PARAMS, &p);
+                       res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_SET_PARAMS, &p);
+                       if (res < 0) {
+                               ast_log(LOG_ERROR, "Unable to set parameters\n");
+