memory leaks: Memory leak cleanup patch by Corey Farrell (second set)
[asterisk/asterisk.git] / channels / chan_dahdi.c
index 9f2dda2..b682994 100644 (file)
@@ -62,13 +62,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #else
 #include <sys/signal.h>
 #endif
-#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <math.h>
-#include <ctype.h>
 
-#include <dahdi/user.h>
-#include <dahdi/tonezone.h>
 #include "sig_analog.h"
 /* Analog signaling is currently still present in chan_dahdi for use with
  * radio. Sig_analog does not currently handle any radio operations. If
@@ -90,11 +86,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 #endif /* defined(HAVE_SS7) */
 
-#ifdef HAVE_OPENR2
+#if defined(HAVE_OPENR2)
 /* put this here until sig_mfcr2 comes along */
 #define SIG_MFCR2_MAX_CHANNELS 672             /*!< No more than a DS3 per trunk group */
-#include <openr2.h>
-#endif
+#endif /* defined(HAVE_OPENR2) */
 
 #include "asterisk/lock.h"
 #include "asterisk/channel.h"
@@ -107,8 +102,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/callerid.h"
 #include "asterisk/adsi.h"
 #include "asterisk/cli.h"
-#include "asterisk/cdr.h"
-#include "asterisk/cel.h"
+#include "asterisk/pickup.h"
 #include "asterisk/features.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/say.h"
@@ -125,11 +119,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/abstract_jb.h"
 #include "asterisk/smdi.h"
 #include "asterisk/astobj.h"
-#include "asterisk/event.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/paths.h"
 #include "asterisk/ccss.h"
 #include "asterisk/data.h"
+#include "asterisk/features_config.h"
+#include "asterisk/bridge.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/parking.h"
+#include "chan_dahdi.h"
+#include "dahdi/bridge_native_dahdi.h"
 
 /*** DOCUMENTATION
        <application name="DAHDISendKeypadFacility" language="en_US">
@@ -295,6 +294,85 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                        <para>Similar to the CLI command "pri show spans".</para>
                </description>
        </manager>
+       <managerEvent language="en_US" name="AlarmClear">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when an alarm is cleared on a DAHDI channel.</synopsis>
+                       <syntax>
+                               <parameter name="DAHDIChannel">
+                                       <para>The DAHDI channel on which the alarm was cleared.</para>
+                                       <note><para>This is not an Asterisk channel identifier.</para></note>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="SpanAlarmClear">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when an alarm is cleared on a DAHDI span.</synopsis>
+                       <syntax>
+                               <parameter name="Span">
+                                       <para>The span on which the alarm was cleared.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="DNDState">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when the Do Not Disturb state is changed on a DAHDI channel.</synopsis>
+                       <syntax>
+                               <parameter name="DAHDIChannel">
+                                       <para>The DAHDI channel on which DND status changed.</para>
+                                       <note><para>This is not an Asterisk channel identifier.</para></note>
+                               </parameter>
+                               <parameter name="Status">
+                                       <enumlist>
+                                               <enum name="enabled"/>
+                                               <enum name="disabled"/>
+                                       </enumlist>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="Alarm">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when an alarm is set on a DAHDI channel.</synopsis>
+                       <syntax>
+                               <parameter name="DAHDIChannel">
+                                       <para>The channel on which the alarm occurred.</para>
+                                       <note><para>This is not an Asterisk channel identifier.</para></note>
+                               </parameter>
+                               <parameter name="Alarm">
+                                       <para>A textual description of the alarm that occurred.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="SpanAlarm">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when an alarm is set on a DAHDI span.</synopsis>
+                       <syntax>
+                               <parameter name="Span">
+                                       <para>The span on which the alarm occurred.</para>
+                               </parameter>
+                               <parameter name="Alarm">
+                                       <para>A textual description of the alarm that occurred.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="DAHDIChannel">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when a DAHDI channel is created or an underlying technology is associated with a DAHDI channel.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="DAHDISpan">
+                                       <para>The DAHDI span associated with this channel.</para>
+                               </parameter>
+                               <parameter name="DAHDIChannel">
+                                       <para>The DAHDI channel associated with this channel.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
  ***/
 
 #define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */
@@ -342,7 +420,7 @@ static struct ast_jb_conf global_jbconf;
 /*! \brief Signaling types that need to use MF detection should be placed in this macro */
 #define NEED_MFDETECT(p) (((p)->sig == SIG_FEATDMF) || ((p)->sig == SIG_FEATDMF_TA) || ((p)->sig == SIG_E911) || ((p)->sig == SIG_FGC_CAMA) || ((p)->sig == SIG_FGC_CAMAMF) || ((p)->sig == SIG_FEATB))
 
-static const char tdesc[] = "DAHDI Telephony Driver"
+static const char tdesc[] = "DAHDI Telephony"
 #if defined(HAVE_PRI) || defined(HAVE_SS7) || defined(HAVE_OPENR2)
        " w/"
        #if defined(HAVE_PRI)
@@ -365,33 +443,6 @@ static const char tdesc[] = "DAHDI Telephony Driver"
 
 static const char config[] = "chan_dahdi.conf";
 
-#define SIG_EM         DAHDI_SIG_EM
-#define SIG_EMWINK     (0x0100000 | DAHDI_SIG_EM)
-#define SIG_FEATD      (0x0200000 | DAHDI_SIG_EM)
-#define        SIG_FEATDMF     (0x0400000 | DAHDI_SIG_EM)
-#define        SIG_FEATB       (0x0800000 | DAHDI_SIG_EM)
-#define        SIG_E911        (0x1000000 | DAHDI_SIG_EM)
-#define        SIG_FEATDMF_TA  (0x2000000 | DAHDI_SIG_EM)
-#define        SIG_FGC_CAMA    (0x4000000 | DAHDI_SIG_EM)
-#define        SIG_FGC_CAMAMF  (0x8000000 | DAHDI_SIG_EM)
-#define SIG_FXSLS      DAHDI_SIG_FXSLS
-#define SIG_FXSGS      DAHDI_SIG_FXSGS
-#define SIG_FXSKS      DAHDI_SIG_FXSKS
-#define SIG_FXOLS      DAHDI_SIG_FXOLS
-#define SIG_FXOGS      DAHDI_SIG_FXOGS
-#define SIG_FXOKS      DAHDI_SIG_FXOKS
-#define SIG_PRI                DAHDI_SIG_CLEAR
-#define SIG_BRI                (0x2000000 | DAHDI_SIG_CLEAR)
-#define SIG_BRI_PTMP   (0X4000000 | DAHDI_SIG_CLEAR)
-#define SIG_SS7                (0x1000000 | DAHDI_SIG_CLEAR)
-#define SIG_MFCR2      DAHDI_SIG_CAS
-#define        SIG_SF          DAHDI_SIG_SF
-#define SIG_SFWINK     (0x0100000 | DAHDI_SIG_SF)
-#define SIG_SF_FEATD   (0x0200000 | DAHDI_SIG_SF)
-#define        SIG_SF_FEATDMF  (0x0400000 | DAHDI_SIG_SF)
-#define        SIG_SF_FEATB    (0x0800000 | DAHDI_SIG_SF)
-#define SIG_EM_E1      DAHDI_SIG_EM_E1
-
 #ifdef LOTS_OF_SPANS
 #define NUM_SPANS      DAHDI_MAX_SPANS
 #else
@@ -409,6 +460,8 @@ static const char config[] = "chan_dahdi.conf";
 static int num_cadence = 4;
 static int user_has_defined_cadences = 0;
 
+static int has_pseudo;
+
 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 */
@@ -456,7 +509,7 @@ static int mwilevel = 512;
 static int dtmfcid_level = 256;
 
 #define REPORT_CHANNEL_ALARMS 1
-#define REPORT_SPAN_ALARMS    2 
+#define REPORT_SPAN_ALARMS    2
 static int report_alarms = REPORT_CHANNEL_ALARMS;
 
 #ifdef HAVE_PRI
@@ -498,11 +551,9 @@ static int num_restart_pending = 0;
 
 static int restart_monitor(void);
 
-static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
-
 static int dahdi_sendtext(struct ast_channel *c, const char *text);
 
