]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
New signaling module to handle analog operations in chan_dahdi
authorJeff Peeler <jpeeler@digium.com>
Fri, 29 May 2009 19:19:51 +0000 (19:19 +0000)
committerJeff Peeler <jpeeler@digium.com>
Fri, 29 May 2009 19:19:51 +0000 (19:19 +0000)
This branch splits all the analog signaling logic out of chan_dahdi.c into
sig_analog.c. Functionality in theory should not change at all. As noted
in the code, there is still some unused code remaining that will be cleaned
up in a later commit.

Review: https://reviewboard.asterisk.org/r/253/

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

channels/Makefile
channels/chan_dahdi.c
channels/sig_analog.c [new file with mode: 0644]
channels/sig_analog.h [new file with mode: 0644]

index cb7676dcf9feafa33d529e092531e0cbc6ba7c2e..6e03b22ff3590031790df2bc7f043a32b4d02631 100644 (file)
@@ -69,6 +69,7 @@ dist-clean::
        rm -f h323/Makefile
 
 $(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): iax2-parser.o iax2-provision.o
+$(if $(filter chan_dahdi,$(EMBEDDED_MODS)),modules.link,chan_dahdi.so): sig_analog.o
 
 ifneq ($(filter chan_h323,$(EMBEDDED_MODS)),)
 modules.link: h323/libchanh323.a
index fb255956e6c090c1192116a72c2d0f02dc5535b2..9e4b487272527b06329287a5aeeaf0c7adc4647d 100644 (file)
@@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include <dahdi/user.h>
 #include <dahdi/tonezone.h>
+#include "sig_analog.h"
 
 #ifdef HAVE_PRI
 #include <libpri.h>
@@ -468,6 +469,8 @@ static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_ch
 
 static int dahdi_sendtext(struct ast_channel *c, const char *text);
 
+static int analog_lib_handles(int signalling, int radio, int oprmode);
+
 static void mwi_event_cb(const struct ast_event *event, void *userdata)
 {
        /* This module does not handle MWI in an event-based manner.  However, it
@@ -753,6 +756,7 @@ struct mwisend_info {
 
 static struct dahdi_pvt {
        ast_mutex_t lock;
+       struct callerid_state *cs;
        struct ast_channel *owner;                      /*!< Our current active owner (if applicable) */
                                                        /*!< Up to three channels can be associated with this call */
 
@@ -1333,6 +1337,7 @@ static struct dahdi_pvt {
        char begindigit;
        /*! \brief TRUE if confrence is muted. */
        int muting;
+       void *sig_pvt;
 } *iflist = NULL, *ifend = NULL;
 
 /*! \brief Channel configuration from chan_dahdi.conf .
@@ -1528,226 +1533,1131 @@ static const struct ast_channel_tech dahdi_tech = {
 #define GET_CHANNEL(p) ((p)->channel)
 #endif
 
-struct dahdi_pvt *round_robin[32];
-
-#if defined(HAVE_PRI)
-static inline int pri_grab(struct dahdi_pvt *pvt, struct dahdi_pri *pri)
+static enum analog_sigtype dahdisig_to_analogsig(int sig)
 {
-       int res;
-       /* Grab the lock first */
-       do {
-               res = ast_mutex_trylock(&pri->lock);
-               if (res) {
-                       DEADLOCK_AVOIDANCE(&pvt->lock);
-               }
-       } while (res);
-       /* Then break the poll */
-       if (pri->master != AST_PTHREADT_NULL)
-               pthread_kill(pri->master, SIGURG);
-       return 0;
+       switch (sig) {
+       case SIG_FXOLS:
+               return ANALOG_SIG_FXOLS;
+       case SIG_FXOGS:
+               return ANALOG_SIG_FXOGS;
+       case SIG_FXOKS:
+               return ANALOG_SIG_FXOKS;
+       case SIG_FXSLS:
+               return ANALOG_SIG_FXSLS;
+       case SIG_FXSGS:
+               return ANALOG_SIG_FXSGS;
+       case SIG_FXSKS:
+               return ANALOG_SIG_FXSKS;
+       case SIG_EMWINK:
+               return ANALOG_SIG_EMWINK;
+       case SIG_EM:
+               return ANALOG_SIG_EM;
+       case SIG_EM_E1:
+               return ANALOG_SIG_EM_E1;
+       case SIG_FEATD:
+               return ANALOG_SIG_FEATD;
+       case SIG_FEATDMF:
+               return ANALOG_SIG_FEATDMF;
+       case SIG_E911:
+               return SIG_E911;
+       case SIG_FGC_CAMA:
+               return ANALOG_SIG_FGC_CAMA;
+       case SIG_FGC_CAMAMF:
+               return ANALOG_SIG_FGC_CAMAMF;
+       case SIG_FEATB:
+               return ANALOG_SIG_FEATB;
+       case SIG_SFWINK:
+               return ANALOG_SIG_SFWINK;
+       case SIG_SF:
+               return ANALOG_SIG_SF;
+       case SIG_SF_FEATD:
+               return ANALOG_SIG_SF_FEATD;
+       case SIG_SF_FEATDMF:
+               return ANALOG_SIG_SF_FEATDMF;
+       case SIG_FEATDMF_TA:
+               return ANALOG_SIG_FEATDMF_TA;
+       case SIG_SF_FEATB:
+               return ANALOG_SIG_FEATB;
+       default:
+               return -1;
+       }
 }
-#endif /* defined(HAVE_PRI) */
 
-#if defined(HAVE_SS7)
-static inline void ss7_rel(struct dahdi_ss7 *ss7)
-{
-       ast_mutex_unlock(&ss7->lock);
-}
-#endif /* defined(HAVE_SS7) */
 
-#if defined(HAVE_SS7)
-static inline int ss7_grab(struct dahdi_pvt *pvt, struct dahdi_ss7 *pri)
+static int analog_tone_to_dahditone(enum analog_tone tone)
 {
-       int res;
-       /* Grab the lock first */
-       do {
-               res = ast_mutex_trylock(&pri->lock);
-               if (res) {
-                       DEADLOCK_AVOIDANCE(&pvt->lock);
-               }
-       } while (res);
-       /* Then break the poll */
-       if (pri->master != AST_PTHREADT_NULL)
-               pthread_kill(pri->master, SIGURG);
-       return 0;
+       switch (tone) {
+       case ANALOG_TONE_RINGTONE:
+               return DAHDI_TONE_RINGTONE;
+       case ANALOG_TONE_STUTTER:
+               return DAHDI_TONE_STUTTER;
+       case ANALOG_TONE_CONGESTION:
+               return DAHDI_TONE_CONGESTION;
+       case ANALOG_TONE_DIALTONE:
+               return DAHDI_TONE_DIALTONE;
+       case ANALOG_TONE_DIALRECALL:
+               return DAHDI_TONE_DIALRECALL;
+       case ANALOG_TONE_INFO:
+               return DAHDI_TONE_INFO;
+       default:
+               return -1;
+       }
 }
-#endif /* defined(HAVE_SS7) */
-#define NUM_CADENCE_MAX 25
-static int num_cadence = 4;
-static int user_has_defined_cadences = 0;
-
-static struct dahdi_ring_cadence cadences[NUM_CADENCE_MAX] = {
-       { { 125, 125, 2000, 4000 } },                   /*!< Quick chirp followed by normal ring */
-       { { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */
-       { { 125, 125, 125, 125, 125, 4000 } },  /*!< Three short bursts */
-       { { 1000, 500, 2500, 5000 } },  /*!< Long ring */
-};
-
-/*! \brief cidrings says in which pause to transmit the cid information, where the first pause
- * is 1, the second pause is 2 and so on.
- */
 
-static int cidrings[NUM_CADENCE_MAX] = {
-       2,                                                                              /*!< Right after first long ring */
-       4,                                                                              /*!< Right after long part */
-       3,                                                                              /*!< After third chirp */
-       2,                                                                              /*!< Second spell */
-};
+static int analogsub_to_dahdisub(enum analog_sub analogsub)
+{
+       int index;
 
-/* ETSI EN300 659-1 specifies the ring pulse between 200 and 300 mS */
-static struct dahdi_ring_cadence AS_RP_cadence = {{250, 10000}};
+       switch (analogsub) {
+       case ANALOG_SUB_REAL:
+               index = SUB_REAL;
+               break;
+       case ANALOG_SUB_CALLWAIT:
+               index = SUB_CALLWAIT;
+               break;
+       case ANALOG_SUB_THREEWAY:
+               index = SUB_THREEWAY;
+               break;
+       default:
+               ast_log(LOG_ERROR, "Unidentified sub!\n");
+               index = SUB_REAL;
+       }
 
-#define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \
-                       (p->sig == SIG_FXSGS) || (p->sig == SIG_PRI))
+       return index;
+}
 
-#define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */)
-#define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */)
+static enum analog_event dahdievent_to_analogevent(int event);
+static int bump_gains(struct dahdi_pvt *p);
+static int dahdi_setlinear(int dfd, int linear);
 
-static int dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok)
+static int my_start_cid_detect(void *pvt, int cid_signalling)
 {
-       int res;
-       if (p->subs[SUB_REAL].owner == ast)
-               res = 0;
-       else if (p->subs[SUB_CALLWAIT].owner == ast)
-               res = 1;
-       else if (p->subs[SUB_THREEWAY].owner == ast)
-               res = 2;
-       else {
-               res = -1;
-               if (!nullok)
-                       ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
+       struct dahdi_pvt *p = pvt;
+       int index = SUB_REAL;
+       p->cs = callerid_new(cid_signalling);
+       if (!p->cs) {
+               ast_log(LOG_ERROR, "Unable to alloc callerid\n");
+               return -1;
        }
-       return res;
+       bump_gains(p);
+       dahdi_setlinear(p->subs[index].dfd, 0);
+
+       return 0;
 }
 
-static void wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri)
+static int my_stop_cid_detect(void *pvt)
 {
-#ifdef HAVE_PRI
-       if (pri)
-               ast_mutex_unlock(&pri->lock);
-#endif
-       for (;;) {
-               if (p->subs[a].owner) {
-                       if (ast_channel_trylock(p->subs[a].owner)) {
-                               DEADLOCK_AVOIDANCE(&p->lock);
-                       } else {
-                               ast_queue_frame(p->subs[a].owner, &ast_null_frame);
-                               ast_channel_unlock(p->subs[a].owner);
-                               break;
-                       }
-               } else
-                       break;
-       }
-#ifdef HAVE_PRI
-       if (pri)
-               ast_mutex_lock(&pri->lock);
-#endif
+       struct dahdi_pvt *p = pvt;
+       int index = SUB_REAL;
+       if (p->cs)
+               callerid_free(p->cs);
+       dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear);
+       return 0;
 }
 
-static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, void *data)
+static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_event *ev, size_t timeout)
 {
-#ifdef HAVE_PRI
-       struct dahdi_pri *pri = (struct dahdi_pri*) data;
-#endif
-#ifdef HAVE_SS7
-       struct dahdi_ss7 *ss7 = (struct dahdi_ss7*) data;
-#endif
-       /* We must unlock the PRI to avoid the possibility of a deadlock */
-#if defined(HAVE_PRI) || defined(HAVE_SS7)
-       if (data) {
-               switch (p->sig) {
-#ifdef HAVE_PRI
-               case SIG_BRI:
-               case SIG_BRI_PTMP:
-               case SIG_PRI:
-                       ast_mutex_unlock(&pri->lock);
-                       break;
-#endif
-#ifdef HAVE_SS7
-               case SIG_SS7:
-                       ast_mutex_unlock(&ss7->lock);
-                       break;
-#endif
-               default:
-                       break;
-               }
+       struct dahdi_pvt *p = pvt;
+       struct pollfd poller;
+       char *name, *num;
+       int index = SUB_REAL;
+       int res;
+       unsigned char buf[256];
+       int flags;
+
+       poller.fd = p->subs[SUB_REAL].dfd;
+       poller.events = POLLPRI | POLLIN;
+       poller.revents = 0;
+
+       res = poll(&poller, 1, timeout);
+
+       if (poller.revents & POLLPRI) {
+               *ev = dahdievent_to_analogevent(dahdi_get_event(p->subs[SUB_REAL].dfd));
+               return 1;
        }
-#endif
-       for (;;) {
-               if (p->owner) {
-                       if (ast_channel_trylock(p->owner)) {
-                               DEADLOCK_AVOIDANCE(&p->lock);
-                       } else {
-                               ast_queue_frame(p->owner, f);
-                               ast_channel_unlock(p->owner);
-                               break;
+
+       if (poller.revents & POLLIN) {
+               /*** NOTES ***/
+               /* Change API: remove cid_signalling from get_callerid, add a new start_cid_detect and stop_cid_detect function
+                * to enable slin mode and allocate cid detector.  get_callerid should be able to be called any number of times until
+                * either a timeout occurss or CID is detected (returns 0).  returning 1 should be event received, and -1 should be fail
+                * and die */
+               res = read(p->subs[index].dfd, buf, sizeof(buf));
+               if (res < 0) {
+                       if (errno != ELAST) {
+                               ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+                               callerid_free(p->cs);
+                               return -1;
                        }
-               } else
-                       break;
-       }
-#if defined(HAVE_PRI) || defined(HAVE_SS7)
-       if (data) {
-               switch (p->sig) {
-#ifdef HAVE_PRI
-               case SIG_BRI:
-               case SIG_BRI_PTMP:
-               case SIG_PRI:
-                       ast_mutex_lock(&pri->lock);
-                       break;
-#endif
-#ifdef HAVE_SS7
-               case SIG_SS7:
-                       ast_mutex_lock(&ss7->lock);
-                       break;
-#endif
-               default:
-                       break;
+               }
+               res = callerid_feed(p->cs, buf, res, AST_LAW(p));
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "CallerID feed failed: %s\n", strerror(errno));
+                       return -1;
+               }
+
+               if (res == 1) {
+                       callerid_get(p->cs, &name, &num, &flags);
+                       if (name)
+                               ast_copy_string(namebuf, name, ANALOG_MAX_CID);
+                       if (num)
+                               ast_copy_string(numbuf, num, ANALOG_MAX_CID);
+
+                       ast_log(LOG_DEBUG, "CallerID number: %s, name: %s, flags=%d\n", num, name, flags);
+                       return 0;
                }
        }
-#endif
+
+       *ev = ANALOG_EVENT_NONE;
+       return 1;
 }
 
-static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
-#ifdef HAVE_OPENR2
+static int send_callerid(struct dahdi_pvt *p);
 
-static int dahdi_r2_answer(struct dahdi_pvt *p)
+static int my_stop_callwait(void *pvt)
 {
-       int res = 0;
-       /* openr2 1.1.0 and older does not even define OR2_LIB_INTERFACE
-       * and does not has support for openr2_chan_answer_call_with_mode
-       *  */
-#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1
-       const char *double_answer = pbx_builtin_getvar_helper(p->owner, "MFCR2_DOUBLE_ANSWER");
-       int wants_double_answer = ast_true(double_answer) ? 1 : 0;
-       if (!double_answer) {
-               /* this still can result in double answer if the channel context
-               * was configured that way */
-               res = openr2_chan_answer_call(p->r2chan);
-       } else if (wants_double_answer) {
-               res = openr2_chan_answer_call_with_mode(p->r2chan, OR2_ANSWER_DOUBLE);
-       } else {
-               res = openr2_chan_answer_call_with_mode(p->r2chan, OR2_ANSWER_SIMPLE);
-       }
-#else
-       res = openr2_chan_answer_call(p->r2chan);
-#endif
-       return res;
-}
+       struct dahdi_pvt *p = pvt;
+       p->callwaitingrepeat = 0;
+       p->cidcwexpire = 0;
 
+       return 0;
+}
 
+static int save_conference(struct dahdi_pvt *p);
 
-/* should be called with the ast_channel locked */
-static openr2_calling_party_category_t dahdi_r2_get_channel_category(struct ast_channel *c)
+static int my_callwait(void *pvt)
 {
-       openr2_calling_party_category_t cat;
-       const char *catstr = pbx_builtin_getvar_helper(c, "MFCR2_CATEGORY");
-       struct dahdi_pvt *p = c->tech_pvt;
-       if (ast_strlen_zero(catstr)) {
-               ast_debug(1, "No MFC/R2 category specified for chan %s, using default %s\n",
-                               c->name, openr2_proto_get_category_string(p->mfcr2_category));
-               return p->mfcr2_category;
+       struct dahdi_pvt *p = pvt;
+       p->callwaitingrepeat = CALLWAITING_REPEAT_SAMPLES;
+       if (p->cidspill) {
+               ast_log(LOG_WARNING, "Spill already exists?!?\n");
+               free(p->cidspill);
        }
-       if ((cat = openr2_proto_get_category(catstr)) == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) {
+       if (!(p->cidspill = ast_malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4)))
+               return -1;
+       save_conference(p);
+       /* Silence */
+       memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4);
+       if (!p->callwaitrings && p->callwaitingcallerid) {
+               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_LAW(p));
+               p->callwaitcas = 0;
+               p->cidlen = 2400 + READ_SIZE * 4;
+       }
+       p->cidpos = 0;
+       send_callerid(p);
+
+       return 0;
+}
+
+static int my_send_callerid(void *pvt, int cwcid, struct ast_callerid *cid)
+{
+       struct dahdi_pvt *p = pvt;
+
+       ast_log(LOG_ERROR, "Starting cid spill\n");
+
+       if (p->cidspill) {
+               ast_log(LOG_WARNING, "cidspill already exists??\n");
+               free(p->cidspill);
+       }
+
+       if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) {
+               if (cwcid == 0) {
+                       p->cidlen = ast_callerid_generate(p->cidspill, cid->cid_name, cid->cid_num, AST_LAW(p));
+               } else {
+                       p->callwaitcas = 0;
+                       p->cidcwexpire = 0;
+                       p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, cid->cid_name, cid->cid_num, AST_LAW(p));
+                       p->cidlen += READ_SIZE * 4;
+               }
+               p->cidpos = 0;
+               send_callerid(p);
+       }
+       return 0;
+}
+
+static int my_dsp_reset_and_flush_digits(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       if (p->dsp)
+               ast_dsp_digitreset(p->dsp);
+
+       return 0;
+}
+
+static int my_dsp_set_digitmode(void *pvt, enum analog_dsp_digitmode mode)
+{
+       struct dahdi_pvt *p = pvt;
+
+       if (p->channel == CHAN_PSEUDO)
+               ast_log(LOG_ERROR, "You have assumed incorrectly sir!\n");
+
+       if (mode == ANALOG_DIGITMODE_DTMF) {
+               /* If we do hardware dtmf, no need for a DSP */
+               if (p->hardwaredtmf) {
+                       if (p->dsp) {
+                               ast_dsp_free(p->dsp);
+                               p->dsp = NULL;
+                       }
+                       return 0;
+               }
+
+               if (!p->dsp) {
+                       p->dsp = ast_dsp_new();
+                       if (!p->dsp) {
+                               ast_log(LOG_ERROR, "Unable to allocate DSP\n");
+                               return -1;
+                       }
+               }
+
+               ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | p->dtmfrelax);
+       } else if (mode == ANALOG_DIGITMODE_MF) {
+               if (!p->dsp) {
+                       p->dsp = ast_dsp_new();
+                       if (!p->dsp) {
+                               ast_log(LOG_ERROR, "Unable to allocate DSP\n");
+                               return -1;
+                       }
+               }
+               ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_MF | p->dtmfrelax);
+       }
+       return 0;
+}
+
+static int dahdi_wink(struct dahdi_pvt *p, int index);
+
+static int my_wink(void *pvt, enum analog_sub sub)
+{
+       struct dahdi_pvt *p = pvt;
+       int index = analogsub_to_dahdisub(sub);
+       if (index != SUB_REAL) {
+               ast_log(LOG_ERROR, "We used a sub other than SUB_REAL (incorrect assumption sir)\n");
+       }
+       return dahdi_wink(p, index);
+}
+
+static void wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri);
+
+static int reset_conf(struct dahdi_pvt *p);
+
+static inline int dahdi_confmute(struct dahdi_pvt *p, int muted);
+
+static void my_handle_dtmfup(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest)
+{
+       struct ast_frame *f = *dest;
+       struct dahdi_pvt *p = pvt;
+       int idx = analogsub_to_dahdisub(analog_index);
+
+       ast_debug(1, "DTMF digit: %c on %s\n", f->subclass, ast->name);
+
+       if (f->subclass == 'f') {
+               /* Fax tone -- Handle and return NULL */
+               if ((p->callprogress & CALLPROGRESS_FAX) && !p->faxhandled) {
+                       /* If faxbuffers are configured, use them for the fax transmission */
+                       if (p->usefaxbuffers && !p->bufferoverrideinuse) {
+                               struct dahdi_bufferinfo bi = {
+                                       .txbufpolicy = p->faxbuf_policy,
+                                       .bufsize = p->bufsize,
+                                       .numbufs = p->faxbuf_no
+                               };
+                               int res;
+
+                               if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) {
+                                       ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno));
+                               } else {
+                                       p->bufferoverrideinuse = 1;
+                               }
+                       }
+                       p->faxhandled = 1;
+                       if (strcmp(ast->exten, "fax")) {
+                               const char *target_context = S_OR(ast->macrocontext, ast->context);
+
+                               /* We need to unlock 'ast' here because ast_exists_extension has the
+                                * potential to start autoservice on the channel. Such action is prone
+                                * to deadlock.
+                                */
+                               ast_mutex_unlock(&p->lock);
+                               ast_channel_unlock(ast);
+                               if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) {
+                                       ast_channel_lock(ast);
+                                       ast_mutex_lock(&p->lock);
+                                       ast_verb(3, "Redirecting %s to fax extension\n", ast->name);
+                                       /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
+                                       pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
+                                       if (ast_async_goto(ast, target_context, "fax", 1))
+                                               ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
+                               } else {
+                                       ast_channel_lock(ast);
+                                       ast_mutex_lock(&p->lock);
+                                       ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
+                               }
+                       } else {
+                               ast_debug(1, "Already in a fax extension, not redirecting\n");
+                       }
+               } else {
+                       ast_debug(1, "Fax already handled\n");
+               }
+               dahdi_confmute(p, 0);
+               p->subs[idx].f.frametype = AST_FRAME_NULL;
+               p->subs[idx].f.subclass = 0;
+               *dest = &p->subs[idx].f;
+       }
+}
+
+static void my_lock_private(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+
+       ast_mutex_lock(&p->lock);
+}
+
+static void my_unlock_private(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+
+       ast_mutex_unlock(&p->lock);
+}
+
+static void my_increase_ss_count(void)
+{
+       ast_mutex_lock(&ss_thread_lock);
+       ss_thread_count++;
+       ast_mutex_unlock(&ss_thread_lock);
+}
+
+static void my_decrease_ss_count(void)
+{
+       ast_mutex_lock(&ss_thread_lock);
+       ss_thread_count--;
+       ast_cond_signal(&ss_thread_complete);
+       ast_mutex_unlock(&ss_thread_lock);
+}
+
+static void my_all_subchannels_hungup(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       int res, law;
+
+       p->faxhandled = 0;
+       p->didtdd = 0;
+
+       if (p->dsp) {
+               ast_dsp_free(p->dsp);
+               p->dsp = NULL;
+       }
+
+       law = DAHDI_LAW_DEFAULT;
+       res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETLAW, &law);
+       if (res < 0) 
+               ast_log(LOG_WARNING, "Unable to set law on channel %d to default: %s\n", p->channel, strerror(errno));
+
+       dahdi_setlinear(p->subs[SUB_REAL].dfd, 0);
+
+#if 1
+       {
+       int i;
+       p->owner = NULL;
+       /* Cleanup owners here */
+       for (i = 0; i < 3; i++) {
+               p->subs[i].owner = NULL;
+       }
+       }
+#endif
+
+       reset_conf(p);
+       if (num_restart_pending == 0) {
+               restart_monitor();
+       }
+}
+
+static int conf_del(struct dahdi_pvt *p, struct dahdi_subchannel *c, int index);
+
+static int my_conf_del(void *pvt, enum analog_sub sub)
+{
+       struct dahdi_pvt *p = pvt;
+       int x = analogsub_to_dahdisub(sub);
+
+       return conf_del(p, &p->subs[x], x);
+}
+
+static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int index, int slavechannel);
+
+static int my_conf_add(void *pvt, enum analog_sub sub)
+{
+       struct dahdi_pvt *p = pvt;
+       int x = analogsub_to_dahdisub(sub);
+
+       return conf_add(p, &p->subs[x], x, 0);
+}
+
+static int isslavenative(struct dahdi_pvt *p, struct dahdi_pvt **out);
+
+static int my_complete_conference_update(void *pvt, int needconference)
+{
+       struct dahdi_pvt *p = pvt;
+       int needconf = needconference;
+       int x;
+       int useslavenative;
+       struct dahdi_pvt *slave = NULL;
+
+       useslavenative = isslavenative(p, &slave);
+
+       /* If we have a slave, add him to our conference now. or DAX
+          if this is slave native */
+       for (x = 0; x < MAX_SLAVES; x++) {
+               if (p->slaves[x]) {
+                       if (useslavenative)
+                               conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p));
+                       else {
+                               conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, 0);
+                               needconf++;
+                       }
+               }
+       }
+       /* If we're supposed to be in there, do so now */
+       if (p->inconference && !p->subs[SUB_REAL].inthreeway) {
+               if (useslavenative)
+                       conf_add(p, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(slave));
+               else {
+                       conf_add(p, &p->subs[SUB_REAL], SUB_REAL, 0);
+                       needconf++;
+               }
+       }
+       /* If we have a master, add ourselves to his conference */
+       if (p->master) {
+               if (isslavenative(p->master, NULL)) {
+                       conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p->master));
+               } else {
+                       conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, 0);
+               }
+       }
+       if (!needconf) {
+               /* Nobody is left (or should be left) in our conference.
+                  Kill it. */
+               p->confno = -1;
+       }
+
+       return 0;
+}
+
+static int check_for_conference(struct dahdi_pvt *p);
+
+static int my_check_for_conference(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       return check_for_conference(p);
+}
+
+static void my_swap_subchannels(void *pvt, enum analog_sub a, struct ast_channel *ast_a,  enum analog_sub b, struct ast_channel *ast_b)
+{
+       struct dahdi_pvt *p = pvt;
+       int da, db;
+       int tchan;
+
+       da = analogsub_to_dahdisub(a);
+       db = analogsub_to_dahdisub(b);
+
+       tchan = p->subs[da].chan;
+
+       p->subs[da].chan = p->subs[db].chan;
+
+       p->subs[db].chan = tchan;
+
+       if (ast_a)
+               ast_a->fds[0] = p->subs[da].dfd;
+       if (ast_b)
+               ast_b->fds[0] = p->subs[db].dfd;
+
+       p->subs[da].owner = ast_a;
+       p->subs[db].owner = ast_b;
+
+       wakeup_sub(p, a, NULL);
+       wakeup_sub(p, b, NULL);
+
+       return;
+}
+
+static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
+
+static struct ast_channel * my_new_analog_ast_channel(void *pvt, int state, int startpbx, enum analog_sub sub)
+{
+       struct dahdi_pvt *p = pvt;
+       int dsub = analogsub_to_dahdisub(sub);
+
+       return dahdi_new(p, state, startpbx, dsub, 0, 0);
+}
+
+static int unalloc_sub(struct dahdi_pvt *p, int x);
+
+static int my_unallocate_sub(void *pvt, enum analog_sub analogsub)
+{
+       struct dahdi_pvt *p = pvt;
+
+       return unalloc_sub(p, analogsub_to_dahdisub(analogsub));
+}
+
+static int alloc_sub(struct dahdi_pvt *p, int x);
+
+static int my_allocate_sub(void *pvt, enum analog_sub analogsub)
+{
+       struct dahdi_pvt *p = pvt;
+
+       return alloc_sub(p, analogsub_to_dahdisub(analogsub));
+}
+
+static int has_voicemail(struct dahdi_pvt *p);
+
+static int my_has_voicemail(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+
+       return has_voicemail(p);
+}
+
+static int my_play_tone(void *pvt, enum analog_sub sub, enum analog_tone tone)
+{
+       struct dahdi_pvt *p = pvt;
+       int index;
+
+       index = analogsub_to_dahdisub(sub);
+
+       return tone_zone_play_tone(p->subs[index].dfd, analog_tone_to_dahditone(tone));
+}
+
+static enum analog_event dahdievent_to_analogevent(int event)
+{
+       enum analog_event res = ANALOG_EVENT_ERROR;
+
+       switch (event) {
+       case DAHDI_EVENT_DIALCOMPLETE:
+               res = ANALOG_EVENT_DIALCOMPLETE;
+               break;
+       case DAHDI_EVENT_WINKFLASH:
+               res = ANALOG_EVENT_WINKFLASH;
+               break;
+       case DAHDI_EVENT_ONHOOK:
+               res = ANALOG_EVENT_ONHOOK;
+               break;
+       case DAHDI_EVENT_RINGOFFHOOK:
+               res = ANALOG_EVENT_RINGOFFHOOK;
+               break;
+       case DAHDI_EVENT_ALARM:
+               res = ANALOG_EVENT_ALARM;
+               break;
+       case DAHDI_EVENT_NOALARM:
+               res = ANALOG_EVENT_NOALARM;
+               break;
+       case DAHDI_EVENT_HOOKCOMPLETE:
+               res = ANALOG_EVENT_HOOKCOMPLETE;
+               break;
+       case DAHDI_EVENT_POLARITY:
+               res = ANALOG_EVENT_POLARITY;
+               break;
+       case DAHDI_EVENT_RINGERON:
+               res = ANALOG_EVENT_RINGERON;
+               break;
+       case DAHDI_EVENT_RINGEROFF:
+               res = ANALOG_EVENT_RINGEROFF;
+               break;
+       case DAHDI_EVENT_RINGBEGIN:
+               res = ANALOG_EVENT_RINGBEGIN;
+               break;
+       case DAHDI_EVENT_PULSE_START:
+               res = ANALOG_EVENT_PULSE_START;
+       break;
+       case DAHDI_EVENT_NEONMWI_ACTIVE:
+               res = ANALOG_EVENT_NEONMWI_ACTIVE;
+               break;
+       case DAHDI_EVENT_NEONMWI_INACTIVE:
+               res = ANALOG_EVENT_NEONMWI_INACTIVE;
+               break;
+       }
+
+       return res;
+}
+
+static inline int dahdi_wait_event(int fd);
+
+static int my_wait_event(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+
+       return dahdi_wait_event(p->subs[SUB_REAL].dfd);
+}
+
+static int my_get_event(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       int res;
+
+       if (p->fake_event) {
+               res = p->fake_event;
+               p->fake_event = 0;
+       } else
+               res = dahdi_get_event(p->subs[SUB_REAL].dfd);
+
+       return dahdievent_to_analogevent(res);
+}
+
+static int my_is_off_hook(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       int res;
+       struct dahdi_params par;
+
+       if (p->subs[SUB_REAL].dfd > -1)
+               res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par);
+       else {
+               /* Assume not off hook on CVRS */
+               res = 0;
+               par.rxisoffhook = 0;
+       }
+       if (res) {
+               ast_log(LOG_WARNING, "Unable to check hook state on channel %d: %s\n", p->channel, strerror(errno));
+       }
+
+       return (par.rxbits > -1) || par.rxisoffhook;
+}
+
+static void dahdi_enable_ec(struct dahdi_pvt *p);
+static void dahdi_disable_ec(struct dahdi_pvt *p);
+
+static int my_set_echocanceller(void *pvt, int enable)
+{
+       struct dahdi_pvt *p = pvt;
+
+       if (enable)
+               dahdi_enable_ec(p);
+       else
+               dahdi_disable_ec(p);
+
+       return 0;
+}
+
+static int dahdi_ring_phone(struct dahdi_pvt *p);
+
+static int my_ring(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+
+       return dahdi_ring_phone(p);
+}
+
+static inline int dahdi_set_hook(int fd, int hs);
+
+static int my_off_hook(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       return dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK);
+}
+
+static int my_start(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       int x = DAHDI_START;
+
+       return ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x);
+}
+
+static int my_dial_digits(void *pvt, enum analog_sub sub, struct analog_dialoperation *dop)
+{
+       int index = analogsub_to_dahdisub(sub);
+       int res;
+       struct dahdi_pvt *p = pvt;
+       struct dahdi_dialoperation ddop;
+
+       if (dop->op != ANALOG_DIAL_OP_REPLACE) {
+               ast_log(LOG_ERROR, "Fix the dial_digits callback!\n");
+               return -1;
+       }
+
+       if (sub != ANALOG_SUB_REAL)
+               printf("Trying to dial digits on sub %d\n", sub);
+
+       ddop.op = DAHDI_DIAL_OP_REPLACE;
+       strncpy(ddop.dialstr, dop->dialstr, sizeof(ddop.dialstr));
+
+       printf("Dialing %s on %d\n", ddop.dialstr, p->channel);
+
+       res = ioctl(p->subs[index].dfd, DAHDI_DIAL, &ddop);
+
+       if (res == -1)
+               ast_log(LOG_DEBUG, "DAHDI_DIAL ioctl failed on %s: %s\n", p->owner->name, strerror(errno));
+
+       return res;
+}
+
+static void dahdi_train_ec(struct dahdi_pvt *p);
+
+static int my_train_echocanceller(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+
+       dahdi_train_ec(p);
+
+       return 0;
+}
+
+static int my_is_dialing(void *pvt, enum analog_sub sub)
+{
+       struct dahdi_pvt *p = pvt;
+       int index;
+       int x;
+
+       index = analogsub_to_dahdisub(sub);
+
+       if (ioctl(p->subs[index].dfd, DAHDI_DIALING, &x)) {
+               ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed!\n");
+               return -1;
+       }
+
+       return x;
+}
+
+static int my_on_hook(void *pvt)
+{
+       struct dahdi_pvt *p = pvt;
+       int x = DAHDI_ONHOOK;
+
+       return ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_HOOK, &x);
+}
+
+/*!
+ * \brief Send MWI state change
+ *
+ * \arg mailbox_full This is the mailbox associated with the FXO line that the
+ *      MWI state has changed on.
+ * \arg thereornot This argument should simply be set to 1 or 0, to indicate
+ *      whether there are messages waiting or not.
+ *
+ *  \return nothing
+ *
+ * This function does two things:
+ *
+ * 1) It generates an internal Asterisk event notifying any other module that
+ *    cares about MWI that the state of a mailbox has changed.
+ *
+ * 2) It runs the script specified by the mwimonitornotify option to allow
+ *    some custom handling of the state change.
+ */
+static void notify_message(char *mailbox_full, int thereornot)
+{
+       char s[sizeof(mwimonitornotify) + 80];
+       struct ast_event *event;
+       char *mailbox, *context;
+
+       /* Strip off @default */
+       context = mailbox = ast_strdupa(mailbox_full);
+       strsep(&context, "@");
+       if (ast_strlen_zero(context))
+               context = "default";
+
+       if (!(event = ast_event_new(AST_EVENT_MWI,
+                       AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
+                       AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
+                       AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot,
+                       AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot,
+                       AST_EVENT_IE_END))) {
+               return;
+       }
+
+       ast_event_queue_and_cache(event);
+
+       if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) {
+               snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot);
+               ast_safe_system(s);
+       }
+}
+
+static void my_handle_notify_message(struct ast_channel *chan, void *pvt, int cid_flags, int neon_mwievent)
+{
+       struct dahdi_pvt *p = pvt;
+
+       if (neon_mwievent > -1 && !p->mwimonitor_neon)
+               return;
+
+       if (neon_mwievent == ANALOG_EVENT_NEONMWI_ACTIVE || cid_flags & CID_MSGWAITING) {
+               ast_log(LOG_NOTICE, "MWI: Channel %d message waiting, mailbox %s\n", p->channel, p->mailbox);
+               notify_message(p->mailbox, 1);
+       } else if (neon_mwievent == ANALOG_EVENT_NEONMWI_INACTIVE || cid_flags & CID_NOMSGWAITING) {
+               ast_log(LOG_NOTICE, "MWI: Channel %d no message waiting, mailbox %s\n", p->channel, p->mailbox);
+               notify_message(p->mailbox, 0);
+       }
+       /* If the CID had Message waiting payload, assume that this for MWI only and hangup the call */
+       /* If generated using Ring Pulse Alert, then ring has been answered as a call and needs to be hungup */
+       if (neon_mwievent == -1 && p->mwimonitor_rpas) {
+               ast_hangup(chan);
+               return;
+       }
+}
+
+
+static const char *event2str(int event);
+
+static struct analog_callback dahdi_analog_callbacks =
+{
+       .play_tone = my_play_tone,
+       .get_event = my_get_event,
+       .wait_event = my_wait_event,
+       .is_off_hook = my_is_off_hook,
+       .set_echocanceller = my_set_echocanceller,
+       .ring = my_ring,
+       .off_hook = my_off_hook,
+       .dial_digits = my_dial_digits,
+       .train_echocanceller = my_train_echocanceller,
+       .on_hook = my_on_hook,
+       .is_dialing = my_is_dialing,
+       .allocate_sub = my_allocate_sub,
+       .unallocate_sub = my_unallocate_sub,
+       .swap_subs = my_swap_subchannels,
+       .has_voicemail = my_has_voicemail,
+       .check_for_conference = my_check_for_conference,
+       .conf_add = my_conf_add,
+       .conf_del = my_conf_del,
+       .complete_conference_update = my_complete_conference_update,
+       .start = my_start,
+       .all_subchannels_hungup = my_all_subchannels_hungup,
+       .lock_private = my_lock_private,
+       .unlock_private = my_unlock_private,
+       .handle_dtmfup = my_handle_dtmfup,
+       .wink = my_wink,
+       .new_ast_channel = my_new_analog_ast_channel,
+       .dsp_set_digitmode = my_dsp_set_digitmode,
+       .dsp_reset_and_flush_digits = my_dsp_reset_and_flush_digits,
+       .send_callerid = my_send_callerid,
+       .callwait = my_callwait,
+       .stop_callwait = my_stop_callwait,
+       .get_callerid = my_get_callerid,
+       .start_cid_detect = my_start_cid_detect,
+       .stop_cid_detect = my_stop_cid_detect,
+       .handle_notify_message = my_handle_notify_message,
+       .increase_ss_count = my_increase_ss_count,
+       .decrease_ss_count = my_decrease_ss_count,
+};
+
+struct dahdi_pvt *round_robin[32];
+
+#if defined(HAVE_PRI)
+static inline int pri_grab(struct dahdi_pvt *pvt, struct dahdi_pri *pri)
+{
+       int res;
+       /* Grab the lock first */
+       do {
+               res = ast_mutex_trylock(&pri->lock);
+               if (res) {
+                       DEADLOCK_AVOIDANCE(&pvt->lock);
+               }
+       } while (res);
+       /* Then break the poll */
+       if (pri->master != AST_PTHREADT_NULL)
+               pthread_kill(pri->master, SIGURG);
+       return 0;
+}
+#endif /* defined(HAVE_PRI) */
+
+#if defined(HAVE_SS7)
+static inline void ss7_rel(struct dahdi_ss7 *ss7)
+{
+       ast_mutex_unlock(&ss7->lock);
+}
+#endif /* defined(HAVE_SS7) */
+
+#if defined(HAVE_SS7)
+static inline int ss7_grab(struct dahdi_pvt *pvt, struct dahdi_ss7 *pri)
+{
+       int res;
+       /* Grab the lock first */
+       do {
+               res = ast_mutex_trylock(&pri->lock);
+               if (res) {
+                       DEADLOCK_AVOIDANCE(&pvt->lock);
+               }
+       } while (res);
+       /* Then break the poll */
+       if (pri->master != AST_PTHREADT_NULL)
+               pthread_kill(pri->master, SIGURG);
+       return 0;
+}
+#endif /* defined(HAVE_SS7) */
+#define NUM_CADENCE_MAX 25
+static int num_cadence = 4;
+static int user_has_defined_cadences = 0;
+
+static struct dahdi_ring_cadence cadences[NUM_CADENCE_MAX] = {
+       { { 125, 125, 2000, 4000 } },                   /*!< Quick chirp followed by normal ring */
+       { { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */
+       { { 125, 125, 125, 125, 125, 4000 } },  /*!< Three short bursts */
+       { { 1000, 500, 2500, 5000 } },  /*!< Long ring */
+};
+
+/*! \brief cidrings says in which pause to transmit the cid information, where the first pause
+ * is 1, the second pause is 2 and so on.
+ */
+
+static int cidrings[NUM_CADENCE_MAX] = {
+       2,                                                                              /*!< Right after first long ring */
+       4,                                                                              /*!< Right after long part */
+       3,                                                                              /*!< After third chirp */
+       2,                                                                              /*!< Second spell */
+};
+
+/* ETSI EN300 659-1 specifies the ring pulse between 200 and 300 mS */
+static struct dahdi_ring_cadence AS_RP_cadence = {{250, 10000}};
+
+#define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \
+                       (p->sig == SIG_FXSGS) || (p->sig == SIG_PRI))
+
+#define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */)
+#define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */)
+
+static int dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok)
+{
+       int res;
+       if (p->subs[SUB_REAL].owner == ast)
+               res = 0;
+       else if (p->subs[SUB_CALLWAIT].owner == ast)
+               res = 1;
+       else if (p->subs[SUB_THREEWAY].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 wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri)
+{
+#ifdef HAVE_PRI
+       if (pri)
+               ast_mutex_unlock(&pri->lock);
+#endif
+       for (;;) {
+               if (p->subs[a].owner) {
+                       if (ast_channel_trylock(p->subs[a].owner)) {
+                               DEADLOCK_AVOIDANCE(&p->lock);
+                       } else {
+                               ast_queue_frame(p->subs[a].owner, &ast_null_frame);
+                               ast_channel_unlock(p->subs[a].owner);
+                               break;
+                       }
+               } else
+                       break;
+       }
+#ifdef HAVE_PRI
+       if (pri)
+               ast_mutex_lock(&pri->lock);
+#endif
+}
+
+static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, void *data)
+{
+#ifdef HAVE_PRI
+       struct dahdi_pri *pri = (struct dahdi_pri*) data;
+#endif
+#ifdef HAVE_SS7
+       struct dahdi_ss7 *ss7 = (struct dahdi_ss7*) data;
+#endif
+       /* We must unlock the PRI to avoid the possibility of a deadlock */
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+       if (data) {
+               switch (p->sig) {
+#ifdef HAVE_PRI
+               case SIG_BRI:
+               case SIG_BRI_PTMP:
+               case SIG_PRI:
+                       ast_mutex_unlock(&pri->lock);
+                       break;
+#endif
+#ifdef HAVE_SS7
+               case SIG_SS7:
+                       ast_mutex_unlock(&ss7->lock);
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
+#endif
+       for (;;) {
+               if (p->owner) {
+                       if (ast_channel_trylock(p->owner)) {
+                               DEADLOCK_AVOIDANCE(&p->lock);
+                       } else {
+                               ast_queue_frame(p->owner, f);
+                               ast_channel_unlock(p->owner);
+                               break;
+                       }
+               } else
+                       break;
+       }
+#if defined(HAVE_PRI) || defined(HAVE_SS7)
+       if (data) {
+               switch (p->sig) {
+#ifdef HAVE_PRI
+               case SIG_BRI:
+               case SIG_BRI_PTMP:
+               case SIG_PRI:
+                       ast_mutex_lock(&pri->lock);
+                       break;
+#endif
+#ifdef HAVE_SS7
+               case SIG_SS7:
+                       ast_mutex_lock(&ss7->lock);
+                       break;
+#endif
+               default:
+                       break;
+               }
+       }
+#endif
+}
+
+static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
+#ifdef HAVE_OPENR2
+
+static int dahdi_r2_answer(struct dahdi_pvt *p)
+{
+       int res = 0;
+       /* openr2 1.1.0 and older does not even define OR2_LIB_INTERFACE
+       * and does not has support for openr2_chan_answer_call_with_mode
+       *  */
+#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1
+       const char *double_answer = pbx_builtin_getvar_helper(p->owner, "MFCR2_DOUBLE_ANSWER");
+       int wants_double_answer = ast_true(double_answer) ? 1 : 0;
+       if (!double_answer) {
+               /* this still can result in double answer if the channel context
+               * was configured that way */
+               res = openr2_chan_answer_call(p->r2chan);
+       } else if (wants_double_answer) {
+               res = openr2_chan_answer_call_with_mode(p->r2chan, OR2_ANSWER_DOUBLE);
+       } else {
+               res = openr2_chan_answer_call_with_mode(p->r2chan, OR2_ANSWER_SIMPLE);
+       }
+#else
+       res = openr2_chan_answer_call(p->r2chan);
+#endif
+       return res;
+}
+
+
+
+/* should be called with the ast_channel locked */
+static openr2_calling_party_category_t dahdi_r2_get_channel_category(struct ast_channel *c)
+{
+       openr2_calling_party_category_t cat;
+       const char *catstr = pbx_builtin_getvar_helper(c, "MFCR2_CATEGORY");
+       struct dahdi_pvt *p = c->tech_pvt;
+       if (ast_strlen_zero(catstr)) {
+               ast_debug(1, "No MFC/R2 category specified for chan %s, using default %s\n",
+                               c->name, openr2_proto_get_category_string(p->mfcr2_category));
+               return p->mfcr2_category;
+       }
+       if ((cat = openr2_proto_get_category(catstr)) == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) {
                ast_log(LOG_WARNING, "Invalid category specified '%s' for chan %s, using default %s\n",
                                catstr, c->name, openr2_proto_get_category_string(p->mfcr2_category));
                return p->mfcr2_category;
@@ -2579,6 +3489,45 @@ static char *dahdi_sig2str(int sig)
        }
 }
 
+int analog_lib_handles(int signalling, int radio, int oprmode)
+{
+       switch (signalling) {
+       case SIG_FXOLS:
+       case SIG_FXOGS:
+       case SIG_FXOKS:
+       case SIG_FXSLS:
+       case SIG_FXSGS:
+       case SIG_FXSKS:
+       case SIG_EMWINK:
+       case SIG_EM:
+       case SIG_EM_E1:
+       case SIG_FEATD:
+       case SIG_FEATDMF:
+       case SIG_E911:
+       case SIG_FGC_CAMA:
+       case SIG_FGC_CAMAMF:
+       case SIG_FEATB:
+       case SIG_SFWINK:
+       case SIG_SF:
+       case SIG_SF_FEATD:
+       case SIG_SF_FEATDMF:
+       case SIG_FEATDMF_TA:
+       case SIG_SF_FEATB:
+               break;
+       default:
+               /* The rest of the function should cover the remainder of signalling types */
+               return 0;
+       }
+
+       if (radio)
+               return 0;
+
+       if (oprmode)
+               return 0;
+
+       return 1;
+}
+
 #define sig2str dahdi_sig2str
 
 static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int idx, int slavechannel)
@@ -3025,53 +3974,6 @@ static int save_conference(struct dahdi_pvt *p)
        return 0;
 }
 