-static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *msg)
+static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 {
        /* This module does not handle MWI in an event-based manner.  However, it
         * subscribes to MWI for each mailbox that is configured so that the core
@@ -545,8 +596,6 @@ static inline int dahdi_wait_event(int fd)
 #define DEFAULT_RINGT                                  ((8000 * 8) / READ_SIZE) /*!< 8,000 ms */
 #define DEFAULT_DIALTONE_DETECT_TIMEOUT ((10000 * 8) / READ_SIZE) /*!< 10,000 ms */
 
-struct dahdi_pvt;
-
 /*!
  * \brief Configured ring timeout base.
  * \note Value computed from "ringtimeout" read in from chan_dahdi.conf if it exists.
@@ -571,14 +620,6 @@ static int cur_defaultdpc = -1;
 #endif /* defined(HAVE_SS7) */
 
 #ifdef HAVE_OPENR2
-struct dahdi_mfcr2 {
-       pthread_t r2master;                    /*!< Thread of master */
-       openr2_context_t *protocol_context;    /*!< OpenR2 context handle */
-       struct dahdi_pvt *pvts[SIG_MFCR2_MAX_CHANNELS];     /*!< Member channel pvt structs */
-       int numchans;                          /*!< Number of channels in this R2 block */
-       int monitored_count;                   /*!< Number of channels being monitored */
-};
-
 struct dahdi_mfcr2_conf {
        openr2_variant_t variant;
        int mfback_timeout;
@@ -613,6 +654,15 @@ struct dahdi_mfcr2_conf {
        openr2_calling_party_category_t category;
 };
 
+/* MFC-R2 pseudo-link structure */
+struct dahdi_mfcr2 {
+       pthread_t r2master;                    /*!< Thread of master */
+       openr2_context_t *protocol_context;    /*!< OpenR2 context handle */
+       struct dahdi_pvt *pvts[SIG_MFCR2_MAX_CHANNELS];     /*!< Member channel pvt structs */
+       int numchans;                          /*!< Number of channels in this R2 block */
+       struct dahdi_mfcr2_conf conf;         /*!< Configuration used to setup this pseudo-link */
+};
+
 /* malloc'd array of malloc'd r2links */
 static struct dahdi_mfcr2 **r2links;
 /* how many r2links have been malloc'd */
@@ -641,644 +691,16 @@ static const char dahdi_pri_cc_type[] = "DAHDI/PRI";
 struct dahdi_pri;
 #endif
 
-#define SUB_REAL       0                       /*!< Active call */
-#define SUB_CALLWAIT   1                       /*!< Call-Waiting call on hold */
-#define SUB_THREEWAY   2                       /*!< Three-way call */
-
 /* Polarity states */
 #define POLARITY_IDLE   0
 #define POLARITY_REV    1
 
-
-struct distRingData {
-       int ring[3];
-       int range;
-};
-struct ringContextData {
-       char contextData[AST_MAX_CONTEXT];
-};
-struct dahdi_distRings {
-       struct distRingData ringnum[3];
-       struct ringContextData ringContext[3];
-};
-
-static const char * const subnames[] = {
+const char * const subnames[] = {
        "Real",
        "Callwait",
        "Threeway"
 };
 
-struct dahdi_subchannel {
-       int dfd;
-       struct ast_channel *owner;
-       int chan;
-       short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE];
-       struct ast_frame f;             /*!< One frame for each channel.  How did this ever work before? */
-       unsigned int needringing:1;
-       unsigned int needbusy:1;
-       unsigned int needcongestion:1;
-       unsigned int needanswer:1;
-       unsigned int needflash:1;
-       unsigned int needhold:1;
-       unsigned int needunhold:1;
-       unsigned int linear:1;
-       unsigned int inthreeway:1;
-       struct dahdi_confinfo curconf;
-};
-
-#define CONF_USER_REAL         (1 << 0)
-#define CONF_USER_THIRDCALL    (1 << 1)
-
-#define MAX_SLAVES     4
-
-/* States for sending MWI message
- * First three states are required for send Ring Pulse Alert Signal
- */
-typedef enum {
-       MWI_SEND_NULL = 0,
-       MWI_SEND_SA,
-       MWI_SEND_SA_WAIT,
-       MWI_SEND_PAUSE,
-       MWI_SEND_SPILL,
-       MWI_SEND_CLEANUP,
-       MWI_SEND_DONE,
-} mwisend_states;
-
-struct mwisend_info {
-       struct  timeval pause;
-       mwisend_states  mwisend_current;
-};
-
-/*! Specify the lists dahdi_pvt can be put in. */
-enum DAHDI_IFLIST {
-       DAHDI_IFLIST_NONE,      /*!< The dahdi_pvt is not in any list. */
-       DAHDI_IFLIST_MAIN,      /*!< The dahdi_pvt is in the main interface list */
-#if defined(HAVE_PRI)
-       DAHDI_IFLIST_NO_B_CHAN, /*!< The dahdi_pvt is in a no B channel interface list */
-#endif /* defined(HAVE_PRI) */
-};
-
-struct dahdi_pvt {
-       ast_mutex_t lock;                                       /*!< Channel private 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 */
-
-       struct dahdi_subchannel sub_unused;             /*!< Just a safety precaution */
-       struct dahdi_subchannel subs[3];                        /*!< Sub-channels */
-       struct dahdi_confinfo saveconf;                 /*!< Saved conference info */
-
-       struct dahdi_pvt *slaves[MAX_SLAVES];           /*!< Slave to us (follows our conferencing) */
-       struct dahdi_pvt *master;                               /*!< Master to us (we follow their conferencing) */
-       int inconference;                               /*!< If our real should be in the conference */
-
-       int bufsize;                /*!< Size of the buffers */
-       int buf_no;                                     /*!< Number of buffers */
-       int buf_policy;                         /*!< Buffer policy */
-       int faxbuf_no;              /*!< Number of Fax buffers */
-       int faxbuf_policy;          /*!< Fax buffer policy */
-       int sig;                                        /*!< Signalling style */
-       /*!
-        * \brief Nonzero if the signaling type is sent over a radio.
-        * \note Set to a couple of nonzero values but it is only tested like a boolean.
-        */
-       int radio;
-       int outsigmod;                                  /*!< Outbound Signalling style (modifier) */
-       int oprmode;                                    /*!< "Operator Services" mode */
-       struct dahdi_pvt *oprpeer;                              /*!< "Operator Services" peer tech_pvt ptr */
-       /*! \brief Amount of gain to increase during caller id */
-       float cid_rxgain;
-       /*! \brief Rx gain set by chan_dahdi.conf */
-       float rxgain;
-       /*! \brief Tx gain set by chan_dahdi.conf */
-       float txgain;
-
-       float txdrc; /*!< Dynamic Range Compression factor. a number between 1 and 6ish */
-       float rxdrc;
-       
-       int tonezone;                                   /*!< tone zone for this chan, or -1 for default */
-       enum DAHDI_IFLIST which_iflist; /*!< Which interface list is this structure listed? */
-       struct dahdi_pvt *next;                         /*!< Next channel in list */
-       struct dahdi_pvt *prev;                         /*!< Prev channel in list */
-
-       /* flags */
-
-       /*!
-        * \brief TRUE if ADSI (Analog Display Services Interface) available
-        * \note Set from the "adsi" value read in from chan_dahdi.conf
-        */
-       unsigned int adsi:1;
-       /*!
-        * \brief TRUE if we can use a polarity reversal to mark when an outgoing
-        * call is answered by the remote party.
-        * \note Set from the "answeronpolarityswitch" value read in from chan_dahdi.conf
-        */
-       unsigned int answeronpolarityswitch:1;
-       /*!
-        * \brief TRUE if busy detection is enabled.
-        * (Listens for the beep-beep busy pattern.)
-        * \note Set from the "busydetect" value read in from chan_dahdi.conf
-        */
-       unsigned int busydetect:1;
-       /*!
-        * \brief TRUE if call return is enabled.
-        * (*69, if your dialplan doesn't catch this first)
-        * \note Set from the "callreturn" value read in from chan_dahdi.conf
-        */
-       unsigned int callreturn:1;
-       /*!
-        * \brief TRUE if busy extensions will hear the call-waiting tone
-        * and can use hook-flash to switch between callers.
-        * \note Can be disabled by dialing *70.
-        * \note Initialized with the "callwaiting" value read in from chan_dahdi.conf
-        */
-       unsigned int callwaiting:1;
-       /*!
-        * \brief TRUE if send caller ID for Call Waiting
-        * \note Set from the "callwaitingcallerid" value read in from chan_dahdi.conf
-        */
-       unsigned int callwaitingcallerid:1;
-       /*!
-        * \brief TRUE if support for call forwarding enabled.
-        * Dial *72 to enable call forwarding.
-        * Dial *73 to disable call forwarding.
-        * \note Set from the "cancallforward" value read in from chan_dahdi.conf
-        */
-       unsigned int cancallforward:1;
-       /*!
-        * \brief TRUE if support for call parking is enabled.
-        * \note Set from the "canpark" value read in from chan_dahdi.conf
-        */
-       unsigned int canpark:1;
-       /*! \brief TRUE if to wait for a DTMF digit to confirm answer */
-       unsigned int confirmanswer:1;
-       /*!
-        * \brief TRUE if the channel is to be destroyed on hangup.
-        * (Used by pseudo channels.)
-        */
-       unsigned int destroy:1;
-       unsigned int didtdd:1;                          /*!< flag to say its done it once */
-       /*! \brief TRUE if analog type line dialed no digits in Dial() */
-       unsigned int dialednone:1;
-       /*!
-        * \brief TRUE if in the process of dialing digits or sending something.
-        * \note This is used as a receive squelch for ISDN until connected.
-        */
-       unsigned int dialing:1;
-       /*! \brief TRUE if the transfer capability of the call is digital. */
-       unsigned int digital:1;
-       /*! \brief TRUE if Do-Not-Disturb is enabled, present only for non sig_analog */
-       unsigned int dnd:1;
-       /*! \brief XXX BOOLEAN Purpose??? */
-       unsigned int echobreak:1;
-       /*!
-        * \brief TRUE if echo cancellation enabled when bridged.
-        * \note Initialized with the "echocancelwhenbridged" value read in from chan_dahdi.conf
-        * \note Disabled if the echo canceller is not setup.
-        */
-       unsigned int echocanbridged:1;
-       /*! \brief TRUE if echo cancellation is turned on. */
-       unsigned int echocanon:1;
-       /*! \brief TRUE if a fax tone has already been handled. */
-       unsigned int faxhandled:1;
-       /*! TRUE if dynamic faxbuffers are configured for use, default is OFF */
-       unsigned int usefaxbuffers:1;
-       /*! TRUE while buffer configuration override is in use */
-       unsigned int bufferoverrideinuse:1;
-       /*! \brief TRUE if over a radio and dahdi_read() has been called. */
-       unsigned int firstradio:1;
-       /*!
-        * \brief TRUE if the call will be considered "hung up" on a polarity reversal.
-        * \note Set from the "hanguponpolarityswitch" value read in from chan_dahdi.conf
-        */
-       unsigned int hanguponpolarityswitch:1;
-       /*! \brief TRUE if DTMF detection needs to be done by hardware. */
-       unsigned int hardwaredtmf:1;
-       /*!
-        * \brief TRUE if the outgoing caller ID is blocked/hidden.
-        * \note Caller ID can be disabled by dialing *67.
-        * \note Caller ID can be enabled by dialing *82.
-        * \note Initialized with the "hidecallerid" value read in from chan_dahdi.conf
-        */
-       unsigned int hidecallerid:1;
-       /*!
-        * \brief TRUE if hide just the name not the number for legacy PBX use.
-        * \note Only applies to PRI channels.
-        * \note Set from the "hidecalleridname" value read in from chan_dahdi.conf
-        */
-       unsigned int hidecalleridname:1;
-       /*! \brief TRUE if DTMF detection is disabled. */
-       unsigned int ignoredtmf:1;
-       /*!
-        * \brief TRUE if the channel should be answered immediately
-        * without attempting to gather any digits.
-        * \note Set from the "immediate" value read in from chan_dahdi.conf
-        */
-       unsigned int immediate:1;
-       /*! \brief TRUE if in an alarm condition. */
-       unsigned int inalarm:1;
-       /*! \brief TRUE if TDD in MATE mode */
-       unsigned int mate:1;
-       /*! \brief TRUE if we originated the call leg. */
-       unsigned int outgoing:1;
-       /* unsigned int overlapdial:1;                  unused and potentially confusing */
-       /*!
-        * \brief TRUE if busy extensions will hear the call-waiting tone
-        * and can use hook-flash to switch between callers.
-        * \note Set from the "callwaiting" value read in from chan_dahdi.conf
-        */
-       unsigned int permcallwaiting:1;
-       /*!
-        * \brief TRUE if the outgoing caller ID is blocked/restricted/hidden.
-        * \note Set from the "hidecallerid" value read in from chan_dahdi.conf
-        */
-       unsigned int permhidecallerid:1;
-       /*!
-        * \brief TRUE if PRI congestion/busy indications are sent out-of-band.
-        * \note Set from the "priindication" value read in from chan_dahdi.conf
-        */
-       unsigned int priindication_oob:1;
-       /*!
-        * \brief TRUE if PRI B channels are always exclusively selected.
-        * \note Set from the "priexclusive" value read in from chan_dahdi.conf
-        */
-       unsigned int priexclusive:1;
-       /*!
-        * \brief TRUE if we will pulse dial.
-        * \note Set from the "pulsedial" value read in from chan_dahdi.conf
-        */
-       unsigned int pulse:1;
-       /*! \brief TRUE if a pulsed digit was detected. (Pulse dial phone detected) */
-       unsigned int pulsedial:1;
-       unsigned int restartpending:1;          /*!< flag to ensure counted only once for restart */
-       /*!
-        * \brief TRUE if caller ID is restricted.
-        * \note Set but not used.  Should be deleted.  Redundant with permhidecallerid.
-        * \note Set from the "restrictcid" value read in from chan_dahdi.conf
-        */
-       unsigned int restrictcid:1;
-       /*!
-        * \brief TRUE if three way calling is enabled
-        * \note Set from the "threewaycalling" value read in from chan_dahdi.conf
-        */
-       unsigned int threewaycalling:1;
-       /*!
-        * \brief TRUE if call transfer is enabled
-        * \note For FXS ports (either direct analog or over T1/E1):
-        *   Support flash-hook call transfer
-        * \note For digital ports using ISDN PRI protocols:
-        *   Support switch-side transfer (called 2BCT, RLT or other names)
-        * \note Set from the "transfer" value read in from chan_dahdi.conf
-        */
-       unsigned int transfer:1;
-       /*!
-        * \brief TRUE if caller ID is used on this channel.
-        * \note PRI and SS7 spans will save caller ID from the networking peer.
-        * \note FXS ports will generate the caller ID spill.
-        * \note FXO ports will listen for the caller ID spill.
-        * \note Set from the "usecallerid" value read in from chan_dahdi.conf
-        */
-       unsigned int use_callerid:1;
-       /*!
-        * \brief TRUE if we will use the calling presentation setting
-        * from the Asterisk channel for outgoing calls.
-        * \note Only applies to PRI and SS7 channels.
-        * \note Set from the "usecallingpres" value read in from chan_dahdi.conf
-        */
-       unsigned int use_callingpres:1;
-       /*!
-        * \brief TRUE if distinctive rings are to be detected.
-        * \note For FXO lines
-        * \note Set indirectly from the "usedistinctiveringdetection" value read in from chan_dahdi.conf
-        */
-       unsigned int usedistinctiveringdetection:1;
-       /*!
-        * \brief TRUE if we should use the callerid from incoming call on dahdi transfer.
-        * \note Set from the "useincomingcalleridondahditransfer" value read in from chan_dahdi.conf
-        */
-       unsigned int dahditrcallerid:1;
-       /*!
-        * \brief TRUE if allowed to flash-transfer to busy channels.
-        * \note Set from the "transfertobusy" value read in from chan_dahdi.conf
-        */
-       unsigned int transfertobusy:1;
-       /*!
-        * \brief TRUE if the FXO port monitors for neon type MWI indications from the other end.
-        * \note Set if the "mwimonitor" value read in contains "neon" from chan_dahdi.conf
-        */
-       unsigned int mwimonitor_neon:1;
-       /*!
-        * \brief TRUE if the FXO port monitors for fsk type MWI indications from the other end.
-        * \note Set if the "mwimonitor" value read in contains "fsk" from chan_dahdi.conf
-        */
-       unsigned int mwimonitor_fsk:1;
-       /*!
-        * \brief TRUE if the FXO port monitors for rpas precursor to fsk MWI indications from the other end.
-        * \note RPAS - Ring Pulse Alert Signal
-        * \note Set if the "mwimonitor" value read in contains "rpas" from chan_dahdi.conf
-        */
-       unsigned int mwimonitor_rpas:1;
-       /*! \brief TRUE if an MWI monitor thread is currently active */
-       unsigned int mwimonitoractive:1;
-       /*! \brief TRUE if a MWI message sending thread is active */
-       unsigned int mwisendactive:1;
-       /*!
-        * \brief TRUE if channel is out of reset and ready
-        * \note Set but not used.
-        */
-       unsigned int inservice:1;
-       /*!
-        * \brief TRUE if the channel is locally blocked.
-        * \note Applies to SS7 and MFCR2 channels.
-        */
-       unsigned int locallyblocked:1;
-       /*!
-        * \brief TRUE if the channel is remotely blocked.
-        * \note Applies to SS7 and MFCR2 channels.
-        */
-       unsigned int remotelyblocked:1;
-       /*!
-        * \brief TRUE if the channel alarms will be managed also as Span ones
-        * \note Applies to all channels
-        */
-       unsigned int manages_span_alarms:1;
-
-#if defined(HAVE_PRI)
-       struct sig_pri_span *pri;
-       int logicalspan;
-#endif
-       /*!
-        * \brief TRUE if SMDI (Simplified Message Desk Interface) is enabled
-        * \note Set from the "usesmdi" value read in from chan_dahdi.conf
-        */
-       unsigned int use_smdi:1;
-       struct mwisend_info mwisend_data;
-       /*! \brief The SMDI interface to get SMDI messages from. */
-       struct ast_smdi_interface *smdi_iface;
-
-       /*! \brief Distinctive Ring data */
-       struct dahdi_distRings drings;
-
-       /*!
-        * \brief The configured context for incoming calls.
-        * \note The "context" string read in from chan_dahdi.conf
-        */
-       char context[AST_MAX_CONTEXT];
-       /*! 
-        * \brief A description for the channel configuration
-        * \note The "description" string read in from chan_dahdi.conf
-        */
-       char description[32];
-       /*!
-        * \brief Saved context string.
-        */
-       char defcontext[AST_MAX_CONTEXT];
-       /*! \brief Extension to use in the dialplan. */
-       char exten[AST_MAX_EXTENSION];
-       /*!
-        * \brief Language configured for calls.
-        * \note The "language" string read in from chan_dahdi.conf
-        */
-       char language[MAX_LANGUAGE];
-       /*!
-        * \brief The configured music-on-hold class to use for calls.
-        * \note The "musicclass" or "mohinterpret" or "musiconhold" string read in from chan_dahdi.conf
-        */
-       char mohinterpret[MAX_MUSICCLASS];
-       /*!
-        * \brief Suggested music-on-hold class for peer channel to use for calls.
-        * \note The "mohsuggest" string read in from chan_dahdi.conf
-        */
-       char mohsuggest[MAX_MUSICCLASS];
-       char parkinglot[AST_MAX_EXTENSION]; /*!< Parking lot for this channel */
-#if defined(HAVE_PRI) || defined(HAVE_SS7)
-       /*! \brief Automatic Number Identification number (Alternate PRI caller ID number) */
-       char cid_ani[AST_MAX_EXTENSION];
-#endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */
-       /*! \brief Automatic Number Identification code from PRI */
-       int cid_ani2;
-       /*! \brief Caller ID number from an incoming call. */
-       char cid_num[AST_MAX_EXTENSION];
-       /*!
-        * \brief Caller ID tag from incoming call
-        * \note the "cid_tag" string read in from chan_dahdi.conf
-        */
-       char cid_tag[AST_MAX_EXTENSION];
-       /*! \brief Caller ID Q.931 TON/NPI field values.  Set by PRI. Zero otherwise. */
-       int cid_ton;
-       /*! \brief Caller ID name from an incoming call. */
-       char cid_name[AST_MAX_EXTENSION];
-       /*! \brief Caller ID subaddress from an incoming call. */
-       char cid_subaddr[AST_MAX_EXTENSION];
-       char *origcid_num;                              /*!< malloced original callerid */
-       char *origcid_name;                             /*!< malloced original callerid */
-       /*! \brief Call waiting number. */
-       char callwait_num[AST_MAX_EXTENSION];
-       /*! \brief Call waiting name. */
-       char callwait_name[AST_MAX_EXTENSION];
-       /*! \brief Redirecting Directory Number Information Service (RDNIS) number */
-       char rdnis[AST_MAX_EXTENSION];
-       /*! \brief Dialed Number Identifier */
-       char dnid[AST_MAX_EXTENSION];
-       /*!
-        * \brief Bitmapped groups this belongs to.
-        * \note The "group" bitmapped group string read in from chan_dahdi.conf
-        */
-       ast_group_t group;
-       /*! \brief Default call PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW. */
-       int law_default;
-       /*! \brief Active PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW */
-       int law;
-       int confno;                                     /*!< Our conference */
-       int confusers;                                  /*!< Who is using our conference */
-       int propconfno;                                 /*!< Propagated conference number */
-       /*!
-        * \brief Bitmapped call groups this belongs to.
-        * \note The "callgroup" bitmapped group string read in from chan_dahdi.conf
-        */
-       ast_group_t callgroup;
-       /*!
-        * \brief Bitmapped pickup groups this belongs to.
-        * \note The "pickupgroup" bitmapped group string read in from chan_dahdi.conf
-        */
-       ast_group_t pickupgroup;
-       /*!
-        * \brief Named call groups this belongs to.
-        * \note The "namedcallgroup" string read in from chan_dahdi.conf
-        */
-       struct ast_namedgroups *named_callgroups;
-       /*!
-        * \brief Named pickup groups this belongs to.
-        * \note The "namedpickupgroup" string read in from chan_dahdi.conf
-        */
-       struct ast_namedgroups *named_pickupgroups;
-       /*!
-        * \brief Channel variable list with associated values to set when a channel is created.
-        * \note The "setvar" strings read in from chan_dahdi.conf
-        */
-       struct ast_variable *vars;
-       int channel;                                    /*!< Channel Number */
-       int span;                                       /*!< Span number */
-       time_t guardtime;                               /*!< Must wait this much time before using for new call */
-       int cid_signalling;                             /*!< CID signalling type bell202 or v23 */
-       int cid_start;                                  /*!< CID start indicator, polarity or ring or DTMF without warning event */
-       int dtmfcid_holdoff_state;              /*!< State indicator that allows for line to settle before checking for dtmf energy */
-       struct timeval  dtmfcid_delay;  /*!< Time value used for allow line to settle */
-       int callingpres;                                /*!< The value of calling presentation that we're going to use when placing a PRI call */
-       int callwaitingrepeat;                          /*!< How many samples to wait before repeating call waiting */
-       int cidcwexpire;                                /*!< When to stop waiting for CID/CW CAS response (In samples) */
-       int cid_suppress_expire;                /*!< How many samples to suppress after a CID spill. */
-       /*! \brief Analog caller ID waveform sample buffer */
-       unsigned char *cidspill;
-       /*! \brief Position in the cidspill buffer to send out next. */
-       int cidpos;
-       /*! \brief Length of the cidspill buffer containing samples. */
-       int cidlen;
-       /*! \brief Ring timeout timer?? */
-       int ringt;
-       /*!
-        * \brief Ring timeout base.
-        * \note Value computed indirectly from "ringtimeout" read in from chan_dahdi.conf
-        */
-       int ringt_base;
-       /*!
-        * \brief Number of most significant digits/characters to strip from the dialed number.
-        * \note Feature is deprecated.  Use dialplan logic.
-        * \note The characters are stripped before the PRI TON/NPI prefix
-        * characters are processed.
-        */
-       int stripmsd;
-       /*!
-        * \brief TRUE if Call Waiting (CW) CPE Alert Signal (CAS) is being sent.
-        * \note
-        * After CAS is sent, the call waiting caller id will be sent if the phone
-        * gives a positive reply.
-        */
-       int callwaitcas;
-       /*! \brief Number of call waiting rings. */
-       int callwaitrings;
-       /*! \brief Echo cancel parameters. */
-       struct {
-               struct dahdi_echocanparams head;
-               struct dahdi_echocanparam params[DAHDI_MAX_ECHOCANPARAMS];
-       } echocancel;
-       /*!
-        * \brief Echo training time. 0 = disabled
-        * \note Set from the "echotraining" value read in from chan_dahdi.conf
-        */
-       int echotraining;
-       /*! \brief Filled with 'w'.  XXX Purpose?? */
-       char echorest[20];
-       /*!
-        * \brief Number of times to see "busy" tone before hanging up.
-        * \note Set from the "busycount" value read in from chan_dahdi.conf
-        */
-       int busycount;
-       /*!
-        * \brief Busy cadence pattern description.
-        * \note Set from the "busypattern" value read from chan_dahdi.conf
-        */
-       struct ast_dsp_busy_pattern busy_cadence;
-       /*!
-        * \brief Bitmapped call progress detection flags. CALLPROGRESS_xxx values.
-        * \note Bits set from the "callprogress" and "faxdetect" values read in from chan_dahdi.conf
-        */
-       int callprogress;
-       /*!
-        * \brief Number of milliseconds to wait for dialtone.
-        * \note Set from the "waitfordialtone" value read in from chan_dahdi.conf
-        */
-       int waitfordialtone;
-       /*!
-        * \brief Number of frames to watch for dialtone in incoming calls
-        * \note Set from the "dialtone_detect" value read in from chan_dahdi.conf
-        */
-       int dialtone_detect;
-       int dialtone_scanning_time_elapsed;     /*!< Amount of audio scanned for dialtone, in frames */
-       struct timeval waitingfordt;                    /*!< Time we started waiting for dialtone */
-       struct timeval flashtime;                       /*!< Last flash-hook time */
-       /*! \brief Opaque DSP configuration structure. */
-       struct ast_dsp *dsp;
-       /*! \brief DAHDI dial operation command struct for ioctl() call. */
-       struct dahdi_dialoperation dop;
-       int whichwink;                                  /*!< SIG_FEATDMF_TA Which wink are we on? */
-       /*! \brief Second part of SIG_FEATDMF_TA wink operation. */
-       char finaldial[64];
-       char accountcode[AST_MAX_ACCOUNT_CODE];         /*!< Account code */
-       int amaflags;                                   /*!< AMA Flags */
-       struct tdd_state *tdd;                          /*!< TDD flag */
-       /*! \brief Accumulated call forwarding number. */
-       char call_forward[AST_MAX_EXTENSION];
-       /*!
-        * \brief Voice mailbox location.
-        * \note Set from the "mailbox" string read in from chan_dahdi.conf
-        */
-       char mailbox[AST_MAX_EXTENSION];
-       /*! \brief Opaque event subscription parameters for message waiting indication support. */
-       struct stasis_subscription *mwi_event_sub;
-       /*! \brief Delayed dialing for E911.  Overlap digits for ISDN. */
-       char dialdest[256];
-#ifdef HAVE_DAHDI_LINEREVERSE_VMWI
-       struct dahdi_vmwi_info mwisend_setting;                         /*!< Which VMWI methods to use */
-       unsigned int mwisend_fsk: 1;            /*! Variable for enabling FSK MWI handling in chan_dahdi */
-       unsigned int mwisend_rpas:1;            /*! Variable for enabling Ring Pulse Alert before MWI FSK Spill */
-#endif
-       int distinctivering;                            /*!< Which distinctivering to use */
-       int dtmfrelax;                                  /*!< whether to run in relaxed DTMF mode */
-       /*! \brief Holding place for event injected from outside normal operation. */
-       int fake_event;
-       /*!
-        * \brief Minimal time period (ms) between the answer polarity
-        * switch and hangup polarity switch.
-        */
-       int polarityonanswerdelay;
-       /*! \brief Start delay time if polarityonanswerdelay is nonzero. */
-       struct timeval polaritydelaytv;
-       /*!
-        * \brief Send caller ID on FXS after this many rings. Set to 1 for US.
-        * \note Set from the "sendcalleridafter" value read in from chan_dahdi.conf
-        */
-       int sendcalleridafter;
-       /*! \brief Current line interface polarity. POLARITY_IDLE, POLARITY_REV */
-       int polarity;
-       /*! \brief DSP feature flags: DSP_FEATURE_xxx */
-       int dsp_features;
-#if defined(HAVE_SS7)
-       /*! \brief SS7 control parameters */
-       struct sig_ss7_linkset *ss7;
-#endif /* defined(HAVE_SS7) */
-#ifdef HAVE_OPENR2
-       struct dahdi_mfcr2 *mfcr2;
-       openr2_chan_t *r2chan;
-       openr2_calling_party_category_t mfcr2_recvd_category;
-       openr2_calling_party_category_t mfcr2_category;
-       int mfcr2_dnis_index;
-       int mfcr2_ani_index;
-       int mfcr2call:1;
-       int mfcr2_answer_pending:1;
-       int mfcr2_charge_calls:1;
-       int mfcr2_allow_collect_calls:1;
-       int mfcr2_forced_release:1;
-       int mfcr2_dnis_matched:1;
-       int mfcr2_call_accepted:1;
-       int mfcr2_accept_on_offer:1;
-       int mfcr2_progress_sent:1;
-#endif
-       /*! \brief DTMF digit in progress.  0 when no digit in progress. */
-       char begindigit;
-       /*! \brief TRUE if confrence is muted. */
-       int muting;
-       void *sig_pvt;
-       struct ast_cc_config_params *cc_params;
-       /* DAHDI channel names may differ greatly from the
-        * string that was provided to an app such as Dial. We
-        * need to save the original string passed to dahdi_request
-        * for call completion purposes. This way, we can replicate
-        * the original dialed string later.
-        */
-       char dialstring[AST_CHANNEL_NAME];
-};
-
 #define DATA_EXPORT_DAHDI_PVT(MEMBER)                                  \
        MEMBER(dahdi_pvt, cid_rxgain, AST_DATA_DOUBLE)                  \
        MEMBER(dahdi_pvt, rxgain, AST_DATA_DOUBLE)                      \
@@ -1396,6 +818,18 @@ struct dahdi_chan_conf {
         * \note Set from the "smdiport" string read in from chan_dahdi.conf
         */
        char smdi_port[SMDI_MAX_FILENAME_LEN];
+
+       /*!
+        * \brief Don't create channels below this number
+        * \note by default is 0 (no limit)
+        */
+       int wanted_channels_start;
+
+       /*!
+        * \brief Don't create channels above this number (infinity by default)
+        * \note by default is 0 (special value that means "no limit").
+        */
+       int wanted_channels_end;
 };
 
 /*! returns a new dahdi_chan_conf with default values (by-value) */
@@ -1553,7 +987,6 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
 static int dahdi_devicestate(const char *data);
 static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback);
 
-/* BUGBUG The DAHDI channel driver needs its own native bridge technology. */
 static struct ast_channel_tech dahdi_tech = {
        .type = "DAHDI",
        .description = tdesc,
@@ -1566,7 +999,6 @@ static struct ast_channel_tech dahdi_tech = {
        .answer = dahdi_answer,
        .read = dahdi_read,
        .write = dahdi_write,
-       .bridge = dahdi_bridge,
        .exception = dahdi_exception,
        .indicate = dahdi_indicate,
        .fixup = dahdi_fixup,
@@ -1580,77 +1012,6 @@ static struct ast_channel_tech dahdi_tech = {
 
 #define GET_CHANNEL(p) ((p)->channel)
 
-#define SIG_PRI_LIB_HANDLE_CASES       \
-       SIG_PRI:                                                \
-       case SIG_BRI:                                   \
-       case SIG_BRI_PTMP
-
-/*!
- * \internal
- * \brief Determine if sig_pri handles the signaling.
- * \since 1.8
- *
- * \param signaling Signaling to determine if is for sig_pri.
- *
- * \return TRUE if the signaling is for sig_pri.
- */
-static inline int dahdi_sig_pri_lib_handles(int signaling)
-{
-       int handles;
-
-       switch (signaling) {
-       case SIG_PRI_LIB_HANDLE_CASES:
-               handles = 1;
-               break;
-       default:
-               handles = 0;
-               break;
-       }
-
-       return handles;
-}
-
-static 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;
-}
-
 static enum analog_sigtype dahdisig_to_analogsig(int sig)
 {
        switch (sig) {
@@ -2268,6 +1629,50 @@ static void my_deadlock_avoidance_private(void *pvt)
        DEADLOCK_AVOIDANCE(&p->lock);
 }
 
+static struct ast_manager_event_blob *dahdichannel_to_ami(struct stasis_message *msg)
+{
+       RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
+       struct ast_channel_blob *obj = stasis_message_data(msg);
+       struct ast_json *span, *channel;
+
+       channel_string = ast_manager_build_channel_state_string(obj->snapshot);
+       if (!channel_string) {
+               return NULL;
+       }
+
+       span = ast_json_object_get(obj->blob, "span");
+       channel = ast_json_object_get(obj->blob, "channel");
+
+       return ast_manager_event_blob_create(EVENT_FLAG_CALL, "DAHDIChannel",
+               "%s"
+               "DAHDISpan: %d\r\n"
+               "DAHDIChannel: %s\r\n",
+               ast_str_buffer(channel_string),
+               (unsigned int)ast_json_integer_get(span),
+               ast_json_string_get(channel));
+}
+
+STASIS_MESSAGE_TYPE_DEFN_LOCAL(dahdichannel_type,
+       .to_ami = dahdichannel_to_ami,
+       );
+
+/*! \brief Sends a DAHDIChannel channel blob used to produce DAHDIChannel AMI messages */
+static void publish_dahdichannel(struct ast_channel *chan, int span, const char *dahdi_channel)
+{
+       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+
+       ast_assert(dahdi_channel != NULL);
+
+       blob = ast_json_pack("{s: i, s: s}",
+               "span", span,
+               "channel", dahdi_channel);
+       if (!blob) {
+               return;
+       }
+
+       ast_channel_publish_blob(chan, dahdichannel_type(), blob);
+}
+
 /*!
  * \internal
  * \brief Post an AMI DAHDI channel association event.
@@ -2292,20 +1697,7 @@ static void dahdi_ami_channel_event(struct dahdi_pvt *p, struct ast_channel *cha
                /* Real channel */
                snprintf(ch_name, sizeof(ch_name), "%d", p->channel);
        }
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when a DAHDI channel is created or an underlying technology is associated with a DAHDI channel.</synopsis>
-               </managerEventInstance>
-       ***/
-       ast_manager_event(chan, EVENT_FLAG_CALL, "DAHDIChannel",
-               "Channel: %s\r\n"
-               "Uniqueid: %s\r\n"
-               "DAHDISpan: %d\r\n"
-               "DAHDIChannel: %s\r\n",
-               ast_channel_name(chan),
-               ast_channel_uniqueid(chan),
-               p->span,
-               ch_name);
+       publish_dahdichannel(chan, p->span, ch_name);
 }
 
 #ifdef HAVE_PRI
@@ -2328,14 +1720,14 @@ static void my_ami_channel_event(void *pvt, struct ast_channel *chan)
 #endif
 
 /* linear_mode = 0 - turn linear mode off, >0 - turn linear mode on
-*      returns the last value of the linear setting 
-*/ 
+*      returns the last value of the linear setting
+*/
 static int my_set_linear_mode(void *pvt, enum analog_sub sub, int linear_mode)
 {
        struct dahdi_pvt *p = pvt;
        int oldval;
        int idx = analogsub_to_dahdisub(sub);
-       
+
        dahdi_setlinear(p->subs[idx].dfd, linear_mode);
        oldval = p->subs[idx].linear;
        p->subs[idx].linear = linear_mode ? 1 : 0;
@@ -2363,12 +1755,12 @@ static void my_get_and_handle_alarms(void *pvt)
 
 static void *my_get_sigpvt_bridged_channel(struct ast_channel *chan)
 {
-       struct ast_channel *bridged = ast_bridged_channel(chan);
+       RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(chan), ast_channel_cleanup);
 
        if (bridged && ast_channel_tech(bridged) == &dahdi_tech) {
                struct dahdi_pvt *p = ast_channel_tech_pvt(bridged);
 
-               if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                        return p->sig_pvt;
                }
        }
@@ -3048,17 +2440,14 @@ static int my_is_off_hook(void *pvt)
        return 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);
+               dahdi_ec_enable(p);
        else
-               dahdi_disable_ec(p);
+               dahdi_ec_disable(p);
 
        return 0;
 }
@@ -3257,6 +2646,8 @@ static int sig_pri_tone_to_dahditone(enum sig_pri_tone tone)
 #endif /* defined(HAVE_PRI) */
 
 #if defined(HAVE_PRI)
+static void pri_destroy_span(struct sig_pri_span *pri);
+
 static void my_handle_dchan_exception(struct sig_pri_span *pri, int index)
 {
        int x;
@@ -3284,6 +2675,9 @@ static void my_handle_dchan_exception(struct sig_pri_span *pri, int index)
        case DAHDI_EVENT_NOALARM:
                pri_event_noalarm(pri, index, 0);
                break;
+       case DAHDI_EVENT_REMOVED:
+               pri_destroy_span(pri);
+               break;
        default:
                break;
        }
@@ -3878,8 +3272,7 @@ struct analog_callback analog_callbacks =
 /*! Round robin search locations. */
 static struct dahdi_pvt *round_robin[32];
 
-#define dahdi_get_index(ast, p, nullok)        _dahdi_get_index(ast, p, nullok, __PRETTY_FUNCTION__, __LINE__)
-static int _dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok, const char *fname, unsigned long line)
+int _dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok, const char *fname, unsigned long line)
 {
        int res;
        if (p->subs[SUB_REAL].owner == ast)
@@ -3954,6 +3347,37 @@ static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f)
        }
 }
 
+static void publish_channel_alarm_clear(int channel)
+{
+       RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+       RAII_VAR(struct ast_str *, dahdi_chan, ast_str_create(32), ast_free);
+       if (!dahdi_chan) {
+               return;
+       }
+
+       ast_str_set(&dahdi_chan, 0, "%d", channel);
+       ast_log(LOG_NOTICE, "Alarm cleared on channel DAHDI/%d\n", channel);
+       body = ast_json_pack("{s: s}", "DAHDIChannel", ast_str_buffer(dahdi_chan));
+       if (!body) {
+               return;
+       }
+
+       ast_manager_publish_event("AlarmClear", EVENT_FLAG_SYSTEM, body);
+}
+
+static void publish_span_alarm_clear(int span)
+{
+       RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+
+       ast_log(LOG_NOTICE, "Alarm cleared on span %d\n", span);
+       body = ast_json_pack("{s: i}", "Span", span);
+       if (!body) {
+               return;
+       }
+
+       ast_manager_publish_event("SpanAlarmClear", EVENT_FLAG_SYSTEM, body);
+}
+
 static void handle_clear_alarms(struct dahdi_pvt *p)
 {
 #if defined(HAVE_PRI)
@@ -3963,22 +3387,10 @@ static void handle_clear_alarms(struct dahdi_pvt *p)
 #endif /* defined(HAVE_PRI) */
 
        if (report_alarms & REPORT_CHANNEL_ALARMS) {
-               ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when an alarm is cleared on a DAHDI channel.</synopsis>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", "Channel: %d\r\n", p->channel);
+               publish_channel_alarm_clear(p->channel);
        }
        if (report_alarms & REPORT_SPAN_ALARMS && p->manages_span_alarms) {
-               ast_log(LOG_NOTICE, "Alarm cleared on span %d\n", p->span);
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when an alarm is cleared on a DAHDI span.</synopsis>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_SYSTEM, "SpanAlarmClear", "Span: %d\r\n", p->span);
+               publish_span_alarm_clear(p->span);
        }
 }
 
@@ -4175,7 +3587,6 @@ static void dahdi_r2_on_call_end(openr2_chan_t *r2chan)
        ast_mutex_unlock(&p->lock);
 }
 
-static void dahdi_enable_ec(struct dahdi_pvt *p);
 static void dahdi_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
 {
        struct dahdi_pvt *p = NULL;
@@ -4183,7 +3594,7 @@ static void dahdi_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t
        struct ast_callid *callid = NULL;
        int callid_created = ast_callid_threadstorage_auto(&callid);
        p = openr2_chan_get_client_data(r2chan);
-       dahdi_enable_ec(p);
+       dahdi_ec_enable(p);
        p->mfcr2_call_accepted = 1;
        /* if it's an incoming call ... */
        if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
@@ -4275,6 +3686,7 @@ static void dahdi_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disco
        snprintf(cause_str, sizeof(cause_str), "R2 DISCONNECT (%s)", openr2_proto_get_disconnect_string(cause));
        datalen += strlen(cause_str);
        cause_code = ast_alloca(datalen);
+       memset(cause_code, 0, datalen);
        cause_code->ast_cause = dahdi_r2_cause_to_ast_cause(cause);
        ast_copy_string(cause_code->chan_name, ast_channel_name(p->owner), AST_CHANNEL_NAME);
        ast_copy_string(cause_code->code, cause_str, datalen + 1 - sizeof(*cause_code));
@@ -4969,7 +4381,7 @@ static int reset_conf(struct dahdi_pvt *p)
        return 0;
 }
 
-static int update_conf(struct dahdi_pvt *p)
+void dahdi_conf_update(struct dahdi_pvt *p)
 {
        int needconf = 0;
        int x;
@@ -5022,10 +4434,9 @@ static int update_conf(struct dahdi_pvt *p)
                p->confno = -1;
        }
        ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
-       return 0;
 }
 
-static void dahdi_enable_ec(struct dahdi_pvt *p)
+void dahdi_ec_enable(struct dahdi_pvt *p)
 {
        int res;
        if (!p)
@@ -5097,7 +4508,7 @@ static void dahdi_train_ec(struct dahdi_pvt *p)
        }
 }
 
-static void dahdi_disable_ec(struct dahdi_pvt *p)
+void dahdi_ec_disable(struct dahdi_pvt *p)
 {
        int res;
 
@@ -5115,13 +4526,22 @@ static void dahdi_disable_ec(struct dahdi_pvt *p)
        p->echocanon = 0;
 }
 
+static int set_hwgain(int fd, float gain, int tx_direction)
+{
+       struct dahdi_hwgain hwgain;
+
+       hwgain.newgain = gain * 10.0;
+       hwgain.tx = tx_direction;
+       return ioctl(fd, DAHDI_SET_HWGAIN, &hwgain) < 0;
+}
+
 /* perform a dynamic range compression transform on the given sample */
 static int drc_sample(int sample, float drc)
 {
        float neg;
        float shallow, steep;
        float max = SHRT_MAX;
-       
+
        neg = (sample < 0 ? -1 : 1);
        steep = drc*sample;
        shallow = neg*(max-max/drc)+(float)sample/drc;
@@ -5427,7 +4847,7 @@ static int has_voicemail(struct dahdi_pvt *p)
        }
 
        ast_str_set(&uniqueid, 0, "%s@%s", mailbox, context);
-       mwi_message = stasis_cache_get(ast_mwi_topic_cached(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
+       mwi_message = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(), ast_str_buffer(uniqueid));
 
        if (mwi_message) {
                struct ast_mwi_state *mwi_state = stasis_message_data(mwi_message);
@@ -5579,7 +4999,7 @@ static int dahdi_call(struct ast_channel *ast, const char *rdest, int timeout)
                set_actual_gain(p->subs[SUB_REAL].dfd, 0, 0, p->rxdrc, p->txdrc, p->law);
        } else {
                set_actual_gain(p->subs[SUB_REAL].dfd, p->rxgain, p->txgain, p->rxdrc, p->txdrc, p->law);
-       }       
+       }
 
 #ifdef HAVE_PRI
        if (dahdi_sig_pri_lib_handles(p->sig)) {
@@ -5599,7 +5019,7 @@ static int dahdi_call(struct ast_channel *ast, const char *rdest, int timeout)
 #endif /* defined(HAVE_SS7) */
 
        /* If this is analog signalling we can exit here */
-       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+       if (dahdi_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);
@@ -5950,7 +5370,7 @@ static void destroy_dahdi_pvt(struct dahdi_pvt *pvt)
        }
 
        if (p->sig_pvt) {
-               if (analog_lib_handles(p->sig, 0, 0)) {
+               if (dahdi_analog_lib_handles(p->sig, 0, 0)) {
                        analog_delete(p->sig_pvt);
                }
                switch (p->sig) {
@@ -6353,7 +5773,7 @@ static int dahdi_hangup(struct ast_channel *ast)
 
        ast_mutex_lock(&p->lock);
        p->exten[0] = '\0';
-       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                dahdi_confmute(p, 0);
                restore_gains(p);
                p->ignoredtmf = 0;
@@ -6411,14 +5831,14 @@ static int dahdi_hangup(struct ast_channel *ast)
                sig_pri_hangup(p->sig_pvt, ast);
 
                tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1);
-               dahdi_disable_ec(p);
+               dahdi_ec_disable(p);
 
                x = 0;
                ast_channel_setoption(ast, AST_OPTION_TDD, &x, sizeof(char), 0);
                p->didtdd = 0;/* Probably not used in this mode. Reset anyway. */
 
                p->rdnis[0] = '\0';
-               update_conf(p);
+               dahdi_conf_update(p);
                reset_conf(p);
 
                /* Restore data mode */
@@ -6473,13 +5893,13 @@ static int dahdi_hangup(struct ast_channel *ast)
                sig_ss7_hangup(p->sig_pvt, ast);
 
                tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1);
-               dahdi_disable_ec(p);
+               dahdi_ec_disable(p);
 
                x = 0;
                ast_channel_setoption(ast, AST_OPTION_TDD, &x, sizeof(char), 0);
                p->didtdd = 0;/* Probably not used in this mode. Reset anyway. */
 
-               update_conf(p);
+               dahdi_conf_update(p);
                reset_conf(p);
 
                /* Restore data mode */
@@ -6700,7 +6120,7 @@ static int dahdi_hangup(struct ast_channel *ast)
                        break;
                }
                if (p->sig)
-                       dahdi_disable_ec(p);
+                       dahdi_ec_disable(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);
@@ -6711,7 +6131,7 @@ static int dahdi_hangup(struct ast_channel *ast)
                p->waitingfordt.tv_sec = 0;
                p->dialing = 0;
                p->rdnis[0] = '\0';