-/*!
- * \brief Send MWI state change
- *
- * \arg mailbox_full This is the mailbox associated with the FXO line that the
- *      MWI state has changed on.
- * \arg thereornot This argument should simply be set to 1 or 0, to indicate
- *      whether there are messages waiting or not.
- *
- *  \return nothing
- *
- * This function does two things:
- *
- * 1) It generates an internal Asterisk event notifying any other module that
- *    cares about MWI that the state of a mailbox has changed.
- *
- * 2) It runs the script specified by the mwimonitornotify option to allow
- *    some custom handling of the state change.
- */
-static void notify_message(char *mailbox_full, int thereornot)
-{
-       char s[sizeof(mwimonitornotify) + 80];
-       struct ast_event *event;
-       char *mailbox, *context;
-
-       /* Strip off @default */
-       context = mailbox = ast_strdupa(mailbox_full);
-       strsep(&context, "@");
-       if (ast_strlen_zero(context))
-               context = "default";
-
-       if (!(event = ast_event_new(AST_EVENT_MWI,
-                       AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
-                       AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
-                       AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot,
-                       AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, thereornot,
-                       AST_EVENT_IE_END))) {
-               return;
-       }
-
-       ast_event_queue_and_cache(event);
-
-       if (!ast_strlen_zero(mailbox) && !ast_strlen_zero(mwimonitornotify)) {
-               snprintf(s, sizeof(s), "%s %s %d", mwimonitornotify, mailbox, thereornot);
-               ast_safe_system(s);
-       }
-}
-
 static int restore_conference(struct dahdi_pvt *p)
 {
        int res;
@@ -3129,6 +4031,8 @@ static int has_voicemail(struct dahdi_pvt *p)
        return new_msgs;
 }
 
+
+
 static int send_callerid(struct dahdi_pvt *p)
 {
        /* Assumes spill in p->cidspill, p->cidlen in length and we're p->cidpos into it */
@@ -3243,6 +4147,14 @@ static int dahdi_call(struct ast_channel *ast, char *rdest, int timeout)
 
        set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain, p->txgain, p->law);
 
+       /* If this is analog signalling we can exit here */
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               p->callwaitrings = 0;
+               res = analog_call(p->sig_pvt, ast, rdest, timeout);
+               ast_mutex_unlock(&p->lock);
+               return res;
+       }
+
        mysig = p->sig;
        if (p->outsigmod > -1)
                mysig = p->outsigmod;
@@ -4437,6 +5349,34 @@ static int dahdi_hangup(struct ast_channel *ast)
                return 0;
        }
 
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               ast_mutex_lock(&p->lock);
+
+               dahdi_confmute(p, 0);
+               restore_gains(p);
+               p->ignoredtmf = 0;
+
+               if (p->bufferoverrideinuse) {
+                       /* faxbuffers are in use, revert them */
+                       struct dahdi_bufferinfo bi = {
+                               .txbufpolicy = p->buf_policy,
+                               .rxbufpolicy = p->buf_policy,
+                               .bufsize = p->bufsize,
+                               .numbufs = p->buf_no
+                       };
+                       int bpres;
+
+                       if ((bpres = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) {
+                               ast_log(LOG_WARNING, "Channel '%s' unable to revert buffer policy: %s\n", ast->name, strerror(errno));
+                       }
+                       p->bufferoverrideinuse = 0;
+               }
+
+               res = analog_hangup(p->sig_pvt, ast);
+
+               goto hangup_out;
+       }
+
        ast_mutex_lock(&p->lock);
 
        idx = dahdi_get_index(ast, p, 1);
@@ -4603,9 +5543,9 @@ static int dahdi_hangup(struct ast_channel *ast)
                        int bpres;
 
                        if ((bpres = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) {
-                               ast_log(LOG_WARNING, "Channel '%s' unable to revert faxbuffer policy: %s\n", ast->name, strerror(errno));
+                               ast_log(LOG_WARNING, "Channel '%s' unable to revert buffer policy: %s\n", ast->name, strerror(errno));
                        }
-                       p->bufferoverrideinuse = 0;     
+                       p->bufferoverrideinuse = 0;
                }
 
                law = DAHDI_LAW_DEFAULT;
@@ -4745,15 +5685,12 @@ static int dahdi_hangup(struct ast_channel *ast)
                default:
                        tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1);
                }
-               if (p->cidspill)
-                       ast_free(p->cidspill);
                if (p->sig)
                        dahdi_disable_ec(p);
                x = 0;
                ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
                ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
                p->didtdd = 0;
-               p->cidspill = NULL;
                p->callwaitcas = 0;
                p->callwaiting = p->permcallwaiting;
                p->hidecallerid = p->permhidecallerid;
@@ -4789,6 +5726,11 @@ static int dahdi_hangup(struct ast_channel *ast)
        p->cidcwexpire = 0;
        p->oprmode = 0;
        ast->tech_pvt = NULL;
+hangup_out:
+       if (p->cidspill)
+               ast_free(p->cidspill);
+       p->cidspill = NULL;
+       
        ast_mutex_unlock(&p->lock);
        ast_module_unref(ast_module_info->self);
        ast_verb(3, "Hungup '%s'\n", ast->name);
@@ -4832,6 +5774,13 @@ static int dahdi_answer(struct ast_channel *ast)
                ast_mutex_unlock(&p->lock);
                return 0;
        }
+
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               res = analog_answer(p->sig_pvt, ast);
+               ast_mutex_unlock(&p->lock);
+               return res;
+       }
+
        switch (p->sig) {
        case SIG_FXSLS:
        case SIG_FXSGS:
@@ -5666,6 +6615,11 @@ static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
                }
        if (newchan->_state == AST_STATE_RINGING)
                dahdi_indicate(newchan, AST_CONTROL_RINGING, NULL, 0);
+
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               analog_fixup(oldchan, newchan, p->sig_pvt);
+       }
+
        update_conf(p);
        ast_mutex_unlock(&p->lock);
        return 0;
@@ -5701,7 +6655,7 @@ static int dahdi_ring_phone(struct dahdi_pvt *p)
        return res;
 }
 
-static void *ss_thread(void *data);
+static void *analog_ss_thread(void *data);
 
 static int attempt_transfer(struct dahdi_pvt *p)
 {
@@ -5848,7 +6802,7 @@ static void dahdi_handle_dtmfup(struct ast_channel *ast, int idx, struct ast_fra
                                int res;
 
                                if ((res = ioctl(p->subs[idx].dfd, DAHDI_SET_BUFINFO, &bi)) < 0) {
-                                       ast_log(LOG_WARNING, "Channel '%s' unable to set faxbuffer policy, reason: %s\n", ast->name, strerror(errno));
+                                       ast_log(LOG_WARNING, "Channel '%s' unable to set buffer policy, reason: %s\n", ast->name, strerror(errno));
                                } else {
                                        p->bufferoverrideinuse = 1;
                                }
@@ -5961,820 +6915,818 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
        }
 
        switch (res) {
-               case DAHDI_EVENT_EC_DISABLED:
-                       ast_verb(3, "Channel %d echo canceler disabled.\n", p->channel);
-                       p->echocanon = 0;
-                       break;
+       case DAHDI_EVENT_EC_DISABLED:
+               ast_verb(3, "Channel %d echo canceler disabled.\n", p->channel);
+               p->echocanon = 0;
+               break;
 #ifdef HAVE_DAHDI_ECHOCANCEL_FAX_MODE
-               case DAHDI_EVENT_TX_CED_DETECTED:
-                       ast_verb(3, "Channel %d detected a CED tone towards the network.\n", p->channel);
-                       break;
-               case DAHDI_EVENT_RX_CED_DETECTED:
-                       ast_verb(3, "Channel %d detected a CED tone from the network.\n", p->channel);
-                       break;
-               case DAHDI_EVENT_EC_NLP_DISABLED:
-                       ast_verb(3, "Channel %d echo canceler disabled its NLP.\n", p->channel);
-                       break;
-               case DAHDI_EVENT_EC_NLP_ENABLED:
-                       ast_verb(3, "Channel %d echo canceler enabled its NLP.\n", p->channel);
-                       break;
+       case DAHDI_EVENT_TX_CED_DETECTED:
+               ast_verb(3, "Channel %d detected a CED tone towards the network.\n", p->channel);
+               break;
+       case DAHDI_EVENT_RX_CED_DETECTED:
+               ast_verb(3, "Channel %d detected a CED tone from the network.\n", p->channel);
+               break;
+       case DAHDI_EVENT_EC_NLP_DISABLED:
+               ast_verb(3, "Channel %d echo canceler disabled its NLP.\n", p->channel);
+               break;
+       case DAHDI_EVENT_EC_NLP_ENABLED:
+               ast_verb(3, "Channel %d echo canceler enabled its NLP.\n", p->channel);
+               break;
 #endif
-               case DAHDI_EVENT_BITSCHANGED:
+       case DAHDI_EVENT_BITSCHANGED:
 #ifdef HAVE_OPENR2
-                       if (p->sig != SIG_MFCR2) {
-                               ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
-                       } else {
-                               ast_log(LOG_DEBUG, "bits changed in chan %d\n", p->channel);
-                               openr2_chan_handle_cas(p->r2chan);
-                       }
-#else
+               if (p->sig != SIG_MFCR2) {
                        ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
+               } else {
+                       ast_log(LOG_DEBUG, "bits changed in chan %d\n", p->channel);
+                       openr2_chan_handle_cas(p->r2chan);
+               }
+#else
+               ast_log(LOG_WARNING, "Recieved bits changed on %s signalling?\n", sig2str(p->sig));
 #endif
-               case DAHDI_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[idx].dfd, -1);
-                       break;
-               case DAHDI_EVENT_DIALCOMPLETE:
+       case DAHDI_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[idx].dfd, -1);
+               break;
+       case DAHDI_EVENT_DIALCOMPLETE:
 #ifdef HAVE_OPENR2
-                       if ((p->sig & SIG_MFCR2) && p->r2chan && ast->_state != AST_STATE_UP) {
-                               /* we don't need to do anything for this event for R2 signaling
-                                  if the call is being setup */
-                               break;
-                       }
+               if ((p->sig & SIG_MFCR2) && p->r2chan && ast->_state != AST_STATE_UP) {
+                       /* we don't need to do anything for this event for R2 signaling
+                          if the call is being setup */
+                       break;
+               }
 #endif
-                       if (p->inalarm) break;
-                       if ((p->radio || (p->oprmode < 0))) break;
-                       if (ioctl(p->subs[idx].dfd,DAHDI_DIALING,&x) == -1) {
-                               ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed on %s: %s\n",ast->name, strerror(errno));
-                               return NULL;
-                       }
-                       if (!x) { /* if not still dialing in driver */
-                               dahdi_enable_ec(p);
-                               if (p->echobreak) {
-                                       dahdi_train_ec(p);
-                                       ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr));
-                                       p->dop.op = DAHDI_DIAL_OP_REPLACE;
-                                       res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
-                                       p->echobreak = 0;
-                               } else {
-                                       p->dialing = 0;
-                                       if ((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) {
-                                               /* if thru with dialing after offhook */
-                                               if (ast->_state == AST_STATE_DIALING_OFFHOOK) {
-                                                       ast_setstate(ast, AST_STATE_UP);
-                                                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                                                       p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
-                                                       break;
-                                               } else { /* if to state wait for offhook to dial rest */
-                                                       /* we now wait for off hook */
-                                                       ast_setstate(ast,AST_STATE_DIALING_OFFHOOK);
-                                               }
+               if (p->inalarm) break;
+               if ((p->radio || (p->oprmode < 0))) break;
+               if (ioctl(p->subs[idx].dfd,DAHDI_DIALING,&x) == -1) {
+                       ast_log(LOG_DEBUG, "DAHDI_DIALING ioctl failed on %s: %s\n",ast->name, strerror(errno));
+                       return NULL;
+               }
+               if (!x) { /* if not still dialing in driver */
+                       dahdi_enable_ec(p);
+                       if (p->echobreak) {
+                               dahdi_train_ec(p);
+                               ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr));
+                               p->dop.op = DAHDI_DIAL_OP_REPLACE;
+                               res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
+                               p->echobreak = 0;
+                       } else {
+                               p->dialing = 0;
+                               if ((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) {
+                                       /* if thru with dialing after offhook */
+                                       if (ast->_state == AST_STATE_DIALING_OFFHOOK) {
+                                               ast_setstate(ast, AST_STATE_UP);
+                                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                                               p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
+                                               break;
+                                       } else { /* if to state wait for offhook to dial rest */
+                                               /* we now wait for off hook */
+                                               ast_setstate(ast,AST_STATE_DIALING_OFFHOOK);
                                        }
-                                       if (ast->_state == AST_STATE_DIALING) {
-                                               if ((p->callprogress & CALLPROGRESS_PROGRESS) && CANPROGRESSDETECT(p) && p->dsp && p->outgoing) {
-                                                       ast_debug(1, "Done dialing, but waiting for progress detection before doing more...\n");
-                                               } else if (p->confirmanswer || (!p->dialednone
-                                                       && ((mysig == SIG_EM) || (mysig == SIG_EM_E1)
-                                                               || (mysig == SIG_EMWINK) || (mysig == SIG_FEATD)
-                                                               || (mysig == SIG_FEATDMF_TA) || (mysig == SIG_FEATDMF)
-                                                               || (mysig == SIG_E911) || (mysig == SIG_FGC_CAMA)
-                                                               || (mysig == SIG_FGC_CAMAMF) || (mysig == SIG_FEATB)
-                                                               || (mysig == SIG_SF) || (mysig == SIG_SFWINK)
-                                                               || (mysig == SIG_SF_FEATD) || (mysig == SIG_SF_FEATDMF)
-                                                               || (mysig == SIG_SF_FEATB)))) {
-                                                       ast_setstate(ast, AST_STATE_RINGING);
-                                               } else if (!p->answeronpolarityswitch) {
-                                                       ast_setstate(ast, AST_STATE_UP);
-                                                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                                                       p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
-                                                       /* If aops=0 and hops=1, this is necessary */
-                                                       p->polarity = POLARITY_REV;
-                                               } else {
-                                                       /* Start clean, so we can catch the change to REV polarity when party answers */
-                                                       p->polarity = POLARITY_IDLE;
-                                               }
+                               }
+                               if (ast->_state == AST_STATE_DIALING) {
+                                       if ((p->callprogress & CALLPROGRESS_PROGRESS) && CANPROGRESSDETECT(p) && p->dsp && p->outgoing) {
+                                               ast_debug(1, "Done dialing, but waiting for progress detection before doing more...\n");
+                                       } else if (p->confirmanswer || (!p->dialednone
+                                               && ((mysig == SIG_EM) || (mysig == SIG_EM_E1)
+                                                       || (mysig == SIG_EMWINK) || (mysig == SIG_FEATD)
+                                                       || (mysig == SIG_FEATDMF_TA) || (mysig == SIG_FEATDMF)
+                                                       || (mysig == SIG_E911) || (mysig == SIG_FGC_CAMA)
+                                                       || (mysig == SIG_FGC_CAMAMF) || (mysig == SIG_FEATB)
+                                                       || (mysig == SIG_SF) || (mysig == SIG_SFWINK)
+                                                       || (mysig == SIG_SF_FEATD) || (mysig == SIG_SF_FEATDMF)
+                                                       || (mysig == SIG_SF_FEATB)))) {
+                                               ast_setstate(ast, AST_STATE_RINGING);
+                                       } else if (!p->answeronpolarityswitch) {
+                                               ast_setstate(ast, AST_STATE_UP);
+                                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                                               p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
+                                               /* If aops=0 and hops=1, this is necessary */
+                                               p->polarity = POLARITY_REV;
+                                       } else {
+                                               /* Start clean, so we can catch the change to REV polarity when party answers */
+                                               p->polarity = POLARITY_IDLE;
                                        }
                                }
                        }
-                       break;
-               case DAHDI_EVENT_ALARM:
+               }
+               break;
+       case DAHDI_EVENT_ALARM:
 #ifdef HAVE_PRI
-                       if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
-                               if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) {
-                                       /* T309 is not enabled : hangup calls when alarm occurs */
-                                       if (p->call) {
-                                               if (p->pri && p->pri->pri) {
-                                                       if (!pri_grab(p, p->pri)) {
-                                                               pri_hangup(p->pri->pri, p->call, -1);
-                                                               pri_destroycall(p->pri->pri, p->call);
-                                                               p->call = NULL;
-                                                               pri_rel(p->pri);
-                                                       } else
-                                                               ast_log(LOG_WARNING, "Failed to grab PRI!\n");
+               if ((p->sig == SIG_PRI) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
+                       if (!p->pri || !p->pri->pri || (pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0)) {
+                               /* T309 is not enabled : hangup calls when alarm occurs */
+                               if (p->call) {
+                                       if (p->pri && p->pri->pri) {
+                                               if (!pri_grab(p, p->pri)) {
+                                                       pri_hangup(p->pri->pri, p->call, -1);
+                                                       pri_destroycall(p->pri->pri, p->call);
+                                                       p->call = NULL;
+                                                       pri_rel(p->pri);
                                                } else
-                                                       ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n");
-                                       }
-                                       if (p->owner)
-                                               p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                       ast_log(LOG_WARNING, "Failed to grab PRI!\n");
+                                       } else
+                                               ast_log(LOG_WARNING, "The PRI Call has not been destroyed\n");
                                }
+                               if (p->owner)
+                                       p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
                        }
-                       if (p->bearer)
-                               p->bearer->inalarm = 1;
-                       else
+               }
+               if (p->bearer)
+                       p->bearer->inalarm = 1;
+               else
 #endif
-                       p->inalarm = 1;
-                       res = get_alarms(p);
-                       handle_alarms(p, res);
+               p->inalarm = 1;
+               res = get_alarms(p);
+               handle_alarms(p, res);
 #ifdef HAVE_PRI
-                       if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) {
-                               /* fall through intentionally */
-                       } else {
-                               break;
-                       }
+               if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) {
+                       /* fall through intentionally */
+               } else {
+                       break;
+               }
 #endif
 #ifdef HAVE_SS7
-                       if (p->sig == SIG_SS7)
-                               break;
+               if (p->sig == SIG_SS7)
+                       break;
 #endif
 #ifdef HAVE_OPENR2
-                       if (p->sig == SIG_MFCR2)
-                               break;
+               if (p->sig == SIG_MFCR2)
+                       break;
 #endif
-               case DAHDI_EVENT_ONHOOK:
-                       if (p->radio) {
-                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                               p->subs[idx].f.subclass = AST_CONTROL_RADIO_UNKEY;
-                               break;
-                       }
-                       if (p->oprmode < 0)
+       case DAHDI_EVENT_ONHOOK:
+               if (p->radio) {
+                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                       p->subs[idx].f.subclass = AST_CONTROL_RADIO_UNKEY;
+                       break;
+               }
+               if (p->oprmode < 0)
+               {
+                       if (p->oprmode != -1) break;
+                       if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS))
                        {
-                               if (p->oprmode != -1) break;
-                               if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS))
-                               {
-                                       /* Make sure it starts ringing */
-                                       dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF);
-                                       dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RING);
-                                       save_conference(p->oprpeer);
-                                       tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
-                               }
-                               break;
+                               /* Make sure it starts ringing */
+                               dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF);
+                               dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RING);
+                               save_conference(p->oprpeer);
+                               tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
                        }