-               update_conf(p);
+               dahdi_conf_update(p);
                reset_conf(p);
                /* Restore data mode */
                switch (p->sig) {
@@ -6769,7 +6189,7 @@ static int dahdi_answer(struct ast_channel *ast)
                return 0;
        }
 
-       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                res = analog_answer(p->sig_pvt, ast);
                ast_mutex_unlock(&p->lock);
                return res;
@@ -6817,7 +6237,7 @@ static int dahdi_answer(struct ast_channel *ast)
        return res;
 }
 
-static void disable_dtmf_detect(struct dahdi_pvt *p)
+void dahdi_dtmf_detect_disable(struct dahdi_pvt *p)
 {
        int val = 0;
 
@@ -6831,7 +6251,7 @@ static void disable_dtmf_detect(struct dahdi_pvt *p)
        }
 }
 
-static void enable_dtmf_detect(struct dahdi_pvt *p)
+void dahdi_dtmf_detect_enable(struct dahdi_pvt *p)
 {
        int val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE;
 
@@ -6956,7 +6376,7 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int
                }
                ast_debug(1, "Set option TDD MODE, value: %s(%d) on %s\n",
                        (*cp == 2) ? "MATE" : "ON", (int) *cp, ast_channel_name(chan));
-               dahdi_disable_ec(p);
+               dahdi_ec_disable(p);
                /* otherwise, turn it on */
                if (!p->didtdd) { /* if havent done it yet */
                        unsigned char mybuf[41000];/*! \todo XXX This is an abuse of the stack!! */
@@ -7038,7 +6458,7 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int
                if (!*cp) {
                        ast_debug(1, "Set option AUDIO MODE, value: OFF(0) on %s\n", ast_channel_name(chan));
                        x = 0;
-                       dahdi_disable_ec(p);
+                       dahdi_ec_disable(p);
                } else {
                        ast_debug(1, "Set option AUDIO MODE, value: ON(1) on %s\n", ast_channel_name(chan));
                        x = 1;
@@ -7073,19 +6493,19 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int
                cp = (char *) data;
                if (*cp) {
                        ast_debug(1, "Enabling echo cancellation on %s\n", ast_channel_name(chan));
-                       dahdi_enable_ec(p);
+                       dahdi_ec_enable(p);
                } else {
                        ast_debug(1, "Disabling echo cancellation on %s\n", ast_channel_name(chan));
-                       dahdi_disable_ec(p);
+                       dahdi_ec_disable(p);
                }
                break;
        case AST_OPTION_DIGIT_DETECT:
                cp = (char *) data;
                ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", ast_channel_name(chan));
                if (*cp) {
-                       enable_dtmf_detect(p);
+                       dahdi_dtmf_detect_enable(p);
                } else {
-                       disable_dtmf_detect(p);
+                       dahdi_dtmf_detect_disable(p);
                }
                break;
        case AST_OPTION_FAX_DETECT:
@@ -7280,11 +6700,11 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
        } else if (!strcasecmp(data, "echocan_mode")) {
                if (!strcasecmp(value, "on")) {
                        ast_mutex_lock(&p->lock);
-                       dahdi_enable_ec(p);
+                       dahdi_ec_enable(p);
                        ast_mutex_unlock(&p->lock);
                } else if (!strcasecmp(value, "off")) {
                        ast_mutex_lock(&p->lock);
-                       dahdi_disable_ec(p);
+                       dahdi_ec_disable(p);
                        ast_mutex_unlock(&p->lock);
 #ifdef HAVE_DAHDI_ECHOCANCEL_FAX_MODE
                } else if (!strcasecmp(value, "fax")) {
@@ -7292,7 +6712,7 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
 
                        ast_mutex_lock(&p->lock);
                        if (!p->echocanon) {
-                               dahdi_enable_ec(p);
+                               dahdi_ec_enable(p);
                        }
                        if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL_FAX_MODE, &blah)) {
                                ast_log(LOG_WARNING, "Unable to place echocan into fax mode on channel %d: %s\n", p->channel, strerror(errno));
@@ -7303,7 +6723,7 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
 
                        ast_mutex_lock(&p->lock);
                        if (!p->echocanon) {
-                               dahdi_enable_ec(p);
+                               dahdi_ec_enable(p);
                        }
                        if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL_FAX_MODE, &blah)) {
                                ast_log(LOG_WARNING, "Unable to place echocan into voice mode on channel %d: %s\n", p->channel, strerror(errno));
@@ -7321,7 +6741,7 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
        return res;
 }
 
-static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock)
+void dahdi_master_slave_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock)
 {
        /* Unlink a specific slave or all slaves/masters from a given master */
        int x;
@@ -7369,7 +6789,7 @@ static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int
                }
                master->master = NULL;
        }
-       update_conf(master);
+       dahdi_conf_update(master);
        if (needlock) {
                if (slave)
                        ast_mutex_unlock(&slave->lock);
@@ -7377,7 +6797,8 @@ static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int
        }
 }
 
-static void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) {
+void dahdi_master_slave_link(struct dahdi_pvt *slave, struct dahdi_pvt *master)
+{
        int x;
        if (!slave || !master) {
                ast_log(LOG_WARNING, "Tried to link to/from NULL??\n");
@@ -7400,373 +6821,6 @@ static void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) {
        ast_debug(1, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x);
 }
 
-static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
-{
-       struct ast_channel *who;
-       struct dahdi_pvt *p0, *p1, *op0, *op1;
-       struct dahdi_pvt *master = NULL, *slave = NULL;
-       struct ast_frame *f;
-       int inconf = 0;
-       int nothingok = 1;
-       int ofd0, ofd1;
-       int oi0, oi1, i0 = -1, i1 = -1, t0, t1;
-       int os0 = -1, os1 = -1;
-       int priority = 0;
-       struct ast_channel *oc0, *oc1;
-       enum ast_bridge_result res;
-       struct timeval start = ast_tvnow();
-#ifdef PRI_2BCT
-       int triedtopribridge = 0;
-       q931_call *q931c0;
-       q931_call *q931c1;
-#endif
-
-       /* For now, don't attempt to native bridge if either channel needs DTMF detection.
-          There is code below to handle it properly until DTMF is actually seen,
-          but due to currently unresolved issues it's ignored...
-       */
-
-       if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
-               return AST_BRIDGE_FAILED_NOWARN;
-
-       ast_channel_lock_both(c0, c1);
-
-       p0 = ast_channel_tech_pvt(c0);
-       p1 = ast_channel_tech_pvt(c1);
-       /* cant do pseudo-channels here */
-       if (!p0 || (!p0->sig) || !p1 || (!p1->sig)) {
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               return AST_BRIDGE_FAILED_NOWARN;
-       }
-
-       oi0 = dahdi_get_index(c0, p0, 0);
-       oi1 = dahdi_get_index(c1, p1, 0);
-       if ((oi0 < 0) || (oi1 < 0)) {
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               return AST_BRIDGE_FAILED;
-       }
-
-       op0 = p0 = ast_channel_tech_pvt(c0);
-       op1 = p1 = ast_channel_tech_pvt(c1);
-       ofd0 = ast_channel_fd(c0, 0);
-       ofd1 = ast_channel_fd(c1, 0);
-       oc0 = p0->owner;
-       oc1 = p1->owner;
-
-       if (ast_mutex_trylock(&p0->lock)) {
-               /* Don't block, due to potential for deadlock */
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               ast_log(LOG_NOTICE, "Avoiding deadlock...\n");
-               return AST_BRIDGE_RETRY;
-       }
-       if (ast_mutex_trylock(&p1->lock)) {
-               /* Don't block, due to potential for deadlock */
-               ast_mutex_unlock(&p0->lock);
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               ast_log(LOG_NOTICE, "Avoiding deadlock...\n");
-               return AST_BRIDGE_RETRY;
-       }
-
-       if ((p0->callwaiting && p0->callwaitingcallerid)
-               || (p1->callwaiting && p1->callwaitingcallerid)) {
-               /*
-                * Call Waiting Caller ID requires DTMF detection to know if it
-                * can send the CID spill.
-                *
-                * For now, don't attempt to native bridge if either channel
-                * needs DTMF detection.  There is code below to handle it
-                * properly until DTMF is actually seen, but due to currently
-                * unresolved issues it's ignored...
-                */
-               ast_mutex_unlock(&p0->lock);
-               ast_mutex_unlock(&p1->lock);
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               return AST_BRIDGE_FAILED_NOWARN;
-       }
-
-#if defined(HAVE_PRI)
-       if ((dahdi_sig_pri_lib_handles(p0->sig)
-                       && ((struct sig_pri_chan *) p0->sig_pvt)->no_b_channel)
-               || (dahdi_sig_pri_lib_handles(p1->sig)
-                       && ((struct sig_pri_chan *) p1->sig_pvt)->no_b_channel)) {
-               /*
-                * PRI nobch channels (hold and call waiting) are equivalent to
-                * pseudo channels and cannot be done here.
-                */
-               ast_mutex_unlock(&p0->lock);
-               ast_mutex_unlock(&p1->lock);
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               return AST_BRIDGE_FAILED_NOWARN;
-       }
-#endif /* defined(HAVE_PRI) */
-
-       if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) {
-               if (p0->owner && p1->owner) {
-                       /* If we don't have a call-wait in a 3-way, and we aren't in a 3-way, we can be master */
-                       if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) {
-                               master = p0;
-                               slave = p1;
-                               inconf = 1;
-                       } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) {
-                               master = p1;
-                               slave = p0;
-                               inconf = 1;
-                       } else {
-                               ast_log(LOG_WARNING, "Huh?  Both calls are callwaits or 3-ways?  That's clever...?\n");
-                               ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n",
-                                       p0->channel,
-                                       oi0, (p0->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0,
-                                       p0->subs[SUB_REAL].inthreeway, p0->channel,
-                                       oi0, (p1->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0,
-                                       p1->subs[SUB_REAL].inthreeway);
-                       }
-                       nothingok = 0;
-               }
-       } else if ((oi0 == SUB_REAL) && (oi1 == SUB_THREEWAY)) {
-               if (p1->subs[SUB_THREEWAY].inthreeway) {
-                       master = p1;
-                       slave = p0;
-                       nothingok = 0;
-               }
-       } else if ((oi0 == SUB_THREEWAY) && (oi1 == SUB_REAL)) {
-               if (p0->subs[SUB_THREEWAY].inthreeway) {
-                       master = p0;
-                       slave = p1;
-                       nothingok = 0;
-               }
-       } else if ((oi0 == SUB_REAL) && (oi1 == SUB_CALLWAIT)) {
-               /* We have a real and a call wait.  If we're in a three way call, put us in it, otherwise,
-                  don't put us in anything */
-               if (p1->subs[SUB_CALLWAIT].inthreeway) {
-                       master = p1;
-                       slave = p0;
-                       nothingok = 0;
-               }
-       } else if ((oi0 == SUB_CALLWAIT) && (oi1 == SUB_REAL)) {
-               /* Same as previous */
-               if (p0->subs[SUB_CALLWAIT].inthreeway) {
-                       master = p0;
-                       slave = p1;
-                       nothingok = 0;
-               }
-       }
-       ast_debug(1, "master: %d, slave: %d, nothingok: %d\n",
-               master ? master->channel : 0, slave ? slave->channel : 0, nothingok);
-       if (master && slave) {
-               /* Stop any tones, or play ringtone as appropriate.  If they're bridged
-                  in an active threeway call with a channel that is ringing, we should
-                  indicate ringing. */
-               if ((oi1 == SUB_THREEWAY) &&
-                       p1->subs[SUB_THREEWAY].inthreeway &&
-                       p1->subs[SUB_REAL].owner &&
-                       p1->subs[SUB_REAL].inthreeway &&
-                       (ast_channel_state(p1->subs[SUB_REAL].owner) == AST_STATE_RINGING)) {
-                       ast_debug(1,
-                               "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n",
-                               p0->channel, oi0, ast_channel_name(c0), p1->channel, oi1, ast_channel_name(c1));
-                       tone_zone_play_tone(p0->subs[oi0].dfd, DAHDI_TONE_RINGTONE);
-                       os1 = ast_channel_state(p1->subs[SUB_REAL].owner);
-               } else {
-                       ast_debug(1, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n",
-                               p0->channel, oi0, ast_channel_name(c0), p1->channel, oi1, ast_channel_name(c1));
-                       tone_zone_play_tone(p0->subs[oi0].dfd, -1);
-               }
-               if ((oi0 == SUB_THREEWAY) &&
-                       p0->subs[SUB_THREEWAY].inthreeway &&
-                       p0->subs[SUB_REAL].owner &&
-                       p0->subs[SUB_REAL].inthreeway &&
-                       (ast_channel_state(p0->subs[SUB_REAL].owner) == AST_STATE_RINGING)) {
-                       ast_debug(1,
-                               "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n",
-                               p1->channel, oi1, ast_channel_name(c1), p0->channel, oi0, ast_channel_name(c0));
-                       tone_zone_play_tone(p1->subs[oi1].dfd, DAHDI_TONE_RINGTONE);
-                       os0 = ast_channel_state(p0->subs[SUB_REAL].owner);
-               } else {
-                       ast_debug(1, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n",
-                               p1->channel, oi1, ast_channel_name(c1), p0->channel, oi0, ast_channel_name(c0));
-                       tone_zone_play_tone(p1->subs[oi1].dfd, -1);
-               }
-               if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) {
-                       if (!p0->echocanbridged || !p1->echocanbridged) {
-                               /* Disable echo cancellation if appropriate */
-                               dahdi_disable_ec(p0);
-                               dahdi_disable_ec(p1);
-                       }
-               }
-               dahdi_link(slave, master);
-               master->inconference = inconf;
-       } else if (!nothingok)
-               ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n", p0->channel, subnames[oi0], p1->channel, subnames[oi1]);
-
-       update_conf(p0);
-       update_conf(p1);
-       t0 = p0->subs[SUB_REAL].inthreeway;
-       t1 = p1->subs[SUB_REAL].inthreeway;
-
-       ast_mutex_unlock(&p0->lock);
-       ast_mutex_unlock(&p1->lock);
-
-       ast_channel_unlock(c0);
-       ast_channel_unlock(c1);
-
-       /* Native bridge failed */
-       if ((!master || !slave) && !nothingok) {
-               dahdi_enable_ec(p0);
-               dahdi_enable_ec(p1);
-               return AST_BRIDGE_FAILED;
-       }
-
-       ast_verb(3, "Native bridging %s and %s\n", ast_channel_name(c0), ast_channel_name(c1));
-
-       if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL))
-               disable_dtmf_detect(op0);
-
-       if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL))
-               disable_dtmf_detect(op1);
-
-       for (;;) {
-               struct ast_channel *c0_priority[2] = {c0, c1};
-               struct ast_channel *c1_priority[2] = {c1, c0};
-               int ms;
-
-               /* Here's our main loop...  Start by locking things, looking for private parts,
-                  and then balking if anything is wrong */
-
-               ast_channel_lock_both(c0, c1);
-
-               p0 = ast_channel_tech_pvt(c0);
-               p1 = ast_channel_tech_pvt(c1);
-
-               if (op0 == p0)
-                       i0 = dahdi_get_index(c0, p0, 1);
-               if (op1 == p1)
-                       i1 = dahdi_get_index(c1, p1, 1);
-
-               ast_channel_unlock(c0);
-               ast_channel_unlock(c1);
-               ms = ast_remaining_ms(start, timeoutms);
-               if (!ms ||
-                       (op0 != p0) ||
-                       (op1 != p1) ||
-                       (ofd0 != ast_channel_fd(c0, 0)) ||
-                       (ofd1 != ast_channel_fd(c1, 0)) ||
-                       (p0->subs[SUB_REAL].owner && (os0 > -1) && (os0 != ast_channel_state(p0->subs[SUB_REAL].owner))) ||
-                       (p1->subs[SUB_REAL].owner && (os1 > -1) && (os1 != ast_channel_state(p1->subs[SUB_REAL].owner))) ||
-                       (oc0 != p0->owner) ||
-                       (oc1 != p1->owner) ||
-                       (t0 != p0->subs[SUB_REAL].inthreeway) ||
-                       (t1 != p1->subs[SUB_REAL].inthreeway) ||
-                       (oi0 != i0) ||
-                       (oi1 != i1)) {
-                       ast_debug(1, "Something changed out on %d/%d to %d/%d, returning -3 to restart\n",
-                               op0->channel, oi0, op1->channel, oi1);
-                       res = AST_BRIDGE_RETRY;
-                       goto return_from_bridge;
-               }
-
-#ifdef PRI_2BCT
-               if (!triedtopribridge) {
-                       triedtopribridge = 1;
-                       if (p0->pri && p0->pri == p1->pri && p0->pri->transfer) {
-                               ast_mutex_lock(&p0->pri->lock);
-                               switch (p0->sig) {
-                               case SIG_PRI_LIB_HANDLE_CASES:
-                                       q931c0 = ((struct sig_pri_chan *) (p0->sig_pvt))->call;
-                                       break;
-                               default:
-                                       q931c0 = NULL;
-                                       break;
-                               }
-                               switch (p1->sig) {
-                               case SIG_PRI_LIB_HANDLE_CASES:
-                                       q931c1 = ((struct sig_pri_chan *) (p1->sig_pvt))->call;
-                                       break;
-                               default:
-                                       q931c1 = NULL;
-                                       break;
-                               }
-                               if (q931c0 && q931c1) {
-                                       pri_channel_bridge(q931c0, q931c1);
-                               }
-                               ast_mutex_unlock(&p0->pri->lock);
-                       }
-               }
-#endif
-
-               who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &ms);
-               if (!who) {
-                       ast_debug(1, "Ooh, empty read...\n");
-                       continue;
-               }
-               f = ast_read(who);
-               switch (f ? f->frametype : AST_FRAME_CONTROL) {
-               case AST_FRAME_CONTROL:
-                       if (f && f->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) {
-                               ast_channel_hangupcause_hash_set((who == c0) ? c1 : c0, f->data.ptr, f->datalen);
-                               break;
-                       }
-                       *fo = f;
-                       *rc = who;
-                       res = AST_BRIDGE_COMPLETE;
-                       goto return_from_bridge;
-               case AST_FRAME_DTMF_END:
-                       if ((who == c0) && p0->pulsedial) {
-                               ast_write(c1, f);
-                       } else if ((who == c1) && p1->pulsedial) {
-                               ast_write(c0, f);
-                       } else {
-                               *fo = f;
-                               *rc = who;
-                               res = AST_BRIDGE_COMPLETE;
-                               goto return_from_bridge;
-                       }
-                       break;
-               case AST_FRAME_TEXT:
-                       if (who == c0) {
-                               ast_write(c1, f);
-                       } else {
-                               ast_write(c0, f);
-                       }
-                       break;
-               case AST_FRAME_VOICE:
-                       /* Native bridge handles voice frames in hardware. */
-               case AST_FRAME_NULL:
-                       break;
-               default:
-                       ast_debug(1, "Chan '%s' is discarding frame of frametype:%d\n",
-                               ast_channel_name(who), f->frametype);
-                       break;
-               }
-               ast_frfree(f);
-
-               /* Swap who gets priority */
-               priority = !priority;
-       }
-
-return_from_bridge:
-       if (op0 == p0)
-               dahdi_enable_ec(p0);
-
-       if (op1 == p1)
-               dahdi_enable_ec(p1);
-
-       if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL))
-               enable_dtmf_detect(op0);
-
-       if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL))
-               enable_dtmf_detect(op1);
-
-       dahdi_unlink(slave, master, 1);
-
-       return res;
-}
-
 static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
        struct dahdi_pvt *p = ast_channel_tech_pvt(newchan);
@@ -7781,12 +6835,12 @@ static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
        for (x = 0; x < 3; x++) {
                if (p->subs[x].owner == oldchan) {
                        if (!x) {
-                               dahdi_unlink(NULL, p, 0);
+                               dahdi_master_slave_unlink(NULL, p, 0);
                        }
                        p->subs[x].owner = newchan;
                }
        }
-       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                analog_fixup(oldchan, newchan, p->sig_pvt);
 #if defined(HAVE_PRI)
        } else if (dahdi_sig_pri_lib_handles(p->sig)) {
@@ -7797,7 +6851,7 @@ static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
                sig_ss7_fixup(oldchan, newchan, p->sig_pvt);
 #endif /* defined(HAVE_SS7) */
        }
-       update_conf(p);
+       dahdi_conf_update(p);
 
        ast_mutex_unlock(&p->lock);
 
@@ -7839,55 +6893,49 @@ static int dahdi_ring_phone(struct dahdi_pvt *p)
 
 static void *analog_ss_thread(void *data);
 
+/*!
+ * \internal
+ * \brief Attempt to transfer 3-way call.
+ *
+ * \param p DAHDI private structure.
+ *
+ * \note On entry these locks are held: real-call, private, 3-way call.
+ * \note On exit these locks are held: real-call, private.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
 static int attempt_transfer(struct dahdi_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[SUB_REAL].owner)) {
-               /* The three-way person we're about to transfer to could still be in MOH, so
-                  stop it now */
-               ast_queue_unhold(p->subs[SUB_THREEWAY].owner);
-               if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RINGING) {
-                       ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_RINGING);
-               }
-               if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RING) {
-                       tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
-               }
-                if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       ast_channel_name(ast_bridged_channel(p->subs[SUB_REAL].owner)), ast_channel_name(p->subs[SUB_THREEWAY].owner));
-                       return -1;
-               }
-               /* Orphan the channel after releasing the lock */
-               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-               unalloc_sub(p, SUB_THREEWAY);
-       } else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
-               ast_queue_unhold(p->subs[SUB_REAL].owner);
-               if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RINGING) {
-                       ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_RINGING);
-               }
-               if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RING) {
-                       tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
-               }
-               if (ast_channel_masquerade(p->subs[SUB_REAL].owner, ast_bridged_channel(p->subs[SUB_THREEWAY].owner))) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       ast_channel_name(ast_bridged_channel(p->subs[SUB_THREEWAY].owner)), ast_channel_name(p->subs[SUB_REAL].owner));
-                       return -1;
-               }
-               /* Three-way is now the REAL */
-               swap_subs(p, SUB_THREEWAY, SUB_REAL);
-               ast_channel_unlock(p->subs[SUB_REAL].owner);
-               unalloc_sub(p, 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",
-                       ast_channel_name(p->subs[SUB_REAL].owner), ast_channel_name(p->subs[SUB_THREEWAY].owner));
-               ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-               return -1;
+       struct ast_channel *owner_real;
+       struct ast_channel *owner_3way;
+       enum ast_transfer_result xfer_res;
+       int res = 0;
+
+       owner_real = ast_channel_ref(p->subs[SUB_REAL].owner);
+       owner_3way = ast_channel_ref(p->subs[SUB_THREEWAY].owner);
+
+       ast_verb(3, "TRANSFERRING %s to %s\n",
+               ast_channel_name(owner_3way), ast_channel_name(owner_real));
+
+       ast_channel_unlock(owner_real);
+       ast_channel_unlock(owner_3way);
+       ast_mutex_unlock(&p->lock);
+
+       xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
+       if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+               ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
+               res = -1;
        }
-       return 0;
+
+       /* Must leave with these locked. */
+       ast_channel_lock(owner_real);
+       ast_mutex_lock(&p->lock);
+
+       ast_channel_unref(owner_real);
+       ast_channel_unref(owner_3way);
+
+       return res;
 }
 
 static int check_for_conference(struct dahdi_pvt *p)
@@ -8041,6 +7089,39 @@ static void dahdi_handle_dtmf(struct ast_channel *ast, int idx, struct ast_frame
        }
 }
 
+static void publish_span_alarm(int span, const char *alarm_txt)
+{
+       RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+
+       body = ast_json_pack("{s: i, s: s}",
+               "Span", span,
+               "Alarm", alarm_txt);
+       if (!body) {
+               return;
+       }
+
+       ast_manager_publish_event("SpanAlarm", EVENT_FLAG_SYSTEM, body);
+}
+
+static void publish_channel_alarm(int channel, const char *alarm_txt)
+{
+       RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+       RAII_VAR(struct ast_str *, dahdi_chan, ast_str_create(32), ast_free);
+       if (!dahdi_chan) {
+               return;
+       }
+
+       ast_str_set(&dahdi_chan, 0, "%d", channel);
+       body = ast_json_pack("{s: s, s: s}",
+               "DAHDIChannel", ast_str_buffer(dahdi_chan),
+               "Alarm", alarm_txt);
+       if (!body) {
+               return;
+       }
+
+       ast_manager_publish_event("Alarm", EVENT_FLAG_SYSTEM, body);
+}
+
 static void handle_alarms(struct dahdi_pvt *p, int alms)
 {
        const char *alarm_str;
@@ -8054,28 +7135,12 @@ static void handle_alarms(struct dahdi_pvt *p, int alms)
        alarm_str = alarm2str(alms);
        if (report_alarms & REPORT_CHANNEL_ALARMS) {
                ast_log(LOG_WARNING, "Detected alarm on channel %d: %s\n", p->channel, alarm_str);
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when an alarm is set on a DAHDI channel.</synopsis>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_SYSTEM, "Alarm",
-                                         "Alarm: %s\r\n"
-                                         "Channel: %d\r\n",
-                                         alarm_str, p->channel);
+               publish_channel_alarm(p->channel, alarm_str);
        }
 
        if (report_alarms & REPORT_SPAN_ALARMS && p->manages_span_alarms) {
                ast_log(LOG_WARNING, "Detected alarm on span %d: %s\n", p->span, alarm_str);
-               /*** DOCUMENTATION
-                       <managerEventInstance>
-                               <synopsis>Raised when an alarm is set on a DAHDI span.</synopsis>
-                       </managerEventInstance>
-               ***/
-               manager_event(EVENT_FLAG_SYSTEM, "SpanAlarm",
-                                         "Alarm: %s\r\n"
-                                         "Span: %d\r\n",
-                                         alarm_str, p->span);
+               publish_span_alarm(p->span, alarm_str);
        }
 }
 