-                       switch (p->sig) {
-                       case SIG_FXOLS:
-                       case SIG_FXOGS:
-                       case SIG_FXOKS:
-                               p->onhooktime = time(NULL);
-                               p->fxsoffhookstate = 0;
-                               p->msgstate = -1;
-                               /* Check for some special conditions regarding call waiting */
-                               if (idx == SUB_REAL) {
-                                       /* The normal line was hung up */
-                                       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);
-                                               ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel);
-                                               unalloc_sub(p, SUB_CALLWAIT);
+                       break;
+               }
+               switch (p->sig) {
+               case SIG_FXOLS:
+               case SIG_FXOGS:
+               case SIG_FXOKS:
+                       p->onhooktime = time(NULL);
+                       p->fxsoffhookstate = 0;
+                       p->msgstate = -1;
+                       /* Check for some special conditions regarding call waiting */
+                       if (idx == SUB_REAL) {
+                               /* The normal line was hung up */
+                               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);
+                                       ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel);
+                                       unalloc_sub(p, SUB_CALLWAIT);
 #if 0
-                                               p->subs[idx].needanswer = 0;
-                                               p->subs[idx].needringing = 0;
+                                       p->subs[idx].needanswer = 0;
+                                       p->subs[idx].needringing = 0;
 #endif
-                                               p->callwaitingrepeat = 0;
-                                               p->cidcwexpire = 0;
-                                               p->owner = NULL;
-                                               /* Don't start streaming audio yet if the incoming call isn't up yet */
-                                               if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP)
-                                                       p->dialing = 1;
-                                               dahdi_ring_phone(p);
-                                       } else if (p->subs[SUB_THREEWAY].owner) {
-                                               unsigned int mssinceflash;
-                                               /* Here we have to retain the lock on both the main channel, the 3-way channel, and
-                                                  the private structure -- not especially easy or clean */
-                                               while (p->subs[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) {
-                                                       /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
-                                                       DLA_UNLOCK(&p->lock);
-                                                       CHANNEL_DEADLOCK_AVOIDANCE(ast);
-                                                       /* We can grab ast and p in that order, without worry.  We should make sure
-                                                          nothing seriously bad has happened though like some sort of bizarre double
-                                                          masquerade! */
-                                                       DLA_LOCK(&p->lock);
-                                                       if (p->owner != ast) {
-                                                               ast_log(LOG_WARNING, "This isn't good...\n");
-                                                               return NULL;
-                                                       }
-                                               }
-                                               if (!p->subs[SUB_THREEWAY].owner) {
-                                                       ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n");
+                                       p->callwaitingrepeat = 0;
+                                       p->cidcwexpire = 0;
+                                       p->owner = NULL;
+                                       /* Don't start streaming audio yet if the incoming call isn't up yet */
+                                       if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP)
+                                               p->dialing = 1;
+                                       dahdi_ring_phone(p);
+                               } else if (p->subs[SUB_THREEWAY].owner) {
+                                       unsigned int mssinceflash;
+                                       /* Here we have to retain the lock on both the main channel, the 3-way channel, and
+                                          the private structure -- not especially easy or clean */
+                                       while (p->subs[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) {
+                                               /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
+                                               DLA_UNLOCK(&p->lock);
+                                               CHANNEL_DEADLOCK_AVOIDANCE(ast);
+                                               /* We can grab ast and p in that order, without worry.  We should make sure
+                                                  nothing seriously bad has happened though like some sort of bizarre double
+                                                  masquerade! */
+                                               DLA_LOCK(&p->lock);
+                                               if (p->owner != ast) {
+                                                       ast_log(LOG_WARNING, "This isn't good...\n");
                                                        return NULL;
                                                }
-                                               mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime);
-                                               ast_debug(1, "Last flash was %d ms ago\n", mssinceflash);
-                                               if (mssinceflash < MIN_MS_SINCE_FLASH) {
-                                                       /* It hasn't been long enough since the last flashook.  This is probably a bounce on
-                                                          hanging up.  Hangup both channels now */
-                                                       if (p->subs[SUB_THREEWAY].owner)
-                                                               ast_queue_hangup_with_cause(p->subs[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
-                                                       p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
-                                                       ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel);
-                                                       ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                               } else 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;
-                                                               /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
-                                                               if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) {
-                                                                       ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                                                       /* Swap subs and dis-own channel */
-                                                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
-                                                                       p->owner = NULL;
-                                                                       /* Ring the phone */
-                                                                       dahdi_ring_phone(p);
-                                                               } else {
-                                                                       if ((res = attempt_transfer(p)) < 0) {
-                                                                               p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
-                                                                               if (p->subs[SUB_THREEWAY].owner)
-                                                                                       ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                                                       } else if (res) {
-                                                                               /* Don't actually hang up at this point */
-                                                                               if (p->subs[SUB_THREEWAY].owner)
-                                                                                       ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                                                               break;
-                                                                       }
-                                                               }
+                                       }
+                                       if (!p->subs[SUB_THREEWAY].owner) {
+                                               ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n");
+                                               return NULL;
+                                       }
+                                       mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime);
+                                       ast_debug(1, "Last flash was %d ms ago\n", mssinceflash);
+                                       if (mssinceflash < MIN_MS_SINCE_FLASH) {
+                                               /* It hasn't been long enough since the last flashook.  This is probably a bounce on
+                                                  hanging up.  Hangup both channels now */
+                                               if (p->subs[SUB_THREEWAY].owner)
+                                                       ast_queue_hangup_with_cause(p->subs[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
+                                               p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                               ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel);
+                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                                       } else 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;
+                                                       /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
+                                                       if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) {
+                                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                                                               /* Swap subs and dis-own channel */
+                                                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                                               p->owner = NULL;
+                                                               /* Ring the phone */
+                                                               dahdi_ring_phone(p);
                                                        } else {
-                                                               p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
-                                                               if (p->subs[SUB_THREEWAY].owner)
-                                                                       ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                                                               if ((res = attempt_transfer(p)) < 0) {
+                                                                       p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                                       if (p->subs[SUB_THREEWAY].owner)
+                                                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                                                               } else if (res) {
+                                                                       /* Don't actually hang up at this point */
+                                                                       if (p->subs[SUB_THREEWAY].owner)
+                                                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                                                                       break;
+                                                               }
                                                        }
                                                } else {
-                                                       ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                                       /* Swap subs and dis-own channel */
-                                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
-                                                       p->owner = NULL;
-                                                       /* Ring the phone */
-                                                       dahdi_ring_phone(p);
+                                                       p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
+                                                       if (p->subs[SUB_THREEWAY].owner)
+                                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
                                                }
+                                       } else {
+                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                                               /* Swap subs and dis-own channel */
+                                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                               p->owner = NULL;
+                                               /* Ring the phone */
+                                               dahdi_ring_phone(p);
                                        }
-                               } else {
-                                       ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", idx);
                                }
-                               /* Fall through */
-                       default:
-                               dahdi_disable_ec(p);
-                               return NULL;
+                       } else {
+                               ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", idx);
                        }
-                       break;
-               case DAHDI_EVENT_RINGOFFHOOK:
-                       if (p->inalarm) break;
-                       if (p->oprmode < 0)
+                       /* Fall through */
+               default:
+                       dahdi_disable_ec(p);
+                       return NULL;
+               }
+               break;
+       case DAHDI_EVENT_RINGOFFHOOK:
+               if (p->inalarm) break;
+               if (p->oprmode < 0)
+               {
+                       if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS))
                        {
-                               if ((p->sig == SIG_FXOLS) || (p->sig == SIG_FXOKS) || (p->sig == SIG_FXOGS))
-                               {
-                                       /* Make sure it stops ringing */
-                                       dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF);
-                                       tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, -1);
-                                       restore_conference(p->oprpeer);
-                               }
-                               break;
+                               /* Make sure it stops ringing */
+                               dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_RINGOFF);
+                               tone_zone_play_tone(p->oprpeer->subs[SUB_REAL].dfd, -1);
+                               restore_conference(p->oprpeer);
                        }
-                       if (p->radio)
-                       {
-                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                               p->subs[idx].f.subclass = AST_CONTROL_RADIO_KEY;
-                               break;
-                       }
-                       /* for E911, its supposed to wait for offhook then dial
-                          the second half of the dial string */
-                       if (((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) {
-                               c = strchr(p->dialdest, '/');
-                               if (c)
-                                       c++;
-                               else
-                                       c = p->dialdest;
-                               if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c);
-                               else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr));
-                               if (strlen(p->dop.dialstr) > 4) {
-                                       memset(p->echorest, 'w', sizeof(p->echorest) - 1);
-                                       strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
-                                       p->echorest[sizeof(p->echorest) - 1] = '\0';
-                                       p->echobreak = 1;
-                                       p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
-                               } else
-                                       p->echobreak = 0;
-                               if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop)) {
-                                       int saveerr = errno;
+                       break;
+               }
+               if (p->radio)
+               {
+                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                       p->subs[idx].f.subclass = AST_CONTROL_RADIO_KEY;
+                       break;
+               }
+               /* for E911, its supposed to wait for offhook then dial
+                  the second half of the dial string */
+               if (((mysig == SIG_E911) || (mysig == SIG_FGC_CAMA) || (mysig == SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) {
+                       c = strchr(p->dialdest, '/');
+                       if (c)
+                               c++;
+                       else
+                               c = p->dialdest;
+                       if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c);
+                       else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr));
+                       if (strlen(p->dop.dialstr) > 4) {
+                               memset(p->echorest, 'w', sizeof(p->echorest) - 1);
+                               strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
+                               p->echorest[sizeof(p->echorest) - 1] = '\0';
+                               p->echobreak = 1;
+                               p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
+                       } else
+                               p->echobreak = 0;
+                       if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop)) {
+                               int saveerr = errno;
 
-                                       x = DAHDI_ONHOOK;
-                                       ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x);
-                                       ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr));
-                                       return NULL;
-                                       }
-                               p->dialing = 1;
-                               return &p->subs[idx].f;
-                       }
-                       switch (p->sig) {
-                       case SIG_FXOLS:
-                       case SIG_FXOGS:
-                       case SIG_FXOKS:
-                               p->fxsoffhookstate = 1;
-                               switch (ast->_state) {
-                               case AST_STATE_RINGING:
-                                       dahdi_enable_ec(p);
-                                       dahdi_train_ec(p);
-                                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                                       p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
-                                       /* Make sure it stops ringing */
-                                       dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK);
-                                       ast_debug(1, "channel %d answered\n", p->channel);
-                                       if (p->cidspill) {
-                                               /* Cancel any running CallerID spill */
-                                               ast_free(p->cidspill);
-                                               p->cidspill = NULL;
-                                       }
-                                       p->dialing = 0;
-                                       p->callwaitcas = 0;
-                                       if (p->confirmanswer) {
-                                               /* Ignore answer if "confirm answer" is enabled */
-                                               p->subs[idx].f.frametype = AST_FRAME_NULL;
-                                               p->subs[idx].f.subclass = 0;
-                                       } else if (!ast_strlen_zero(p->dop.dialstr)) {
-                                               /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */
-                                               res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
-                                               if (res < 0) {
-                                                       ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
-                                                       p->dop.dialstr[0] = '\0';
-                                                       return NULL;
-                                               } else {
-                                                       ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr);
-                                                       p->subs[idx].f.frametype = AST_FRAME_NULL;
-                                                       p->subs[idx].f.subclass = 0;
-                                                       p->dialing = 1;
-                                               }
-                                               p->dop.dialstr[0] = '\0';
-                                               ast_setstate(ast, AST_STATE_DIALING);
-                                       } else
-                                               ast_setstate(ast, AST_STATE_UP);
-                                       return &p->subs[idx].f;
-                               case AST_STATE_DOWN:
-                                       ast_setstate(ast, AST_STATE_RING);
-                                       ast->rings = 1;
-                                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                                       p->subs[idx].f.subclass = AST_CONTROL_OFFHOOK;
-                                       ast_debug(1, "channel %d picked up\n", p->channel);
-                                       return &p->subs[idx].f;
-                               case AST_STATE_UP:
-                                       /* Make sure it stops ringing */
-                                       dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK);
-                                       /* Okay -- probably call waiting*/
-                                       if (ast_bridged_channel(p->owner))
-                                               ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
-                                       p->subs[idx].needunhold = 1;
-                                       break;
-                               case AST_STATE_RESERVED:
-                                       /* Start up dialtone */
-                                       if (has_voicemail(p))
-                                               res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER);
-                                       else
-                                               res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE);
-                                       break;
-                               default:
-                                       ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state);
+                               x = DAHDI_ONHOOK;
+                               ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x);
+                               ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr));
+                               return NULL;
                                }
-                               break;
-                       case SIG_FXSLS:
-                       case SIG_FXSGS:
-                       case SIG_FXSKS:
-                               if (ast->_state == AST_STATE_RING) {
-                                       p->ringt = p->ringt_base;
+                       p->dialing = 1;
+                       return &p->subs[idx].f;
+               }
+               switch (p->sig) {
+               case SIG_FXOLS:
+               case SIG_FXOGS:
+               case SIG_FXOKS:
+                       p->fxsoffhookstate = 1;
+                       switch (ast->_state) {
+                       case AST_STATE_RINGING:
+                               dahdi_enable_ec(p);
+                               dahdi_train_ec(p);
+                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
+                               /* Make sure it stops ringing */
+                               dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK);
+                               ast_debug(1, "channel %d answered\n", p->channel);
+                               if (p->cidspill) {
+                                       /* Cancel any running CallerID spill */
+                                       ast_free(p->cidspill);
+                                       p->cidspill = NULL;
                                }
-
-                               /* If we get a ring then we cannot be in
-                                * reversed polarity. So we reset to idle */
-                               ast_debug(1, "Setting IDLE polarity due "
-                                       "to ring. Old polarity was %d\n",
-                                       p->polarity);
-                               p->polarity = POLARITY_IDLE;
-
-                               /* Fall through */
-                       case SIG_EM:
-                       case SIG_EM_E1:
-                       case SIG_EMWINK:
-                       case SIG_FEATD:
-                       case SIG_FEATDMF:
-                       case SIG_FEATDMF_TA:
-                       case SIG_E911:
-                       case SIG_FGC_CAMA:
-                       case SIG_FGC_CAMAMF:
-                       case SIG_FEATB:
-                       case SIG_SF:
-                       case SIG_SFWINK:
-                       case SIG_SF_FEATD:
-                       case SIG_SF_FEATDMF:
-                       case SIG_SF_FEATB:
-                               if (ast->_state == AST_STATE_PRERING)
-                                       ast_setstate(ast, AST_STATE_RING);
-                               if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) {
-                                       ast_debug(1, "Ring detected\n");
-                                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                                       p->subs[idx].f.subclass = AST_CONTROL_RING;
-                               } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) {
-                                       ast_debug(1, "Line answered\n");
-                                       if (p->confirmanswer) {
+                               p->dialing = 0;
+                               p->callwaitcas = 0;
+                               if (p->confirmanswer) {
+                                       /* Ignore answer if "confirm answer" is enabled */
+                                       p->subs[idx].f.frametype = AST_FRAME_NULL;
+                                       p->subs[idx].f.subclass = 0;
+                               } else if (!ast_strlen_zero(p->dop.dialstr)) {
+                                       /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */
+                                       res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
+                                       if (res < 0) {
+                                               ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
+                                               p->dop.dialstr[0] = '\0';
+                                               return NULL;
+                                       } else {
+                                               ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr);
                                                p->subs[idx].f.frametype = AST_FRAME_NULL;
                                                p->subs[idx].f.subclass = 0;
-                                       } else {
-                                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                                               p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
-                                               ast_setstate(ast, AST_STATE_UP);
+                                               p->dialing = 1;
                                        }
-                               } else if (ast->_state != AST_STATE_RING)
-                                       ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel);
+                                       p->dop.dialstr[0] = '\0';
+                                       ast_setstate(ast, AST_STATE_DIALING);
+                               } else
+                                       ast_setstate(ast, AST_STATE_UP);
+                               return &p->subs[idx].f;
+                       case AST_STATE_DOWN:
+                               ast_setstate(ast, AST_STATE_RING);
+                               ast->rings = 1;
+                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[idx].f.subclass = AST_CONTROL_OFFHOOK;
+                               ast_debug(1, "channel %d picked up\n", p->channel);
+                               return &p->subs[idx].f;
+                       case AST_STATE_UP:
+                               /* Make sure it stops ringing */
+                               dahdi_set_hook(p->subs[idx].dfd, DAHDI_OFFHOOK);
+                               /* Okay -- probably call waiting*/
+                               if (ast_bridged_channel(p->owner))
+                                       ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+                               p->subs[idx].needunhold = 1;
+                               break;
+                       case AST_STATE_RESERVED:
+                               /* Start up dialtone */
+                               if (has_voicemail(p))
+                                       res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_STUTTER);
+                               else
+                                       res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE);
                                break;
                        default:
-                               ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig);
+                               ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state);
                        }
                        break;
-               case DAHDI_EVENT_RINGBEGIN:
-                       switch (p->sig) {
-                       case SIG_FXSLS:
-                       case SIG_FXSGS:
-                       case SIG_FXSKS:
-                               if (ast->_state == AST_STATE_RING) {
-                                       p->ringt = p->ringt_base;
-                               }
-                               break;
+               case SIG_FXSLS:
+               case SIG_FXSGS:
+               case SIG_FXSKS:
+                       if (ast->_state == AST_STATE_RING) {
+                               p->ringt = p->ringt_base;
                        }
+
+                       /* If we get a ring then we cannot be in
+                        * reversed polarity. So we reset to idle */
+                       ast_debug(1, "Setting IDLE polarity due "
+                               "to ring. Old polarity was %d\n",
+                               p->polarity);
+                       p->polarity = POLARITY_IDLE;
+
+                       /* Fall through */
+               case SIG_EM:
+               case SIG_EM_E1:
+               case SIG_EMWINK:
+               case SIG_FEATD:
+               case SIG_FEATDMF:
+               case SIG_FEATDMF_TA:
+               case SIG_E911:
+               case SIG_FGC_CAMA:
+               case SIG_FGC_CAMAMF:
+               case SIG_FEATB:
+               case SIG_SF:
+               case SIG_SFWINK:
+               case SIG_SF_FEATD:
+               case SIG_SF_FEATDMF:
+               case SIG_SF_FEATB:
+                       if (ast->_state == AST_STATE_PRERING)
+                               ast_setstate(ast, AST_STATE_RING);
+                       if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) {
+                               ast_debug(1, "Ring detected\n");
+                               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[idx].f.subclass = AST_CONTROL_RING;
+                       } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) {
+                               ast_debug(1, "Line answered\n");
+                               if (p->confirmanswer) {
+                                       p->subs[idx].f.frametype = AST_FRAME_NULL;
+                                       p->subs[idx].f.subclass = 0;
+                               } else {
+                                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+                                       p->subs[idx].f.subclass = AST_CONTROL_ANSWER;
+                                       ast_setstate(ast, AST_STATE_UP);
+                               }
+                       } else if (ast->_state != AST_STATE_RING)
+                               ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel);
                        break;
-               case DAHDI_EVENT_RINGEROFF:
-                       if (p->inalarm) break;
-                       if ((p->radio || (p->oprmode < 0))) break;
-                       ast->rings++;
-                       if ((ast->rings > p->cidrings) && (p->cidspill)) {
-                               ast_log(LOG_WARNING, "Didn't finish Caller-ID spill.  Cancelling.\n");
-                               ast_free(p->cidspill);
-                               p->cidspill = NULL;
-                               p->callwaitcas = 0;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig);
+               }
+               break;
+       case DAHDI_EVENT_RINGBEGIN:
+               switch (p->sig) {
+               case SIG_FXSLS:
+               case SIG_FXSGS:
+               case SIG_FXSKS:
+                       if (ast->_state == AST_STATE_RING) {
+                               p->ringt = p->ringt_base;
                        }
-                       p->subs[idx].f.frametype = AST_FRAME_CONTROL;
-                       p->subs[idx].f.subclass = AST_CONTROL_RINGING;
-                       break;
-               case DAHDI_EVENT_RINGERON:
                        break;
-               case DAHDI_EVENT_NOALARM:
-                       p->inalarm = 0;
+               }
+               break;
+       case DAHDI_EVENT_RINGEROFF:
+               if (p->inalarm) break;
+               if ((p->radio || (p->oprmode < 0))) break;
+               ast->rings++;
+               if ((ast->rings > p->cidrings) && (p->cidspill)) {
+                       ast_log(LOG_WARNING, "Didn't finish Caller-ID spill.  Cancelling.\n");
+                       ast_free(p->cidspill);
+                       p->cidspill = NULL;
+                       p->callwaitcas = 0;
+               }
+               p->subs[idx].f.frametype = AST_FRAME_CONTROL;
+               p->subs[idx].f.subclass = AST_CONTROL_RINGING;
+               break;
+       case DAHDI_EVENT_RINGERON:
+               break;
+       case DAHDI_EVENT_NOALARM:
+               p->inalarm = 0;
 #ifdef HAVE_PRI
-                       /* Extremely unlikely but just in case */
-                       if (p->bearer)
-                               p->bearer->inalarm = 0;
+               /* Extremely unlikely but just in case */
+               if (p->bearer)
+                       p->bearer->inalarm = 0;
 #endif
-                       ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
-                       manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
-                                                               "Channel: %d\r\n", p->channel);
-                       break;
-               case DAHDI_EVENT_WINKFLASH:
-                       if (p->inalarm) break;
-                       if (p->radio) break;
-                       if (p->oprmode < 0) break;
-                       if (p->oprmode > 1)
+               ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
+               manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
+                                                       "Channel: %d\r\n", p->channel);
+               break;
+       case DAHDI_EVENT_WINKFLASH:
+               if (p->inalarm) break;
+               if (p->radio) break;
+               if (p->oprmode < 0) break;
+               if (p->oprmode > 1)
+               {
+                       struct dahdi_params par;
+
+                       memset(&par, 0, sizeof(par));
+                       if (ioctl(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par) != -1)
                        {
-                               struct dahdi_params par;
+                               if (!par.rxisoffhook)
+                               {
+                                       /* Make sure it stops ringing */
+                                       dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RINGOFF);
+                                       dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RING);
+                                       save_conference(p);
+                                       tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
+                               }
+                       }
+                       break;
+               }
+               /* Remember last time we got a flash-hook */
+               p->flashtime = ast_tvnow();
+               switch (mysig) {
+               case SIG_FXOLS:
+               case SIG_FXOGS:
+               case SIG_FXOKS:
+                       ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
+                               idx, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd);
+                       p->callwaitcas = 0;
 
-                               memset(&par, 0, sizeof(par));
-                               if (ioctl(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &par) != -1)
-                               {
-                                       if (!par.rxisoffhook)
-                                       {
-                                               /* Make sure it stops ringing */
-                                               dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RINGOFF);
-                                               dahdi_set_hook(p->oprpeer->subs[SUB_REAL].dfd, DAHDI_RING);
-                                               save_conference(p);
-                                               tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
-                                       }
-                               }
-                               break;
+                       if (idx != SUB_REAL) {
+                               ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", idx, p->channel);
+                               goto winkflashdone;
                        }
-                       /* Remember last time we got a flash-hook */
-                       p->flashtime = ast_tvnow();
-                       switch (mysig) {
-                       case SIG_FXOLS:
-                       case SIG_FXOGS:
-                       case SIG_FXOKS:
-                               ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
-                                       idx, p->subs[SUB_REAL].dfd, p->subs[SUB_CALLWAIT].dfd, p->subs[SUB_THREEWAY].dfd);
-                               p->callwaitcas = 0;
 
-                               if (idx != SUB_REAL) {
-                                       ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", idx, p->channel);
-                                       goto winkflashdone;
+                       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].dfd, -1);
+                               p->owner = p->subs[SUB_REAL].owner;
+                               ast_debug(1, "Making %s the new owner\n", p->owner->name);
+                               if (p->owner->_state == AST_STATE_RINGING) {
+                                       ast_setstate(p->owner, AST_STATE_UP);
+                                       p->subs[SUB_REAL].needanswer = 1;
                                }
-
-                               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].dfd, -1);
-                                       p->owner = p->subs[SUB_REAL].owner;
-                                       ast_debug(1, "Making %s the new owner\n", p->owner->name);
-                                       if (p->owner->_state == AST_STATE_RINGING) {
-                                               ast_setstate(p->owner, AST_STATE_UP);
-                                               p->subs[SUB_REAL].needanswer = 1;
-                                       }
-                                       p->callwaitingrepeat = 0;
-                                       p->cidcwexpire = 0;
-                                       /* Start music on hold if appropriate */
-                                       if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
-                                               ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
-                                                       S_OR(p->mohsuggest, NULL),
-                                                       !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               p->callwaitingrepeat = 0;
+                               p->cidcwexpire = 0;
+                               /* Start music on hold if appropriate */
+                               if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
+                                       ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
+                                               S_OR(p->mohsuggest, NULL),
+                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               }
+                               p->subs[SUB_CALLWAIT].needhold = 1;
+                               if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
+                                       ast_queue_control_data(p->subs[SUB_REAL].owner, AST_CONTROL_HOLD,
+                                               S_OR(p->mohsuggest, NULL),
+                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               }
+                               p->subs[SUB_REAL].needunhold = 1;
+                       } else if (!p->subs[SUB_THREEWAY].owner) {
+                               if (!p->threewaycalling) {
+                                       /* Just send a flash if no 3-way calling */
+                                       p->subs[SUB_REAL].needflash = 1;
+                                       goto winkflashdone;
+                               } else if (!check_for_conference(p)) {
+                                       char cid_num[256];
+                                       char cid_name[256];
+
+                                       cid_num[0] = 0;
+                                       cid_name[0] = 0;
+                                       if (p->dahditrcallerid && p->owner) {
+                                               if (p->owner->cid.cid_num)
+                                                       ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num));
+                                               if (p->owner->cid.cid_name)
+                                                       ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name));
                                        }
-                                       p->subs[SUB_CALLWAIT].needhold = 1;
-                                       if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
-                                               ast_queue_control_data(p->subs[SUB_REAL].owner, AST_CONTROL_HOLD,
-                                                       S_OR(p->mohsuggest, NULL),
-                                                       !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                                       /* 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))) {
+                                               ast_debug(1, "Flash when call not up or ringing\n");
+                                               goto winkflashdone;
                                        }
-                                       p->subs[SUB_REAL].needunhold = 1;
-                               } else if (!p->subs[SUB_THREEWAY].owner) {
-                                       if (!p->threewaycalling) {
-                                               /* Just send a flash if no 3-way calling */
-                                               p->subs[SUB_REAL].needflash = 1;
+                                       if (alloc_sub(p, SUB_THREEWAY)) {
+                                               ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n");
                                                goto winkflashdone;
-                                       } else if (!check_for_conference(p)) {
-                                               char cid_num[256];
-                                               char cid_name[256];
-
-                                               cid_num[0] = 0;
-                                               cid_name[0] = 0;
-                                               if (p->dahditrcallerid && p->owner) {
-                                                       if (p->owner->cid.cid_num)
-                                                               ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num));
-                                                       if (p->owner->cid.cid_name)
-                                                               ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name));
-                                               }
-                                               /* 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))) {
-                                                       ast_debug(1, "Flash when call not up or ringing\n");
-                                                       goto winkflashdone;
-                                               }
-                                               if (alloc_sub(p, SUB_THREEWAY)) {
-                                                       ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n");
-                                                       goto winkflashdone;
-                                               }
-                                               /* Make new channel */
-                                               chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0);
-                                               if (p->dahditrcallerid) {
-                                                       if (!p->origcid_num)
-                                                               p->origcid_num = ast_strdup(p->cid_num);
-                                                       if (!p->origcid_name)
-                                                               p->origcid_name = ast_strdup(p->cid_name);
-                                                       ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num));
-                                                       ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name));
-                                               }
-                                               /* Swap things around between the three-way and real call */
-                                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
-                                               /* Disable echo canceller for better dialing */
-                                               dahdi_disable_ec(p);
-                                               res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALRECALL);
-                                               if (res)
-                                                       ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
-                                               p->owner = chan;
-                                               if (!chan) {
-                                                       ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel);
-                                               } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
-                                                       ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
-                                                       res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
-                                                       dahdi_enable_ec(p);
-                                                       ast_hangup(chan);
-                                               } else {
-                                                       struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner);
-                                                       int way3bridge = 0, cdr3way = 0;
+                                       }
+                                       /* Make new channel */
+                                       chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0);
+                                       if (p->dahditrcallerid) {
+                                               if (!p->origcid_num)
+                                                       p->origcid_num = ast_strdup(p->cid_num);
+                                               if (!p->origcid_name)
+                                                       p->origcid_name = ast_strdup(p->cid_name);
+                                               ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num));
+                                               ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name));
+                                       }
+                                       /* Swap things around between the three-way and real call */
+                                       swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                       /* Disable echo canceller for better dialing */
+                                       dahdi_disable_ec(p);
+                                       res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_DIALRECALL);
+                                       if (res)
+                                               ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
+                                       p->owner = chan;
+                                       if (!chan) {
+                                               ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel);
+                                       } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) {
+                                               ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
+                                               res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
+                                               dahdi_enable_ec(p);
+                                               ast_hangup(chan);
+                                       } else {
+                                               struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner);
+                                               int way3bridge = 0, cdr3way = 0;
 
-                                                       if (!other) {
-                                                               other = ast_bridged_channel(p->subs[SUB_REAL].owner);
-                                                       } else
-                                                               way3bridge = 1;
+                                                       if (!other) {
+                                                       other = ast_bridged_channel(p->subs[SUB_REAL].owner);
+                                               } else
+                                                       way3bridge = 1;
 
-                                                       if (p->subs[SUB_THREEWAY].owner->cdr)
-                                                               cdr3way = 1;
+                                                       if (p->subs[SUB_THREEWAY].owner->cdr)
+                                                       cdr3way = 1;
 
-                                                       ast_verb(3, "Started three way call on channel %d\n", p->channel);
+                                               ast_verb(3, "Started three way call on channel %d\n", p->channel);
 
-                                                       /* Start music on hold if appropriate */
-                                                       if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
-                                                               ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD,
-                                                                       S_OR(p->mohsuggest, NULL),
-                                                                       !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
-                                                       }
-                                                       p->subs[SUB_THREEWAY].needhold = 1;
+                                               /* Start music on hold if appropriate */
+                                               if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
+                                                       ast_queue_control_data(p->subs[SUB_THREEWAY].owner, AST_CONTROL_HOLD,
+                                                               S_OR(p->mohsuggest, NULL),
+                                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
                                                }
+                                               p->subs[SUB_THREEWAY].needhold = 1;
+                                       }
+                               }
+                       } else {
+                               /* Already have a 3 way call */
+                               if (p->subs[SUB_THREEWAY].inthreeway) {
+                                       /* Call is already up, drop the last person */
+                                       ast_debug(1, "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 dropping 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 */
+                                       ast_verb(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 {
-                                       /* Already have a 3 way call */
-                                       if (p->subs[SUB_THREEWAY].inthreeway) {
-                                               /* Call is already up, drop the last person */
-                                               ast_debug(1, "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 dropping 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 */
-                                               ast_verb(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)) &&
-                                                       (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) {
-                                                       int otherindex = SUB_THREEWAY;
-                                                       struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner);
-                                                       int way3bridge = 0, cdr3way = 0;
-
-                                                       if (!other) {
-                                                               other = ast_bridged_channel(p->subs[SUB_REAL].owner);
-                                                       } else
-                                                               way3bridge = 1;
+                                       /* Lets see what we're up to */
+                                       if (((ast->pbx) || (ast->_state == AST_STATE_UP)) &&
+                                               (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) {
+                                               int otherindex = SUB_THREEWAY;
+                                               struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner);
+                                               int way3bridge = 0, cdr3way = 0;
+
+                                               if (!other) {
+                                                       other = ast_bridged_channel(p->subs[SUB_REAL].owner);
+                                               } else
+                                                       way3bridge = 1;
 
-                                                       if (p->subs[SUB_THREEWAY].owner->cdr)
-                                                               cdr3way = 1;
+                                               if (p->subs[SUB_THREEWAY].owner->cdr)
+                                                       cdr3way = 1;
 
-                                                       ast_verb(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 && ast_bridged_channel(p->subs[otherindex].owner))
-                                                               ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD);
-                                                       p->subs[otherindex].needunhold = 1;
-                                                       p->owner = p->subs[SUB_REAL].owner;
-                                                       if (ast->_state == AST_STATE_RINGING) {
-                                                               ast_debug(1, "Enabling ringtone on real and threeway\n");
-                                                               res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
-                                                               res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
-                                                       }
-                                               } else {
-                                                       ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name);
+                                               ast_verb(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);
-                                                       p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
-                                                       p->owner = p->subs[SUB_REAL].owner;
-                                                       if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner))
-                                                               ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
-                                                       p->subs[SUB_REAL].needunhold = 1;
-                                                       dahdi_enable_ec(p);
+                                                       otherindex = SUB_REAL;
+                                               }
+                                               if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner))
+                                                       ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD);
+                                               p->subs[otherindex].needunhold = 1;
+                                               p->owner = p->subs[SUB_REAL].owner;
+                                               if (ast->_state == AST_STATE_RINGING) {
+                                                       ast_debug(1, "Enabling ringtone on real and threeway\n");
+                                                       res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
+                                                       res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
                                                }
+                                       } else {
+                                               ast_verb(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 && ast_bridged_channel(p->subs[SUB_REAL].owner))
+                                                       ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+                                               p->subs[SUB_REAL].needunhold = 1;
+                                               dahdi_enable_ec(p);
                                        }
                                }
-winkflashdone:
-                               update_conf(p);
-                               break;
-                       case SIG_EM:
-                       case SIG_EM_E1:
-                       case SIG_EMWINK:
-                       case SIG_FEATD:
-                       case SIG_SF:
-                       case SIG_SFWINK:
-                       case SIG_SF_FEATD:
-                       case SIG_FXSLS:
-                       case SIG_FXSGS:
-                               if (option_debug) {
-                                       if (p->dialing)
-                                               ast_debug(1, "Ignoring wink on channel %d\n", p->channel);
-                                       else
-                                               ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel);
-                               }
-                               break;
-                       case SIG_FEATDMF_TA:
-                               switch (p->whichwink) {
-                               case 0:
-                                       ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
-                                       snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
-                                       break;
-                               case 1:
-                                       ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr));
-                                       break;
-                               case 2:
-                                       ast_log(LOG_WARNING, "Received unexpected wink on channel of type SIG_FEATDMF_TA\n");
-                                       return NULL;
-                               }
-                               p->whichwink++;
-                               /* Fall through */
-                       case SIG_FEATDMF:
-                       case SIG_E911:
-                       case SIG_FGC_CAMAMF:
-                       case SIG_FGC_CAMA:
-                       case SIG_FEATB:
-                       case SIG_SF_FEATDMF:
-                       case SIG_SF_FEATB:
-                               /* FGD MF *Must* wait for wink */
-                               if (!ast_strlen_zero(p->dop.dialstr)) {
-                                       res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
-                                       if (res < 0) {
-                                               ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
-                                               p->dop.dialstr[0] = '\0';
-                                               return NULL;
-                                       } else
-                                               ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr);
-                               }
-                               p->dop.dialstr[0] = '\0';
-                               break;
-                       default:
-                               ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig);
                        }
+winkflashdone:
+                       update_conf(p);
                        break;
-               case DAHDI_EVENT_HOOKCOMPLETE:
-                       if (p->inalarm) break;
-                       if ((p->radio || (p->oprmode < 0))) break;
-                       if (p->waitingfordt.tv_sec) break;
-                       switch (mysig) {
-                       case SIG_FXSLS:  /* only interesting for FXS */
-                       case SIG_FXSGS:
-                       case SIG_FXSKS:
-                       case SIG_EM:
-                       case SIG_EM_E1:
-                       case SIG_EMWINK:
-                       case SIG_FEATD:
-                       case SIG_SF:
-                       case SIG_SFWINK:
-                       case SIG_SF_FEATD:
-                               if (!ast_strlen_zero(p->dop.dialstr)) {
-                                       res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
-                                       if (res < 0) {
-                                               ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
-                                               p->dop.dialstr[0] = '\0';
-                                               return NULL;
-                                       } else
-                                               ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr);
-                               }
-                               p->dop.dialstr[0] = '\0';
-                               p->dop.op = DAHDI_DIAL_OP_REPLACE;
-                               break;
-                       case SIG_FEATDMF:
-                       case SIG_FEATDMF_TA:
-                       case SIG_E911:
-                       case SIG_FGC_CAMA:
-                       case SIG_FGC_CAMAMF:
-                       case SIG_FEATB:
-                       case SIG_SF_FEATDMF:
-                       case SIG_SF_FEATB:
-                               ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel);
+               case SIG_EM:
+               case SIG_EM_E1:
+               case SIG_EMWINK:
+               case SIG_FEATD:
+               case SIG_SF:
+               case SIG_SFWINK:
+               case SIG_SF_FEATD:
+               case SIG_FXSLS:
+               case SIG_FXSGS:
+                       if (p->dialing)
+                               ast_debug(1, "Ignoring wink on channel %d\n", p->channel);
+                       else
+                               ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel);
+                       break;
+               case SIG_FEATDMF_TA:
+                       switch (p->whichwink) {
+                       case 0:
+                               ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
                                break;
-                       default:
+                       case 1:
+                               ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr));
                                break;
+                       case 2:
+                               ast_log(LOG_WARNING, "Received unexpected wink on channel of type SIG_FEATDMF_TA\n");
+                               return NULL;
                        }
-                       break;
-               case DAHDI_EVENT_POLARITY:
-                       /*
-                        * If we get a Polarity Switch event, check to see
-                        * if we should change the polarity state and
-                        * mark the channel as UP or if this is an indication
-                        * of remote end disconnect.
-                        */
-                       if (p->polarity == POLARITY_IDLE) {
-                               p->polarity = POLARITY_REV;
-                               if (p->answeronpolarityswitch &&
-                                       ((ast->_state == AST_STATE_DIALING) ||
-                                       (ast->_state == AST_STATE_RINGING))) {
-                                       ast_debug(1, "Answering on polarity switch!\n");
-                                       ast_setstate(p->owner, AST_STATE_UP);
-                                       if (p->hanguponpolarityswitch) {
-                                               p->polaritydelaytv = ast_tvnow();
-                                       }
+                       p->whichwink++;
+                       /* Fall through */
+               case SIG_FEATDMF:
+               case SIG_E911:
+               case SIG_FGC_CAMAMF:
+               case SIG_FGC_CAMA:
+               case SIG_FEATB:
+               case SIG_SF_FEATDMF:
+               case SIG_SF_FEATB:
+                       /* FGD MF *Must* wait for wink */
+                       if (!ast_strlen_zero(p->dop.dialstr)) {
+                               res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
+                               if (res < 0) {
+                                       ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
+                                       p->dop.dialstr[0] = '\0';
+                                       return NULL;
                                } else
-                                       ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state);
+                                       ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr);
                        }
-                       /* Removed else statement from here as it was preventing hangups from ever happening*/
-                       /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */
-                       if (p->hanguponpolarityswitch &&
-                               (p->polarityonanswerdelay > 0) &&
-                               (p->polarity == POLARITY_REV) &&
-                               ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) {
-                               /* Added log_debug information below to provide a better indication of what is going on */
-                               ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
-
-                               if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) {
-                                       ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel);
-                                       ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
-                                       p->polarity = POLARITY_IDLE;
+                       p->dop.dialstr[0] = '\0';
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig);
+               }
+               break;
+       case DAHDI_EVENT_HOOKCOMPLETE:
+               if (p->inalarm) break;
+               if ((p->radio || (p->oprmode < 0))) break;
+               if (p->waitingfordt.tv_sec) break;
+               switch (mysig) {
+               case SIG_FXSLS:  /* only interesting for FXS */
+               case SIG_FXSGS:
+               case SIG_FXSKS:
+               case SIG_EM:
+               case SIG_EM_E1:
+               case SIG_EMWINK:
+               case SIG_FEATD:
+               case SIG_SF:
+               case SIG_SFWINK:
+               case SIG_SF_FEATD:
+                       if (!ast_strlen_zero(p->dop.dialstr)) {
+                               res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_DIAL, &p->dop);
+                               if (res < 0) {
+                                       ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
+                                       p->dop.dialstr[0] = '\0';
+                                       return NULL;
                                } else
-                                       ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state);
-
-                       } else {
-                               p->polarity = POLARITY_IDLE;
-                               ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state);
+                                       ast_debug(1, "Sent deferred digit string: %s\n", p->dop.dialstr);
                        }
-                       /* Added more log_debug information below to provide a better indication of what is going on */
-                       ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+                       p->dop.dialstr[0] = '\0';
+                       p->dop.op = DAHDI_DIAL_OP_REPLACE;
+                       break;
+               case SIG_FEATDMF:
+               case SIG_FEATDMF_TA:
+               case SIG_E911:
+               case SIG_FGC_CAMA:
+               case SIG_FGC_CAMAMF:
+               case SIG_FEATB:
+               case SIG_SF_FEATDMF:
+               case SIG_SF_FEATB:
+                       ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel);
                        break;
                default:
-                       ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel);
+                       break;
+               }
+               break;
+       case DAHDI_EVENT_POLARITY:
+               /*
+                * If we get a Polarity Switch event, check to see
+                * if we should change the polarity state and
+                * mark the channel as UP or if this is an indication
+                * of remote end disconnect.
+                */
+               if (p->polarity == POLARITY_IDLE) {
+                       p->polarity = POLARITY_REV;
+                       if (p->answeronpolarityswitch &&
+                               ((ast->_state == AST_STATE_DIALING) ||
+                               (ast->_state == AST_STATE_RINGING))) {
+                               ast_debug(1, "Answering on polarity switch!\n");
+                               ast_setstate(p->owner, AST_STATE_UP);
+                               if (p->hanguponpolarityswitch) {
+                                       p->polaritydelaytv = ast_tvnow();
+                               }
+                       } else
+                               ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state);
+               }
+               /* Removed else statement from here as it was preventing hangups from ever happening*/
+               /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */
+               if (p->hanguponpolarityswitch &&
+                       (p->polarityonanswerdelay > 0) &&
+                       (p->polarity == POLARITY_REV) &&
+                       ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) {
+                       /* Added log_debug information below to provide a better indication of what is going on */
+                       ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+
+                       if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) {
+                               ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel);
+                               ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
+                               p->polarity = POLARITY_IDLE;
+                       } else
+                               ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state);
+
+               } else {
+                       p->polarity = POLARITY_IDLE;
+                       ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state);
+               }
+               /* Added more log_debug information below to provide a better indication of what is going on */
+               ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+               break;
+       default:
+               ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel);
        }
        return &p->subs[idx].f;
 }
@@ -6890,7 +7842,12 @@ static struct ast_frame *dahdi_exception(struct ast_channel *ast)
        struct dahdi_pvt *p = ast->tech_pvt;
        struct ast_frame *f;
        ast_mutex_lock(&p->lock);
-       f = __dahdi_exception(ast);
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               struct analog_pvt *analog_p = p->sig_pvt;
+               f = analog_exception(analog_p, ast);
+       } else {
+               f = __dahdi_exception(ast);
+       }
        ast_mutex_unlock(&p->lock);
        return f;
 }
@@ -7138,7 +8095,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
                p->subs[idx].f.datalen = READ_SIZE;
 
        /* Handle CallerID Transmission */
-       if ((p->owner == ast) && p->cidspill &&((ast->_state == AST_STATE_UP) || (ast->rings == p->cidrings))) {
+       if ((p->owner == ast) && p->cidspill) {
                send_callerid(p);
        }
 
@@ -7151,7 +8108,9 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
 #if 0
        ast_debug(1, "Read %d of voice on %s\n", p->subs[idx].f.datalen, ast->name);
 #endif
-       if (p->dialing || /* Transmitting something */
+       {
+       struct analog_pvt *ap = p->sig_pvt;
+       if ((analog_lib_handles(p->sig ,p->radio, p->oprmode) && ap->dialing) || p->dialing ||  p->radio || /* Transmitting something */
                (idx && (ast->_state != AST_STATE_UP)) || /* Three-way or callwait that isn't up */
                ((idx == SUB_CALLWAIT) && !p->subs[SUB_CALLWAIT].inthreeway) /* Inactive and non-confed call-wait */
                ) {
@@ -7165,6 +8124,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
                p->subs[idx].f.data.ptr = NULL;
                p->subs[idx].f.datalen= 0;
        }
+       }
        if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress || p->waitingfordt.tv_sec) && !idx) {
                /* Perform busy detection etc on the dahdi line */
                int mute;
@@ -7231,8 +8191,12 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
        } else
                f = &p->subs[idx].f;
 
-       if (f && (f->frametype == AST_FRAME_DTMF))
-               dahdi_handle_dtmfup(ast, idx, &f);
+       if (f && (f->frametype == AST_FRAME_DTMF)) {
+               if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+                       analog_handle_dtmfup(p->sig_pvt, ast, idx, &f);
+               } else 
+                       dahdi_handle_dtmfup(ast, idx, &f);
+       }
 
        /* If we have a fake_event, trigger exception to handle it */
        if (p->fake_event)
@@ -7303,6 +8267,12 @@ static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame)
                ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
                return -1;
        }
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               struct analog_pvt *ap = p->sig_pvt;
+
+               if (ap->dialing)
+                       return 0;
+       }
        if (p->dialing) {
                ast_debug(1, "Dropping frame since I'm still dialing on %s...\n",ast->name);
                return 0;
@@ -7849,7 +8819,7 @@ static void dahdi_dnd(struct dahdi_pvt *dahdichan, int on)
                        on? "enabled" : "disabled");
 }
 
-static void *ss_thread(void *data)
+static void *analog_ss_thread(void *data)
 {
        struct ast_channel *chan = data;
        struct dahdi_pvt *p = chan->tech_pvt;
@@ -9008,24 +9978,8 @@ static void *ss_thread(void *data)
 
                if (cs)
                        callerid_free(cs);
-               /* If the CID had Message waiting payload, assume that this for MWI only and hangup the call */
-               if (flags & CID_MSGWAITING) {
-                       ast_log(LOG_NOTICE, "MWI: Channel %d message waiting!\n", p->channel);
-                       notify_message(p->mailbox, 1);
-                       /* If generated using Ring Pulse Alert, then ring has been answered as a call and needs to be hungup */
-                       if (p->mwimonitor_rpas) {
-                               ast_hangup(chan);
-                               return NULL;
-                       }
-               } else if (flags & CID_NOMSGWAITING) {
-                       ast_log(LOG_NOTICE, "MWI: Channel %d no message waiting!\n", p->channel);
-                       notify_message(p->mailbox, 0);
-                       /* If generated using Ring Pulse Alert, then ring has been answered as a call and needs to be hungup */
-                       if (p->mwimonitor_rpas) {
-                               ast_hangup(chan);
-                               return NULL;
-                       }
-               }
+
+               my_handle_notify_message(chan, p, flags, -1);
 
                ast_setstate(chan, AST_STATE_RING);
                chan->rings = 1;
@@ -9129,14 +10083,20 @@ static void *mwi_thread(void *data)
                                handle_alarms(mtd->pvt, res);
                                break; /* What to do on channel alarm ???? -- fall thru intentionally?? */
                        default:
-                               ast_log(LOG_NOTICE, "Got event %d (%s)...  Passing along to ss_thread\n", res, event2str(res));
+                               ast_log(LOG_NOTICE, "Got event %d (%s)...  Passing along to analog_ss_thread\n", res, event2str(res));
                                callerid_free(cs);
 
                                restore_gains(mtd->pvt);
                                mtd->pvt->ringt = mtd->pvt->ringt_base;
 
                                if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, 0))) {
-                                       if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+                                       int result;
+                                       if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
+                                               result = analog_ss_thread_start(mtd->pvt->sig_pvt, chan);
+                                       } else {
+                                               result = ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan);
+                                       }
+                                       if (result) {
                                                ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", mtd->pvt->channel);
                                                res = tone_zone_play_tone(mtd->pvt->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
                                                if (res < 0)
@@ -9407,7 +10367,7 @@ static int dahdi_destroy_channel_bynum(int channel)
        while (tmp) {
                if (tmp->channel == channel) {
                        int x = DAHDI_FLASH;
-                       ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all ss_threads terminate */
+                       ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */
                        destroy_channel(prev, tmp, 1);
                        ast_module_unref(ast_module_info->self);
                        return RESULT_SUCCESS;
@@ -9471,7 +10431,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
                                                res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_DIALTONE);
                                        if (res < 0)
                                                ast_log(LOG_WARNING, "Unable to play dialtone on channel %d, do you have defaultzone and loadzone defined?\n", i->channel);
-                                       if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+                                       if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) {
                                                ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
                                                res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
                                                if (res < 0)
@@ -9513,7 +10473,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
 
                        if (!chan) {
                                ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel);
-                       } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+                       } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) {
                                ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
                                res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
                                if (res < 0) {
@@ -9619,7 +10579,7 @@ static int handle_init_event(struct dahdi_pvt *i, int event)
                                chan = dahdi_new(i, AST_STATE_PRERING, 0, SUB_REAL, 0, 0);
                                if (!chan) {
                                        ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel);
-                               } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+                               } else if (ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, chan)) {
                                        ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
                                } else {
                                        thread_spawned = 1;
@@ -9697,19 +10657,39 @@ static void *do_monitor(void *data)
                count = 0;
                i = iflist;
                while (i) {
+                       ast_mutex_lock(&i->lock);
                        if ((i->subs[SUB_REAL].dfd > -1) && i->sig && (!i->radio) && !(i->sig & SIG_MFCR2)) {
-                               if (!i->owner && !i->subs[SUB_REAL].owner && !i->mwimonitoractive ) {
-                                       /* This needs to be watched, as it lacks an owner */
-                                       pfds[count].fd = i->subs[SUB_REAL].dfd;
-                                       pfds[count].events = POLLPRI;
-                                       pfds[count].revents = 0;
-                                       /* If we are monitoring for VMWI or sending CID, we need to
-                                          read from the channel as well */
-                                       if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk)
-                                               pfds[count].events |= POLLIN;
-                                       count++;
+                               if (analog_lib_handles(i->sig, i->radio, i->oprmode)) {
+                                       struct analog_pvt *p = i->sig_pvt;
+
+                                       if (!p)
+                                               ast_log(LOG_ERROR, "No sig_pvt?\n");
+
+                                       if (!p->owner && !p->subs[SUB_REAL].owner) {
+                                               /* This needs to be watched, as it lacks an owner */
+                                               pfds[count].fd = i->subs[SUB_REAL].dfd;
+                                               pfds[count].events = POLLPRI;
+                                               pfds[count].revents = 0;
+                                               /* Message waiting or r2 channels also get watched for reading */
+                                               if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk)
+                                                       pfds[count].events |= POLLIN;
+                                               count++;
+                                       }
+                               } else {
+                                       if (!i->owner && !i->subs[SUB_REAL].owner && !i->mwimonitoractive ) {
+                                               /* This needs to be watched, as it lacks an owner */
+                                               pfds[count].fd = i->subs[SUB_REAL].dfd;
+                                               pfds[count].events = POLLPRI;
+                                               pfds[count].revents = 0;
+                                               /* If we are monitoring for VMWI or sending CID, we need to
+                                                  read from the channel as well */
+                                               if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk)
+                                                       pfds[count].events |= POLLIN;
+                                               count++;
+                                       }
                                }
                        }
+                       ast_mutex_unlock(&i->lock);
                        i = i->next;
                }
                /* Okay, now that we know what to do, release the interface lock */
@@ -9774,7 +10754,10 @@ static void *do_monitor(void *data)
                                                ast_debug(1, "Monitor doohicky got event %s on radio channel %d\n", event2str(res), i->channel);
                                                /* Don't hold iflock while handling init events */
                                                ast_mutex_unlock(&iflock);
-                                               handle_init_event(i, res);
+                                               if (analog_lib_handles(i->sig, i->radio, i->oprmode))
+                                                       analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res));
+                                               else
+                                                       handle_init_event(i, res);
                                                ast_mutex_lock(&iflock);
                                        }
                                        i = i->next;
@@ -9840,7 +10823,10 @@ static void *do_monitor(void *data)
                                        /* Don't hold iflock while handling init events */
                                        ast_mutex_unlock(&iflock);
                                        if (0 == i->mwisendactive || 0 == mwi_send_process_event(i, res)) {
-                                               handle_init_event(i, res);
+                                               if (analog_lib_handles(i->sig, i->radio, i->oprmode))
+                                                       analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res));
+                                               else
+                                                       handle_init_event(i, res);
                                        }
                                        ast_mutex_lock(&iflock);
                                }
@@ -10151,6 +11137,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
        int x;
        struct dahdi_pvt **wlist;
        struct dahdi_pvt **wend;
+       struct analog_pvt *analog_p = NULL;
        struct dahdi_params p;
 
        wlist = &iflist;
@@ -10236,6 +11223,20 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                        return NULL;
                                }
                        }
+
+                       if (analog_lib_handles(chan_sig, tmp->radio, tmp->oprmode)) {
+                               if (tmp->sig_pvt) {
+                                       ast_log(LOG_WARNING, "Private already exists!\n");
+                                       analog_p = tmp->sig_pvt;
+                               } else
+                                       analog_p = analog_new(dahdisig_to_analogsig(chan_sig), &dahdi_analog_callbacks, tmp);
+                               if (!analog_p) {
+                                       destroy_dahdi_pvt(&tmp);
+                                       return NULL;
+                               }
+                               tmp->sig_pvt = analog_p;
+
+                       }
 #ifdef HAVE_SS7
                        if (chan_sig == SIG_SS7) {
                                struct dahdi_ss7 *ss7;
@@ -10740,11 +11741,44 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                        }
                                }
 #endif
-                       } else { 
+                       } else {
                                 /* We default to in service on protocols that don't have a reset */
                                tmp->inservice = 1;
                        }
                }