@@ -8229,7 +7294,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                        return NULL;
                }
                if (!x) { /* if not still dialing in driver */
-                       dahdi_enable_ec(p);
+                       dahdi_ec_enable(p);
                        if (p->echobreak) {
                                dahdi_train_ec(p);
                                ast_copy_string(p->dop.dialstr, p->echorest, sizeof(p->dop.dialstr));
@@ -8397,17 +7462,13 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                                                                p->owner = NULL;
                                                                /* Ring the phone */
                                                                dahdi_ring_phone(p);
-                                                       } else {
-                                                               if ((res = attempt_transfer(p)) < 0) {
-                                                                       ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, 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 if (!attempt_transfer(p)) {
+                                                               /*
+                                                                * Transfer successful.  Don't actually hang up at this point.
+                                                                * Let our channel legs of the calls die off as the transfer
+                                                                * percolates through the core.
+                                                                */
+                                                               break;
                                                        }
                                                } else {
                                                        ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
@@ -8428,7 +7489,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                        }
                        /* Fall through */
                default:
-                       dahdi_disable_ec(p);
+                       dahdi_ec_disable(p);
                        return NULL;
                }
                break;
@@ -8483,7 +7544,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                case SIG_FXOKS:
                        switch (ast_channel_state(ast)) {
                        case AST_STATE_RINGING:
-                               dahdi_enable_ec(p);
+                               dahdi_ec_enable(p);
                                dahdi_train_ec(p);
                                p->subs[idx].f.frametype = AST_FRAME_CONTROL;
                                p->subs[idx].f.subclass.integer = AST_CONTROL_ANSWER;
@@ -8751,7 +7812,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                                        /* 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);
+                                       dahdi_ec_disable(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);
@@ -8761,7 +7822,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                                        } 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);
+                                               dahdi_ec_enable(p);
                                                ast_hangup(chan);
                                        } else {
                                                ast_verb(3, "Started three way call on channel %d\n", p->channel);
@@ -8818,12 +7879,12 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                                                        ast_queue_unhold(p->subs[SUB_REAL].owner);
                                                }
                                                p->subs[SUB_REAL].needunhold = 1;
-                                               dahdi_enable_ec(p);
+                                               dahdi_ec_enable(p);
                                        }
                                }
                        }
 winkflashdone:
-                       update_conf(p);
+                       dahdi_conf_update(p);
                        break;
                case SIG_EM:
                case SIG_EM_E1:
@@ -9016,7 +8077,7 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
                }
                switch (res) {
                case DAHDI_EVENT_ONHOOK:
-                       dahdi_disable_ec(p);
+                       dahdi_ec_disable(p);
                        if (p->owner) {
                                ast_verb(3, "Channel %s still has call, ringing phone\n", ast_channel_name(p->owner));
                                dahdi_ring_phone(p);
@@ -9025,10 +8086,10 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
                                p->cid_suppress_expire = 0;
                        } else
                                ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
-                       update_conf(p);
+                       dahdi_conf_update(p);
                        break;
                case DAHDI_EVENT_RINGOFFHOOK:
-                       dahdi_enable_ec(p);
+                       dahdi_ec_enable(p);
                        dahdi_set_hook(p->subs[SUB_REAL].dfd, DAHDI_OFFHOOK);
                        if (p->owner && (ast_channel_state(p->owner) == AST_STATE_RINGING)) {
                                p->subs[SUB_REAL].needanswer = 1;
@@ -9059,7 +8120,7 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
                                p->subs[SUB_REAL].needunhold = 1;
                        } else
                                ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
-                       update_conf(p);
+                       dahdi_conf_update(p);
                        break;
                default:
                        ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
@@ -9097,7 +8158,7 @@ static struct ast_frame *dahdi_exception(struct ast_channel *ast)
        struct dahdi_pvt *p = ast_channel_tech_pvt(ast);
        struct ast_frame *f;
        ast_mutex_lock(&p->lock);
-       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                struct analog_pvt *analog_p = p->sig_pvt;
                f = analog_exception(analog_p, ast);
        } else {
@@ -9203,7 +8264,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
                        /* if the call is already accepted and we already delivered AST_CONTROL_RINGING
                         * now enqueue a progress frame to bridge the media up */
                        if (p->mfcr2_call_accepted &&
-                               !p->mfcr2_progress_sent && 
+                               !p->mfcr2_progress_sent &&
                                ast_channel_state(ast) == AST_STATE_RINGING) {
                                ast_debug(1, "Enqueuing progress frame after R2 accept in chan %d\n", p->channel);
                                ast_queue_frame(p->owner, &fr);
@@ -9294,7 +8355,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
         * if this channel owns the private.
         */
        if (p->fake_event && p->owner == ast) {
-               if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                        struct analog_pvt *analog_p = p->sig_pvt;
 
                        f = analog_exception(analog_p, ast);
@@ -9338,7 +8399,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
                                ast_mutex_unlock(&p->lock);
                                return &p->subs[idx].f;
                        } else if (errno == ELAST) {
-                               if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+                               if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                                        struct analog_pvt *analog_p = p->sig_pvt;
                                        f = analog_exception(analog_p, ast);
                                } else {
@@ -9352,7 +8413,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
        }
        if (res != (p->subs[idx].linear ? READ_SIZE * 2 : READ_SIZE)) {
                ast_debug(1, "Short read (%d/%d), must be an event...\n", res, p->subs[idx].linear ? READ_SIZE * 2 : READ_SIZE);
-               if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+               if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                        struct analog_pvt *analog_p = p->sig_pvt;
                        f = analog_exception(analog_p, ast);
                } else {
@@ -9530,7 +8591,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
                switch (f->frametype) {
                case AST_FRAME_DTMF_BEGIN:
                case AST_FRAME_DTMF_END:
-                       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+                       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                                analog_handle_dtmf(p->sig_pvt, ast, idx, &f);
                        } else {
                                dahdi_handle_dtmf(ast, idx, &f);
@@ -9858,6 +8919,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
                return NULL;
        }
 
+       ast_channel_stage_snapshot(tmp);
+
        if (callid) {
                ast_channel_callid_set(tmp, callid);
        }
@@ -9978,7 +9041,7 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
                ast_channel_amaflags_set(tmp, i->amaflags);
        i->subs[idx].owner = tmp;
        ast_channel_context_set(tmp, i->context);
-       if (!analog_lib_handles(i->sig, i->radio, i->oprmode)) {
+       if (!dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode)) {
                ast_channel_call_forward_set(tmp, i->call_forward);
        }
        /* If we've been told "no ADSI" then enforce it */
@@ -10035,6 +9098,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
        for (v = i->vars ; v ; v = v->next)
                pbx_builtin_setvar_helper(tmp, v->name, v->value);
 
+       ast_channel_stage_snapshot_done(tmp);
+
        ast_module_ref(ast_module_info->self);
 
        dahdi_ami_channel_event(i, tmp);
@@ -10091,6 +9156,26 @@ static int dahdi_wink(struct dahdi_pvt *p, int idx)
        return 0;
 }
 
+static void publish_dnd_state(int channel, const char *status)
+{
+       RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+       RAII_VAR(struct ast_str *, dahdichan, ast_str_create(32), ast_free);
+       if (!dahdichan) {
+               return;
+       }
+
+       ast_str_set(&dahdichan, 0, "%d", channel);
+
+       body = ast_json_pack("{s: s, s: s}",
+               "DAHDIChannel", ast_str_buffer(dahdichan),
+               "Status", status);
+       if (!body) {
+               return;
+       }
+
+       ast_manager_publish_event("DNDState", EVENT_FLAG_SYSTEM, body);
+}
+
 /*! \brief enable or disable the chan_dahdi Do-Not-Disturb mode for a DAHDI channel
  * \param dahdichan "Physical" DAHDI channel (e.g: DAHDI/5)
  * \param flag on 1 to enable, 0 to disable, -1 return dnd value
@@ -10102,7 +9187,7 @@ static int dahdi_wink(struct dahdi_pvt *p, int idx)
  */
 static int dahdi_dnd(struct dahdi_pvt *dahdichan, int flag)
 {
-       if (analog_lib_handles(dahdichan->sig, dahdichan->radio, dahdichan->oprmode)) {
+       if (dahdi_analog_lib_handles(dahdichan->sig, dahdichan->radio, dahdichan->oprmode)) {
                return analog_dnd(dahdichan->sig_pvt, flag);
        }
 
@@ -10115,36 +9200,19 @@ static int dahdi_dnd(struct dahdi_pvt *dahdichan, int flag)
        ast_verb(3, "%s DND on channel %d\n",
                        flag? "Enabled" : "Disabled",
                        dahdichan->channel);
-       /*** DOCUMENTATION
-               <managerEventInstance>
-                       <synopsis>Raised when the Do Not Disturb state is changed on a DAHDI channel.</synopsis>
-                       <syntax>
-                               <parameter name="Status">
-                                       <enumlist>
-                                               <enum name="enabled"/>
-                                               <enum name="disabled"/>
-                                       </enumlist>
-                               </parameter>
-                       </syntax>
-               </managerEventInstance>
-       ***/
-       manager_event(EVENT_FLAG_SYSTEM, "DNDState",
-                       "Channel: DAHDI/%d\r\n"
-                       "Status: %s\r\n", dahdichan->channel,
-                       flag? "enabled" : "disabled");
-
+       publish_dnd_state(dahdichan->channel, flag ? "enabled" : "disabled");
        return 0;
 }
 
-static int canmatch_featurecode(const char *exten)
+static int canmatch_featurecode(const char *pickupexten, const char *exten)
 {
        int extlen = strlen(exten);
-       const char *pickup_ext;
+
        if (!extlen) {
                return 1;
        }
-       pickup_ext = ast_pickup_ext();
-       if (extlen < strlen(pickup_ext) && !strncmp(pickup_ext, exten, extlen)) {
+
+       if (extlen < strlen(pickupexten) && !strncmp(pickupexten, exten, extlen)) {
                return 1;
        }
        /* hardcoded features are *60, *67, *69, *70, *72, *73, *78, *79, *82, *0 */
@@ -10190,6 +9258,8 @@ static void *analog_ss_thread(void *data)
        int res;
        int idx;
        struct ast_format tmpfmt;
+       RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
+       const char *pickupexten;
 
        ast_mutex_lock(&ss_thread_lock);
        ss_thread_count++;
@@ -10209,6 +9279,17 @@ static void *analog_ss_thread(void *data)
                ast_hangup(chan);
                goto quit;
        }
+
+       ast_channel_lock(chan);
+       pickup_cfg = ast_get_chan_features_pickup_config(chan);
+       if (!pickup_cfg) {
+               ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
+               pickupexten = "";
+       } else {
+               pickupexten = ast_strdupa(pickup_cfg->pickupexten);
+       }
+       ast_channel_unlock(chan);
+
        if (p->dsp)
                ast_dsp_digitreset(p->dsp);
        switch (p->sig) {
@@ -10443,7 +9524,7 @@ static void *analog_ss_thread(void *data)
                                goto quit;
                        }
                }
-               dahdi_enable_ec(p);
+               dahdi_ec_enable(p);
                if (NEED_MFDETECT(p)) {
                        if (p->dsp) {
                                if (!p->hardwaredtmf)
@@ -10491,6 +9572,8 @@ static void *analog_ss_thread(void *data)
                if (p->subs[SUB_THREEWAY].owner)
                        timeout = 999999;
                while (len < AST_MAX_EXTENSION-1) {
+                       int is_exten_parking = 0;
+
                        /* Read digit unless it's supposed to be immediate, in which case the
                           only answer is 's' */
                        if (p->immediate)
@@ -10508,11 +9591,15 @@ static void *analog_ss_thread(void *data)
                                exten[len++]=res;
                                exten[len] = '\0';
                        }
-                       if (!ast_ignore_pattern(ast_channel_context(chan), exten))
+                       if (!ast_ignore_pattern(ast_channel_context(chan), exten)) {
                                tone_zone_play_tone(p->subs[idx].dfd, -1);
-                       else
+                       } else {
                                tone_zone_play_tone(p->subs[idx].dfd, DAHDI_TONE_DIALTONE);
-                       if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !ast_parking_ext_valid(exten, chan, ast_channel_context(chan))) {
+                       }
+                       if (ast_parking_provider_registered()) {
+                               is_exten_parking = ast_parking_is_exten_park(ast_channel_context(chan), exten);
+                       }
+                       if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !is_exten_parking) {
                                if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
                                        if (getforward) {
                                                /* Record this as the forwarding extension */
@@ -10542,7 +9629,7 @@ static void *analog_ss_thread(void *data)
                                                                ast_set_callerid(chan, NULL, p->cid_name, NULL);
                                                }
                                                ast_setstate(chan, AST_STATE_RING);
-                                               dahdi_enable_ec(p);
+                                               dahdi_ec_enable(p);
                                                res = ast_pbx_run(chan);
                                                if (res) {
                                                        ast_log(LOG_WARNING, "PBX exited non-zero\n");
@@ -10575,7 +9662,7 @@ static void *analog_ss_thread(void *data)
                                memset(exten, 0, sizeof(exten));
                                timeout = firstdigittimeout;
 
-                       } else if (!strcmp(exten,ast_pickup_ext())) {
+                       } else if (!strcmp(exten, pickupexten)) {
                                /* Scan all channels and see if there are any
                                 * ringing channels that have call groups
                                 * that equal this channels pickup group
@@ -10589,7 +9676,7 @@ static void *analog_ss_thread(void *data)
                                                swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
                                                unalloc_sub(p, SUB_THREEWAY);
                                        }
-                                       dahdi_enable_ec(p);
+                                       dahdi_ec_enable(p);
                                        if (ast_pickup_call(chan)) {
                                                ast_debug(1, "No call pickup possible...\n");
                                                res = tone_zone_play_tone(p->subs[idx].dfd, DAHDI_TONE_CONGESTION);
@@ -10648,14 +9735,35 @@ static void *analog_ss_thread(void *data)
                                getforward = 0;
                                memset(exten, 0, sizeof(exten));
                                len = 0;
-                       } else if ((p->transfer || p->canpark) && ast_parking_ext_valid(exten, chan, ast_channel_context(chan)) &&
-                                               p->subs[SUB_THREEWAY].owner &&
-                                               ast_bridged_channel(p->subs[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_exten(ast_bridged_channel(p->subs[SUB_THREEWAY].owner),
-                                       chan, exten, ast_channel_context(chan), 0, NULL);
-                               ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
+                       } else if ((p->transfer || p->canpark) && is_exten_parking
+                               && p->subs[SUB_THREEWAY].owner) {
+                               struct ast_bridge_channel *bridge_channel;
+
+                               /*
+                                * This is a three way call, the main call being a real channel,
+                                * and we're parking the first call.
+                                */
+                               ast_channel_lock(p->subs[SUB_THREEWAY].owner);
+                               bridge_channel = ast_channel_get_bridge_channel(p->subs[SUB_THREEWAY].owner);
+                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+                               if (bridge_channel) {
+                                       if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
+                                               /*
+                                                * Swap things around between the three-way and real call so we
+                                                * can hear where the channel got parked.
+                                                */
+                                               ast_mutex_lock(&p->lock);
+                                               p->owner = p->subs[SUB_THREEWAY].owner;
+                                               swap_subs(p, SUB_THREEWAY, SUB_REAL);
+                                               ast_mutex_unlock(&p->lock);
+
+                                               ast_verb(3, "%s: Parked call\n", ast_channel_name(chan));
+                                               ast_hangup(chan);
+                                               ao2_ref(bridge_channel, -1);
+                                               goto quit;
+                                       }
+                                       ao2_ref(bridge_channel, -1);
+                               }
                                break;
                        } else if (p->hidecallerid && !strcmp(exten, "*82")) {
                                ast_verb(3, "Enabling Caller*ID on %s\n", ast_channel_name(chan));
@@ -10674,12 +9782,15 @@ static void *analog_ss_thread(void *data)
                                struct ast_channel *nbridge =
                                        p->subs[SUB_THREEWAY].owner;
                                struct dahdi_pvt *pbridge = NULL;
+                               RAII_VAR(struct ast_channel *, bridged, nbridge ? ast_channel_bridge_peer(nbridge) : NULL, ast_channel_cleanup);
+
                                /* set up the private struct of the bridged one, if any */
-                               if (nbridge && ast_bridged_channel(nbridge))
-                                       pbridge = ast_channel_tech_pvt(ast_bridged_channel(nbridge));
+                               if (nbridge && bridged) {
+                                       pbridge = ast_channel_tech_pvt(bridged);
+                               }
                                if (nbridge && pbridge &&
                                        (ast_channel_tech(nbridge) == &dahdi_tech) &&
-                                       (ast_channel_tech(ast_bridged_channel(nbridge)) == &dahdi_tech) &&
+                                       (ast_channel_tech(bridged) == &dahdi_tech) &&
                                        ISTRUNK(pbridge)) {
                                        int func = DAHDI_FLASH;
                                        /* Clear out the dial buffer */
@@ -10707,7 +9818,7 @@ static void *analog_ss_thread(void *data)
                                }
                        } else if (!ast_canmatch_extension(chan, ast_channel_context(chan), exten, 1,
                                S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
-                               && !canmatch_featurecode(exten)) {
+                               && !canmatch_featurecode(pickupexten, exten)) {
                                ast_debug(1, "Can't match %s from '%s' in context %s\n", exten,
                                        S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<Unknown Caller>"),
                                        ast_channel_context(chan));
@@ -11373,7 +10484,7 @@ static void *mwi_thread(void *data)
                        case DAHDI_EVENT_BITSCHANGED:
                                break;
                        case DAHDI_EVENT_NOALARM:
-                               if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
+                               if (dahdi_analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
                                        struct analog_pvt *analog_p = mtd->pvt->sig_pvt;
 
                                        analog_p->inalarm = 0;
@@ -11382,7 +10493,7 @@ static void *mwi_thread(void *data)
                                handle_clear_alarms(mtd->pvt);
                                break;
                        case DAHDI_EVENT_ALARM:
-                               if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
+                               if (dahdi_analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
                                        struct analog_pvt *analog_p = mtd->pvt->sig_pvt;
 
                                        analog_p->inalarm = 1;
@@ -11402,7 +10513,7 @@ static void *mwi_thread(void *data)
                                if ((chan = dahdi_new(mtd->pvt, AST_STATE_RING, 0, SUB_REAL, 0, NULL, callid))) {
                                        int result;
 
-                                       if (analog_lib_handles(mtd->pvt->sig, mtd->pvt->radio, mtd->pvt->oprmode)) {
+                                       if (dahdi_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);
@@ -11670,29 +10781,121 @@ static int mwi_send_process_event(struct dahdi_pvt * pvt, int event)
        return handled;
 }
 
-/* destroy a DAHDI channel, identified by its number */
-static int dahdi_destroy_channel_bynum(int channel)
+/* destroy a range DAHDI channels, identified by their number */
+static void dahdi_destroy_channel_range(int start, int end)
 {
        struct dahdi_pvt *cur;
+       struct dahdi_pvt *next;
+       int destroyed_first = 0;
+       int destroyed_last = 0;
 
        ast_mutex_lock(&iflock);
-       for (cur = iflist; cur; cur = cur->next) {
-               if (cur->channel == channel) {
+       ast_debug(1, "range: %d-%d\n", start, end);
+       for (cur = iflist; cur; cur = next) {
+               next = cur->next;
+               if (cur->channel >= start && cur->channel <= end) {
                        int x = DAHDI_FLASH;
 
+                       if (cur->channel > destroyed_last) {
+                               destroyed_last = cur->channel;
+                       }
+                       if (destroyed_first < 1 || cur->channel < destroyed_first) {
+                               destroyed_first = cur->channel;
+                       }
+                       ast_debug(3, "Destroying %d\n", cur->channel);
                        /* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */
                        ioctl(cur->subs[SUB_REAL].dfd, DAHDI_HOOK, &x);
 
                        destroy_channel(cur, 1);
-                       ast_mutex_unlock(&iflock);
                        ast_module_unref(ast_module_info->self);
-                       return RESULT_SUCCESS;
                }
        }
        ast_mutex_unlock(&iflock);
-       return RESULT_FAILURE;
+       if (destroyed_first > start || destroyed_last < end) {
+               ast_debug(1, "Asked to destroy %d-%d, destroyed %d-%d,\n",
+                       start, end, destroyed_first, destroyed_last);
+       }
+}
+
+static int setup_dahdi(int reload);
+static int setup_dahdi_int(int reload, struct dahdi_chan_conf *default_conf, struct dahdi_chan_conf *base_conf, struct dahdi_chan_conf *conf);
+
+/*!
+ * \internal
+ * \brief create a range of new DAHDI channels
+ *
+ * \param start first channel in the range
+ * \param end last channel in the range
+ *
+ * \retval RESULT_SUCCESS on success.
+ * \retval RESULT_FAILURE on error.
+ */
+static int dahdi_create_channel_range(int start, int end)
+{
+       struct dahdi_pvt *cur;
+       struct dahdi_chan_conf default_conf = dahdi_chan_conf_default();
+       struct dahdi_chan_conf base_conf = dahdi_chan_conf_default();
+       struct dahdi_chan_conf conf = dahdi_chan_conf_default();
+       int i, x;
+       int ret = RESULT_FAILURE; /* be pessimistic */
+
+       ast_debug(1, "channel range caps: %d - %d\n", start, end);
+       ast_mutex_lock(&iflock);
+       for (cur = iflist; cur; cur = cur->next) {
+               if (cur->channel >= start && cur->channel <= end) {
+                       ast_log(LOG_ERROR,
+                               "channel range %d-%d is occupied\n",
+                               start, end);
+                       goto out;
+               }
+       }
+       for (x = 0; x < NUM_SPANS; x++) {
+#ifdef HAVE_PRI
+               struct dahdi_pri *pri = pris + x;
+
+               if (!pris[x].pri.pvts[0]) {
+                       break;
+               }
+               for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
+                       int channo = pri->dchannels[i];
+
+                       if (!channo) {
+                               break;
+                       }
+                       if (!pri->pri.fds[i]) {
+                               break;
+                       }
+                       if (channo >= start && channo <= end) {
+                               ast_log(LOG_ERROR,
+                                               "channel range %d-%d is occupied by span %d\n",
+                                               start, end, x + 1);
+                               goto out;
+                       }
+               }
+#endif
+       }
+       if (!default_conf.chan.cc_params || !base_conf.chan.cc_params ||
+               !conf.chan.cc_params) {
+               goto out;
+       }
+       default_conf.wanted_channels_start = start;
+       base_conf.wanted_channels_start = start;
+       conf.wanted_channels_start = start;
+       default_conf.wanted_channels_end = end;
+       base_conf.wanted_channels_end = end;
+       conf.wanted_channels_end = end;
+       if (setup_dahdi_int(0, &default_conf, &base_conf, &conf) == 0) {
+               ret = RESULT_SUCCESS;
+       }
+out:
+       ast_cc_config_params_destroy(default_conf.chan.cc_params);
+       ast_cc_config_params_destroy(base_conf.chan.cc_params);
+       ast_cc_config_params_destroy(conf.chan.cc_params);
+       ast_mutex_unlock(&iflock);
+       return ret;
 }
 
+
 static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event)
 {
        int res;
@@ -11729,7 +10932,7 @@ static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event)
                        restore_conference(i);
 
                        if (i->immediate) {
-                               dahdi_enable_ec(i);
+                               dahdi_ec_enable(i);
                                /* The channel is immediately up.  Start right away */
                                res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
                                chan = dahdi_new(i, AST_STATE_RING, 1, SUB_REAL, 0, NULL, callid);
@@ -11878,7 +11081,7 @@ static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event)
                case SIG_FXSGS:
                case SIG_FXSKS:
                case SIG_FXOKS:
-                       dahdi_disable_ec(i);
+                       dahdi_ec_disable(i);
                        /* Diddle the battery for the zhone */
 #ifdef ZHONE_HACK
                        dahdi_set_hook(i->subs[SUB_REAL].dfd, DAHDI_OFFHOOK);
@@ -11889,7 +11092,7 @@ static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event)
                        break;
                case SIG_SS7:
                case SIG_PRI_LIB_HANDLE_CASES:
-                       dahdi_disable_ec(i);
+                       dahdi_ec_disable(i);
                        res = tone_zone_play_tone(i->subs[SUB_REAL].dfd, -1);
                        break;
                default:
@@ -11952,6 +11155,11 @@ static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event)
        return NULL;
 }
 
+static void monitor_pfds_clean(void *arg) {
+       struct pollfd **pfds = arg;
+       ast_free(*pfds);
+}
+
 static void *do_monitor(void *data)
 {
        int count, res, res2, spoint, pollres=0;
@@ -11975,6 +11183,7 @@ static void *do_monitor(void *data)
 #endif
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
 
+       pthread_cleanup_push(monitor_pfds_clean, &pfds);
        for (;;) {
                /* Lock the interface list */
                ast_mutex_lock(&iflock);
@@ -11997,7 +11206,7 @@ static void *do_monitor(void *data)
                for (i = iflist; i; i = i->next) {
                        ast_mutex_lock(&i->lock);
                        if (pfds && (i->subs[SUB_REAL].dfd > -1) && i->sig && (!i->radio) && !(i->sig & SIG_MFCR2)) {
-                               if (analog_lib_handles(i->sig, i->radio, i->oprmode)) {
+                               if (dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode)) {
                                        struct analog_pvt *p = i->sig_pvt;
 
                                        if (!p) {
@@ -12008,7 +11217,7 @@ static void *do_monitor(void *data)
                                                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 || 
+                                               if (i->cidspill || i->mwisendactive || i->mwimonitor_fsk ||
                                                        (i->cid_start == CID_START_DTMF_NOALERT && (i->sig == SIG_FXSLS || i->sig == SIG_FXSGS || i->sig == SIG_FXSKS))) {
                                                        pfds[count].events |= POLLIN;
                                                }
@@ -12058,11 +11267,7 @@ static void *do_monitor(void *data)
                doomed = NULL;
                for (i = iflist;; i = i->next) {
                        if (doomed) {
-                               int res;
-                               res = dahdi_destroy_channel_bynum(doomed->channel);
-                               if (res != RESULT_SUCCESS) {
-                                       ast_log(LOG_WARNING, "Couldn't find channel to destroy, hopefully another destroy operation just happened.\n");
-                               }
+                               dahdi_destroy_channel_range(doomed->channel, doomed->channel);
                                doomed = NULL;
                        }
                        if (!i) {
@@ -12111,7 +11316,7 @@ 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);
-                                               if (analog_lib_handles(i->sig, i->radio, i->oprmode))
+                                               if (dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode))
                                                        doomed = (struct dahdi_pvt *) analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res));
                                                else
                                                        doomed = handle_init_event(i, res);
@@ -12161,7 +11366,7 @@ static void *do_monitor(void *data)
                                                        int energy;
                                                        struct timeval now;
                                                        /* State machine dtmfcid_holdoff_state allows for the line to settle
-                                                        * before checking agin for dtmf energy.  Presently waits for 500 mS before checking again 
+                                                        * before checking agin for dtmf energy.  Presently waits for 500 mS before checking again
                                                        */
                                                        if (1 == i->dtmfcid_holdoff_state) {
                                                                gettimeofday(&i->dtmfcid_delay, NULL);
@@ -12177,9 +11382,9 @@ static void *do_monitor(void *data)
                                                                        pthread_t threadid;
                                                                        struct ast_channel *chan;
                                                                        ast_mutex_unlock(&iflock);
-                                                                       if (analog_lib_handles(i->sig, i->radio, i->oprmode)) {
+                                                                       if (dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode)) {
                                                                                /* just in case this event changes or somehow destroys a channel, set doomed here too */
-                                                                               doomed = analog_handle_init_event(i->sig_pvt, ANALOG_EVENT_DTMFCID);  
+                                                                               doomed = analog_handle_init_event(i->sig_pvt, ANALOG_EVENT_DTMFCID);
                                                                                i->dtmfcid_holdoff_state = 1;
                                                                        } else {
                                                                                struct ast_callid *callid = NULL;
@@ -12222,7 +11427,7 @@ 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)) {
-                                               if (analog_lib_handles(i->sig, i->radio, i->oprmode))
+                                               if (dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode))
                                                        doomed = (struct dahdi_pvt *) analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res));
                                                else
                                                        doomed = handle_init_event(i, res);
@@ -12234,6 +11439,7 @@ static void *do_monitor(void *data)
                ast_mutex_unlock(&iflock);
        }
        /* Never reached */
+       pthread_cleanup_pop(1);
        return NULL;
 
 }
@@ -12433,14 +11639,20 @@ static void dahdi_r2_destroy_links(void)
        r2links_count = 0;
 }
 
-#define R2_LINK_CAPACITY 10
-static struct dahdi_mfcr2 *dahdi_r2_get_link(void)
+/* This is an artificial convenient capacity, to keep at most a full E1 of channels in a single thread */
+#define R2_LINK_CAPACITY 30
+static struct dahdi_mfcr2 *dahdi_r2_get_link(const struct dahdi_chan_conf *conf)
 {
        struct dahdi_mfcr2 *new_r2link = NULL;
        struct dahdi_mfcr2 **new_r2links = NULL;
-       /* this function is called just when starting up and no monitor threads have been launched,
-          no need to lock monitored_count member */
-       if (!r2links_count || (r2links[r2links_count - 1]->monitored_count == R2_LINK_CAPACITY)) {
+
+       /* Only create a new R2 link if
+          1. This is the first link requested
+          2. Configuration changed
+          3. We got more channels than supported per link */
+       if (!r2links_count ||
+           memcmp(&conf->mfcr2, &r2links[r2links_count - 1]->conf, sizeof(conf->mfcr2)) ||
+          (r2links[r2links_count - 1]->numchans == R2_LINK_CAPACITY)) {
                new_r2link = ast_calloc(1, sizeof(**r2links));
                if (!new_r2link) {
                        ast_log(LOG_ERROR, "Cannot allocate R2 link!\n");
@@ -12512,7 +11724,8 @@ static int dahdi_r2_set_context(struct dahdi_mfcr2 *r2_link, const struct dahdi_
                        ast_log(LOG_ERROR, "Failed to configure r2context from advanced configuration file %s\n", conf->mfcr2.r2proto_file);
                }
        }
-       r2_link->monitored_count = 0;
+       /* Save the configuration used to setup this link */
+       memcpy(&r2_link->conf, conf, sizeof(r2_link->conf));
        return 0;
 }
 #endif
@@ -12684,7 +11897,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                        tmp->sig = chan_sig;
                        tmp->outsigmod = conf->chan.outsigmod;
 
-                       if (analog_lib_handles(chan_sig, tmp->radio, tmp->oprmode)) {
+                       if (dahdi_analog_lib_handles(chan_sig, tmp->radio, tmp->oprmode)) {
                                analog_p = analog_new(dahdisig_to_analogsig(chan_sig), tmp);
                                if (!analog_p) {
                                        destroy_dahdi_pvt(tmp);
@@ -12743,7 +11956,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
 #ifdef HAVE_OPENR2
                        if (chan_sig == SIG_MFCR2) {
                                struct dahdi_mfcr2 *r2_link;
-                               r2_link = dahdi_r2_get_link();
+                               r2_link = dahdi_r2_get_link(conf);
                                if (!r2_link) {
                                        ast_log(LOG_WARNING, "Cannot get another R2 DAHDI context!\n");
                                        destroy_dahdi_pvt(tmp);
@@ -12785,7 +11998,6 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                tmp->mfcr2call = 0;
                                tmp->mfcr2_dnis_index = 0;
                                tmp->mfcr2_ani_index = 0;
-                               r2_link->monitored_count++;
                        }
 #endif
 #ifdef HAVE_PRI
@@ -13199,7 +12411,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                ast_copy_string(tmp->description, conf->chan.description, sizeof(tmp->description));
                ast_copy_string(tmp->parkinglot, conf->chan.parkinglot, sizeof(tmp->parkinglot));
                tmp->cid_ton = 0;
-               if (analog_lib_handles(tmp->sig, tmp->radio, tmp->oprmode)) {
+               if (dahdi_analog_lib_handles(tmp->sig, tmp->radio, tmp->oprmode)) {
                        ast_copy_string(tmp->cid_num, conf->chan.cid_num, sizeof(tmp->cid_num));
                        ast_copy_string(tmp->cid_name, conf->chan.cid_name, sizeof(tmp->cid_name));
                } else {
@@ -13255,6 +12467,10 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                                }
                        }
                }
+               tmp->hwrxgain_enabled = conf->chan.hwrxgain_enabled;
+               tmp->hwtxgain_enabled = conf->chan.hwtxgain_enabled;
+               tmp->hwrxgain = conf->chan.hwrxgain;
+               tmp->hwtxgain = conf->chan.hwtxgain;
                tmp->cid_rxgain = conf->chan.cid_rxgain;
                tmp->rxgain = conf->chan.rxgain;
                tmp->txgain = conf->chan.txgain;
@@ -13262,10 +12478,16 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                tmp->rxdrc = conf->chan.rxdrc;
                tmp->tonezone = conf->chan.tonezone;
                if (tmp->subs[SUB_REAL].dfd > -1) {
+                       if (tmp->hwrxgain_enabled) {
+                               tmp->hwrxgain_enabled = !set_hwgain(tmp->subs[SUB_REAL].dfd, tmp->hwrxgain, 0);
+                       }
+                       if (tmp->hwtxgain_enabled) {
+                               tmp->hwtxgain_enabled = !set_hwgain(tmp->subs[SUB_REAL].dfd, tmp->hwtxgain, 1);
+                       }
                        set_actual_gain(tmp->subs[SUB_REAL].dfd, tmp->rxgain, tmp->txgain, tmp->rxdrc, tmp->txdrc, tmp->law);
                        if (tmp->dsp)
                                ast_dsp_set_digitmode(tmp->dsp, DSP_DIGITMODE_DTMF | tmp->dtmfrelax);
-                       update_conf(tmp);
+                       dahdi_conf_update(tmp);
                        if (!here) {
                                switch (chan_sig) {
                                case SIG_PRI_LIB_HANDLE_CASES:
@@ -13525,7 +12747,7 @@ static int available(struct dahdi_pvt **pvt, int is_specific_channel)
        if (p->inalarm)
                return 0;
 
-       if (analog_lib_handles(p->sig, p->radio, p->oprmode))
+       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode))
                return analog_available(p->sig_pvt);
 
        switch (p->sig) {
@@ -14031,7 +13253,7 @@ static struct ast_channel *dahdi_request(const char *type, struct ast_format_cap
                        }
 
                        p->outgoing = 1;
-                       if (analog_lib_handles(p->sig, p->radio, p->oprmode)) {
+                       if (dahdi_analog_lib_handles(p->sig, p->radio, p->oprmode)) {
                                tmp = analog_request(p->sig_pvt, &callwait, requestor);
 #ifdef HAVE_PRI
                        } else if (dahdi_sig_pri_lib_handles(p->sig)) {
@@ -14502,10 +13724,21 @@ static int prepare_pri(struct dahdi_pri *pri)
        for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
                if (!pri->dchannels[i])
                        break;
+               if (pri->pri.fds[i] >= 0) {
+                       /* A partial range addition. Not a complete setup. */
+                       break;
+               }
                pri->pri.fds[i] = open("/dev/dahdi/channel", O_RDWR);
+               if ((pri->pri.fds[i] < 0)) {
+                       ast_log(LOG_ERROR, "Unable to open D-channel (fd=%d) (%s)\n",
+                               pri->pri.fds[i], strerror(errno));
+                       return -1;
+               }
                x = pri->dchannels[i];
-               if ((pri->pri.fds[i] < 0) || (ioctl(pri->pri.fds[i],DAHDI_SPECIFY,&x) == -1)) {
-                       ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno));
+               res = ioctl(pri->pri.fds[i], DAHDI_SPECIFY, &x);
+               if (res) {
+                       dahdi_close_pri_fd(pri, i);
+                       ast_log(LOG_ERROR, "Unable to SPECIFY channel %d (%s)\n", x, strerror(errno));
                        return -1;
                }
                memset(&p, 0, sizeof(p));
@@ -14897,6 +14130,107 @@ static char *handle_pri_show_spans(struct ast_cli_entry *e, int cmd, struct ast_
 #endif /* defined(HAVE_PRI) */
 
 #if defined(HAVE_PRI)
+#define container_of(ptr, type, member) \
+       ((type *)((char *)(ptr) - offsetof(type, member)))
+/*!
+ * \internal
+ * \brief Destroy a D-Channel of a PRI span
+ * \since 12
+ *
+ * \param pri the pri span
+ *
+ * Shuts down a span and destroys its D-Channel. Further destruction
+ * of the B-channels using dahdi_destroy_channel() would probably be required
+ * for the B-Channels.
+ */
+static void pri_destroy_span(struct sig_pri_span *pri)
+{
+       int i;
+       int res;
+       int cancel_code;
+       struct dahdi_pri* dahdi_pri;
+       pthread_t master = pri->master;
+
+       if (!master || (master == AST_PTHREADT_NULL)) {
+               return;
+       }
+       ast_debug(2, "About to destroy DAHDI channels of span %d.\n", pri->span);
+       for (i = 0; i < pri->numchans; i++) {
+               int channel;
+               struct sig_pri_chan *pvt = pri->pvts[i];
+
+               if (!pvt) {
+                       continue;
+               }
+               channel = pvt->channel;
+               ast_debug(2, "About to destroy B-channel %d.\n", channel);
+               dahdi_destroy_channel_range(channel, channel);
+       }
+
+       cancel_code = pthread_cancel(master);
+       pthread_kill(master, SIGURG);
+       ast_debug(4,
+               "Waiting to join thread of span %d "
+               "with pid=%p cancel_code=%d\n",
+               pri->span, (void *)master, cancel_code);
+       res = pthread_join(master, NULL);
+       if (res != 0) {
+               ast_log(LOG_NOTICE, "pthread_join failed: %d\n", res);
+       }
+       pri->master = AST_PTHREADT_NULL;
+
+       /* The 'struct dahdi_pri' that contains our 'struct sig_pri_span' */
+       dahdi_pri = container_of(pri, struct dahdi_pri, pri);
+       for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
+               ast_debug(4, "closing pri_fd %d\n", i);
+               dahdi_close_pri_fd(dahdi_pri, i);
+       }
+       sig_pri_init_pri(pri);
+       ast_debug(1, "PRI span %d destroyed\n", pri->span);
+}
+
+static char *handle_pri_destroy_span(struct ast_cli_entry *e, int cmd,
+               struct ast_cli_args *a)
+{
+       int span;
+       int res;
+       struct sig_pri_span *pri;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "pri destroy span";
+               e->usage =
+                       "Usage: pri destroy span <span>\n"
+                       "       Destorys D-channel of span and its B-channels.\n"
+                       "       DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_span_4(a->line, a->word, a->pos, a->n);
+       }
+
+       if (a->argc < 4) {
+               return CLI_SHOWUSAGE;
+       }
+       res = sscanf(a->argv[3], "%30d", &span);
+       if ((res != 1) || span < 1 || span > NUM_SPANS) {
+               ast_cli(a->fd,
+                       "Invalid span '%s'.  Should be a number from %d to %d\n",
+                       a->argv[3], 1, NUM_SPANS);
+               return CLI_SUCCESS;
+       }
+       pri = &pris[span - 1].pri;
+       if (!pri->pri) {
+               ast_cli(a->fd, "No PRI running on span %d\n", span);
+               return CLI_SUCCESS;
+       }
+
+       pri_destroy_span(pri);
+       return CLI_SUCCESS;
+}
+
+#endif /* defined(HAVE_PRI) */
+
+#if defined(HAVE_PRI)
 static char *handle_pri_show_span(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        int span;
@@ -15002,6 +14336,7 @@ static struct ast_cli_entry dahdi_pri_cli[] = {
        AST_CLI_DEFINE(handle_pri_show_channels, "Displays PRI channel information"),
        AST_CLI_DEFINE(handle_pri_show_spans, "Displays PRI span information"),
        AST_CLI_DEFINE(handle_pri_show_span, "Displays PRI span information"),
+       AST_CLI_DEFINE(handle_pri_destroy_span, "Destroy a PRI span"),
        AST_CLI_DEFINE(handle_pri_show_debug, "Displays current PRI debug settings"),
        AST_CLI_DEFINE(handle_pri_set_debug_file, "Sends PRI debug output to the specified file"),
        AST_CLI_DEFINE(handle_pri_version, "Displays libpri version"),
@@ -15341,26 +14676,97 @@ static struct ast_cli_entry dahdi_mfcr2_cli[] = {
 
 #endif /* HAVE_OPENR2 */
 
-static char *dahdi_destroy_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *dahdi_destroy_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       int channel;
-       int ret;
+       int start;
+       int end;
        switch (cmd) {
        case CLI_INIT:
-               e->command = "dahdi destroy channel";
+               e->command = "dahdi destroy channels";
                e->usage =
-                       "Usage: dahdi destroy channel <chan num>\n"
+                       "Usage: dahdi destroy channels <from_channel> [<to_channel>]\n"
                        "       DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.  Immediately removes a given channel, whether it is in use or not\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
        }
-       if (a->argc != 4)
+       if ((a->argc < 4) || a->argc > 5) {
                return CLI_SHOWUSAGE;
+       }
+       start = atoi(a->argv[3]);
+       if (start < 1) {
+               ast_cli(a->fd, "Invalid starting channel number %s.\n",
+                               a->argv[4]);
+               return CLI_FAILURE;
+       }
+       if (a->argc == 5) {
+               end = atoi(a->argv[4]);
+               if (end < 1) {
+                       ast_cli(a->fd, "Invalid ending channel number %s.\n",
+                                       a->argv[4]);
+                       return CLI_FAILURE;
+               }
+       } else {
+               end = start;
+       }
 
-       channel = atoi(a->argv[3]);
-       ret = dahdi_destroy_channel_bynum(channel);
-       return ( RESULT_SUCCESS == ret ) ? CLI_SUCCESS : CLI_FAILURE;
+       if (end < start) {
+               ast_cli(a->fd,
+                       "range end (%d) is smaller than range start (%d)\n",
+                       end, start);
+               return CLI_FAILURE;
+       }
+       dahdi_destroy_channel_range(start, end);
+       return CLI_SUCCESS;
+}
+
+static char *dahdi_create_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       int start;
+       int end;
+       int ret;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "dahdi create channels";
+               e->usage = "Usage: dahdi create channels <from> [<to>] - a range of channels\n"
+                          "       dahdi create channels new           - add channels not yet created\n"
+                          "For ISDN  and SS7 the range should include complete spans.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+       if ((a->argc < 4) || a->argc > 5) {
+               return CLI_SHOWUSAGE;
+       }
+       if (a->argc == 4 && !strcmp(a->argv[3], "new")) {
+               ret = dahdi_create_channel_range(0, 0);
+               return (RESULT_SUCCESS == ret) ? CLI_SUCCESS : CLI_FAILURE;
+       }
+       start = atoi(a->argv[3]);
+       if (start <= 0) {
+               ast_cli(a->fd, "Invalid starting channel number '%s'.\n",
+                               a->argv[3]);
+               return CLI_FAILURE;
+       }
+       if (a->argc == 5) {
+               end = atoi(a->argv[4]);
+               if (end <= 0) {
+                       ast_cli(a->fd, "Invalid ending channel number '%s'.\n",
+                                       a->argv[4]);
+                       return CLI_FAILURE;
+               }
+       } else {
+               end = start;
+       }
+       if (end < start) {
+               ast_cli(a->fd,
+                       "range end (%d) is smaller than range start (%d)\n",
+                       end, start);
+               return CLI_FAILURE;
+       }
+       ret = dahdi_create_channel_range(start, end);
+       return (RESULT_SUCCESS == ret) ? CLI_SUCCESS : CLI_FAILURE;
 }
 
 static void dahdi_softhangup_all(void)
@@ -15391,7 +14797,6 @@ retry:
        ast_mutex_unlock(&iflock);
 }
 
-static int setup_dahdi(int reload);
 static int dahdi_restart(void)
 {
 #if defined(HAVE_PRI) || defined(HAVE_SS7)
@@ -15462,6 +14867,7 @@ static int dahdi_restart(void)
        dahdi_softhangup_all();
        ast_verb(4, "Final softhangup of all DAHDI channels complete.\n");
        destroy_all_channels();
+       memset(round_robin, 0, sizeof(round_robin));
        ast_debug(1, "Channels destroyed. Now re-reading config. %d active channels remaining.\n", ast_active_channels());
 
        ast_mutex_unlock(&monlock);
@@ -15630,6 +15036,8 @@ static char *dahdi_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli
        struct dahdi_confinfo ci;
        struct dahdi_params ps;
        int x;
+       char hwrxgain[15];
+       char hwtxgain[15];
 
        switch (cmd) {
        case CLI_INIT:
@@ -15703,7 +15111,18 @@ static char *dahdi_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli
                        ast_cli(a->fd, "Default law: %s\n", tmp->law_default == DAHDI_LAW_MULAW ? "ulaw" : tmp->law_default == DAHDI_LAW_ALAW ? "alaw" : "unknown");
                        ast_cli(a->fd, "Fax Handled: %s\n", tmp->faxhandled ? "yes" : "no");
                        ast_cli(a->fd, "Pulse phone: %s\n", tmp->pulsedial ? "yes" : "no");
-                       ast_cli(a->fd, "Gains (RX/TX): %.2f/%.2f\n", tmp->rxgain, tmp->txgain);
+                       if (tmp->hwrxgain_enabled) {
+                               snprintf(hwrxgain, sizeof(hwrxgain), "%.1f", tmp->hwrxgain);
+                       } else {
+                               ast_copy_string(hwrxgain, "Disabled", sizeof(hwrxgain));
+                       }
+                       if (tmp->hwtxgain_enabled) {
+                               snprintf(hwtxgain, sizeof(hwtxgain), "%.1f", tmp->hwtxgain);
+                       } else {
+                               ast_copy_string(hwtxgain, "Disabled", sizeof(hwtxgain));
+                       }
+                       ast_cli(a->fd, "HW Gains (RX/TX): %s/%s\n", hwrxgain, hwtxgain);
+                       ast_cli(a->fd, "SW Gains (RX/TX): %.2f/%.2f\n", tmp->rxgain, tmp->txgain);
                        ast_cli(a->fd, "Dynamic Range Compression (RX/TX): %.2f/%.2f\n", tmp->rxdrc, tmp->txdrc);
                        ast_cli(a->fd, "DND: %s\n", dahdi_dnd(tmp, -1) ? "yes" : "no");
                        ast_cli(a->fd, "Echo Cancellation:\n");
@@ -15969,22 +15388,26 @@ static char *dahdi_show_version(struct ast_cli_entry *e, int cmd, struct ast_cli
 static char *dahdi_set_hwgain(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        int channel;
-       int gain;
+       float gain;
        int tx;
-       struct dahdi_hwgain hwgain;
        struct dahdi_pvt *tmp = NULL;
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "dahdi set hwgain";
+               e->command = "dahdi set hwgain {rx|tx}";
                e->usage =
                        "Usage: dahdi set hwgain <rx|tx> <chan#> <gain>\n"
-                       "       Sets the hardware gain on a a given channel, overriding the\n"
-                       "   value provided at module loadtime, whether the channel is in\n"
-                       "   use or not.  Changes take effect immediately.\n"
+                       "   Sets the hardware gain on a given channel and overrides the\n"
+                       "   value provided at module loadtime.  Changes take effect\n"
+                       "   immediately whether the channel is in use or not.\n"
+                       "\n"
                        "   <rx|tx> which direction do you want to change (relative to our module)\n"
                        "   <chan num> is the channel number relative to the device\n"
-                       "   <gain> is the gain in dB (e.g. -3.5 for -3.5dB)\n";
+                       "   <gain> is the gain in dB (e.g. -3.5 for -3.5dB)\n"
+                       "\n"
+                       "   Please note:\n"
+                       "   * hwgain is only supportable by hardware with analog ports because\n"
+                       "     hwgain works on the analog side of an analog-digital conversion.\n";
                return NULL;
        case CLI_GENERATE:
                return NULL;
@@ -16001,7 +15424,7 @@ static char *dahdi_set_hwgain(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                return CLI_SHOWUSAGE;
 
        channel = atoi(a->argv[4]);
-       gain = atof(a->argv[5])*10.0;
+       gain = atof(a->argv[5]);
 
        ast_mutex_lock(&iflock);
 
@@ -16013,15 +15436,21 @@ static char *dahdi_set_hwgain(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                if (tmp->subs[SUB_REAL].dfd == -1)
                        break;
 
-               hwgain.newgain = gain;
-               hwgain.tx = tx;
-               if (ioctl(tmp->subs[SUB_REAL].dfd, DAHDI_SET_HWGAIN, &hwgain) < 0) {
+               if (set_hwgain(tmp->subs[SUB_REAL].dfd, gain, tx)) {
                        ast_cli(a->fd, "Unable to set the hardware gain for channel %d: %s\n", channel, strerror(errno));
                        ast_mutex_unlock(&iflock);
                        return CLI_FAILURE;
                }
-               ast_cli(a->fd, "hardware %s gain set to %d (%.1f dB) on channel %d\n",
-                       tx ? "tx" : "rx", gain, (float)gain/10.0, channel);
+               ast_cli(a->fd, "Hardware %s gain set to %.1f dB on channel %d.\n",
+                       tx ? "tx" : "rx", gain, channel);
+
+               if (tx) {
+                       tmp->hwtxgain_enabled = 1;
+                       tmp->hwtxgain = gain;
+               } else {
+                       tmp->hwrxgain_enabled = 1;
+                       tmp->hwrxgain = gain;
+               }
                break;
        }
 
@@ -16045,12 +15474,13 @@ static char *dahdi_set_swgain(struct ast_cli_entry *e, int cmd, struct ast_cli_a
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "dahdi set swgain";
+               e->command = "dahdi set swgain {rx|tx}";
                e->usage =
                        "Usage: dahdi set swgain <rx|tx> <chan#> <gain>\n"
-                       "       Sets the software gain on a a given channel, overriding the\n"
-                       "   value provided at module loadtime, whether the channel is in\n"
-                       "   use or not.  Changes take effect immediately.\n"
+                       "   Sets the software gain on a given channel and overrides the\n"
+                       "   value provided at module loadtime.  Changes take effect\n"
+                       "   immediately whether the channel is in use or not.\n"
+                       "\n"
                        "   <rx|tx> which direction do you want to change (relative to our module)\n"
                        "   <chan num> is the channel number relative to the device\n"
                        "   <gain> is the gain in dB (e.g. -3.5 for -3.5dB)\n";
@@ -16092,8 +15522,14 @@ static char *dahdi_set_swgain(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                        return CLI_FAILURE;
                }
 
-               ast_cli(a->fd, "software %s gain set to %.1f on channel %d\n",
+               ast_cli(a->fd, "Software %s gain set to %.2f dB on channel %d.\n",
                        tx ? "tx" : "rx", gain, channel);
+
+               if (tx) {
+                       tmp->txgain = gain;
+               } else {
+                       tmp->rxgain = gain;
+               }
                break;
        }
        ast_mutex_unlock(&iflock);
@@ -16167,7 +15603,8 @@ static struct ast_cli_entry dahdi_cli[] = {
        AST_CLI_DEFINE(handle_dahdi_show_cadences, "List cadences"),
        AST_CLI_DEFINE(dahdi_show_channels, "Show active DAHDI channels"),
        AST_CLI_DEFINE(dahdi_show_channel, "Show information on a channel"),
-       AST_CLI_DEFINE(dahdi_destroy_channel, "Destroy a channel"),
+       AST_CLI_DEFINE(dahdi_destroy_channels, "Destroy channels"),
+       AST_CLI_DEFINE(dahdi_create_channels, "Create channels"),
        AST_CLI_DEFINE(dahdi_restart_cmd, "Fully restart DAHDI channels"),
        AST_CLI_DEFINE(dahdi_show_status, "Show all DAHDI cards status"),
        AST_CLI_DEFINE(dahdi_show_version, "Show the DAHDI version in use"),
@@ -16282,7 +15719,7 @@ static int action_transfer(struct mansession *s, const struct message *m)
                astman_send_error(s, m, "No such channel");
                return 0;
        }
-       if (!analog_lib_handles(p->sig, 0, 0)) {
+       if (!dahdi_analog_lib_handles(p->sig, 0, 0)) {
                astman_send_error(s, m, "Channel signaling is not analog");
                return 0;
        }
@@ -16305,7 +15742,7 @@ static int action_transferhangup(struct mansession *s, const struct message *m)
                astman_send_error(s, m, "No such channel");
                return 0;
        }
-       if (!analog_lib_handles(p->sig, 0, 0)) {
+       if (!dahdi_analog_lib_handles(p->sig, 0, 0)) {
                astman_send_error(s, m, "Channel signaling is not analog");
                return 0;
        }
@@ -17066,8 +16503,10 @@ static int __unload_module(void)
 
 #ifdef HAVE_PRI
        for (i = 0; i < NUM_SPANS; i++) {
-               if (pris[i].pri.master != AST_PTHREADT_NULL)
+               if (pris[i].pri.master != AST_PTHREADT_NULL) {
                        pthread_cancel(pris[i].pri.master);
+                       pthread_kill(pris[i].pri.master, SIGURG);
+               }
        }
        ast_cli_unregister_multiple(dahdi_pri_cli, ARRAY_LEN(dahdi_pri_cli));
        ast_unregister_application(dahdi_send_keypad_facility_app);
@@ -17077,9 +16516,11 @@ static int __unload_module(void)
 #endif
 #if defined(HAVE_SS7)
        for (i = 0; i < NUM_SPANS; i++) {
-               if (linksets[i].ss7.master != AST_PTHREADT_NULL)
+               if (linksets[i].ss7.master != AST_PTHREADT_NULL) {
                        pthread_cancel(linksets[i].ss7.master);
+                       pthread_kill(linksets[i].ss7.master, SIGURG);
                }
+       }
        ast_cli_unregister_multiple(dahdi_ss7_cli, ARRAY_LEN(dahdi_ss7_cli));
 #endif /* defined(HAVE_SS7) */
 #if defined(HAVE_OPENR2)
@@ -17123,8 +16564,9 @@ static int __unload_module(void)
 
 #if defined(HAVE_PRI)
        for (i = 0; i < NUM_SPANS; i++) {
-               if (pris[i].pri.master && (pris[i].pri.master != AST_PTHREADT_NULL))
+               if (pris[i].pri.master && (pris[i].pri.master != AST_PTHREADT_NULL)) {
                        pthread_join(pris[i].pri.master, NULL);
+               }
                for (j = 0; j < SIG_PRI_NUM_DCHANS; j++) {
                        dahdi_close_pri_fd(&(pris[i]), j);
                }
@@ -17139,8 +16581,9 @@ static int __unload_module(void)
 
 #if defined(HAVE_SS7)
        for (i = 0; i < NUM_SPANS; i++) {
-               if (linksets[i].ss7.master && (linksets[i].ss7.master != AST_PTHREADT_NULL))
+               if (linksets[i].ss7.master && (linksets[i].ss7.master != AST_PTHREADT_NULL)) {
                        pthread_join(linksets[i].ss7.master, NULL);
+               }
                for (j = 0; j < SIG_SS7_NUM_DCHANS; j++) {
                        dahdi_close_ss7_fd(&(linksets[i]), j);
                }
@@ -17148,7 +16591,10 @@ static int __unload_module(void)
 #endif /* defined(HAVE_SS7) */
        ast_cond_destroy(&ss_thread_complete);
 
+       dahdi_native_unload();
+
        dahdi_tech.capabilities = ast_format_cap_destroy(dahdi_tech.capabilities);
+       STASIS_MESSAGE_TYPE_CLEANUP(dahdichannel_type);
        return 0;
 }
 
@@ -17191,7 +16637,7 @@ static char *parse_spanchan(char *chanstr, char **subdir)
        return p;
 }
 
-static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno, int *found_pseudo)
+static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno)
 {
        char *c, *chan;
        char *subdir;
@@ -17214,8 +16660,6 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r
                        finish = start;
                } else if (!strcasecmp(chan, "pseudo")) {
                        finish = start = CHAN_PSEUDO;
-                       if (found_pseudo)
-                               *found_pseudo = 1;
                } else {
                        ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", value, chan);
                        return -1;
@@ -17245,6 +16689,12 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r
                                        }
                                }
                        }
+                       if (conf->wanted_channels_start &&
+                               (real_channel < conf->wanted_channels_start ||
+                                real_channel > conf->wanted_channels_end)
+                          ) {
+                               continue;
+                       }
                        tmp = mkintf(real_channel, conf, reload);
 
                        if (tmp) {
@@ -17254,6 +16704,9 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r
                                                (reload == 1) ? "reconfigure" : "register", value);
                                return -1;
                        }
+                       if (real_channel == CHAN_PSEUDO) {
+                               has_pseudo = 1;
+                       }
                }
        }
 
@@ -17409,13 +16862,13 @@ static void parse_busy_pattern(struct ast_variable *v, struct ast_dsp_busy_patte
 
        for (; ;) {
                /* Scans the string for the next value in the pattern. If none, it checks to see if any have been entered so far. */
-               if(!sscanf(v->value, "%30d", &norval) && count_pattern == 0) { 
+               if(!sscanf(v->value, "%30d", &norval) && count_pattern == 0) {
                        ast_log(LOG_ERROR, "busypattern= expects either busypattern=tonelength,quietlength or busypattern=t1length, q1length, t2length, q2length at line %d.\n", v->lineno);
                        break;
                }
 
-               busy_cadence->pattern[count_pattern] = norval; 
-               
+               busy_cadence->pattern[count_pattern] = norval;
+
                count_pattern++;
                if (count_pattern == 4) {
                        break;
@@ -17429,18 +16882,17 @@ static void parse_busy_pattern(struct ast_variable *v, struct ast_dsp_busy_patte
        }
        busy_cadence->length = count_pattern;
 
-       if (count_pattern % 2 != 0) { 
+       if (count_pattern % 2 != 0) {
                /* The pattern length must be divisible by two */
                ast_log(LOG_ERROR, "busypattern= expects either busypattern=tonelength,quietlength or busypattern=t1length, q1length, t2length, q2length at line %d.\n", v->lineno);
        }
-       
+
 }
 
 static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct ast_variable *v, int reload, int options)
 {
        struct dahdi_pvt *tmp;
        int y;
-       int found_pseudo = 0;
        struct ast_variable *dahdichan = NULL;
 
        for (; v; v = v->next) {
@@ -17453,7 +16905,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                ast_log(LOG_WARNING, "Channel '%s' ignored.\n", v->value);
                                continue;
                        }
-                       if (build_channels(confp, v->value, reload, v->lineno, &found_pseudo)) {
+                       if (build_channels(confp, v->value, reload, v->lineno)) {
                                if (confp->ignore_failed_channels) {
                                        ast_log(LOG_WARNING, "Channel '%s' failure ignored: ignore_failed_channels.\n", v->value);
                                        continue;
@@ -17674,6 +17126,24 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                        if (ast_true(v->value)) {
                                confp->chan.mwimonitor_fsk = 1;
                        }
+               } else if (!strcasecmp(v->name, "hwrxgain")) {
+                       confp->chan.hwrxgain_enabled = 0;
+                       if (strcasecmp(v->value, "disabled")) {
+                               if (sscanf(v->value, "%30f", &confp->chan.hwrxgain) == 1) {
+                                       confp->chan.hwrxgain_enabled = 1;
+                               } else {
+                                       ast_log(LOG_WARNING, "Invalid hwrxgain: %s at line %d.\n", v->value, v->lineno);
+                               }
+                       }
+               } else if (!strcasecmp(v->name, "hwtxgain")) {
+                       confp->chan.hwtxgain_enabled = 0;
+                       if (strcasecmp(v->value, "disabled")) {
+                               if (sscanf(v->value, "%30f", &confp->chan.hwtxgain) == 1) {
+                                       confp->chan.hwtxgain_enabled = 1;
+                               } else {
+                                       ast_log(LOG_WARNING, "Invalid hwtxgain: %s at line %d.\n", v->value, v->lineno);
+                               }
+                       }
                } else if (!strcasecmp(v->name, "cid_rxgain")) {
                        if (sscanf(v->value, "%30f", &confp->chan.cid_rxgain) != 1) {
                                ast_log(LOG_WARNING, "Invalid cid_rxgain: %s at line %d.\n", v->value, v->lineno);
@@ -17720,7 +17190,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                } else if (!strcasecmp(v->name, "accountcode")) {
                        ast_copy_string(confp->chan.accountcode, v->value, sizeof(confp->chan.accountcode));
                } else if (!strcasecmp(v->name, "amaflags")) {
-                       y = ast_cdr_amaflags2int(v->value);
+                       y = ast_channel_string2amaflag(v->value);
                        if (y < 0)
                                ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d.\n", v->value, v->lineno);
                        else
@@ -18091,7 +17561,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 #if defined(HAVE_PRI_SERVICE_MESSAGES)
                        } else if (!strcasecmp(v->name, "service_message_support")) {
                                /* assuming switchtype for this channel group has been configured already */
-                               if ((confp->pri.pri.switchtype == PRI_SWITCH_ATT4ESS 
+                               if ((confp->pri.pri.switchtype == PRI_SWITCH_ATT4ESS
                                        || confp->pri.pri.switchtype == PRI_SWITCH_LUCENT5E
                                        || confp->pri.pri.switchtype == PRI_SWITCH_NI2) && ast_true(v->value)) {
                                        confp->pri.pri.enable_service_message_support = 1;
@@ -18586,8 +18056,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 
        if (dahdichan) {
                /* Process the deferred dahdichan value. */
-               if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno,
-                       &found_pseudo)) {
+               if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno)) {
                        if (confp->ignore_failed_channels) {
                                ast_log(LOG_WARNING,
                                        "Dahdichan '%s' failure ignored: ignore_failed_channels.\n",
@@ -18602,7 +18071,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
        for (tmp = iflist, y=-1; tmp; tmp = tmp->next) {
                if (!tmp->destroy && tmp->span != y) {
                        tmp->manages_span_alarms = 1;
-                       y = tmp->span; 
+                       y = tmp->span;
                } else {
                        tmp->manages_span_alarms = 0;
                }
@@ -18610,7 +18079,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
 
        /*< \todo why check for the pseudo in the per-channel section.
         * Any actual use for manual setup of the pseudo channel? */
-       if (!found_pseudo && reload != 1 && !(options & PROC_DAHDI_OPT_NOCHAN)) {
+       if (!has_pseudo && reload != 1 && !(options & PROC_DAHDI_OPT_NOCHAN)) {
                /* use the default configuration for a channel, so
                   that any settings from real configured channels
                   don't "leak" into the pseudo channel config
@@ -18624,6 +18093,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                }
                if (tmp) {
                        ast_verb(3, "Automatically generated pseudo channel\n");
+                       has_pseudo = 1;
                } else {
                        ast_log(LOG_WARNING, "Unable to register pseudo channel!\n");
                }
@@ -18903,7 +18373,8 @@ static int setup_dahdi_int(int reload, struct dahdi_chan_conf *default_conf, str
        if (reload != 1) {
                int x;
                for (x = 0; x < NUM_SPANS; x++) {
-                       if (pris[x].pri.pvts[0]) {
+                       if (pris[x].pri.pvts[0] &&
+                                       pris[x].pri.master == AST_PTHREADT_NULL) {
                                prepare_pri(pris + x);
                                if (sig_pri_start_pri(&pris[x].pri)) {
                                        ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);
@@ -19137,8 +18608,8 @@ static const struct ast_data_entry dahdi_data_providers[] = {
  * Module loading including tests for configuration or dependencies.
  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
- * configuration file or other non-critical problem return 
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
+ * configuration file or other non-critical problem return
  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
  */
 static int load_module(void)
@@ -19149,13 +18620,21 @@ static int load_module(void)
        int y;
 #endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */
 
-       if (!(dahdi_tech.capabilities = ast_format_cap_alloc())) {
+       if (STASIS_MESSAGE_TYPE_INIT(dahdichannel_type)) {
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       if (!(dahdi_tech.capabilities = ast_format_cap_alloc(0))) {
                return AST_MODULE_LOAD_FAILURE;
        }
        ast_format_cap_add(dahdi_tech.capabilities, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
        ast_format_cap_add(dahdi_tech.capabilities, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0));
        ast_format_cap_add(dahdi_tech.capabilities, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0));
 
+       if (dahdi_native_load(ast_module_info->self, &dahdi_tech)) {
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
 #ifdef HAVE_PRI
        memset(pris, 0, sizeof(pris));
        for (y = 0; y < NUM_SPANS; y++) {
@@ -19376,5 +18855,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, tdesc,
        .unload = unload_module,
        .reload = reload,
        .load_pri = AST_MODPRI_CHANNEL_DRIVER,
-               .nonoptreq = "res_smdi",
+       .nonoptreq = "res_smdi",
        );