+
+               analog_p = tmp->sig_pvt;
+               if (analog_p) {
+                       analog_p->channel = tmp->channel;
+                       analog_p->polarityonanswerdelay = conf->chan.polarityonanswerdelay;
+                       analog_p->answeronpolarityswitch = conf->chan.answeronpolarityswitch;
+                       analog_p->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
+                       analog_p->sendcalleridafter = conf->chan.sendcalleridafter;
+                       analog_p->permcallwaiting = 1;
+                       analog_p->callreturn = conf->chan.callreturn;
+                       analog_p->cancallforward = conf->chan.cancallforward;
+                       analog_p->canpark = conf->chan.canpark;
+                       analog_p->dahditrcallerid = conf->chan.dahditrcallerid;
+                       analog_p->immediate = conf->chan.immediate;
+                       analog_p->permhidecallerid = conf->chan.permhidecallerid;
+                       analog_p->pulse = conf->chan.pulse;
+                       analog_p->threewaycalling = conf->chan.threewaycalling;
+                       analog_p->transfer = conf->chan.transfer;
+                       analog_p->transfertobusy = conf->chan.transfertobusy;
+                       analog_p->use_callerid = conf->chan.use_callerid;
+                       analog_p->outsigmod = ANALOG_SIG_NONE;
+                       analog_p->echotraining = conf->chan.echotraining;
+                       analog_p->cid_signalling = conf->chan.cid_signalling;
+                       analog_p->stripmsd = conf->chan.stripmsd;
+                       analog_p->cid_start = ANALOG_CID_START_RING;
+                       tmp->callwaitingcallerid = analog_p->callwaitingcallerid = 1;
+
+                       ast_copy_string(analog_p->mohsuggest, conf->chan.mohsuggest, sizeof(analog_p->mohsuggest));
+                       ast_copy_string(analog_p->cid_num, conf->chan.cid_num, sizeof(analog_p->cid_num));
+                       ast_copy_string(analog_p->cid_name, conf->chan.cid_name, sizeof(analog_p->cid_name));
+
+                       analog_config_complete(analog_p);
+               }
        }
        if (tmp && !here) {
                /* nothing on the iflist */
@@ -10810,6 +11844,10 @@ static inline int available(struct dahdi_pvt *p, int channelmatch, ast_group_t g
                        return 0;
                *channelmatched = 1;
        }
+
+       if (analog_lib_handles(p->sig, p->radio, p->oprmode))
+               return analog_available(p->sig_pvt, channelmatch, groupmatch, reason, channelmatched, groupmatched);
+
        /* We're at least busy at this point */
        if (reason) {
                if ((p->sig == SIG_FXOKS) || (p->sig == SIG_FXOLS) || (p->sig == SIG_FXOGS))
@@ -11193,7 +12231,10 @@ static struct ast_channel *dahdi_request(const char *type, int format, void *dat
                                }
                        }
                        p->outgoing = 1;
-                       tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0);
+                       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+                               tmp = analog_request(p->sig_pvt, &callwait);
+                       } else
+                               tmp = dahdi_new(p, AST_STATE_RESERVED, 0, p->owner ? SUB_CALLWAIT : SUB_REAL, 0, 0);
 #ifdef HAVE_PRI
                        if (p->bearer) {
                                /* Log owner to bearer channel, too */
@@ -11258,7 +12299,11 @@ next:
 #if defined(HAVE_PRI) || defined(HAVE_SS7)
 static int dahdi_setlaw(int dfd, int law)
 {
-       return ioctl(dfd, DAHDI_SETLAW, &law);
+       int res;
+       res = ioctl(dfd, DAHDI_SETLAW, &law);
+       if (res)
+               return res;
+       return 0;
 }
 #endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */
 
@@ -13219,7 +14264,7 @@ static void *pri_dchannel(void *vpri)
 
                                                        ast_mutex_lock(&pri->pvts[chanpos]->lock);
                                                        ast_mutex_lock(&pri->lock);
-                                                       if (c && !ast_pthread_create_detached(&threadid, NULL, ss_thread, c)) {
+                                                       if (c && !ast_pthread_create_detached(&threadid, NULL, analog_ss_thread, c)) {
                                                                ast_verb(3, "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n",
                                                                                plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"),
                                                                                pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
@@ -14788,14 +15833,14 @@ static int dahdi_restart(void)
        ast_mutex_lock(&ss_thread_lock);
        while (ss_thread_count > 0) { /* let ss_threads finish and run dahdi_hangup before dahvi_pvts are destroyed */
                int x = DAHDI_FLASH;
-               ast_debug(3, "Waiting on %d ss_thread(s) to finish\n", ss_thread_count);
+               ast_debug(3, "Waiting on %d analog_ss_thread(s) to finish\n", ss_thread_count);
 
                for (p = iflist; p; p = p->next) {
                        if (p->owner)
-                               ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all ss_threads terminate */
+                               ioctl(p->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); /* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */
                        }
-                       ast_cond_wait(&ss_thread_complete, &ss_thread_lock);
-               }
+               ast_cond_wait(&ss_thread_complete, &ss_thread_lock);
+       }
 
        /* ensure any created channels before monitor threads were stopped are hungup */
        dahdi_softhangup_all();
@@ -15583,14 +16628,14 @@ static int dahdi_fake_event(struct dahdi_pvt *p, int mode)
 {
        if (p) {
                switch (mode) {
-                       case TRANSFER:
-                               p->fake_event = DAHDI_EVENT_WINKFLASH;
-                               break;
-                       case HANGUP:
-                               p->fake_event = DAHDI_EVENT_ONHOOK;
-                               break;
-                       default:
-                               ast_log(LOG_WARNING, "I don't know how to handle transfer event with this: %d on channel %s\n",mode, p->owner->name);
+               case TRANSFER:
+                       p->fake_event = DAHDI_EVENT_WINKFLASH;
+                       break;
+               case HANGUP:
+                       p->fake_event = DAHDI_EVENT_ONHOOK;
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "I don't know how to handle transfer event with this: %d on channel %s\n",mode, p->owner->name);
                }
        }
        return 0;
@@ -17847,6 +18892,7 @@ static int reload(void)
        return 0;
 }
 
+
 /* This is a workaround so that menuselect displays a proper description
  * AST_MODULE_INFO(, , "DAHDI Telephony"
  */
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
new file mode 100644 (file)
index 0000000..8cdec1a
--- /dev/null
@@ -0,0 +1,3262 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2009, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * 
+ * \brief Analog signaling module
+ *
+ * \author Matthew Fredrickson <creslin@digium.com>
+ */
+
+#include "asterisk.h"
+
+#include <errno.h>
+#include <ctype.h>
+
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/say.h"
+#include "asterisk/manager.h"
+#include "asterisk/astdb.h"
+#include "asterisk/features.h"
+
+#include "sig_analog.h"
+
+#define POLARITY_IDLE 0
+#define POLARITY_REV    1
+#define MIN_MS_SINCE_FLASH                     ( (2000) )      /*!< 2000 ms */
+static int analog_matchdigittimeout = 3000;
+static int analog_gendigittimeout = 8000;
+static int analog_firstdigittimeout = 16000;
+static char analog_defaultcic[64] = "";
+static char analog_defaultozz[64] = "";
+
+static const struct {
+       enum analog_sigtype sigtype;
+       const char const *name;
+} sigtypes[] = {
+       { ANALOG_SIG_FXOLS, "fxo_ls" },
+       { ANALOG_SIG_FXOKS, "fxo_ks" },
+       { ANALOG_SIG_FXOGS, "fxo_gs" },
+       { ANALOG_SIG_FXSLS, "fxs_ls" },
+       { ANALOG_SIG_FXSKS, "fxs_ks" },
+       { ANALOG_SIG_FXSGS, "fxs_gs" },
+       { ANALOG_SIG_EMWINK, "em_w" },
+       { ANALOG_SIG_EM, "em" },
+       { ANALOG_SIG_EM_E1, "em_e1" },
+       { ANALOG_SIG_FEATD, "featd" },
+       { ANALOG_SIG_FEATDMF, "featdmf" },
+       { ANALOG_SIG_FEATDMF_TA, "featdmf_ta" },
+       { ANALOG_SIG_FEATB, "featb" },
+       { ANALOG_SIG_FGC_CAMA, "fgccama" },
+       { ANALOG_SIG_FGC_CAMAMF, "fgccamamf" },
+       { ANALOG_SIG_SF, "sf" },
+       { ANALOG_SIG_SFWINK, "sf_w" },
+       { ANALOG_SIG_SF_FEATD, "sf_featd" },
+       { ANALOG_SIG_SF_FEATDMF, "sf_featdmf" },
+       { ANALOG_SIG_SF_FEATB, "sf_featb" },
+       { ANALOG_SIG_E911, "e911" },
+};
+
+static const struct {
+       unsigned int cid_type;
+       const char const *name;
+} cidtypes[] = {
+       { CID_SIG_BELL,   "bell" },
+       { CID_SIG_V23,    "v23" },
+       { CID_SIG_V23_JP, "v23_jp" },
+       { CID_SIG_DTMF,   "dtmf" },
+       /* "smdi" is intentionally not supported here, as there is a much better
+        * way to do this in the dialplan now. */
+};
+
+enum analog_sigtype analog_str_to_sigtype(const char *name)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_LEN(sigtypes); i++) {
+               if (!strcasecmp(sigtypes[i].name, name)) {
+                       return sigtypes[i].sigtype;
+               }
+       }
+
+       return 0;
+}
+
+const char *analog_sigtype_to_str(enum analog_sigtype sigtype)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_LEN(sigtypes); i++) {
+               if (sigtype == sigtypes[i].sigtype) {
+                       return sigtypes[i].name;
+               }
+       }
+
+       return "Unknown";
+}
+
+unsigned int analog_str_to_cidtype(const char *name)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_LEN(cidtypes); i++) {
+               if (!strcasecmp(cidtypes[i].name, name)) {
+                       return cidtypes[i].cid_type;
+               }
+       }
+
+       return 0;
+}
+
+const char *analog_cidtype_to_str(unsigned int cid_type)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_LEN(cidtypes); i++) {
+               if (cid_type == cidtypes[i].cid_type) {
+                       return cidtypes[i].name;
+               }
+       }
+
+       return "Unknown";
+}
+
+static int analog_start_cid_detect(struct analog_pvt *p, int cid_signalling)
+{
+       if (p->calls->start_cid_detect)
+               return p->calls->start_cid_detect(p->chan_pvt, cid_signalling);
+       else
+               return -1;
+}
+
+static int analog_stop_cid_detect(struct analog_pvt *p)
+{
+       if (p->calls->stop_cid_detect)
+               return p->calls->stop_cid_detect(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_get_callerid(struct analog_pvt *p, char *name, char *number, enum analog_event *ev, size_t timeout)
+{
+       if (p->calls->get_callerid)
+               return p->calls->get_callerid(p->chan_pvt, name, number, ev, timeout);
+       else
+               return -1;
+}
+
+static int analog_get_event(struct analog_pvt *p)
+{
+       if (p->calls->get_event)
+               return p->calls->get_event(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_wait_event(struct analog_pvt *p)
+{
+       if (p->calls->wait_event)
+               return p->calls->wait_event(p->chan_pvt);
+       else
+               return -1;
+}
+
+enum analog_cid_start analog_str_to_cidstart(const char *value)
+{
+       if (!strcasecmp(value, "ring")) {
+               return ANALOG_CID_START_RING;
+       } else if (!strcasecmp(value, "polarity")) {
+               return ANALOG_CID_START_POLARITY;
+       } else if (!strcasecmp(value, "polarity_in")) {
+               return ANALOG_CID_START_POLARITY_IN;
+       }
+
+       return 0;
+}
+
+const char *analog_cidstart_to_str(enum analog_cid_start cid_start)
+{
+       switch (cid_start) {
+       case ANALOG_CID_START_RING:
+               return "Ring";
+       case ANALOG_CID_START_POLARITY:
+               return "Polarity";
+       case ANALOG_CID_START_POLARITY_IN:
+               return "Polarity_In";
+       }
+
+       return "Unknown";
+}
+
+static char *analog_event2str(enum analog_event event)
+{
+       char *res;
+       switch (event) {
+       case ANALOG_EVENT_DIALCOMPLETE:
+               res = "ANALOG_EVENT_DIALCOMPLETE";
+               break;
+       case ANALOG_EVENT_WINKFLASH:
+               res = "ANALOG_EVENT_WINKFLASH";
+               break;
+       case ANALOG_EVENT_ONHOOK:
+               res = "ANALOG_EVENT_ONHOOK";
+               break;
+       case ANALOG_EVENT_RINGOFFHOOK:
+               res = "ANALOG_EVENT_RINGOFFHOOK";
+               break;
+       case ANALOG_EVENT_ALARM:
+               res = "ANALOG_EVENT_ALARM";
+               break;
+       case ANALOG_EVENT_NOALARM:
+               res = "ANALOG_EVENT_NOALARM";
+               break;
+       case ANALOG_EVENT_HOOKCOMPLETE:
+               res = "ANALOG_EVENT_HOOKCOMPLETE";
+               break;
+       case ANALOG_EVENT_POLARITY:
+               res = "ANALOG_EVENT_POLARITY";
+               break;
+       case ANALOG_EVENT_RINGERON:
+               res = "ANALOG_EVENT_RINGERON";
+               break;
+       case ANALOG_EVENT_RINGEROFF:
+               res = "ANALOG_EVENT_RINGEROFF";
+               break;
+       case ANALOG_EVENT_RINGBEGIN:
+               res = "ANALOG_EVENT_RINGBEGIN";
+               break;
+       case ANALOG_EVENT_PULSE_START:
+               res = "ANALOG_EVENT_PULSE_START";
+               break;
+       case ANALOG_EVENT_NEONMWI_ACTIVE:
+               res = "ANALOG_EVENT_NEONMWI_ACTIVE";
+               break;
+       case ANALOG_EVENT_NEONMWI_INACTIVE:
+               res = "ANALOG_EVENT_NEONMWI_INACTIVE";
+               break;
+       default:
+               res = "UNKNOWN/OTHER";
+               break;
+       }
+
+       return res;
+}
+
+static void analog_swap_subs(struct analog_pvt *p, enum analog_sub a, enum analog_sub b)
+{
+       int tinthreeway;
+       struct ast_channel *towner;
+
+       ast_debug(1, "Swapping %d and %d\n", a, b);
+
+       towner = p->subs[a].owner;
+       tinthreeway = p->subs[a].inthreeway;
+
+       p->subs[a].owner = p->subs[b].owner;
+       p->subs[a].inthreeway = p->subs[b].inthreeway;
+
+       p->subs[b].owner = towner;
+       p->subs[b].inthreeway = tinthreeway;
+
+       if (p->calls->swap_subs)
+               p->calls->swap_subs(p->chan_pvt, a, p->subs[a].owner, b, p->subs[b].owner);
+
+}
+
+static int analog_alloc_sub(struct analog_pvt *p, enum analog_sub x)
+{
+       p->subs[x].allocd = 1;
+       if (p->calls->allocate_sub)
+               return p->calls->allocate_sub(p->chan_pvt, x);
+
+       return 0;
+}
+
+static int analog_unalloc_sub(struct analog_pvt *p, enum analog_sub x)
+{
+       p->subs[x].allocd = 0;
+       p->subs[x].owner = NULL;
+       if (p->calls->unallocate_sub)
+               return p->calls->unallocate_sub(p->chan_pvt, x);
+
+       return 0;
+}
+
+static int analog_send_callerid(struct analog_pvt *p, int cwcid, struct ast_callerid *cid)
+{
+       ast_debug(1, "Sending callerid.  CID_NAME: '%s' CID_NUM: '%s'\n", cid->cid_name, cid->cid_num);
+
+       if (cwcid) {
+               p->callwaitcas = 0;
+       }
+
+       if (p->calls->send_callerid)
+               return p->calls->send_callerid(p->chan_pvt, cwcid, cid);
+       else
+               return 0;
+}
+
+static int analog_get_index(struct ast_channel *ast, struct analog_pvt *p, int nullok)
+{
+       int res;
+       if (p->subs[ANALOG_SUB_REAL].owner == ast)
+               res = ANALOG_SUB_REAL;
+       else if (p->subs[ANALOG_SUB_CALLWAIT].owner == ast)
+               res = ANALOG_SUB_CALLWAIT;
+       else if (p->subs[ANALOG_SUB_THREEWAY].owner == ast)
+               res = ANALOG_SUB_THREEWAY;
+       else {
+               res = -1;
+               if (!nullok)
+                       ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
+       }
+       return res;
+}
+
+static int analog_dsp_reset_and_flush_digits(struct analog_pvt *p)
+{
+       if (p->calls->dsp_reset_and_flush_digits)
+               return p->calls->dsp_reset_and_flush_digits(p->chan_pvt);
+       else
+               /* Return 0 since I think this is unnecessary to do in most cases it is used.  Mostly only for ast_dsp */
+               return 0;
+}
+
+static int analog_play_tone(struct analog_pvt *p, enum analog_sub sub, enum analog_tone tone)
+{
+       if (p->calls->play_tone)
+               return p->calls->play_tone(p->chan_pvt, sub, tone);
+       else
+               return -1;
+}
+
+static struct ast_channel * analog_new_ast_channel(struct analog_pvt *p, int state, int startpbx, enum analog_sub sub)
+{
+       struct ast_channel *c;
+
+       if (p->calls->new_ast_channel)
+               c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, sub);
+       else
+               return NULL;
+
+       p->subs[sub].owner = c;
+       if (!p->owner)
+               p->owner = c;
+       return c;
+}
+
+static int analog_set_echocanceller(struct analog_pvt *p, int enable)
+{
+       if (p->calls->set_echocanceller)
+               return p->calls->set_echocanceller(p->chan_pvt, enable);
+       else
+               return -1;
+}
+
+static int analog_train_echocanceller(struct analog_pvt *p)
+{
+       if (p->calls->train_echocanceller)
+               return p->calls->train_echocanceller(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_is_off_hook(struct analog_pvt *p)
+{
+       if (p->calls->is_off_hook)
+               return p->calls->is_off_hook(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_ring(struct analog_pvt *p)
+{
+       if (p->calls->ring)
+               return p->calls->ring(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_start(struct analog_pvt *p)
+{
+       if (p->calls->start)
+               return p->calls->start(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_dial_digits(struct analog_pvt *p, enum analog_sub sub, struct analog_dialoperation *dop)
+{
+       if (p->calls->dial_digits)
+               return p->calls->dial_digits(p->chan_pvt, sub, dop);
+       else
+               return -1;
+}
+
+static int analog_on_hook(struct analog_pvt *p)
+{
+       if (p->calls->on_hook)
+               return p->calls->on_hook(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_check_for_conference(struct analog_pvt *p)
+{
+       if (p->calls->check_for_conference)
+               return p->calls->check_for_conference(p->chan_pvt);
+       else
+               return -1;
+}
+
+static void analog_all_subchannels_hungup(struct analog_pvt *p)
+{
+       if (p->calls->all_subchannels_hungup)
+               p->calls->all_subchannels_hungup(p->chan_pvt);
+}
+
+static void analog_unlock_private(struct analog_pvt *p)
+{
+       if (p->calls->unlock_private)
+               p->calls->unlock_private(p->chan_pvt);
+}
+
+static void analog_lock_private(struct analog_pvt *p)
+{
+       if (p->calls->lock_private)
+               p->calls->lock_private(p->chan_pvt);
+}
+
+static int analog_off_hook(struct analog_pvt *p)
+{
+       if (p->calls->off_hook)
+               return p->calls->off_hook(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_dsp_set_digitmode(struct analog_pvt *p, enum analog_dsp_digitmode mode)
+{
+       if (p->calls->dsp_set_digitmode)
+               return p->calls->dsp_set_digitmode(p->chan_pvt, mode);
+       else
+               return -1;
+}
+
+static void analog_cb_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest)
+{
+       if (p->calls->handle_dtmfup)
+               p->calls->handle_dtmfup(p->chan_pvt, ast, analog_index, dest);
+}
+
+static int analog_wink(struct analog_pvt *p, enum analog_sub index)
+{
+       if (p->calls->wink)
+               return p->calls->wink(p->chan_pvt, index);
+       else
+               return -1;
+}
+
+static int analog_has_voicemail(struct analog_pvt *p)
+{
+       if (p->calls->has_voicemail)
+               return p->calls->has_voicemail(p->chan_pvt);
+       else
+               return -1;
+}
+
+static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index)
+{
+       if (p->calls->is_dialing)
+               return p->calls->is_dialing(p->chan_pvt, index);
+       else
+               return -1;
+}
+
+static int analog_attempt_transfer(struct analog_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 (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) {
+               /* The three-way person we're about to transfer to could still be in MOH, so
+                  stop if now if appropriate */
+               if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner))
+                       ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_UNHOLD);
+               if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RINGING) {
+                       ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner), AST_CONTROL_RINGING);
+               }
+               if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RING) {
+                       analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE);
+               }
+                if (ast_channel_masquerade(p->subs[ANALOG_SUB_THREEWAY].owner, ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))) {
+                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                                       ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
+                       return -1;
+               }
+               /* Orphan the channel after releasing the lock */
+               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+       } else if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+               if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RINGING) {
+                       ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), AST_CONTROL_RINGING);
+               }
+               if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RING) {
+                       analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
+               }
+               if (ast_channel_masquerade(p->subs[ANALOG_SUB_REAL].owner, ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner))) {
+                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                                       ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)->name, p->subs[ANALOG_SUB_REAL].owner->name);
+                       return -1;
+               }
+               /* Three-way is now the REAL */
+               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+               /* Tell the caller not to hangup */
+               return 1;
+       } else {
+               ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+                                       p->subs[ANALOG_SUB_REAL].owner->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
+               ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+               return -1;
+       }
+       return 0;
+}
+
+static int analog_update_conf(struct analog_pvt *p)
+{
+       int x;
+       int needconf = 0;
+
+       /* Start with the obvious, general stuff */
+       for (x = 0; x < 3; x++) {
+               /* Look for three way calls */
+               if ((p->subs[x].allocd) && p->subs[x].inthreeway) {
+                       if (p->calls->conf_add)
+                               p->calls->conf_add(p->chan_pvt, x);
+                       needconf++;
+               } else {
+                       if (p->calls->conf_del)
+                               p->calls->conf_del(p->chan_pvt, x);
+               }
+       }
+       ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
+
+       if (p->calls->complete_conference_update)
+               p->calls->complete_conference_update(p->chan_pvt, needconf);
+       return 0;
+}
+
+struct ast_channel * analog_request(struct analog_pvt *p, int *callwait)
+{
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+       *callwait = (p->owner != NULL);
+
+       if (p->owner) {
+               if (analog_alloc_sub(p, ANALOG_SUB_CALLWAIT)) {
+                       ast_log(LOG_ERROR, "Unable to alloc subchannel\n");
+                       return NULL;
+               }
+       }
+
+       return analog_new_ast_channel(p, AST_STATE_RESERVED, 0, p->owner ? ANALOG_SUB_CALLWAIT : ANALOG_SUB_REAL);
+}
+
+int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched)
+{
+       int offhook;
+
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+       /* We're at least busy at this point */
+       if (busy) {
+               if ((p->sig == ANALOG_SIG_FXOKS) || (p->sig == ANALOG_SIG_FXOLS) || (p->sig == ANALOG_SIG_FXOGS))
+                       *busy = 1;
+       }
+       /* If do not disturb, definitely not */
+       if (p->dnd)
+               return 0;
+       /* If guard time, definitely not */
+       if (p->guardtime && (time(NULL) < p->guardtime)) 
+               return 0;
+
+       /* If no owner definitely available */
+       if (!p->owner) {
+               if (p->sig == ANALOG_SIG_FXSLS)
+                       return 1;
+
+               offhook = analog_is_off_hook(p);
+
+               if ((p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) {
+                       /* When "onhook" that means no battery on the line, and thus
+                         it is out of service..., if it's on a TDM card... If it's a channel
+                         bank, there is no telling... */
+                       if (offhook)
+                               return 1;
+                       else
+                               return 0;
+               } else if (offhook) {
+                       ast_debug(1, "Channel %d off hook, can't use\n", p->channel);
+                       /* Not available when the other end is off hook */
+                       return 0;
+               }
+               return 1;
+       }
+
+       /* If it's not an FXO, forget about call wait */
+       if ((p->sig != ANALOG_SIG_FXOKS) && (p->sig != ANALOG_SIG_FXOLS) && (p->sig != ANALOG_SIG_FXOGS)) 
+               return 0;
+
+       if (!p->callwaiting) {
+               /* If they don't have call waiting enabled, then for sure they're unavailable at this point */
+               return 0;
+       }
+
+       if (p->subs[ANALOG_SUB_CALLWAIT].allocd) {
+               /* If there is already a call waiting call, then we can't take a second one */
+               return 0;
+       }
+
+       if ((p->owner->_state != AST_STATE_UP) &&
+           ((p->owner->_state != AST_STATE_RINGING) || p->outgoing)) {
+               /* If the current call is not up, then don't allow the call */
+               return 0;
+       }
+       if ((p->subs[ANALOG_SUB_THREEWAY].owner) && (!p->subs[ANALOG_SUB_THREEWAY].inthreeway)) {
+               /* Can't take a call wait when the three way calling hasn't been merged yet. */
+               return 0;
+       }
+       /* We're cool */
+       return 1;
+}
+
+static int analog_stop_callwait(struct analog_pvt *p)
+{
+       if (p->callwaitingcallerid)
+               p->callwaitcas = 0;
+
+       if (p->calls->stop_callwait)
+               return p->calls->stop_callwait(p->chan_pvt);
+       else
+               return 0;
+}
+
+static int analog_callwait(struct analog_pvt *p)
+{
+       if (p->callwaitingcallerid)
+               p->callwaitcas = 1;
+       if (p->calls->callwait)
+               return p->calls->callwait(p->chan_pvt);
+       else
+               return 0;
+}
+
+int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int timeout)
+{
+       int res, index,mysig;
+       char *c, *n, *l;
+       char dest[256]; /* must be same length as p->dialdest */
+
+       ast_log(LOG_DEBUG, "CALLING CID_NAME: %s CID_NUM:: %s\n", ast->connected.id.name, ast->connected.id.number);
+
+       ast_copy_string(dest, rdest, sizeof(dest));
+       ast_copy_string(p->dialdest, rdest, sizeof(p->dialdest));
+
+       if ((ast->_state == AST_STATE_BUSY)) {
+               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_BUSY);
+               return 0;
+       }
+
+       if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+               ast_log(LOG_WARNING, "analog_call called on %s, neither down nor reserved\n", ast->name);
+               return -1;
+       }
+
+       p->dialednone = 0;
+
+       mysig = p->sig;
+       if (p->outsigmod > -1)
+               mysig = p->outsigmod;
+
+       switch (mysig) {
+       case ANALOG_SIG_FXOLS:
+       case ANALOG_SIG_FXOGS:
+       case ANALOG_SIG_FXOKS:
+               if (p->owner == ast) {
+                       /* Normal ring, on hook */
+
+                       /* Don't send audio while on hook, until the call is answered */
+                       p->dialing = 1;
+                       /* XXX */
+#if 0
+                       /* Choose proper cadence */
+                       if ((p->distinctivering > 0) && (p->distinctivering <= num_cadence)) {
+                               if (ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_SETCADENCE, &cadences[p->distinctivering - 1]))
+                                       ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s': %s\n", p->distinctivering, ast->name, strerror(errno));
+                               p->cidrings = cidrings[p->distinctivering - 1];
+                       } else {
+                               if (ioctl(p->subs[ANALOG_SUB_REAL].dfd, DAHDI_SETCADENCE, NULL))
+                                       ast_log(LOG_WARNING, "Unable to reset default ring on '%s': %s\n", ast->name, strerror(errno));
+                               p->cidrings = p->sendcalleridafter;
+                       }
+#endif
+                       p->cidrings = p->sendcalleridafter;
+
+                       /* nick@dccinc.com 4/3/03 mods to allow for deferred dialing */
+                       c = strchr(dest, '/');
+                       if (c)
+                               c++;
+                       if (c && (strlen(c) < p->stripmsd)) {
+                               ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+                               c = NULL;
+                       }
+                       if (c) {
+                               p->dop.op = ANALOG_DIAL_OP_REPLACE;
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "Tw%s", c);
+                               ast_debug(1, "FXO: setup deferred dialstring: %s\n", c);
+                       } else {
+                               p->dop.dialstr[0] = '\0';
+                       }
+
+                       if (analog_ring(p)) {
+                               ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
+                               return -1;
+                       }
+                       p->dialing = 1;
+               } else {
+                       if (ast->connected.id.number)
+                               ast_copy_string(p->callwait_num, ast->connected.id.number, sizeof(p->callwait_num));
+                       else
+                               p->callwait_num[0] = '\0';
+                       if (ast->connected.id.name)
+                               ast_copy_string(p->callwait_name, ast->connected.id.name, sizeof(p->callwait_name));
+                       else
+                               p->callwait_name[0] = '\0';
+
+                       /* Call waiting tone instead */
+                       if (analog_callwait(p)) {
+                               return -1;
+                       }
+                       /* Make ring-back */
+                       if (analog_play_tone(p, ANALOG_SUB_CALLWAIT, ANALOG_TONE_RINGTONE))
+                               ast_log(LOG_WARNING, "Unable to generate call-wait ring-back on channel %s\n", ast->name);
+
+               }
+               n = ast->connected.id.name;
+               l = ast->connected.id.number;
+               if (l)
+                       ast_copy_string(p->lastcid_num, l, sizeof(p->lastcid_num));
+               else
+                       p->lastcid_num[0] = '\0';
+               if (n)
+                       ast_copy_string(p->lastcid_name, n, sizeof(p->lastcid_name));
+               else
+                       p->lastcid_name[0] = '\0';
+
+               if (p->use_callerid) {
+                       //p->callwaitcas = 0;
+                       p->cid.cid_name = p->lastcid_name;
+                       p->cid.cid_num = p->lastcid_num;
+               }
+
+
+               ast_setstate(ast, AST_STATE_RINGING);
+               index = analog_get_index(ast, p, 0);
+               if (index > -1) {
+                       ast_queue_control(p->subs[index].owner, AST_CONTROL_RINGING);
+               }
+               break;
+       case ANALOG_SIG_FXSLS:
+       case ANALOG_SIG_FXSGS:
+       case ANALOG_SIG_FXSKS:
+       case ANALOG_SIG_EMWINK:
+       case ANALOG_SIG_EM:
+       case ANALOG_SIG_EM_E1:
+       case ANALOG_SIG_FEATD:
+       case ANALOG_SIG_FEATDMF:
+       case ANALOG_SIG_E911:
+       case ANALOG_SIG_FGC_CAMA:
+       case ANALOG_SIG_FGC_CAMAMF:
+       case ANALOG_SIG_FEATB:
+       case ANALOG_SIG_SFWINK:
+       case ANALOG_SIG_SF:
+       case ANALOG_SIG_SF_FEATD:
+       case ANALOG_SIG_SF_FEATDMF:
+       case ANALOG_SIG_FEATDMF_TA:
+       case ANALOG_SIG_SF_FEATB:
+               c = strchr(dest, '/');
+               if (c)
+                       c++;
+               else
+                       c = "";
+               if (strlen(c) < p->stripmsd) {
+                       ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+                       return -1;
+               }
+               res = analog_start(p);
+               if (res < 0) {
+                       if (errno != EINPROGRESS) {
+                               return -1;
+                       }
+               }
+               ast_log(LOG_DEBUG, "Dialing '%s'\n", c);
+               p->dop.op = ANALOG_DIAL_OP_REPLACE;
+
+               c += p->stripmsd;
+
+               switch (mysig) {
+               case ANALOG_SIG_FEATD:
+                       l = ast->connected.id.number;
+                       if (l) 
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T*%s*%s*", l, c);
+                       else
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T**%s*", c);
+                       break;
+               case ANALOG_SIG_FEATDMF:
+                       l = ast->connected.id.number;
+                       if (l) 
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*00%s#*%s#", l, c);
+                       else
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*02#*%s#", c);
+                       break;
+               case ANALOG_SIG_FEATDMF_TA:
+               {
+                       const char *cic = "", *ozz = "";
+
+                       /* If you have to go through a Tandem Access point you need to use this */
+#ifndef STANDALONE
+                       ozz = pbx_builtin_getvar_helper(p->owner, "FEATDMF_OZZ");
+                       if (!ozz)
+                               ozz = analog_defaultozz;
+                       cic = pbx_builtin_getvar_helper(p->owner, "FEATDMF_CIC");
+                       if (!cic)
+                               cic = analog_defaultcic;
+#endif
+                       if (!ozz || !cic) {
+                               ast_log(LOG_WARNING, "Unable to dial channel of type feature group D MF tandem access without CIC or OZZ set\n");
+                               return -1;
+                       }
+                       snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s%s#", ozz, cic);
+                       snprintf(p->finaldial, sizeof(p->finaldial), "M*%s#", c);
+                       p->whichwink = 0;
+               }
+                       break;
+               case ANALOG_SIG_E911:
+                       ast_copy_string(p->dop.dialstr, "M*911#", sizeof(p->dop.dialstr));
+                       break;
+               case ANALOG_SIG_FGC_CAMA:
+                       snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%s", c);
+                       break;
+               case ANALOG_SIG_FGC_CAMAMF:
+               case ANALOG_SIG_FEATB:
+                       snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%s#", c);
+                       break;
+               default:
+                       if (p->pulse)
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "P%sw", c);
+                       else
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "T%sw", c);
+                       break;
+               }
+
+               if (p->echotraining && (strlen(p->dop.dialstr) > 4)) {
+                       memset(p->echorest, 'w', sizeof(p->echorest) - 1);
+                       strcpy(p->echorest + (p->echotraining / 400) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
+                       p->echorest[sizeof(p->echorest) - 1] = '\0';
+                       p->echobreak = 1;
+                       p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
+               } else
+                       p->echobreak = 0;
+               if (!res) {
+                       if (analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop)) {
+                               int saveerr = errno;
+
+                               analog_on_hook(p);
+                               ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr));
+                               return -1;
+                       }
+               } else
+                       ast_debug(1, "Deferring dialing...\n");
+               p->dialing = 1;
+               if (ast_strlen_zero(c))
+                       p->dialednone = 1;
+               ast_setstate(ast, AST_STATE_DIALING);
+               break;
+       default:
+               ast_debug(1, "not yet implemented\n");
+               return -1;
+       }
+       return 0;
+}
+
+int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
+{
+       int res;
+       int index, x;
+
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+       if (!ast->tech_pvt) {
+               ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+               return 0;
+       }
+
+       index = analog_get_index(ast, p, 1);
+
+       x = 0;
+       if (p->origcid_num) {
+               ast_copy_string(p->cid_num, p->origcid_num, sizeof(p->cid_num));
+               free(p->origcid_num);
+               p->origcid_num = NULL;
+       }
+       if (p->origcid_name) {
+               ast_copy_string(p->cid_name, p->origcid_name, sizeof(p->cid_name));
+               free(p->origcid_name);
+               p->origcid_name = NULL;
+       }
+
+       analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
+
+       ast_debug(1, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+               p->channel, index, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd);
+       if (index > -1) {
+               /* Real channel, do some fixup */
+               p->subs[index].owner = NULL;
+               p->subs[index].needcallerid = 0;
+               p->polarity = POLARITY_IDLE;
+               if (index == ANALOG_SUB_REAL) {
+                       if (p->subs[ANALOG_SUB_CALLWAIT].allocd && p->subs[ANALOG_SUB_THREEWAY].allocd) {
+                               ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n");
+                               if (p->subs[ANALOG_SUB_CALLWAIT].inthreeway) {
+                                       /* We had flipped over to answer a callwait and now it's gone */
+                                       ast_debug(1, "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. */
+                                       analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL);
+                                       analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+                                       p->owner = NULL;
+                               } else {
+                                       /* The three way hung up, but we still have a call wait */
+                                       ast_debug(1, "We were in the threeway and have a callwait still.  Ditching the threeway.\n");
+                                       analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                       analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+                                       if (p->subs[ANALOG_SUB_REAL].inthreeway) {
+                                               /* This was part of a three way call.  Immediately make way for
+                                                  another call */
+                                               ast_debug(1, "Call was complete, setting owner to former third call\n");
+                                               p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                                       } else {
+                                               /* This call hasn't been completed yet...  Set owner to NULL */
+                                               ast_debug(1, "Call was incomplete, setting owner to NULL\n");
+                                               p->owner = NULL;
+                                       }
+                                       p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+                               }
+                       } else if (p->subs[ANALOG_SUB_CALLWAIT].allocd) {
+                               /* Move to the call-wait and switch back to them. */
+                               analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL);
+                               analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+                               p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                               if (p->owner->_state != AST_STATE_UP)
+                                       ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER);
+                               if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))
+                                       ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+                       } else if (p->subs[ANALOG_SUB_THREEWAY].allocd) {
+                               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+                               if (p->subs[ANALOG_SUB_REAL].inthreeway) {
+                                       /* This was part of a three way call.  Immediately make way for
+                                          another call */
+                                       ast_debug(1, "Call was complete, setting owner to former third call\n");
+                                       p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                               } else {
+                                       /* This call hasn't been completed yet...  Set owner to NULL */
+                                       ast_debug(1, "Call was incomplete, setting owner to NULL\n");
+                                       p->owner = NULL;
+                               }
+                               p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+                       }
+               } else if (index == ANALOG_SUB_CALLWAIT) {
+                       /* Ditch the holding callwait call, and immediately make it availabe */
+                       if (p->subs[ANALOG_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[ANALOG_SUB_THREEWAY].owner && ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+                                       ast_queue_control_data(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_HOLD, 
+                                               S_OR(p->mohsuggest, NULL),
+                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               }
+                               p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0;
+                               /* Make it the call wait now */
+                               analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY);
+                               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+                       } else
+                               analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+               } else if (index == ANALOG_SUB_THREEWAY) {
+                       if (p->subs[ANALOG_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[ANALOG_SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) {
+                                       ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD, 
+                                               S_OR(p->mohsuggest, NULL),
+                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               }
+                               p->subs[ANALOG_SUB_CALLWAIT].inthreeway = 0;
+                       }
+                       p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+                       /* If this was part of a three way call index, let us make
+                          another three way call */
+                       analog_unalloc_sub(p, ANALOG_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");
+               }
+       }
+
+       if (!p->subs[ANALOG_SUB_REAL].owner && !p->subs[ANALOG_SUB_CALLWAIT].owner && !p->subs[ANALOG_SUB_THREEWAY].owner) {
+               p->owner = NULL;
+#if 0
+               p->ringt = 0;
+#endif
+#if 0 /* Since we set it in _call */
+               p->cidrings = 1;
+#endif
+               p->outgoing = 0;
+
+               /* Perform low level hangup if no owner left */
+               res = analog_on_hook(p);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+               }
+               switch (p->sig) {
+               case ANALOG_SIG_FXOGS:
+               case ANALOG_SIG_FXOLS:
+               case ANALOG_SIG_FXOKS:
+                       /* If they're off hook, try playing congestion */
+                       if (analog_is_off_hook(p))
+                               analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
+                       else
+                               analog_play_tone(p, ANALOG_SUB_REAL, -1);
+                       break;
+               case ANALOG_SIG_FXSGS:
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSKS:
+                       /* Make sure we're not made available for at least two seconds assuming
+                          we were actually used for an inbound or outbound call. */
+                       if (ast->_state != AST_STATE_RESERVED) {
+                               time(&p->guardtime);
+                               p->guardtime += 2;
+                       }
+                       break;
+               default:
+                       analog_play_tone(p, ANALOG_SUB_REAL, -1);
+               }
+
+
+               analog_set_echocanceller(p, 0);
+
+               x = 0;
+               ast_channel_setoption(ast,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
+               ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
+               p->callwaitcas = 0;
+               p->callwaiting = p->permcallwaiting;
+               p->hidecallerid = p->permhidecallerid;
+               p->dialing = 0;
+               analog_update_conf(p);
+               analog_all_subchannels_hungup(p);
+       }
+
+       analog_stop_callwait(p);
+       ast->tech_pvt = NULL;
+
+       if (option_verbose > 2) {
+               ast_verbose(VERBOSE_PREFIX_3 "Hanging up on '%s'\n", ast->name);
+       }
+
+       return 0;
+}
+
+int analog_answer(struct analog_pvt *p, struct ast_channel *ast)
+{
+       int res = 0;
+       int index;
+       int oldstate = ast->_state;
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+       ast_setstate(ast, AST_STATE_UP);
+       index = analog_get_index(ast, p, 1);
+       if (index < 0)
+               index = ANALOG_SUB_REAL;
+       switch (p->sig) {
+       case ANALOG_SIG_FXSLS:
+       case ANALOG_SIG_FXSGS:
+       case ANALOG_SIG_FXSKS:
+#if 0
+               p->ringt = 0;
+#endif
+               /* Fall through */
+       case ANALOG_SIG_EM:
+       case ANALOG_SIG_EM_E1:
+       case ANALOG_SIG_EMWINK:
+       case ANALOG_SIG_FEATD:
+       case ANALOG_SIG_FEATDMF:
+       case ANALOG_SIG_FEATDMF_TA:
+       case ANALOG_SIG_E911:
+       case ANALOG_SIG_FGC_CAMA:
+       case ANALOG_SIG_FGC_CAMAMF:
+       case ANALOG_SIG_FEATB:
+       case ANALOG_SIG_SF:
+       case ANALOG_SIG_SFWINK:
+       case ANALOG_SIG_SF_FEATD:
+       case ANALOG_SIG_SF_FEATDMF:
+       case ANALOG_SIG_SF_FEATB:
+       case ANALOG_SIG_FXOLS:
+       case ANALOG_SIG_FXOGS:
+       case ANALOG_SIG_FXOKS:
+               /* Pick up the line */
+               ast_debug(1, "Took %s off hook\n", ast->name);
+               if (p->hanguponpolarityswitch) {
+                       gettimeofday(&p->polaritydelaytv, NULL);
+               }
+               res = analog_off_hook(p);
+               analog_play_tone(p, index, -1);
+               p->dialing = 0;
+               if ((index == ANALOG_SUB_REAL) && p->subs[ANALOG_SUB_THREEWAY].inthreeway) {
+                       if (oldstate == AST_STATE_RINGING) {
+                               ast_debug(1, "Finally swapping real and threeway\n");
+                               analog_play_tone(p, ANALOG_SUB_THREEWAY, -1);
+                               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                               p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                       }
+               }
+               if ((p->sig == ANALOG_SIG_FXSLS) || (p->sig == ANALOG_SIG_FXSKS) || (p->sig == ANALOG_SIG_FXSGS)) {
+                       analog_set_echocanceller(p, 1);
+                       analog_train_echocanceller(p);
+               }
+               break;
+       default:
+               ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel);
+               res = -1;
+       }
+       ast_setstate(ast, AST_STATE_UP);
+       return res;
+}
+
+static int analog_handles_digit(struct ast_frame *f)
+{
+       char subclass = toupper(f->subclass);
+
+       switch (subclass) {
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '9':
+       case 'A':
+       case 'B':
+       case 'C':
+       case 'D':
+       case 'E':
+       case 'F':
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest)
+{
+       struct ast_frame *f = *dest;
+
+       if (p->callwaitcas) {
+               if ((f->subclass == 'A') || (f->subclass == 'D')) {
+                       ast_log(LOG_ERROR, "Got some DTMF, but it's for the CAS\n");
+                       p->cid.cid_name = p->callwait_name;
+                       p->cid.cid_num = p->callwait_num;
+                       analog_send_callerid(p, 1, &p->cid);
+               }
+               if (analog_handles_digit(f))
+                       p->callwaitcas = 0;
+               p->subs[index].f.frametype = AST_FRAME_NULL;
+               p->subs[index].f.subclass = 0;
+               *dest = &p->subs[index].f;
+       } else {
+               analog_cb_handle_dtmfup(p, ast, index, dest);
+       }
+}
+
+static int analog_my_getsigstr(struct ast_channel *chan, char *str, const 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 (strchr(term, c))
+                       return 1;
+       }
+}
+
+static int analog_handle_notify_message(struct ast_channel *chan, struct analog_pvt *p, int cid_flags, int neon_mwievent)
+{
+       if (p->calls->handle_notify_message) {
+               p->calls->handle_notify_message(chan, p->chan_pvt, cid_flags, neon_mwievent);
+               return 0;
+       }
+       else
+               return -1;
+}
+
+static int analog_increase_ss_count(struct analog_pvt *p)
+{
+       if (p->calls->increase_ss_count) {
+               p->calls->increase_ss_count();
+               return 0;
+       } else
+               return -1;
+}
+
+static int analog_decrease_ss_count(struct analog_pvt *p)
+{
+       if (p->calls->decrease_ss_count) {
+               p->calls->decrease_ss_count();
+               return 0;
+       } else
+               return -1;
+}
+
+#define ANALOG_NEED_MFDETECT(p) (((p)->sig == ANALOG_SIG_FEATDMF) || ((p)->sig == ANALOG_SIG_FEATDMF_TA) || ((p)->sig == ANALOG_SIG_E911) || ((p)->sig == ANALOG_SIG_FGC_CAMA) || ((p)->sig == ANALOG_SIG_FGC_CAMAMF) || ((p)->sig == ANALOG_SIG_FEATB))
+
+/* Note by jpeeler: This function has a rather large section of code ifdefed
+ * away. I'd like to leave the code there until more testing is done and I
+ * know for sure that nothing got left out. The plan is at the latest for this
+ * comment and code below to be removed shortly after the merging of sig_pri.
+ */
+static void *__analog_ss_thread(void *data)
+{
+       struct analog_pvt *p = data;
+       struct ast_channel *chan = p->ss_astchan;
+       char exten[AST_MAX_EXTENSION] = "";
+       char exten2[AST_MAX_EXTENSION] = "";
+       char dtmfcid[300];
+       char dtmfbuf[300];
+       char namebuf[ANALOG_MAX_CID];
+       char numbuf[ANALOG_MAX_CID];
+       struct callerid_state *cs = NULL;
+       char *name = NULL, *number = NULL;
+       int flags;
+#if 0
+       unsigned char buf[256];
+       int distMatches;
+       int curRingData[3];
+       int receivedRingT;
+       int samples = 0;
+       int counter1;
+       int counter;
+       int i;
+#endif
+       int timeout;
+       int getforward = 0;
+       char *s1, *s2;
+       int len = 0;
+       int res;
+       int index;
+
+       analog_increase_ss_count(p);
+
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+
+       /* in the bizarre case where the channel has become a zombie before we
+          even get started here, abort safely
+       */
+       if (!p) {
+               ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", chan->name);
+               ast_hangup(chan);
+               goto quit;
+       }
+
+       ast_verb(3, "Starting simple switch on '%s'\n", chan->name);
+       index = analog_get_index(chan, p, 1);
+       if (index < 0) {
+               ast_log(LOG_WARNING, "Huh?\n");
+               ast_hangup(chan);
+               goto quit;
+       }
+       analog_dsp_reset_and_flush_digits(p);
+       switch (p->sig) {
+       case ANALOG_SIG_FEATD:
+       case ANALOG_SIG_FEATDMF:
+       case ANALOG_SIG_FEATDMF_TA:
+       case ANALOG_SIG_E911:
+       case ANALOG_SIG_FGC_CAMAMF:
+       case ANALOG_SIG_FEATB:
+       case ANALOG_SIG_EMWINK:
+       case ANALOG_SIG_SF_FEATD:
+       case ANALOG_SIG_SF_FEATDMF:
+       case ANALOG_SIG_SF_FEATB:
+       case ANALOG_SIG_SFWINK:
+               if (analog_wink(p, index))
+                       goto quit;
+               /* Fall through */
+       case ANALOG_SIG_EM:
+       case ANALOG_SIG_EM_E1:
+       case ANALOG_SIG_SF:
+       case ANALOG_SIG_FGC_CAMA:
+               res = analog_play_tone(p, index, -1);
+
+               analog_dsp_reset_and_flush_digits(p);
+
+               if (ANALOG_NEED_MFDETECT(p)) {
+                       analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF);
+               } else
+                       analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
+
+               memset(dtmfbuf, 0, sizeof(dtmfbuf));
+               /* Wait for the first digit only if immediate=no */
+               if (!p->immediate)
+                       /* Wait for the first digit (up to 5 seconds). */
+                       res = ast_waitfordigit(chan, 5000);
+               else
+                       res = 0;
+               if (res > 0) {
+                       /* save first char */
+                       dtmfbuf[0] = res;
+                       switch (p->sig) {
+                       case ANALOG_SIG_FEATD:
+                       case ANALOG_SIG_SF_FEATD:
+                               res = analog_my_getsigstr(chan, dtmfbuf + 1, "*", 3000);
+                               if (res > 0)
+                                       res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
+                               if (res < 1)
+                                       analog_dsp_reset_and_flush_digits(p);
+                               break;
+                       case ANALOG_SIG_FEATDMF_TA:
+                               res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+                               if (res < 1)
+                                       analog_dsp_reset_and_flush_digits(p);
+                               if (analog_wink(p, index)) goto quit;
+                               dtmfbuf[0] = 0;
+                               /* Wait for the first digit (up to 5 seconds). */
+                               res = ast_waitfordigit(chan, 5000);
+                               if (res <= 0) break;
+                               dtmfbuf[0] = res;
+                               /* fall through intentionally */
+                       case ANALOG_SIG_FEATDMF:
+                       case ANALOG_SIG_E911:
+                       case ANALOG_SIG_FGC_CAMAMF:
+                       case ANALOG_SIG_SF_FEATDMF:
+                               res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+                               /* if international caca, do it again to get real ANO */
+                               if ((p->sig == ANALOG_SIG_FEATDMF) && (dtmfbuf[1] != '0') && (strlen(dtmfbuf) != 14))
+                               {
+                                       if (analog_wink(p, index)) goto quit;
+                                       dtmfbuf[0] = 0;
+                                       /* Wait for the first digit (up to 5 seconds). */
+                                       res = ast_waitfordigit(chan, 5000);
+                                       if (res <= 0) break;
+                                       dtmfbuf[0] = res;
+                                       res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+                               }
+                               if (res > 0) {
+                                       /* if E911, take off hook */
+                                       if (p->sig == ANALOG_SIG_E911)
+                                               analog_off_hook(p);
+                                       res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "#", 3000);
+                               }
+                               if (res < 1)
+                                       analog_dsp_reset_and_flush_digits(p);
+                               break;
+                       case ANALOG_SIG_FEATB:
+                       case ANALOG_SIG_SF_FEATB:
+                               res = analog_my_getsigstr(chan, dtmfbuf + 1, "#", 3000);
+                               if (res < 1)
+                                       analog_dsp_reset_and_flush_digits(p);
+                               break;
+                       case ANALOG_SIG_EMWINK:
+                               /* if we received a '*', we are actually receiving Feature Group D
+                                  dial syntax, so use that mode; otherwise, fall through to normal
+                                  mode
+                               */
+                               if (res == '*') {
+                                       res = analog_my_getsigstr(chan, dtmfbuf + 1, "*", 3000);
+                                       if (res > 0)
+                                               res = analog_my_getsigstr(chan, dtmfbuf + strlen(dtmfbuf), "*", 3000);
+                                       if (res < 1)
+                                               analog_dsp_reset_and_flush_digits(p);
+                                       break;
+                               }
+                       default:
+                               /* If we got the first digit, get the rest */
+                               len = 1;
+                               dtmfbuf[len] = '\0';
+                               while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) {
+                                       if (ast_exists_extension(chan, chan->context, dtmfbuf, 1, p->cid_num)) {
+                                               timeout = analog_matchdigittimeout;
+                                       } else {
+                                               timeout = analog_gendigittimeout;
+                                       }
+                                       res = ast_waitfordigit(chan, timeout);
+                                       if (res < 0) {
+                                               ast_debug(1, "waitfordigit returned < 0...\n");
+                                               ast_hangup(chan);
+                                               goto quit;
+                                       } else if (res) {
+                                               dtmfbuf[len++] = res;
+                                               dtmfbuf[len] = '\0';
+                                       } else {
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+               }
+               if (res == -1) {
+                       ast_log(LOG_WARNING, "getdtmf on channel %d: %s\n", p->channel, strerror(errno));
+                       ast_hangup(chan);
+                       goto quit;
+               } else if (res < 0) {
+                       ast_debug(1, "Got hung up before digits finished\n");
+                       ast_hangup(chan);
+                       goto quit;
+               }
+
+               if (p->sig == ANALOG_SIG_FGC_CAMA) {
+                       char anibuf[100];
+
+                       if (ast_safe_sleep(chan,1000) == -1) {
+                               ast_hangup(chan);
+                               goto quit;
+                       }
+                       analog_off_hook(p);
+                       analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_MF);
+                        res = analog_my_getsigstr(chan, anibuf, "#", 10000);
+                        if ((res > 0) && (strlen(anibuf) > 2)) {
+                               if (anibuf[strlen(anibuf) - 1] == '#')
+                                       anibuf[strlen(anibuf) - 1] = 0;
+                               ast_set_callerid(chan, anibuf + 2, NULL, anibuf + 2);
+                       }
+                       analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
+               }
+
+               ast_copy_string(exten, dtmfbuf, sizeof(exten));
+               if (ast_strlen_zero(exten))
+                       ast_copy_string(exten, "s", sizeof(exten));
+               if (p->sig == ANALOG_SIG_FEATD || p->sig == ANALOG_SIG_EMWINK) {
+                       /* Look for Feature Group D on all E&M Wink and Feature Group D trunks */
+                       if (exten[0] == '*') {
+                               char *stringp=NULL;
+                               ast_copy_string(exten2, exten, sizeof(exten2));
+                               /* Parse out extension and callerid */
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "*");
+                               s2 = strsep(&stringp, "*");
+                               if (s2) {
+                                       if (!ast_strlen_zero(p->cid_num))
+                                               ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
+                                       else
+                                               ast_set_callerid(chan, s1, NULL, s1);
+                                       ast_copy_string(exten, s2, sizeof(exten));
+                               } else
+                                       ast_copy_string(exten, s1, sizeof(exten));
+                       } else if (p->sig == ANALOG_SIG_FEATD)
+                               ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d.  Assuming E&M Wink instead\n", p->channel);
+               }
+               if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) {
+                       if (exten[0] == '*') {
+                               char *stringp=NULL;
+                               ast_copy_string(exten2, exten, sizeof(exten2));
+                               /* Parse out extension and callerid */
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "#");
+                               s2 = strsep(&stringp, "#");
+                               if (s2) {
+                                       if (!ast_strlen_zero(p->cid_num))
+                                               ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
+                                       else
+                                               if (*(s1 + 2))
+                                                       ast_set_callerid(chan, s1 + 2, NULL, s1 + 2);
+                                       ast_copy_string(exten, s2 + 1, sizeof(exten));
+                               } else
+                                       ast_copy_string(exten, s1 + 2, sizeof(exten));
+                       } else
+                               ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d.  Assuming E&M Wink instead\n", p->channel);
+               }
+               if ((p->sig == ANALOG_SIG_E911) || (p->sig == ANALOG_SIG_FGC_CAMAMF)) {
+                       if (exten[0] == '*') {
+                               char *stringp=NULL;
+                               ast_copy_string(exten2, exten, sizeof(exten2));
+                               /* Parse out extension and callerid */
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "#");
+                               s2 = strsep(&stringp, "#");
+                               if (s2 && (*(s2 + 1) == '0')) {
+                                       if (*(s2 + 2))
+                                               ast_set_callerid(chan, s2 + 2, NULL, s2 + 2);
+                               }
+                               if (s1) ast_copy_string(exten, s1, sizeof(exten));
+                               else ast_copy_string(exten, "911", sizeof(exten));
+                       } else
+                               ast_log(LOG_WARNING, "Got a non-E911/FGC CAMA input on channel %d.  Assuming E&M Wink instead\n", p->channel);
+               }
+               if (p->sig == ANALOG_SIG_FEATB) {
+                       if (exten[0] == '*') {
+                               char *stringp=NULL;
+                               ast_copy_string(exten2, exten, sizeof(exten2));
+                               /* Parse out extension and callerid */
+                               stringp=exten2 +1;
+                               s1 = strsep(&stringp, "#");
+                               ast_copy_string(exten, exten2 + 1, sizeof(exten));
+                       } else
+                               ast_log(LOG_WARNING, "Got a non-Feature Group B input on channel %d.  Assuming E&M Wink instead\n", p->channel);
+               }
+               if ((p->sig == ANALOG_SIG_FEATDMF) || (p->sig == ANALOG_SIG_FEATDMF_TA)) {
+                       analog_wink(p, index);
+                       /* some switches require a minimum guard time between
+                       the last FGD wink and something that answers
+                       immediately. This ensures it */
+                       if (ast_safe_sleep(chan,100)) goto quit;
+               }
+               analog_set_echocanceller(p, 1);
+
+               analog_dsp_set_digitmode(p, ANALOG_DIGITMODE_DTMF);
+
+               if (ast_exists_extension(chan, chan->context, exten, 1, chan->cid.cid_num)) {
+                       ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+                       analog_dsp_reset_and_flush_digits(p);
+                       res = ast_pbx_run(chan);
+                       if (res) {
+                               ast_log(LOG_WARNING, "PBX exited non-zero\n");
+                               res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+                       }
+                       goto quit;
+               } else {
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_2 "Unknown extension '%s' in context '%s' requested\n", exten, chan->context);
+                       sleep(2);
+                       res = analog_play_tone(p, index, ANALOG_TONE_INFO);
+                       if (res < 0)
+                               ast_log(LOG_WARNING, "Unable to start special tone on %d\n", p->channel);
+                       else
+                               sleep(1);
+                       res = ast_streamfile(chan, "ss-noservice", chan->language);
+                       if (res >= 0)
+                               ast_waitstream(chan, "");
+                       res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+                       ast_hangup(chan);
+                       goto quit;
+               }
+               break;
+       case ANALOG_SIG_FXOLS:
+       case ANALOG_SIG_FXOGS:
+       case ANALOG_SIG_FXOKS:
+               /* Read the first digit */
+               timeout = analog_firstdigittimeout;
+               /* If starting a threeway call, never timeout on the first digit so someone
+                  can use flash-hook as a "hold" feature */
+               if (p->subs[ANALOG_SUB_THREEWAY].owner) 
+                       timeout = 999999;
+               while (len < AST_MAX_EXTENSION-1) {
+                       /* Read digit unless it's supposed to be immediate, in which case the
+                          only answer is 's' */
+                       if (p->immediate) 
+                               res = 's';
+                       else
+                               res = ast_waitfordigit(chan, timeout);
+                       timeout = 0;
+                       if (res < 0) {
+                               ast_debug(1, "waitfordigit returned < 0...\n");
+                               res = analog_play_tone(p, index, -1);
+                               ast_hangup(chan);
+                               goto quit;
+                       } else if (res)  {
+                               exten[len++]=res;
+                               exten[len] = '\0';
+                       }
+                       if (!ast_ignore_pattern(chan->context, exten))
+                               analog_play_tone(p, index, -1);
+                       else
+                               analog_play_tone(p, index, ANALOG_TONE_DIALTONE);
+                       if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num) && strcmp(exten, ast_parking_ext())) {
+                               if (!res || !ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
+                                       if (getforward) {
+                                               /* Record this as the forwarding extension */
+                                               ast_copy_string(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 = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                                               if (res)
+                                                       break;
+                                               usleep(500000);
+                                               res = analog_play_tone(p, index, -1);
+                                               sleep(1);
+                                               memset(exten, 0, sizeof(exten));
+                                               res = analog_play_tone(p, index, ANALOG_TONE_DIALTONE);
+                                               len = 0;
+                                               getforward = 0;
+                                       } else  {
+                                               res = analog_play_tone(p, index, -1);
+                                               ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+                                               if (!ast_strlen_zero(p->cid_num)) {
+                                                       if (!p->hidecallerid)
+                                                               ast_set_callerid(chan, p->cid_num, NULL, p->cid_num); 
+                                                       else
+                                                               ast_set_callerid(chan, NULL, NULL, p->cid_num); 
+                                               }
+                                               if (!ast_strlen_zero(p->cid_name)) {
+                                                       if (!p->hidecallerid)
+                                                               ast_set_callerid(chan, NULL, p->cid_name, NULL);
+                                               }
+                                               ast_setstate(chan, AST_STATE_RING);
+                                               analog_set_echocanceller(p, 1);
+                                               res = ast_pbx_run(chan);
+                                               if (res) {
+                                                       ast_log(LOG_WARNING, "PBX exited non-zero\n");
+                                                       res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+                                               }
+                                               goto quit;
+                                       }
+                               } else {
+                                       /* It's a match, but they just typed a digit, and there is an ambiguous match,
+                                          so just set the timeout to analog_matchdigittimeout and wait some more */
+                                       timeout = analog_matchdigittimeout;
+                               }
+                       } else if (res == 0) {
+                               ast_debug(1, "not enough digits (and no ambiguous match)...\n");
+                               res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+                               analog_wait_event(p);
+                               ast_hangup(chan);
+                               goto quit;
+                       } 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 = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n", 
+                                               chan->name, strerror(errno));
+                               }
+                               len = 0;
+                               memset(exten, 0, sizeof(exten));
+                               timeout = analog_firstdigittimeout;
+
+                       } else if (!strcmp(exten,ast_pickup_ext())) {
+                               /* Scan all channels and see if there are any
+                                * ringing channels that have call groups
+                                * that equal this channels pickup group
+                                */
+                               if (index == ANALOG_SUB_REAL) {
+                                       /* Switch us from Third call to Call Wait */
+                                       if (p->subs[ANALOG_SUB_THREEWAY].owner) {
+                                               /* If you make a threeway call and the *8# a call, it should actually
+                                                  look like a callwait */
+                                               analog_alloc_sub(p, ANALOG_SUB_CALLWAIT);
+                                               analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_THREEWAY);
+                                               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+                                       }
+                                       analog_set_echocanceller(p, 1);
+                                       if (ast_pickup_call(chan)) {
+                                               ast_debug(1, "No call pickup possible...\n");
+                                               res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+                                               analog_wait_event(p);
+                                       }
+                                       ast_hangup(chan);
+                                       goto quit;
+                               } else {
+                                       ast_log(LOG_WARNING, "Huh?  Got *8# on call not on real\n");
+                                       ast_hangup(chan);
+                                       goto quit;
+                               }
+                       } else if (!p->hidecallerid && !strcmp(exten, "*67")) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
+                               /* Disable Caller*ID if enabled */
+                               p->hidecallerid = 1;
+                               if (chan->cid.cid_num)
+                                       free(chan->cid.cid_num);
+                               chan->cid.cid_num = NULL;
+                               if (chan->cid.cid_name)
+                                       free(chan->cid.cid_name);
+                               chan->cid.cid_name = NULL;
+                               res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+                                               chan->name, strerror(errno));
+                               }
+                               len = 0;
+                               memset(exten, 0, sizeof(exten));
+                               timeout = analog_firstdigittimeout;
+                       } else if (p->callreturn && !strcmp(exten, "*69")) {
+                               res = 0;
+                               if (!ast_strlen_zero(p->lastcid_num)) {
+                                       res = ast_say_digit_str(chan, p->lastcid_num, "", chan->language);
+                               }
+                               if (!res)
+                                       res = analog_play_tone(p, index, ANALOG_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);
+#if 0
+                               manager_event(EVENT_FLAG_SYSTEM, "DNDState",
+                                             "Channel: %s/%d\r\n"
+                                             "Status: enabled\r\n", dahdi_chan_name, p->channel);
+#endif
+                               res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                               p->dnd = 1;
+                               getforward = 0;
+                               memset(exten, 0, sizeof(exten));
+                               len = 0;
+                       } else if (!strcmp(exten, "*79")) {
+                               /* Do not disturb */
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "Disabled DND on channel %d\n", p->channel);
+#if 0
+                               manager_event(EVENT_FLAG_SYSTEM, "DNDState",
+                                             "Channel: %s/%d\r\n"
+                                             "Status: disabled\r\n", dahdi_chan_name, p->channel);
+#endif
+                               res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                               p->dnd = 0;
+                               getforward = 0;
+                               memset(exten, 0, sizeof(exten));
+                               len = 0;
+                       } else if (p->cancallforward && !strcmp(exten, "*72")) {
+                               res = analog_play_tone(p, index, ANALOG_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 = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                               memset(p->call_forward, 0, sizeof(p->call_forward));
+                               getforward = 0;
+                               memset(exten, 0, sizeof(exten));
+                               len = 0;
+                       } else if ((p->transfer || p->canpark) && !strcmp(exten, ast_parking_ext()) &&
+                                               p->subs[ANALOG_SUB_THREEWAY].owner &&
+                                               ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+                               /* This is a three way call, the main call being a real channel,
+                                       and we're parking the first call. */
+                               ast_masq_park_call(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), chan, 0, NULL);
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "Parking call to '%s'\n", chan->name);
+                               break;
+                       } else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) {
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "Blacklisting number %s\n", p->lastcid_num);
+                               res = ast_db_put("blacklist", p->lastcid_num, "1");
+                               if (!res) {
+                                       res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                                       memset(exten, 0, sizeof(exten));
+                                       len = 0;
+                               }
+                       } else if (p->hidecallerid && !strcmp(exten, "*82")) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
+                               /* Enable Caller*ID if enabled */
+                               p->hidecallerid = 0;
+                               if (chan->cid.cid_num)
+                                       free(chan->cid.cid_num);
+                               chan->cid.cid_num = NULL;
+                               if (chan->cid.cid_name)
+                                       free(chan->cid.cid_name);
+                               chan->cid.cid_name = NULL;
+                               ast_set_callerid(chan, p->cid_num, p->cid_name, NULL);
+                               res = analog_play_tone(p, index, ANALOG_TONE_DIALRECALL);
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+                                               chan->name, strerror(errno));
+                               }
+                               len = 0;
+                               memset(exten, 0, sizeof(exten));
+                               timeout = analog_firstdigittimeout;
+                       } else if (!strcmp(exten, "*0")) {
+#ifdef XXX
+                               struct ast_channel *nbridge = p->subs[ANALOG_SUB_THREEWAY].owner;
+                               struct dahdi_pvt *pbridge = NULL;
+                                 /* set up the private struct of the bridged one, if any */
+                               if (nbridge && ast_bridged_channel(nbridge))
+                                       pbridge = ast_bridged_channel(nbridge)->tech_pvt;
+                               if (nbridge && pbridge &&
+                                   (nbridge->tech == chan_tech) &&
+                                   (ast_bridged_channel(nbridge)->tech == chan_tech) &&
+                                   ISTRUNK(pbridge)) {
+                                       int func = DAHDI_FLASH;
+                                       /* Clear out the dial buffer */
+                                       p->dop.dialstr[0] = '\0';
+                                       /* flash hookswitch */
+                                       if ((ioctl(pbridge->subs[ANALOG_SUB_REAL].dfd,DAHDI_HOOK,&func) == -1) && (errno != EINPROGRESS)) {
+                                               ast_log(LOG_WARNING, "Unable to flash external trunk on channel %s: %s\n",
+                                                       nbridge->name, strerror(errno));
+                                       }
+                                       analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_THREEWAY);
+                                       analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+                                       p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                                       if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))
+                                               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+                                       ast_hangup(chan);
+                                       goto quit;
+                               } else {
+                                       analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+                                       analog_wait_event(p);
+                                       analog_play_tone(p, index, -1);
+                                       analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_THREEWAY);
+                                       analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
+                                       p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                                       ast_hangup(chan);
+                                       goto quit;
+                               }
+#endif
+                       } else if (!ast_canmatch_extension(chan, chan->context, exten, 1, chan->cid.cid_num) &&
+                                                       ((exten[0] != '*') || (strlen(exten) > 2))) {
+                               ast_debug(1, "Can't match %s from '%s' in context %s\n", exten, chan->cid.cid_num ? chan->cid.cid_num : "<Unknown Caller>", chan->context);
+                               break;
+                       }
+                       if (!timeout)
+                               timeout = analog_gendigittimeout;
+                       if (len && !ast_ignore_pattern(chan->context, exten))
+                               analog_play_tone(p, index, -1);
+               }
+               break;
+       case ANALOG_SIG_FXSLS:
+       case ANALOG_SIG_FXSGS:
+       case ANALOG_SIG_FXSKS:
+
+               /* If we want caller id, we're in a prering state due to a polarity reversal
+                * and we're set to use a polarity reversal to trigger the start of caller id,
+                * grab the caller id and wait for ringing to start... */
+               if (p->use_callerid && (chan->_state == AST_STATE_PRERING && (p->cid_start == ANALOG_CID_START_POLARITY || p->cid_start == ANALOG_CID_START_POLARITY_IN))) {
+                       /* If set to use DTMF CID signalling, listen for DTMF */
+                       if (p->cid_signalling == CID_SIG_DTMF) {
+                               int i = 0;
+                               cs = NULL;
+                               ast_debug(1, "Receiving DTMF cid on "
+                                       "channel %s\n", chan->name);
+#if 0
+                               dahdi_setlinear(p->subs[index].dfd, 0);
+#endif
+                               res = 2000;
+                               for (;;) {
+                                       struct ast_frame *f;
+                                       res = ast_waitfor(chan, res);
+                                       if (res <= 0) {
+                                               ast_log(LOG_WARNING, "DTMFCID timed out waiting for ring. "
+                                                       "Exiting simple switch\n");
+                                               ast_hangup(chan);
+                                               goto quit;
+                                       }
+                                       f = ast_read(chan);
+                                       if (!f)
+                                               break;
+                                       if (f->frametype == AST_FRAME_DTMF) {
+                                               dtmfbuf[i++] = f->subclass;
+                                               ast_debug(1, "CID got digit '%c'\n", f->subclass);
+                                               res = 2000;
+                                       }
+                                       ast_frfree(f);
+                                       if (chan->_state == AST_STATE_RING ||
+                                           chan->_state == AST_STATE_RINGING)
+                                               break; /* Got ring */
+                               }
+                               dtmfbuf[i] = '\0';
+#if 0
+                               dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear);
+#endif
+                               /* Got cid and ring. */
+                               ast_debug(1, "CID got string '%s'\n", dtmfbuf);
+                               callerid_get_dtmf(dtmfbuf, dtmfcid, &flags);
+                               ast_debug(1, "CID is '%s', flags %d\n",
+                                       dtmfcid, flags);
+                               /* If first byte is NULL, we have no cid */
+                               if (!ast_strlen_zero(dtmfcid))
+                                       number = dtmfcid;
+                               else
+                                       number = NULL;
+#if 0
+                       /* If set to use V23 Signalling, launch our FSK gubbins and listen for it */
+                       } else if ((p->cid_signalling == CID_SIG_V23) || (p->cid_signalling == CID_SIG_V23_JP)) {
+                               cs = callerid_new(p->cid_signalling);
+                               if (cs) {
+                                       samples = 0;
+#if 1
+                                       bump_gains(p);
+#endif                         
+                                       /* Take out of linear mode for Caller*ID processing */
+                                       dahdi_setlinear(p->subs[index].dfd, 0);
+                                       
+                                       /* First we wait and listen for the Caller*ID */
+                                       for (;;) {      
+                                               i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT;
+                                               if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) {
+                                                       ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+                                                       callerid_free(cs);
+                                                       ast_hangup(chan);
+                                                       goto quit;
+                                               }
+                                               if (i & DAHDI_IOMUX_SIGEVENT) {
+                                                       res = dahdi_get_event(p->subs[index].dfd);
+                                                       ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
+
+                                                       if (p->cid_signalling == CID_SIG_V23_JP) {
+#ifdef DAHDI_EVENT_RINGBEGIN
+                                                               if (res == ANALOG_EVENT_RINGBEGIN) {
+                                                                       res = analog_off_hook(p);
+                                                                       usleep(1);
+                                                               }
+#endif
+                                                       } else {
+                                                               res = 0;
+                                                               break;
+                                                       }
+                                               } else if (i & DAHDI_IOMUX_READ) {
+                                                       res = read(p->subs[index].dfd, buf, sizeof(buf));
+                                                       if (res < 0) {
+                                                               if (errno != ELAST) {
+                                                                       ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+                                                                       callerid_free(cs);
+                                                                       ast_hangup(chan);
+                                                                       goto quit;
+                                                               }
+                                                               break;
+                                                       }
+                                                       samples += res;
+
+                                                       if  (p->cid_signalling == CID_SIG_V23_JP) {
+                                                               res = callerid_feed_jp(cs, buf, res, AST_LAW(p));
+                                                       } else {
+                                                               res = callerid_feed(cs, buf, res, AST_LAW(p));
+                                                       }
+
+                                                       if (res < 0) {
+                                                               ast_log(LOG_WARNING, "CallerID feed failed on channel '%s'\n", chan->name);
+                                                               break;
+                                                       } else if (res)
+                                                               break;
+                                                       else if (samples > (8000 * 10))
+                                                               break;
+                                               }
+                                       }
+                                       if (res == 1) {
+                                               callerid_get(cs, &name, &number, &flags);
+                                               ast_log(LOG_NOTICE, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags);
+                                       }
+
+                                       if (p->cid_signalling == CID_SIG_V23_JP) {
+                                               res = analog_on_hook(p);
+                                               usleep(1);
+                                               res = 4000;
+                                       } else {
+
+                                               /* Finished with Caller*ID, now wait for a ring to make sure there really is a call coming */ 
+                                               res = 2000;
+                                       }
+
+                                       for (;;) {
+                                               struct ast_frame *f;
+                                               res = ast_waitfor(chan, res);
+                                               if (res <= 0) {
+                                                       ast_log(LOG_WARNING, "CID timed out waiting for ring. "
+                                                               "Exiting simple switch\n");
+                                                       ast_hangup(chan);
+                                                       goto quit;
+                                               } 
+                                               if (!(f = ast_read(chan))) {
+                                                       ast_log(LOG_WARNING, "Hangup received waiting for ring. Exiting simple switch\n");
+                                                       ast_hangup(chan);
+                                                       goto quit;
+                                               }
+                                               ast_frfree(f);
+                                               if (chan->_state == AST_STATE_RING ||
+                                                   chan->_state == AST_STATE_RINGING) 
+                                                       break; /* Got ring */
+                                       }
+       
+                                       /* Restore linear mode (if appropriate) for Caller*ID processing */
+                                       dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear);
+#if 1
+                                       restore_gains(p);
+#endif                         
+                               } else
+                                       ast_log(LOG_WARNING, "Unable to get caller ID space\n");                        
+#endif
+                       } else {
+                               ast_log(LOG_WARNING, "Channel %s in prering "
+                                       "state, but I have nothing to do. "
+                                       "Terminating simple switch, should be "
+                                       "restarted by the actual ring.\n", 
+                                       chan->name);
+                               ast_hangup(chan);
+                               goto quit;
+                       }
+               } else if (p->use_callerid && p->cid_start == ANALOG_CID_START_RING) {
+                       int timeout = 10000;  /* Ten seconds */
+                       struct timeval start = ast_tvnow();
+                       enum analog_event ev;
+
+                       namebuf[0] = 0;
+                       numbuf[0] = 0;
+
+                       if (!analog_start_cid_detect(p, p->cid_signalling)) {
+                               while (1) {
+                                       res = analog_get_callerid(p, namebuf, numbuf, &ev, timeout - ast_tvdiff_ms(ast_tvnow(), start));
+
+                                       if (res == 0) {
+                                               break;
+                                       }
+
+                                       if (res == 1) {
+                                               if (ev == ANALOG_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) {
+                                                       ast_debug(1, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel);
+                                                       p->polarity = POLARITY_IDLE;
+                                                       ast_hangup(chan);
+                                                       goto quit;
+                                               } else if (ev != ANALOG_EVENT_NONE) {
+                                                       break;
+                                               }
+                                       }
+
+                                       if (ast_tvdiff_ms(ast_tvnow(), start) > timeout)
+                                               break;
+
+                               }
+                               name = namebuf;
+                               number = numbuf;
+
+                               analog_stop_cid_detect(p);
+
+#if 0
+                       /* XXX */
+                       if (strcmp(p->context,p->defcontext) != 0) {
+                               ast_copy_string(p->context, p->defcontext, sizeof(p->context));
+                               ast_copy_string(chan->context,p->defcontext,sizeof(chan->context));
+                       }
+
+                       analog_get_callerid(p, name, number);
+                       /* FSK Bell202 callerID */
+                       cs = callerid_new(p->cid_signalling);
+                       if (cs) {
+#if 1
+                               bump_gains(p);
+#endif                         
+                               samples = 0;
+                               len = 0;
+                               distMatches = 0;
+                               /* Clear the current ring data array so we dont have old data in it. */
+                               for (receivedRingT = 0; receivedRingT < (sizeof(curRingData) / sizeof(curRingData[0])); receivedRingT++)
+                                       curRingData[receivedRingT] = 0;
+                               receivedRingT = 0;
+                               counter = 0;
+                               counter1 = 0;
+                               /* Check to see if context is what it should be, if not set to be. */
+
+                               /* Take out of linear mode for Caller*ID processing */
+                               dahdi_setlinear(p->subs[index].dfd, 0);
+                               for (;;) {      
+                                       i = DAHDI_IOMUX_READ | DAHDI_IOMUX_SIGEVENT;
+                                       if ((res = ioctl(p->subs[index].dfd, DAHDI_IOMUX, &i))) {
+                                               ast_log(LOG_WARNING, "I/O MUX failed: %s\n", strerror(errno));
+                                               callerid_free(cs);
+                                               ast_hangup(chan);
+                                               goto quit;
+                                       }
+                                       if (i & DAHDI_IOMUX_SIGEVENT) {
+                                               res = dahdi_get_event(p->subs[index].dfd);
+                                               ast_log(LOG_NOTICE, "Got event %d (%s)...\n", res, event2str(res));
+                                               /* If we get a PR event, they hung up while processing calerid */
+                                               if ( res == ANALOG_EVENT_POLARITY && p->hanguponpolarityswitch && p->polarity == POLARITY_REV) {
+                                                       ast_debug(1, "Hanging up due to polarity reversal on channel %d while detecting callerid\n", p->channel);
+                                                       p->polarity = POLARITY_IDLE;
+                                                       callerid_free(cs);
+                                                       ast_hangup(chan);
+                                                       goto quit;
+                                               }
+                                               res = 0;
+                                               /* Let us detect callerid when the telco uses distinctive ring */
+
+                                               curRingData[receivedRingT] = p->ringt;
+
+                                               if (p->ringt < p->ringt_base/2)
+                                                       break;
+                                               /* Increment the ringT counter so we can match it against
+                                                  values in chan_dahdi.conf for distinctive ring */
+                                               if (++receivedRingT == (sizeof(curRingData) / sizeof(curRingData[0])))
+                                                       break;
+                                       } else if (i & DAHDI_IOMUX_READ) {
+                                               res = read(p->subs[index].dfd, buf, sizeof(buf));
+                                               if (res < 0) {
+                                                       if (errno != ELAST) {
+                                                               ast_log(LOG_WARNING, "read returned error: %s\n", strerror(errno));
+                                                               callerid_free(cs);
+                                                               ast_hangup(chan);
+                                                               goto quit;
+                                                       }
+                                                       break;
+                                               }
+                                               if (p->ringt) 
+                                                       p->ringt--;
+                                               if (p->ringt == 1) {
+                                                       res = -1;
+                                                       break;
+                                               }
+                                               samples += 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;
+                                               } else if (res)
+                                                       break;
+                                               else if (samples > (8000 * 10))
+                                                       break;
+                                       }
+                               }
+                               if (res == 1) {
+                                       callerid_get(cs, &name, &number, &flags);
+                                       ast_debug(1, "CallerID number: %s, name: %s, flags=%d\n", number, name, flags);
+                               }
+                               /* Restore linear mode (if appropriate) for Caller*ID processing */
+                               dahdi_setlinear(p->subs[index].dfd, p->subs[index].linear);
+#if 1
+                               restore_gains(p);
+#endif                         
+                               if (res < 0) {
+                                       ast_log(LOG_WARNING, "CallerID returned with error on channel '%s'\n", chan->name);
+                               }
+                       } else
+                               ast_log(LOG_WARNING, "Unable to get caller ID space\n");
+#endif
+                       } else
+                               ast_log(LOG_WARNING, "Unable to get caller ID space\n");
+               }
+               else
+                       cs = NULL;
+
+               if (number)
+                       ast_shrink_phone_number(number);
+               ast_set_callerid(chan, number, name, number);
+
+               if (cs)
+                       callerid_free(cs);
+
+               analog_handle_notify_message(chan, p, flags, -1);
+
+               ast_setstate(chan, AST_STATE_RING);
+               chan->rings = 1;
+#if 0
+               p->ringt = p->ringt_base;
+#endif
+               res = ast_pbx_run(chan);
+               if (res) {
+                       ast_hangup(chan);
+                       ast_log(LOG_WARNING, "PBX exited non-zero\n");
+               }
+               goto quit;
+       default:
+               ast_log(LOG_WARNING, "Don't know how to handle simple switch with signalling %s on channel %d\n", analog_sigtype_to_str(p->sig), p->channel);
+               res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+               if (res < 0)
+                               ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
+       }
+       res = analog_play_tone(p, index, ANALOG_TONE_CONGESTION);
+       if (res < 0)
+                       ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
+       ast_hangup(chan);
+quit:
+       analog_decrease_ss_count(p);
+       return NULL;
+}
+
+int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *chan)
+{
+       pthread_t threadid;
+       return ast_pthread_create_detached(&threadid, NULL, __analog_ss_thread, chan);
+}
+
+static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_channel *ast)
+{
+       int res, x;
+       int mysig;
+       enum analog_sub index;
+       char *c;
+       pthread_t threadid;
+       pthread_attr_t attr;
+       struct ast_channel *chan;
+       struct ast_frame *f;
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+
+       index = analog_get_index(ast, p, 0);
+       mysig = p->sig;
+       if (p->outsigmod > -1)
+               mysig = p->outsigmod;
+       p->subs[index].f.frametype = AST_FRAME_NULL;
+       p->subs[index].f.subclass = 0;
+       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 = "dahdi_handle_event";
+       p->subs[index].f.data.ptr = NULL;
+       f = &p->subs[index].f;
+
+       if (index < 0)
+               return &p->subs[index].f;
+
+       if (index != ANALOG_SUB_REAL) {
+               ast_log(LOG_ERROR, "We got an event on a non real sub.  Fix it!\n");
+       }
+
+       res = analog_get_event(p);
+
+       ast_debug(1, "Got event %s(%d) on channel %d (index %d)\n", analog_event2str(res), res, p->channel, index);
+
+       switch (res) {
+#ifdef ANALOG_EVENT_EC_DISABLED
+       case ANALOG_EVENT_EC_DISABLED:
+               if (option_verbose > 2) 
+                       ast_verbose(VERBOSE_PREFIX_3 "Channel %d echo canceler disabled due to CED detection\n", p->channel);
+               p->echocanon = 0;
+               break;
+#endif
+       case ANALOG_EVENT_PULSE_START:
+               /* Stop tone if there's a pulse start and the PBX isn't started */
+               if (!ast->pbx)
+                       analog_play_tone(p, ANALOG_SUB_REAL, -1);
+               break;
+       case ANALOG_EVENT_DIALCOMPLETE:
+               if (p->inalarm) break;
+               x = analog_is_dialing(p, index);
+               if (!x) { /* if not still dialing in driver */
+                       analog_set_echocanceller(p, 1);
+                       if (p->echobreak) {
+                               analog_train_echocanceller(p);
+                               ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr));
+                               p->dop.op = ANALOG_DIAL_OP_REPLACE;
+                               analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop);
+                               p->echobreak = 0;
+                       } else {
+                               p->dialing = 0;
+                               if ((mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF)) {
+                                       /* if thru with dialing after offhook */
+                                       if (ast->_state == AST_STATE_DIALING_OFFHOOK) {
+                                               ast_setstate(ast, AST_STATE_UP);
+                                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                               p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+                                               break;
+                                       } else { /* if to state wait for offhook to dial rest */
+                                               /* we now wait for off hook */
+                                               ast_setstate(ast,AST_STATE_DIALING_OFFHOOK);
+                                       }
+                               }
+                               if (ast->_state == AST_STATE_DIALING) {
+                                       if ((!p->dialednone && ((mysig == ANALOG_SIG_EM) || (mysig == ANALOG_SIG_EM_E1) ||  (mysig == ANALOG_SIG_EMWINK) || (mysig == ANALOG_SIG_FEATD) || (mysig == ANALOG_SIG_FEATDMF_TA) || (mysig == ANALOG_SIG_FEATDMF) || (mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF) || (mysig == ANALOG_SIG_FEATB) || (mysig == ANALOG_SIG_SF) || (mysig == ANALOG_SIG_SFWINK) || (mysig == ANALOG_SIG_SF_FEATD) || (mysig == ANALOG_SIG_SF_FEATDMF) || (mysig == ANALOG_SIG_SF_FEATB)))) {
+                                               ast_setstate(ast, AST_STATE_RINGING);
+                                       } else if (!p->answeronpolarityswitch) {
+                                               ast_setstate(ast, AST_STATE_UP);
+                                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                                               p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+                                               /* If aops=0 and hops=1, this is necessary */
+                                               p->polarity = POLARITY_REV;
+                                       } else {
+                                               /* Start clean, so we can catch the change to REV polarity when party answers */
+                                               p->polarity = POLARITY_IDLE;
+                                       }
+                               }
+                       }
+               }
+               break;
+       case ANALOG_EVENT_ALARM:
+               p->inalarm = 1;
+#if 0
+               res = get_alarms(p);
+               handle_alarms(p, res);  
+#endif
+       case ANALOG_EVENT_ONHOOK:
+               switch (p->sig) {
+               case ANALOG_SIG_FXOLS:
+               case ANALOG_SIG_FXOGS:
+               case ANALOG_SIG_FXOKS:
+                       /* Check for some special conditions regarding call waiting */
+                       if (index == ANALOG_SUB_REAL) {
+                               /* The normal line was hung up */
+                               if (p->subs[ANALOG_SUB_CALLWAIT].owner) {
+                                       /* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
+                                       analog_swap_subs(p, ANALOG_SUB_CALLWAIT, ANALOG_SUB_REAL);
+                                       if (option_verbose > 2) 
+                                               ast_verbose(VERBOSE_PREFIX_3 "Channel %d still has (callwait) call, ringing phone\n", p->channel);
+                                       analog_unalloc_sub(p, ANALOG_SUB_CALLWAIT);
+                                       analog_stop_callwait(p);
+                                       p->owner = NULL;
+                                       /* Don't start streaming audio yet if the incoming call isn't up yet */
+                                       if (p->subs[ANALOG_SUB_REAL].owner->_state != AST_STATE_UP)
+                                               p->dialing = 1;
+                                       analog_ring(p);
+                               } else if (p->subs[ANALOG_SUB_THREEWAY].owner) {
+                                       unsigned int mssinceflash;
+                                       /* Here we have to retain the lock on both the main channel, the 3-way channel, and
+                                          the private structure -- not especially easy or clean */
+                                       while (p->subs[ANALOG_SUB_THREEWAY].owner && ast_channel_trylock(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+                                               /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
+                                               analog_unlock_private(p);
+                                               CHANNEL_DEADLOCK_AVOIDANCE(ast);
+                                               /* We can grab ast and p in that order, without worry.  We should make sure
+                                                  nothing seriously bad has happened though like some sort of bizarre double
+                                                  masquerade! */
+                                               analog_lock_private(p);
+                                               if (p->owner != ast) {
+                                                       ast_log(LOG_WARNING, "This isn't good...\n");
+                                                       return NULL;
+                                               }
+                                       }
+                                       if (!p->subs[ANALOG_SUB_THREEWAY].owner) {
+                                               ast_log(LOG_NOTICE, "Whoa, threeway disappeared kinda randomly.\n");
+                                               return NULL;
+                                       }
+                                       mssinceflash = ast_tvdiff_ms(ast_tvnow(), p->flashtime);
+                                       ast_debug(1, "Last flash was %d ms ago\n", mssinceflash);
+                                       if (mssinceflash < MIN_MS_SINCE_FLASH) {
+                                               /* It hasn't been long enough since the last flashook.  This is probably a bounce on
+                                                  hanging up.  Hangup both channels now */
+                                               if (p->subs[ANALOG_SUB_THREEWAY].owner)
+                                                       ast_queue_hangup(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                               ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+                                               ast_debug(1, "Looks like a bounced flash, hanging up both calls on %d\n", p->channel);
+                                               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                       } else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) {
+                                               if (p->transfer) {
+                                                       /* In any case this isn't a threeway call anymore */
+                                                       p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+                                                       p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0;
+                                                       /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
+                                                       if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) {
+                                                               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                                               /* Swap subs and dis-own channel */
+                                                               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                                               p->owner = NULL;
+                                                               /* Ring the phone */
+                                                               analog_ring(p);
+                                                       } else {
+                                                               if ((res = analog_attempt_transfer(p)) < 0) {
+                                                                       ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+                                                                       if (p->subs[ANALOG_SUB_THREEWAY].owner)
+                                                                               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                                               } else if (res) {
+                                                                       /* Don't actually hang up at this point */
+                                                                       if (p->subs[ANALOG_SUB_THREEWAY].owner)
+                                                                               ast_channel_unlock(&p->subs[ANALOG_SUB_THREEWAY].owner);
+                                                                       break;
+                                                               }
+                                                       }
+                                               } else {
+                                                       ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+                                                       if (p->subs[ANALOG_SUB_THREEWAY].owner)
+                                                               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                               }
+                                       } else {
+                                               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                               /* Swap subs and dis-own channel */
+                                               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                               p->owner = NULL;
+                                               /* Ring the phone */
+                                               analog_ring(p);
+                                       }
+                               }
+                       } else {
+                               ast_log(LOG_WARNING, "Got a hangup and my index is %d?\n", index);
+                       }
+                       /* Fall through */
+               default:
+                       analog_set_echocanceller(p, 0);
+                       return NULL;
+               }
+               break;
+       case ANALOG_EVENT_RINGOFFHOOK:
+               if (p->inalarm) break;
+               /* for E911, its supposed to wait for offhook then dial
+                  the second half of the dial string */
+               if (((mysig == ANALOG_SIG_E911) || (mysig == ANALOG_SIG_FGC_CAMA) || (mysig == ANALOG_SIG_FGC_CAMAMF)) && (ast->_state == AST_STATE_DIALING_OFFHOOK)) {
+                       c = strchr(p->dialdest, '/');
+                       if (c)
+                               c++;
+                       else
+                               c = p->dialdest;
+                       if (*c) snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*0%s#", c);
+                       else ast_copy_string(p->dop.dialstr,"M*2#", sizeof(p->dop.dialstr));
+                       if (strlen(p->dop.dialstr) > 4) {
+                               memset(p->echorest, 'w', sizeof(p->echorest) - 1);
+                               strcpy(p->echorest + (p->echotraining / 401) + 1, p->dop.dialstr + strlen(p->dop.dialstr) - 2);
+                               p->echorest[sizeof(p->echorest) - 1] = '\0';
+                               p->echobreak = 1;
+                               p->dop.dialstr[strlen(p->dop.dialstr)-2] = '\0';
+                       } else
+                               p->echobreak = 0;
+                       if (analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop)) {
+                               int saveerr = errno;
+                               analog_on_hook(p);
+                               ast_log(LOG_WARNING, "Dialing failed on channel %d: %s\n", p->channel, strerror(saveerr));
+                               return NULL;
+                       }
+                       p->dialing = 1;
+                       return &p->subs[index].f;
+               }
+               switch (p->sig) {
+               case ANALOG_SIG_FXOLS:
+               case ANALOG_SIG_FXOGS:
+               case ANALOG_SIG_FXOKS:
+                       switch (ast->_state) {
+                       case AST_STATE_RINGING:
+                               analog_set_echocanceller(p, 1);
+                               analog_train_echocanceller(p);
+                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[index].f.subclass = AST_CONTROL_ANSWER;
+                               /* Make sure it stops ringing */
+                               analog_off_hook(p);
+                               ast_debug(1, "channel %d answered\n", p->channel);
+                               p->dialing = 0;
+                               p->callwaitcas = 0;
+                               if (!ast_strlen_zero(p->dop.dialstr)) {
+                                       /* nick@dccinc.com 4/3/03 - fxo should be able to do deferred dialing */
+                                       res = analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop);
+                                       if (res < 0) {
+                                               ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
+                                               p->dop.dialstr[0] = '\0';
+                                               return NULL;
+                                       } else {
+                                               ast_debug(1, "Sent FXO deferred digit string: %s\n", p->dop.dialstr);
+                                               p->subs[index].f.frametype = AST_FRAME_NULL;
+                                               p->subs[index].f.subclass = 0;
+                                               p->dialing = 1;
+                                       }
+                                       p->dop.dialstr[0] = '\0';
+                                       ast_setstate(ast, AST_STATE_DIALING);
+                               } else
+                                       ast_setstate(ast, AST_STATE_UP);
+                               return &p->subs[index].f;
+                       case AST_STATE_DOWN:
+                               ast_setstate(ast, AST_STATE_RING);
+                               ast->rings = 1;
+                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[index].f.subclass = AST_CONTROL_OFFHOOK;
+                               ast_debug(1, "channel %d picked up\n", p->channel);
+                               return &p->subs[index].f;
+                       case AST_STATE_UP:
+                               /* Make sure it stops ringing */
+                               analog_off_hook(p);
+                               /* Okay -- probably call waiting*/
+                               if (ast_bridged_channel(p->owner))
+                                       ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+                               break;
+                       case AST_STATE_RESERVED:
+                               /* Start up dialtone */
+                               if (analog_has_voicemail(p))
+                                       res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_STUTTER);
+                               else
+                                       res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_DIALTONE);
+                               break;
+                       default:
+                               ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->_state);
+                       }
+                       break;
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSGS:
+               case ANALOG_SIG_FXSKS:
+#if 0
+                       if (ast->_state == AST_STATE_RING) {
+                               p->ringt = p->ringt_base;
+                       }
+#endif
+
+                       /* Fall through */
+               case ANALOG_SIG_EM:
+               case ANALOG_SIG_EM_E1:
+               case ANALOG_SIG_EMWINK:
+               case ANALOG_SIG_FEATD:
+               case ANALOG_SIG_FEATDMF:
+               case ANALOG_SIG_FEATDMF_TA:
+               case ANALOG_SIG_E911:
+               case ANALOG_SIG_FGC_CAMA:
+               case ANALOG_SIG_FGC_CAMAMF:
+               case ANALOG_SIG_FEATB:
+               case ANALOG_SIG_SF:
+               case ANALOG_SIG_SFWINK:
+               case ANALOG_SIG_SF_FEATD:
+               case ANALOG_SIG_SF_FEATDMF:
+               case ANALOG_SIG_SF_FEATB:
+                       if (ast->_state == AST_STATE_PRERING)
+                               ast_setstate(ast, AST_STATE_RING);
+                       if ((ast->_state == AST_STATE_DOWN) || (ast->_state == AST_STATE_RING)) {
+                               ast_debug(1, "Ring detected\n");
+                               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+                               p->subs[index].f.subclass = AST_CONTROL_RING;
+                       } else if (p->outgoing && ((ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING))) {
+                               ast_debug(1, "Line answered\n");
+                               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)
+                               ast_log(LOG_WARNING, "Ring/Off-hook in strange state %d on channel %d\n", ast->_state, p->channel);
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to handle ring/off hook for signalling %d\n", p->sig);
+               }
+               break;
+#ifdef ANALOG_EVENT_RINGBEGIN
+       case ANALOG_EVENT_RINGBEGIN:
+               switch (p->sig) {
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSGS:
+               case ANALOG_SIG_FXSKS:
+#if 0
+                       if (ast->_state == AST_STATE_RING) {
+                               p->ringt = p->ringt_base;
+                       }
+#endif
+                       break;
+               }
+               break;
+#endif
+       case ANALOG_EVENT_RINGEROFF:
+               if (p->inalarm) break;
+               ast->rings++;
+               if (ast->rings == p->cidrings) {
+                       analog_send_callerid(p, 0, &p->cid);
+               }
+
+               if (ast->rings > p->cidrings) {
+                       p->callwaitcas = 0;
+               }
+               p->subs[index].f.frametype = AST_FRAME_CONTROL;
+               p->subs[index].f.subclass = AST_CONTROL_RINGING;
+               break;
+       case ANALOG_EVENT_RINGERON:
+               break;
+       case ANALOG_EVENT_NOALARM:
+               p->inalarm = 0;
+               if (!p->unknown_alarm) {
+                       ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
+                       manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
+                               "Channel: %d\r\n", p->channel);
+               } else {
+                       p->unknown_alarm = 0;
+               }
+               break;
+       case ANALOG_EVENT_WINKFLASH:
+               if (p->inalarm) break;
+               /* Remember last time we got a flash-hook */
+               gettimeofday(&p->flashtime, NULL);
+               switch (mysig) {
+               case ANALOG_SIG_FXOLS:
+               case ANALOG_SIG_FXOGS:
+               case ANALOG_SIG_FXOKS:
+#if 0
+                       ast_debug(1, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
+                               index, p->subs[ANALOG_SUB_REAL].dfd, p->subs[ANALOG_SUB_CALLWAIT].dfd, p->subs[ANALOG_SUB_THREEWAY].dfd);
+#endif
+                       p->callwaitcas = 0;
+
+                       if (index != ANALOG_SUB_REAL) {
+                               ast_log(LOG_WARNING, "Got flash hook with index %d on channel %d?!?\n", index, p->channel);
+                               goto winkflashdone;
+                       }
+
+                       if (p->subs[ANALOG_SUB_CALLWAIT].owner) {
+                               /* Swap to call-wait */
+                               int previous_state = p->subs[ANALOG_SUB_CALLWAIT].owner->_state;
+                               if (p->subs[ANALOG_SUB_CALLWAIT].owner->_state == AST_STATE_RINGING) {
+                                       ast_setstate(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_STATE_UP);
+                               }
+                               analog_swap_subs(p, ANALOG_SUB_REAL, ANALOG_SUB_CALLWAIT);
+                               analog_play_tone(p, ANALOG_SUB_REAL, -1);
+                               p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                               ast_debug(1, "Making %s the new owner\n", p->owner->name);
+                               if (previous_state == AST_STATE_RINGING) {
+                                       ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER);
+                               }
+                               analog_stop_callwait(p);
+                               /* Start music on hold if appropriate */
+                               if (!p->subs[ANALOG_SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[ANALOG_SUB_CALLWAIT].owner)) {
+                                       ast_queue_control_data(p->subs[ANALOG_SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
+                                               S_OR(p->mohsuggest, NULL),
+                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               }
+                               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER);
+                               if (ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)) {
+                                       ast_queue_control_data(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_HOLD,
+                                               S_OR(p->mohsuggest, NULL),
+                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                               }
+                               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+                       } else if (!p->subs[ANALOG_SUB_THREEWAY].owner) {
+                               char cid_num[256];
+                               char cid_name[256];
+
+                               if (!p->threewaycalling) {
+                                       /* Just send a flash if no 3-way calling */
+                                       ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_FLASH);
+                                       goto winkflashdone;
+                               } else if (!analog_check_for_conference(p)) {
+                                       if (p->dahditrcallerid && p->owner) {
+                                               if (p->owner->cid.cid_num)
+                                                       ast_copy_string(cid_num, p->owner->cid.cid_num, sizeof(cid_num));
+                                               if (p->owner->cid.cid_name)
+                                                       ast_copy_string(cid_name, p->owner->cid.cid_name, sizeof(cid_name));
+                                       }
+                                       /* 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))) {
+                                               ast_debug(1, "Flash when call not up or ringing\n");
+                                                       goto winkflashdone;
+                                       }
+                                       if (analog_alloc_sub(p, ANALOG_SUB_THREEWAY)) {
+                                               ast_log(LOG_WARNING, "Unable to allocate three-way subchannel\n");
+                                               goto winkflashdone;
+                                       }
+                                       /* Make new channel */
+                                       chan = analog_new_ast_channel(p, AST_STATE_RESERVED, 0, ANALOG_SUB_THREEWAY);
+                                       if (p->dahditrcallerid) {
+                                               if (!p->origcid_num)
+                                                       p->origcid_num = ast_strdup(p->cid_num);
+                                               if (!p->origcid_name)
+                                                       p->origcid_name = ast_strdup(p->cid_name);
+                                               ast_copy_string(p->cid_num, cid_num, sizeof(p->cid_num));
+                                               ast_copy_string(p->cid_name, cid_name, sizeof(p->cid_name));
+                                       }
+                                       /* Swap things around between the three-way and real call */
+                                       analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                       /* Disable echo canceller for better dialing */
+                                       analog_set_echocanceller(p, 0);
+                                       res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_DIALRECALL);
+                                       if (res)
+                                               ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
+                                       p->ss_astchan = p->owner = chan;
+                                       pthread_attr_init(&attr);
+                                       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+                                       if (!chan) {
+                                               ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel);
+                                       } else if (ast_pthread_create(&threadid, &attr, __analog_ss_thread, p)) {
+                                               ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
+                                               res = analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
+                                               analog_set_echocanceller(p, 1);
+                                               ast_hangup(chan);
+                                       } else {
+                                               struct ast_channel *other = ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                               int way3bridge = 0, cdr3way = 0;
+
+                                               if (!other) {
+                                                       other = ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner);
+                                               } else
+                                                       way3bridge = 1;
+
+                                               if (p->subs[ANALOG_SUB_THREEWAY].owner->cdr)
+                                                       cdr3way = 1;
+                                               
+                                               if (option_verbose > 2) 
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Started three way call on channel %d\n", p->channel);
+                                               /* Start music on hold if appropriate */
+                                               if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
+                                                       ast_queue_control_data(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_HOLD,
+                                                               S_OR(p->mohsuggest, NULL),
+                                                               !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+                                               }
+                                       }
+                                       pthread_attr_destroy(&attr);
+                               }
+                       } else {
+                               /* Already have a 3 way call */
+                               if (p->subs[ANALOG_SUB_THREEWAY].inthreeway) {
+                                       /* Call is already up, drop the last person */
+                                       ast_debug(1, "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[ANALOG_SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_UP)) {
+                                               /* Swap back -- we're dropping the real 3-way that isn't finished yet*/
+                                               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                               p->owner = p->subs[ANALOG_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[ANALOG_SUB_THREEWAY].owner->name);
+                                       ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+                                       p->subs[ANALOG_SUB_REAL].inthreeway = 0;
+                                       p->subs[ANALOG_SUB_THREEWAY].inthreeway = 0;
+                               } else {
+                                       /* Lets see what we're up to */
+                                       if (((ast->pbx) || (ast->_state == AST_STATE_UP)) && 
+                                           (p->transfertobusy || (ast->_state != AST_STATE_BUSY))) {
+                                               int otherindex = ANALOG_SUB_THREEWAY;
+                                               struct ast_channel *other = ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner);
+                                               int way3bridge = 0, cdr3way = 0;
+
+                                               if (!other) {
+                                                       other = ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner);
+                                               } else
+                                                       way3bridge = 1;
+
+                                               if (p->subs[ANALOG_SUB_THREEWAY].owner->cdr)
+                                                       cdr3way = 1;
+
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Building conference on call on %s and %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name, p->subs[ANALOG_SUB_REAL].owner->name);
+                                               /* Put them in the threeway, and flip */
+                                               p->subs[ANALOG_SUB_THREEWAY].inthreeway = 1;
+                                               p->subs[ANALOG_SUB_REAL].inthreeway = 1;
+                                               if (ast->_state == AST_STATE_UP) {
+                                                       analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                                       otherindex = ANALOG_SUB_REAL;
+                                               }
+                                               if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner))
+                                                       ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD);
+                                               p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                                               if (ast->_state == AST_STATE_RINGING) {
+                                                       ast_debug(1, "Enabling ringtone on real and threeway\n");
+                                                       analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
+                                                       analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE);
+                                               }
+                                       } else {
+                                               if (option_verbose > 2)
+                                                       ast_verbose(VERBOSE_PREFIX_3 "Dumping incomplete call on on %s\n", p->subs[ANALOG_SUB_THREEWAY].owner->name);
+                                               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
+                                               ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
+                                               p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                                               if (p->subs[ANALOG_SUB_REAL].owner && ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))
+                                                       ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
+                                               analog_set_echocanceller(p, 1);
+                                       }
+                               }
+                       }
+               winkflashdone:
+                       analog_update_conf(p);
+                       break;
+               case ANALOG_SIG_EM:
+               case ANALOG_SIG_EM_E1:
+               case ANALOG_SIG_EMWINK:
+               case ANALOG_SIG_FEATD:
+               case ANALOG_SIG_SF:
+               case ANALOG_SIG_SFWINK:
+               case ANALOG_SIG_SF_FEATD:
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSGS:
+                       if (p->dialing)
+                               ast_debug(1, "Ignoring wink on channel %d\n", p->channel);
+                       else
+                               ast_debug(1, "Got wink in weird state %d on channel %d\n", ast->_state, p->channel);
+                       break;
+               case ANALOG_SIG_FEATDMF_TA:
+                       switch (p->whichwink) {
+                       case 0:
+                               ast_debug(1, "ANI2 set to '%d' and ANI is '%s'\n", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
+                               snprintf(p->dop.dialstr, sizeof(p->dop.dialstr), "M*%d%s#", p->owner->cid.cid_ani2, p->owner->cid.cid_ani);
+                               break;
+                       case 1:
+                               ast_copy_string(p->dop.dialstr, p->finaldial, sizeof(p->dop.dialstr));
+                               break;
+                       case 2:
+                               ast_log(LOG_WARNING, "Received unexpected wink on channel of type ANALOG_SIG_FEATDMF_TA\n");
+                               return NULL;
+                       }
+                       p->whichwink++;
+                       /* Fall through */
+               case ANALOG_SIG_FEATDMF:
+               case ANALOG_SIG_E911:
+               case ANALOG_SIG_FGC_CAMAMF:
+               case ANALOG_SIG_FGC_CAMA:
+               case ANALOG_SIG_FEATB:
+               case ANALOG_SIG_SF_FEATDMF:
+               case ANALOG_SIG_SF_FEATB:
+                       /* FGD MF *Must* wait for wink */
+                       if (!ast_strlen_zero(p->dop.dialstr)) {
+                               res = analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop);
+                               if (res < 0) {
+                                       ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
+                                       p->dop.dialstr[0] = '\0';
+                                       return NULL;
+                               } else
+                                       ast_debug(1, "Sent deferred digit string on channel %d: %s\n", p->channel, p->dop.dialstr);
+                       }
+                       p->dop.dialstr[0] = '\0';
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to handle ring/off hoook for signalling %d\n", p->sig);
+               }
+               break;
+       case ANALOG_EVENT_HOOKCOMPLETE:
+               if (p->inalarm) break;
+               switch (mysig) {
+               case ANALOG_SIG_FXSLS:  /* only interesting for FXS */
+               case ANALOG_SIG_FXSGS:
+               case ANALOG_SIG_FXSKS:
+               case ANALOG_SIG_EM:
+               case ANALOG_SIG_EM_E1:
+               case ANALOG_SIG_EMWINK:
+               case ANALOG_SIG_FEATD:
+               case ANALOG_SIG_SF:
+               case ANALOG_SIG_SFWINK:
+               case ANALOG_SIG_SF_FEATD:
+                       if (!ast_strlen_zero(p->dop.dialstr)) {
+                               res = analog_dial_digits(p, ANALOG_SUB_REAL, &p->dop);
+                               if (res < 0) {
+                                       ast_log(LOG_WARNING, "Unable to initiate dialing on trunk channel %d: %s\n", p->channel, strerror(errno));
+                                       p->dop.dialstr[0] = '\0';
+                                       return NULL;
+                               } else 
+                                       ast_debug(1, "Sent deferred digit string on channel %d: %s\n", p->channel, p->dop.dialstr);
+                       }
+                       p->dop.dialstr[0] = '\0';
+                       p->dop.op = ANALOG_DIAL_OP_REPLACE;
+                       break;
+               case ANALOG_SIG_FEATDMF:
+               case ANALOG_SIG_FEATDMF_TA:
+               case ANALOG_SIG_E911:
+               case ANALOG_SIG_FGC_CAMA:
+               case ANALOG_SIG_FGC_CAMAMF:
+               case ANALOG_SIG_FEATB:
+               case ANALOG_SIG_SF_FEATDMF:
+               case ANALOG_SIG_SF_FEATB:
+                       ast_debug(1, "Got hook complete in MF FGD, waiting for wink now on channel %d\n",p->channel);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case ANALOG_EVENT_POLARITY:
+               /*
+                * If we get a Polarity Switch event, check to see
+                * if we should change the polarity state and
+                * mark the channel as UP or if this is an indication
+                * of remote end disconnect.
+                */
+               if (p->polarity == POLARITY_IDLE) {
+                       p->polarity = POLARITY_REV;
+                       if (p->answeronpolarityswitch &&
+                           ((ast->_state == AST_STATE_DIALING) ||
+                                (ast->_state == AST_STATE_RINGING))) {
+                               ast_debug(1, "Answering on polarity switch!\n");
+                               ast_setstate(p->owner, AST_STATE_UP);
+                               if (p->hanguponpolarityswitch) {
+                                       gettimeofday(&p->polaritydelaytv, NULL);
+                               }
+                       } else
+                               ast_debug(1, "Ignore switch to REVERSED Polarity on channel %d, state %d\n", p->channel, ast->_state);
+               }
+               /* Removed else statement from here as it was preventing hangups from ever happening*/
+               /* Added AST_STATE_RING in if statement below to deal with calling party hangups that take place when ringing */
+               if (p->hanguponpolarityswitch &&
+                       (p->polarityonanswerdelay > 0) &&
+                      (p->polarity == POLARITY_REV) &&
+                       ((ast->_state == AST_STATE_UP) || (ast->_state == AST_STATE_RING)) ) {
+                               /* Added log_debug information below to provide a better indication of what is going on */
+                       ast_debug(1, "Polarity Reversal event occured - DEBUG 1: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+
+                       if (ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) > p->polarityonanswerdelay) {
+                               ast_debug(1, "Polarity Reversal detected and now Hanging up on channel %d\n", p->channel);
+                               ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
+                               p->polarity = POLARITY_IDLE;
+                       } else {
+                               ast_debug(1, "Polarity Reversal detected but NOT hanging up (too close to answer event) on channel %d, state %d\n", p->channel, ast->_state);
+                       }
+               } else {
+                       p->polarity = POLARITY_IDLE;
+                       ast_debug(1, "Ignoring Polarity switch to IDLE on channel %d, state %d\n", p->channel, ast->_state);
+               }
+               /* Added more log_debug information below to provide a better indication of what is going on */
+               ast_debug(1, "Polarity Reversal event occured - DEBUG 2: channel %d, state %d, pol= %d, aonp= %d, honp= %d, pdelay= %d, tv= %d\n", p->channel, ast->_state, p->polarity, p->answeronpolarityswitch, p->hanguponpolarityswitch, p->polarityonanswerdelay, ast_tvdiff_ms(ast_tvnow(), p->polaritydelaytv) );
+               break;
+       default:
+               ast_debug(1, "Dunno what to do with event %d on channel %d\n", res, p->channel);
+       }
+       return &p->subs[index].f;
+}
+
+struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast)
+{
+       int res;
+       int usedindex=-1;
+       int index;
+       struct ast_frame *f;
+
+       ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
+
+       index = analog_get_index(ast, p, 1);
+
+       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.delivery = ast_tv(0,0);
+       p->subs[index].f.src = "dahdi_exception";
+       p->subs[index].f.data.ptr = NULL;
+
+
+       if (!p->owner) {
+               /* 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 = analog_get_event(p);
+
+               /* Switch to real if there is one and this isn't something really silly... */
+               if ((res != ANALOG_EVENT_RINGEROFF) && (res != ANALOG_EVENT_RINGERON) &&
+                       (res != ANALOG_EVENT_HOOKCOMPLETE)) {
+                       ast_debug(1, "Restoring owner of channel %d on event %d\n", p->channel, res);
+                       p->owner = p->subs[ANALOG_SUB_REAL].owner;
+                       if (p->owner && ast_bridged_channel(p->owner))
+                               ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+               }
+               switch (res) {
+               case ANALOG_EVENT_ONHOOK:
+                       analog_set_echocanceller(p, 0);
+                       if (p->owner) {
+                               if (option_verbose > 2) 
+                                       ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has call, ringing phone\n", p->owner->name);
+                               analog_ring(p);
+                               analog_stop_callwait(p);
+                       } else
+                               ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+                       analog_update_conf(p);
+                       break;
+               case ANALOG_EVENT_RINGOFFHOOK:
+                       analog_set_echocanceller(p, 1);
+                       analog_off_hook(p);
+                       if (p->owner && (p->owner->_state == AST_STATE_RINGING)) {
+                               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_ANSWER);
+                               p->dialing = 0;
+                       }
+                       break;
+               case ANALOG_EVENT_HOOKCOMPLETE:
+               case ANALOG_EVENT_RINGERON:
+               case ANALOG_EVENT_RINGEROFF:
+                       /* Do nothing */
+                       break;
+               case ANALOG_EVENT_WINKFLASH:
+                       gettimeofday(&p->flashtime, NULL);
+                       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 (p->owner->_state != AST_STATE_UP) {
+                                       /* Answer if necessary */
+                                       usedindex = analog_get_index(p->owner, p, 0);
+                                       if (usedindex > -1) {
+                                               ast_queue_control(p->subs[usedindex].owner, AST_CONTROL_ANSWER);
+                                       }
+                                       ast_setstate(p->owner, AST_STATE_UP);
+                               }
+                               analog_stop_callwait(p);
+                               if (ast_bridged_channel(p->owner))
+                                       ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
+                       } else
+                               ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+                       analog_update_conf(p);
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", analog_event2str(res));
+               }
+               f = &p->subs[index].f;
+               return f;
+       }
+       ast_debug(1, "Exception on %d, channel %d\n", ast->fds[0],p->channel);
+       /* If it's not us, return NULL immediately */
+       if (ast != p->owner) {
+               ast_log(LOG_WARNING, "We're %s, not %s\n", ast->name, p->owner->name);
+               f = &p->subs[index].f;
+               return f;
+       }
+       f = __analog_handle_event(p, ast);
+       return f;
+}
+
+int analog_handle_init_event(struct analog_pvt *i, int event)
+{
+       int res;
+       pthread_t threadid;
+       pthread_attr_t attr;
+       struct ast_channel *chan;
+
+       ast_debug(1, "channel (%d) - signaling (%d) - event (%s)\n",
+                               i->channel, i->sig, analog_event2str(event));
+
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       /* Handle an event on a given channel for the monitor thread. */
+       switch (event) {
+       case ANALOG_EVENT_WINKFLASH:
+       case ANALOG_EVENT_RINGOFFHOOK:
+               if (i->inalarm) break;
+               /* Got a ring/answer.  What kind of channel are we? */
+               switch (i->sig) {
+               case ANALOG_SIG_FXOLS:
+               case ANALOG_SIG_FXOGS:
+               case ANALOG_SIG_FXOKS:
+                       res = analog_off_hook(i);
+                       if (res && (errno == EBUSY))
+                               break;
+                       if (i->immediate) {
+                               analog_set_echocanceller(i, 1);
+                               /* The channel is immediately up.  Start right away */
+                               res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
+                               chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL);
+                               if (!chan) {
+                                       ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
+                                       res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_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 = analog_new_ast_channel(i, AST_STATE_RESERVED, 0, ANALOG_SUB_REAL);
+                               i->ss_astchan = chan;
+                               if (chan) {
+                                       if (analog_has_voicemail(i))
+                                               res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_STUTTER);
+                                       else
+                                               res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_DIALTONE);
+                                       if (res < 0)
+                                               ast_log(LOG_WARNING, "Unable to play dialtone on channel %d, do you have defaultzone and loadzone defined?\n", i->channel);
+                                       if (ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) {
+                                               ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
+                                               res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
+                                               if (res < 0)
+                                                       ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+                                               ast_hangup(chan);
+                                       }
+                               } else
+                                       ast_log(LOG_WARNING, "Unable to create channel\n");
+                       }
+                       break;
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSGS:
+               case ANALOG_SIG_FXSKS:
+#if 0
+                               i->ringt = i->ringt_base;
+#endif
+                               /* Fall through */
+               case ANALOG_SIG_EMWINK:
+               case ANALOG_SIG_FEATD:
+               case ANALOG_SIG_FEATDMF:
+               case ANALOG_SIG_FEATDMF_TA:
+               case ANALOG_SIG_E911:
+               case ANALOG_SIG_FGC_CAMA:
+               case ANALOG_SIG_FGC_CAMAMF:
+               case ANALOG_SIG_FEATB:
+               case ANALOG_SIG_EM:
+               case ANALOG_SIG_EM_E1:
+               case ANALOG_SIG_SFWINK:
+               case ANALOG_SIG_SF_FEATD:
+               case ANALOG_SIG_SF_FEATDMF:
+               case ANALOG_SIG_SF_FEATB:
+               case ANALOG_SIG_SF:
+                               /* Check for callerid, digits, etc */
+                               if (i->cid_start == ANALOG_CID_START_POLARITY_IN) {
+                                       chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL);
+                               } else {
+                                       chan = analog_new_ast_channel(i, AST_STATE_RING, 0, ANALOG_SUB_REAL);
+                               }
+                               i->ss_astchan = chan;
+                               if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) {
+                                       ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
+                                       res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
+                                       if (res < 0)
+                                               ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+                                       ast_hangup(chan);
+                               } else if (!chan) {
+                                       ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", i->channel);
+                               }
+                               break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to handle ring/answer with signalling %s on channel %d\n", analog_sigtype_to_str(i->sig), i->channel);
+                       res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_CONGESTION);
+                       if (res < 0)
+                                       ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
+                       return -1;
+               }
+               break;
+       case ANALOG_EVENT_NOALARM:
+               i->inalarm = 0;
+               if (!i->unknown_alarm) {
+                       ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel);
+                       manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
+                                     "Channel: %d\r\n", i->channel);
+               } else {
+                       i->unknown_alarm = 0;
+               }
+               break;
+       case ANALOG_EVENT_ALARM:
+               i->inalarm = 1;
+#if 0
+               res = get_alarms(i);
+               handle_alarms(i, res);  
+#endif
+               /* fall thru intentionally */
+       case ANALOG_EVENT_ONHOOK:
+               /* Back on hook.  Hang up. */
+               switch (i->sig) {
+               case ANALOG_SIG_FXOLS:
+               case ANALOG_SIG_FXOGS:
+               case ANALOG_SIG_FEATD:
+               case ANALOG_SIG_FEATDMF:
+               case ANALOG_SIG_FEATDMF_TA:
+               case ANALOG_SIG_E911:
+               case ANALOG_SIG_FGC_CAMA:
+               case ANALOG_SIG_FGC_CAMAMF:
+               case ANALOG_SIG_FEATB:
+               case ANALOG_SIG_EM:
+               case ANALOG_SIG_EM_E1:
+               case ANALOG_SIG_EMWINK:
+               case ANALOG_SIG_SF_FEATD:
+               case ANALOG_SIG_SF_FEATDMF:
+               case ANALOG_SIG_SF_FEATB:
+               case ANALOG_SIG_SF:
+               case ANALOG_SIG_SFWINK:
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSGS:
+               case ANALOG_SIG_FXSKS:
+                       analog_set_echocanceller(i, 0);
+                       res = analog_play_tone(i, ANALOG_SUB_REAL, -1);
+                       analog_on_hook(i);
+                       break;
+               case ANALOG_SIG_FXOKS:
+                       analog_set_echocanceller(i, 0);
+                       /* Diddle the battery for the zhone */
+#ifdef ZHONE_HACK
+                       analog_off_hook(i);
+                       usleep(1);
+#endif
+                       res = analog_play_tone(i, ANALOG_SUB_REAL, -1);
+                       analog_on_hook(i);
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", analog_sigtype_to_str(i->sig), i->channel);
+                       res = analog_play_tone(i, ANALOG_SUB_REAL, -1);
+                       return -1;
+               }
+               break;
+       case ANALOG_EVENT_POLARITY:
+               switch (i->sig) {
+               case ANALOG_SIG_FXSLS:
+               case ANALOG_SIG_FXSKS:
+               case ANALOG_SIG_FXSGS:
+                       /* We have already got a PR before the channel was
+                          created, but it wasn't handled. We need polarity
+                          to be REV for remote hangup detection to work.
+                          At least in Spain */
+                       if (i->hanguponpolarityswitch)
+                               i->polarity = POLARITY_REV;
+
+                       if (i->cid_start == ANALOG_CID_START_POLARITY || i->cid_start == ANALOG_CID_START_POLARITY_IN) {
+                               i->polarity = POLARITY_REV;
+                               ast_verbose(VERBOSE_PREFIX_2 "Starting post polarity "
+                                           "CID detection on channel %d\n",
+                                           i->channel);
+                               chan = analog_new_ast_channel(i, AST_STATE_PRERING, 0, ANALOG_SUB_REAL);
+                               i->ss_astchan = chan;
+                               if (chan && ast_pthread_create(&threadid, &attr, __analog_ss_thread, i)) {
+                                       ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
+                               }
+                       }
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "handle_init_event detected "
+                               "polarity reversal on non-FXO (ANALOG_SIG_FXS) "
+                               "interface %d\n", i->channel);
+               }
+               break;
+       case ANALOG_EVENT_NEONMWI_ACTIVE:
+               analog_handle_notify_message(NULL, i, -1, ANALOG_EVENT_NEONMWI_ACTIVE);
+               break;
+       case ANALOG_EVENT_NEONMWI_INACTIVE:
+               analog_handle_notify_message(NULL, i, -1, ANALOG_EVENT_NEONMWI_INACTIVE);
+               break;
+       }
+       pthread_attr_destroy(&attr);
+       return 0;
+}
+
+
+struct analog_pvt * analog_new(enum analog_sigtype signallingtype, struct analog_callback *c, void *private_data)
+{
+       struct analog_pvt *p;
+
+       p = ast_calloc(1, sizeof(*p));
+
+       if (!p)
+               return p;
+
+       p->calls = c;
+       p->outsigmod = ANALOG_SIG_NONE;
+       p->sig = signallingtype;
+       p->chan_pvt = private_data;
+
+       /* Some defaults for values */
+       p->sendcalleridafter = 1;
+       p->cid_start = ANALOG_CID_START_RING;
+       p->cid_signalling = CID_SIG_BELL;
+       /* Sub real is assumed to always be alloc'd */
+       p->subs[ANALOG_SUB_REAL].allocd = 1;
+
+       return p;
+}
+
+int analog_config_complete(struct analog_pvt *p)
+{
+       /* No call waiting on non FXS channels */
+       if ((p->sig != ANALOG_SIG_FXOKS) && (p->sig != ANALOG_SIG_FXOLS) && (p->sig != ANALOG_SIG_FXOGS))
+               p->permcallwaiting = 0;
+
+       p->callwaiting = p->permcallwaiting;
+
+       return 0;
+}
+
+void analog_free(struct analog_pvt *p)
+{
+       free(p);
+}
+
+/* called while dahdi_pvt is locked in dahdi_fixup */
+int analog_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, void *newp)
+{
+       struct analog_pvt *new_pvt = newp;
+       int x;
+       ast_debug(1, "New owner for channel %d is %s\n", new_pvt->channel, newchan->name);
+       if (new_pvt->owner == oldchan) {
+               new_pvt->owner = newchan;
+       }
+       for (x = 0; x < 3; x++)
+               if (new_pvt->subs[x].owner == oldchan) {
+                       new_pvt->subs[x].owner = newchan;
+               }
+
+       analog_update_conf(new_pvt);
+       return 0;
+}
diff --git a/channels/sig_analog.h b/channels/sig_analog.h
new file mode 100644 (file)
index 0000000..91e1543
--- /dev/null
@@ -0,0 +1,329 @@
+#ifndef _SIG_ANALOG_H
+#define _SIG_ANALOG_H
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2009, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Interface header for analog signaling module
+ *
+ * \author Matthew Fredrickson <creslin@digium.com>
+ */
+
+#include "asterisk/channel.h"
+#include "asterisk/frame.h"
+
+/* Signalling types supported */
+enum analog_sigtype {
+       ANALOG_SIG_NONE = -1,
+       ANALOG_SIG_FXOLS = 1,
+       ANALOG_SIG_FXOKS,
+       ANALOG_SIG_FXOGS,
+       ANALOG_SIG_FXSLS,
+       ANALOG_SIG_FXSKS,
+       ANALOG_SIG_FXSGS,
+       ANALOG_SIG_EMWINK,
+       ANALOG_SIG_EM,
+       ANALOG_SIG_EM_E1,
+       ANALOG_SIG_FEATD,
+       ANALOG_SIG_FEATDMF,
+       ANALOG_SIG_E911,
+       ANALOG_SIG_FGC_CAMA,
+       ANALOG_SIG_FGC_CAMAMF,
+       ANALOG_SIG_FEATB,
+       ANALOG_SIG_SFWINK,
+       ANALOG_SIG_SF,
+       ANALOG_SIG_SF_FEATD,
+       ANALOG_SIG_SF_FEATDMF,
+       ANALOG_SIG_FEATDMF_TA,
+       ANALOG_SIG_SF_FEATB,
+};
+
+enum analog_tone {
+       ANALOG_TONE_RINGTONE = 0,
+       ANALOG_TONE_STUTTER,
+       ANALOG_TONE_CONGESTION,
+       ANALOG_TONE_DIALTONE,
+       ANALOG_TONE_DIALRECALL,
+       ANALOG_TONE_INFO,
+};
+
+enum analog_event {
+       ANALOG_EVENT_NONE = 0,
+       ANALOG_EVENT_DIALCOMPLETE,
+       ANALOG_EVENT_WINKFLASH,
+       ANALOG_EVENT_ONHOOK,
+       ANALOG_EVENT_RINGOFFHOOK,
+       ANALOG_EVENT_ALARM,
+       ANALOG_EVENT_NOALARM,
+       ANALOG_EVENT_HOOKCOMPLETE,
+       ANALOG_EVENT_POLARITY,
+       ANALOG_EVENT_RINGERON,
+       ANALOG_EVENT_RINGEROFF,
+       ANALOG_EVENT_RINGBEGIN,
+       ANALOG_EVENT_PULSE_START,
+       ANALOG_EVENT_ERROR,
+       ANALOG_EVENT_NEONMWI_ACTIVE,
+       ANALOG_EVENT_NEONMWI_INACTIVE,
+};
+
+enum analog_sub {
+       ANALOG_SUB_REAL = 0,                    /*!< Active call */
+       ANALOG_SUB_CALLWAIT,                    /*!< Call-Waiting call on hold */
+       ANALOG_SUB_THREEWAY,                    /*!< Three-way call */
+};
+
+enum analog_dsp_digitmode {
+       ANALOG_DIGITMODE_DTMF = 1,
+       ANALOG_DIGITMODE_MF,
+};
+
+enum analog_cid_start {
+       ANALOG_CID_START_POLARITY = 1,
+       ANALOG_CID_START_POLARITY_IN,
+       ANALOG_CID_START_RING,
+};
+
+#define ANALOG_MAX_CID 300
+
+enum dialop {
+       ANALOG_DIAL_OP_REPLACE = 2,
+};
+
+
+struct analog_dialoperation {
+       enum dialop op;
+       char dialstr[256];
+};
+
+struct analog_callback {
+       /* Unlock the private in the signalling private structure.  This is used for three way calling madness. */
+       void (* const unlock_private)(void *pvt);
+       /* Lock the private in the signalling private structure.  ... */
+       void (* const lock_private)(void *pvt);
+       /* Function which is called back to handle any other DTMF up events that are received.  Called by analog_handle_event.  Why is this
+        * important to use, instead of just directly using events received before they are passed into the library?  Because sometimes,
+        * (CWCID) the library absorbs DTMF events received. */
+       void (* const handle_dtmfup)(void *pvt, struct ast_channel *ast, enum analog_sub analog_index, struct ast_frame **dest);
+
+       int (* const get_event)(void *pvt);
+       int (* const wait_event)(void *pvt);
+       int (* const is_off_hook)(void *pvt);
+       int (* const is_dialing)(void *pvt, enum analog_sub sub);
+       /* Start a trunk type signalling protocol (everything except phone ports basically */
+       int (* const start)(void *pvt);
+       int (* const ring)(void *pvt);
+       int (* const flash)(void *pvt);
+       /*! \brief Set channel on hook */
+       int (* const on_hook)(void *pvt);
+       /*! \brief Set channel off hook */
+       int (* const off_hook)(void *pvt);
+       /* We're assuming that we're going to only wink on ANALOG_SUB_REAL - even though in the code there's an argument to the index
+        * function */
+       int (* const wink)(void *pvt, enum analog_sub sub);
+       int (* const dial_digits)(void *pvt, enum analog_sub sub, struct analog_dialoperation *dop);
+       int (* const send_fsk)(void *pvt, struct ast_channel *ast, char *fsk);
+       int (* const play_tone)(void *pvt, enum analog_sub sub, enum analog_tone tone);
+
+       int (* const set_echocanceller)(void *pvt, int enable);
+       int (* const train_echocanceller)(void *pvt);
+       int (* const dsp_set_digitmode)(void *pvt, enum analog_dsp_digitmode mode);
+       int (* const dsp_reset_and_flush_digits)(void *pvt);
+       int (* const send_callerid)(void *pvt, int cwcid, struct ast_callerid *cid);
+       /* Returns 0 if CID received.  Returns 1 if event received, and -1 if error.  name and num are size ANALOG_MAX_CID */
+       int (* const get_callerid)(void *pvt, char *name, char *num, enum analog_event *ev, size_t timeout);
+       /* Start CID detection */
+       int (* const start_cid_detect)(void *pvt, int cid_signalling);
+       /* Stop CID detection */
+       int (* const stop_cid_detect)(void *pvt);
+
+       /* Play the CAS callwait tone on the REAL sub, then repeat after 10 seconds, and then stop */
+       int (* const callwait)(void *pvt);
+       /* Stop playing any CAS call waiting announcement tones that might be running on the REAL sub */
+       int (* const stop_callwait)(void *pvt);
+
+       /* Bearer control related (non signalling) callbacks */
+       int (* const allocate_sub)(void *pvt, enum analog_sub sub);
+       int (* const unallocate_sub)(void *pvt, enum analog_sub sub);
+       /*! This function is for swapping of the owners with the underlying subs.  Typically it means you need to change the fds
+        * of the new owner to be the fds of the sub specified, for each of the two subs given */
+       void (* const swap_subs)(void *pvt, enum analog_sub a, struct ast_channel *new_a_owner, enum analog_sub b, struct ast_channel *new_b_owner);
+       struct ast_channel * (* const new_ast_channel)(void *pvt, int state, int startpbx, enum analog_sub sub);
+
+       /* Add the given sub to a conference */
+       int (* const conf_add)(void *pvt, enum analog_sub sub);
+       /* Delete the given sub from any conference that might be running on the channels */
+       int (* const conf_del)(void *pvt, enum analog_sub sub);
+
+       /* If you would like to do any optimizations after the conference members have been added and removed,
+        * you can do so here */
+       int (* const complete_conference_update)(void *pvt, int needconf);
+
+       /* This is called when there are no more subchannels on the given private that are left up,
+        * for any cleanup or whatever else you would like to do.  Called from analog_hangup() */
+       void (* const all_subchannels_hungup)(void *pvt);
+
+       int (* const has_voicemail)(void *pvt);
+       int (* const check_for_conference)(void *pvt);
+       void (* const handle_notify_message)(struct ast_channel *chan, void *pvt, int cid_flags, int neon_mwievent);
+
+       /* callbacks for increasing and decreasing ss_thread_count, will handle locking and condition signal */
+       void (* const increase_ss_count)(void);
+       void (* const decrease_ss_count)(void);
+};
+
+
+
+#define READ_SIZE 160
+
+struct analog_subchannel {
+       struct ast_channel *owner;
+       struct ast_frame f;             /*!< One frame for each channel.  How did this ever work before? */
+       unsigned int needcallerid:1;
+       unsigned int inthreeway:1;
+       /* Have we allocated a subchannel yet or not */
+       unsigned int allocd:1;
+};
+
+struct analog_pvt {
+       /* Analog signalling type used in this private */
+       enum analog_sigtype sig;
+       /* To contain the private structure passed into the channel callbacks */
+       void *chan_pvt;
+       /* Callbacks for various functions needed by the analog API */
+       struct analog_callback *calls;
+       /* All members after this are giong to be transient, and most will probably change */
+       struct ast_channel *owner;                      /*!< Our current active owner (if applicable) */
+
+       struct analog_subchannel subs[3];                       /*!< Sub-channels */
+       struct analog_dialoperation dop;
+
+       /* XXX: Option Variables - Set by allocator of private structure */
+       unsigned int answeronpolarityswitch:1;
+       unsigned int callreturn:1;
+       unsigned int cancallforward:1;
+       unsigned int canpark:1;
+       unsigned int dahditrcallerid:1;                 /*!< should we use the callerid from incoming call on dahdi transfer or not */
+       unsigned int hanguponpolarityswitch:1;
+       unsigned int immediate:1;
+       unsigned int permcallwaiting:1;
+       unsigned int permhidecallerid:1;                /*!< Whether to hide our outgoing caller ID or not */
+       unsigned int pulse:1;
+       unsigned int threewaycalling:1;
+       unsigned int transfer:1;
+       unsigned int transfertobusy:1;                  /*!< allow flash-transfers to busy channels */
+       unsigned int use_callerid:1;                    /*!< Whether or not to use caller id on this channel */
+
+       /* Not used for anything but log messages.  Could be just the TCID */
+       int channel;                                    /*!< Channel Number or CRV */
+       enum analog_sigtype outsigmod;
+       int echotraining;
+       int cid_signalling;                             /*!< Asterisk callerid type we're using */
+       int polarityonanswerdelay;
+       int stripmsd;
+       enum analog_cid_start cid_start;
+       /* Number of rings to wait to send callerid on FXS.  Set to 1 for US */
+       int sendcalleridafter;
+       int callwaitingcallerid;
+       char mohsuggest[MAX_MUSICCLASS];
+       char cid_num[AST_MAX_EXTENSION];
+       char cid_name[AST_MAX_EXTENSION];
+
+
+       /* XXX: All variables after this are internal */
+       unsigned int callwaiting:1;
+       unsigned int dialednone:1;
+       unsigned int dialing:1;
+       unsigned int dnd:1;
+       unsigned int echobreak:1;
+       unsigned int hidecallerid:1;
+       unsigned int outgoing:1;
+       unsigned int pulsedial:1;                       /*!< whether a pulse dial phone is detected */
+
+       char callwait_num[AST_MAX_EXTENSION];
+       char callwait_name[AST_MAX_EXTENSION];
+       char lastcid_num[AST_MAX_EXTENSION];
+       char lastcid_name[AST_MAX_EXTENSION];
+       struct ast_callerid cid;
+       int cidrings;                                   /*!< Which ring to deliver CID on */
+       char echorest[20];
+       int polarity;
+       struct timeval polaritydelaytv;
+       char dialdest[256];
+       time_t guardtime;                               /*!< Must wait this much time before using for new call */
+       struct timeval flashtime;                       /*!< Last flash-hook time */
+       int whichwink;                                  /*!< SIG_FEATDMF_TA Which wink are we on? */
+       char finaldial[64];
+       char *origcid_num;                              /*!< malloced original callerid */
+       char *origcid_name;                             /*!< malloced original callerid */
+       char call_forward[AST_MAX_EXTENSION];
+
+       /* Ast channel to pass to __ss_analog_thread */
+       void *ss_astchan;
+
+       /* All variables after this are definitely going to be audited */
+       unsigned int inalarm:1; //
+       unsigned int unknown_alarm:1;//
+
+       int callwaitcas;
+
+#if 0
+       int ringt; //
+       int ringt_base;
+#endif
+};
+
+struct analog_pvt * analog_new(enum analog_sigtype signallingtype, struct analog_callback *c, void *private_data);
+
+void analog_free(struct analog_pvt *p);
+
+int analog_call(struct analog_pvt *p, struct ast_channel *ast, char *rdest, int timeout);
+
+int analog_hangup(struct analog_pvt *p, struct ast_channel *ast);
+
+int analog_answer(struct analog_pvt *p, struct ast_channel *ast);
+
+struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast);
+
+struct ast_channel * analog_request(struct analog_pvt *p, int *callwait);
+
+int analog_available(struct analog_pvt *p, int channelmatch, ast_group_t groupmatch, int *busy, int *channelmatched, int *groupmatched);
+
+int analog_handle_init_event(struct analog_pvt *i, int event);
+
+int analog_config_complete(struct analog_pvt *p);
+
+void analog_handle_dtmfup(struct analog_pvt *p, struct ast_channel *ast, enum analog_sub index, struct ast_frame **dest);
+
+enum analog_cid_start analog_str_to_cidstart(const char *value);
+
+const char *analog_cidstart_to_str(enum analog_cid_start cid_start);
+
+enum analog_sigtype analog_str_to_sigtype(const char *name);
+
+const char *analog_sigtype_to_str(enum analog_sigtype sigtype);
+
+unsigned int analog_str_to_cidtype(const char *name);
+
+const char *analog_cidtype_to_str(unsigned int cid_type);
+
+int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *ast);
+
+int analog_fixup(struct ast_channel *oldchan, struct ast_channel *newchan, void *newp);
+
+#endif /* _SIG_ANSLOG_H */