res_fax.c: Add chan locked precondition comments.
[asterisk/asterisk.git] / res / res_fax.c
index e7070ff..94c512d 100644 (file)
@@ -36,6 +36,7 @@
 
 /*** MODULEINFO
        <conflict>app_fax</conflict>
+       <support_level>core</support_level>
 ***/
 
 /*! \file
  * \ingroup applications
  */
 
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
+/*! \li \ref res_fax.c uses the configuration file \ref res_fax.conf
+ * \addtogroup configuration_file Configuration Files
+ */
+
+/*!
+ * \page res_fax.conf res_fax.conf
+ * \verbinclude res_fax.conf.sample
+ */
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
 
 #include "asterisk/io.h"
 #include "asterisk/file.h"
@@ -77,11 +83,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
-#include "asterisk/manager.h"
 #include "asterisk/dsp.h"
 #include "asterisk/indications.h"
 #include "asterisk/ast_version.h"
 #include "asterisk/translate.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/smoother.h"
+#include "asterisk/format_cache.h"
 
 /*** DOCUMENTATION
        <application name="ReceiveFAX" language="en_US" module="res_fax">
@@ -216,18 +225,203 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        <enum name="statusstr">
                                                <para>R/O Verbose Result Status of the FAX transmission.</para>
                                        </enum>
+                                       <enum name="t38timeout">
+                                               <para>R/W The timeout used for T.38 negotiation.</para>
+                                       </enum>
                                </enumlist>
                        </parameter>
                </syntax>
                <description>
                        <para>FAXOPT can be used to override the settings for a FAX session listed in <filename>res_fax.conf</filename>,
-                       it can also be used to retreive information about a FAX session that has finished eg. pages/status.</para>
+                       it can also be used to retrieve information about a FAX session that has finished eg. pages/status.</para>
                </description>
                <see-also>
                        <ref type="application">ReceiveFax</ref>
                        <ref type="application">SendFax</ref>
                </see-also>
        </function>
+       <manager name="FAXSessions" language="en_US">
+               <synopsis>
+                       Lists active FAX sessions
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>Will generate a series of FAXSession events with information about each FAXSession. Closes with
+                       a FAXSessionsComplete event which includes a count of the included FAX sessions. This action works in
+                       the same manner as the CLI command 'fax show sessions'</para>
+               </description>
+       </manager>
+       <managerEvent language="en_US" name="FAXSessionsEntry">
+               <managerEventInstance class="EVENT_FLAG_REPORTING">
+                       <synopsis>A single list item for the FAXSessions AMI command</synopsis>
+                       <syntax>
+                               <parameter name="ActionID" required="false"/>
+                               <parameter name="Channel">
+                                       <para>Name of the channel responsible for the FAX session</para>
+                               </parameter>
+                               <parameter name="Technology">
+                                       <para>The FAX technology that the FAX session is using</para>
+                               </parameter>
+                               <parameter name="SessionNumber">
+                                       <para>The numerical identifier for this particular session</para>
+                               </parameter>
+                               <parameter name="SessionType">
+                                       <para>FAX session passthru/relay type</para>
+                                       <enumlist>
+                                               <enum name="G.711" />
+                                               <enum name="T.38" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Operation">
+                                       <para>FAX session operation type</para>
+                                       <enumlist>
+                                               <enum name="gateway" />
+                                               <enum name="V.21" />
+                                               <enum name="send" />
+                                               <enum name="receive" />
+                                               <enum name="none" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="State">
+                                       <para>Current state of the FAX session</para>
+                                       <enumlist>
+                                               <enum name="Uninitialized" />
+                                               <enum name="Initialized" />
+                                               <enum name="Open" />
+                                               <enum name="Active" />
+                                               <enum name="Complete" />
+                                               <enum name="Reserved" />
+                                               <enum name="Inactive" />
+                                               <enum name="Unknown" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Files">
+                                       <para>File or list of files associated with this FAX session</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="FAXSessionsComplete">
+               <managerEventInstance class="EVENT_FLAG_CALL">
+                       <synopsis>Raised when all FAXSession events are completed for a FAXSessions command</synopsis>
+                       <syntax>
+                               <parameter name="ActionID" required="false"/>
+                               <parameter name="Total">
+                                       <para>Count of FAXSession events sent in response to FAXSessions action</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <manager name="FAXSession" language="en_US">
+               <synopsis>
+                       Responds with a detailed description of a single FAX session
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="SessionNumber" required="true">
+                               <para>The session ID of the fax the user is interested in.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Provides details about a specific FAX session. The response will include a common subset of
+                       the output from the CLI command 'fax show session &lt;session_number&gt;' for each technology. If the
+                       FAX technolgy used by this session does not include a handler for FAXSession, then this action
+                       will fail.</para>
+               </description>
+       </manager>
+       <managerEvent language="en_US" name="FAXSession">
+               <managerEventInstance class="EVENT_FLAG_REPORTING">
+                       <synopsis>Raised in response to FAXSession manager command</synopsis>
+                       <syntax>
+                               <parameter name="ActionID" required="false"/>
+                               <parameter name="SessionNumber">
+                                       <para>The numerical identifier for this particular session</para>
+                               </parameter>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='FAXSessionsEntry']/managerEventInstance/syntax/parameter[@name='Operation'])" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='FAXSessionsEntry']/managerEventInstance/syntax/parameter[@name='State'])" />
+                               <parameter name="ErrorCorrectionMode" required="false">
+                                       <para>Whether error correcting mode is enabled for the FAX session. This field is not
+                                       included when operation is 'V.21 Detect' or if operation is 'gateway' and state is
+                                       'Uninitialized'
+                                       </para>
+                                       <enumlist>
+                                               <enum name="yes" />
+                                               <enum name="no" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="DataRate" required="false">
+                                       <para>Bit rate of the FAX. This field is not included when operation is 'V.21 Detect' or
+                                       if operation is 'gateway' and state is 'Uninitialized'.</para>
+                               </parameter>
+                               <parameter name="ImageResolution" required="false">
+                                       <para>Resolution of each page of the FAX. Will be in the format of X_RESxY_RES. This field
+                                       is not included if the operation is anything other than Receive/Transmit.</para>
+                               </parameter>
+                               <parameter name="PageNumber" required="false">
+                                       <para>Current number of pages transferred during this FAX session. May change as the FAX
+                                       progresses. This field is not included when operation is 'V.21 Detect' or if operation is
+                                       'gateway' and state is 'Uninitialized'.</para>
+                               </parameter>
+                               <parameter name="FileName" required="false">
+                                       <para>Filename of the image being sent/recieved for this FAX session. This field is not
+                                       included if Operation isn't 'send' or 'receive'.</para>
+                               </parameter>
+                               <parameter name="PagesTransmitted" required="false">
+                                       <para>Total number of pages sent during this session. This field is not included if
+                                       Operation isn't 'send' or 'receive'. Will always be 0 for 'receive'.</para>
+                               </parameter>
+                               <parameter name="PagesReceived" required="false">
+                                       <para>Total number of pages received during this session. This field is not included if
+                                       Operation is not 'send' or 'receive'. Will be 0 for 'send'.</para>
+                               </parameter>
+                               <parameter name="TotalBadLines" required="false">
+                                       <para>Total number of bad lines sent/recieved during this session. This field is not
+                                       included if Operation is not 'send' or 'received'.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <manager name="FAXStats" language="en_US">
+               <synopsis>
+                       Responds with fax statistics
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>Provides FAX statistics including the number of active sessions, reserved sessions, completed
+                       sessions, failed sessions, and the number of receive/transmit attempts. This command provides all
+                       of the non-technology specific information provided by the CLI command 'fax show stats'</para>
+               </description>
+       </manager>
+       <managerEvent language="en_US" name="FAXStats">
+               <managerEventInstance class="EVENT_FLAG_REPORTING">
+                       <synopsis>Raised in response to FAXStats manager command</synopsis>
+                       <syntax>
+                               <parameter name="ActionID" required="false"/>
+                               <parameter name="CurrentSessions" required="true">
+                                       <para>Number of active FAX sessions</para>
+                               </parameter>
+                               <parameter name="ReservedSessions" required="true">
+                                       <para>Number of reserved FAX sessions</para>
+                               </parameter>
+                               <parameter name="TransmitAttempts" required="true">
+                                       <para>Total FAX sessions for which Asterisk is/was the transmitter</para>
+                               </parameter>
+                               <parameter name="ReceiveAttempts" required="true">
+                                       <para>Total FAX sessions for which Asterisk is/was the recipient</para>
+                               </parameter>
+                               <parameter name="CompletedFAXes" required="true">
+                                       <para>Total FAX sessions which have been completed successfully</para>
+                               </parameter>
+                               <parameter name="FailedFAXes" required="true">
+                                       <para>Total FAX sessions which failed to complete successfully</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
 ***/
 
 static const char app_receivefax[] = "ReceiveFAX";
@@ -264,22 +458,20 @@ struct fax_gateway {
        /*! \brief a flag to track the state of our negotiation */
        enum ast_t38_state t38_state;
        /*! \brief original audio formats */
-       struct ast_format chan_read_format;
-       struct ast_format chan_write_format;
-       struct ast_format peer_read_format;
-       struct ast_format peer_write_format;
+       struct ast_format *chan_read_format;
+       struct ast_format *chan_write_format;
+       struct ast_format *peer_read_format;
+       struct ast_format *peer_write_format;
 };
 
 /*! \brief used for fax detect framehook */
 struct fax_detect {
        /*! \brief the start of our timeout counter */
        struct timeval timeout_start;
-       /*! \brief faxdetect timeout */
-       int timeout;
        /*! \brief DSP Processor */
        struct ast_dsp *dsp;
        /*! \brief original audio formats */
-       struct ast_format orig_format;
+       struct ast_format *orig_format;
        /*! \brief fax session details */
        struct ast_fax_session_details *details;
        /*! \brief mode */
@@ -326,18 +518,36 @@ struct fax_module {
 };
 static AST_RWLIST_HEAD_STATIC(faxmodules, fax_module);
 
-#define RES_FAX_MINRATE 2400
+#define RES_FAX_MINRATE 4800
 #define RES_FAX_MAXRATE 14400
 #define RES_FAX_STATUSEVENTS 0
-#define RES_FAX_MODEM (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27 | AST_FAX_MODEM_V29)
+#define RES_FAX_MODEM (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27TER | AST_FAX_MODEM_V29)
+#define RES_FAX_T38TIMEOUT 5000
 
-static struct {
+struct fax_options {
        enum ast_fax_modems modems;
        uint32_t statusevents:1;
        uint32_t ecm:1;
        unsigned int minrate;
        unsigned int maxrate;
-} general_options;
+       unsigned int t38timeout;
+};
+
+static struct fax_options general_options;
+
+static const struct fax_options default_options = {
+       .minrate = RES_FAX_MINRATE,
+       .maxrate = RES_FAX_MAXRATE,
+       .statusevents = RES_FAX_STATUSEVENTS,
+       .modems = RES_FAX_MODEM,
+       .ecm = AST_FAX_OPTFLAG_TRUE,
+       .t38timeout = RES_FAX_T38TIMEOUT,
+};
+
+AST_RWLOCK_DEFINE_STATIC(options_lock);
+
+static void get_general_options(struct fax_options* options);
+static void set_general_options(const struct fax_options* options);
 
 static const char *config = "res_fax.conf";
 
@@ -363,12 +573,6 @@ AST_APP_OPTIONS(fax_exec_options, BEGIN_OPTIONS
        AST_APP_OPTION('z', OPT_REQUEST_T38),
 END_OPTIONS);
 
-struct manager_event_info {
-       char context[AST_MAX_CONTEXT];
-       char exten[AST_MAX_EXTENSION];
-       char cid[128];
-};
-
 static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned int c2s, struct ast_frame *frame)
 {
        struct debug_info_history *history = c2s ? &s->debug_info->c2s : &s->debug_info->s2c;
@@ -391,7 +595,7 @@ static void debug_check_frame_for_silence(struct ast_fax_session *s, unsigned in
                history->consec_ms = 0;
 
                if ((last_consec_frames != 0)) {
-                       ast_verb(6, "Channel '%s' fax session '%d', [ %.3ld.%.6ld ], %s sent %d frames (%d ms) of %s.\n",
+                       ast_verb(0, "Channel '%s' fax session '%u', [ %.3ld.%.6ld ], %s sent %u frames (%u ms) of %s.\n",
                                 s->channame, s->id, (long) diff.tv_sec, (long int) diff.tv_usec,
                                 (c2s) ? "channel" : "stack", last_consec_frames, last_consec_ms,
                                 (wassil) ? "silence" : "energy");
@@ -409,11 +613,49 @@ static void destroy_callback(void *data)
        }
 }
 
+static void fixup_callback(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+
 static const struct ast_datastore_info fax_datastore = {
        .type = "res_fax",
        .destroy = destroy_callback,
+       .chan_fixup = fixup_callback,
 };
 
+static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details);
+static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags);
+static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan);
+static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan);
+
+
+/*! \brief Copies fax detection and gateway framehooks during masquerades
+ *
+ * \note must be called with both old_chan and new_chan locked. Since this
+ *       is only called by do_masquerade, that shouldn't be an issue.
+ */
+static void fixup_callback(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+       struct ast_fax_session_details *old_details = data;
+       struct ast_datastore *datastore = ast_channel_datastore_find(old_chan, &fax_datastore, NULL);
+
+       if (old_details->gateway_id >= 0) {
+               struct ast_fax_session_details *new_details = find_or_create_details(new_chan);
+
+               ast_framehook_detach(old_chan, old_details->gateway_id);
+               fax_gateway_attach(new_chan, new_details);
+               ao2_cleanup(new_details);
+       }
+
+       if (old_details->faxdetect_id >= 0) {
+               ast_framehook_detach(old_chan, old_details->faxdetect_id);
+               fax_detect_attach(new_chan, old_details->faxdetect_timeout, old_details->faxdetect_flags);
+       }
+
+       if (datastore) {
+               ast_channel_datastore_remove(old_chan, datastore);
+               ast_datastore_free(datastore);
+       }
+}
+
 /*! \brief returns a reference counted pointer to a fax datastore, if it exists */
 static struct ast_fax_session_details *find_details(struct ast_channel *chan)
 {
@@ -426,7 +668,7 @@ static struct ast_fax_session_details *find_details(struct ast_channel *chan)
                return NULL;
        }
        if (!(details = datastore->data)) {
-               ast_log(LOG_WARNING, "Huh?  channel '%s' has a FAX datastore without data!\n", chan->name);
+               ast_log(LOG_WARNING, "Huh?  channel '%s' has a FAX datastore without data!\n", ast_channel_name(chan));
                ast_channel_unlock(chan);
                return NULL;
        }
@@ -452,6 +694,7 @@ static void destroy_session_details(void *details)
 static struct ast_fax_session_details *session_details_new(void)
 {
        struct ast_fax_session_details *d;
+       struct fax_options options;
 
        if (!(d = ao2_alloc(sizeof(*d), destroy_session_details))) {
                return NULL;
@@ -462,6 +705,8 @@ static struct ast_fax_session_details *session_details_new(void)
                return NULL;
        }
 
+       get_general_options(&options);
+
        AST_LIST_HEAD_INIT_NOLOCK(&d->documents);
 
        /* These options need to be set to the configured default and may be overridden by
@@ -469,11 +714,12 @@ static struct ast_fax_session_details *session_details_new(void)
        d->option.request_t38 = AST_FAX_OPTFLAG_FALSE;
        d->option.send_cng = AST_FAX_OPTFLAG_FALSE;
        d->option.send_ced = AST_FAX_OPTFLAG_FALSE;
-       d->option.ecm = general_options.ecm;
-       d->option.statusevents = general_options.statusevents;
-       d->modems = general_options.modems;
-       d->minrate = general_options.minrate;
-       d->maxrate = general_options.maxrate;
+       d->option.ecm = options.ecm;
+       d->option.statusevents = options.statusevents;
+       d->modems = options.modems;
+       d->minrate = options.minrate;
+       d->maxrate = options.maxrate;
+       d->t38timeout = options.t38timeout;
        d->gateway_id = -1;
        d->faxdetect_id = -1;
        d->gateway_timeout = 0;
@@ -522,12 +768,12 @@ static struct ast_fax_session_details *find_or_create_details(struct ast_channel
        }
        /* channel does not have one so we must create one */
        if (!(details = session_details_new())) {
-               ast_log(LOG_WARNING, "channel '%s' can't get a FAX details structure for the datastore!\n", chan->name);
+               ast_log(LOG_WARNING, "channel '%s' can't get a FAX details structure for the datastore!\n", ast_channel_name(chan));
                return NULL;
        }
        if (!(datastore = ast_datastore_alloc(&fax_datastore, NULL))) {
                ao2_ref(details, -1);
-               ast_log(LOG_WARNING, "channel '%s' can't get a datastore!\n", chan->name);
+               ast_log(LOG_WARNING, "channel '%s' can't get a datastore!\n", ast_channel_name(chan));
                return NULL;
        }
        /* add the datastore to the channel and increment the refcount */
@@ -546,12 +792,18 @@ static struct ast_fax_session_details *find_or_create_details(struct ast_channel
 
 unsigned int ast_fax_maxrate(void)
 {
-       return general_options.maxrate;
+       struct fax_options options;
+       get_general_options(&options);
+
+       return options.maxrate;
 }
 
 unsigned int ast_fax_minrate(void)
 {
-       return general_options.minrate;
+       struct fax_options options;
+       get_general_options(&options);
+
+       return options.minrate;
 }
 
 static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
@@ -559,12 +811,12 @@ static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
        char *m[5], *tok, *v = (char *)value;
        int i = 0, j;
 
-       if (!(tok = strchr(v, ','))) {
+       if (!strchr(v, ',')) {
                m[i++] = v;
                m[i] = NULL;
        } else {
                tok = strtok(v, ", ");
-               while (tok && (i < 5)) {
+               while (tok && i < ARRAY_LEN(m) - 1) {
                        m[i++] = tok;
                        tok = strtok(NULL, ", ");
                }
@@ -576,7 +828,7 @@ static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
                if (!strcasecmp(m[j], "v17")) {
                        *bits |= AST_FAX_MODEM_V17;
                } else if (!strcasecmp(m[j], "v27")) {
-                       *bits |= AST_FAX_MODEM_V27;
+                       *bits |= AST_FAX_MODEM_V27TER;
                } else if (!strcasecmp(m[j], "v29")) {
                        *bits |= AST_FAX_MODEM_V29;
                } else if (!strcasecmp(m[j], "v34")) {
@@ -587,6 +839,7 @@ static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
        }
        return 0;
 }
+
 static char *ast_fax_caps_to_str(enum ast_fax_capabilities caps, char *buf, size_t bufsize)
 {
        char *out = buf;
@@ -654,7 +907,7 @@ static int ast_fax_modem_to_str(enum ast_fax_modems bits, char *tbuf, size_t buf
                strcat(tbuf, "V17");
                count++;
        }
-       if (bits & AST_FAX_MODEM_V27) {
+       if (bits & AST_FAX_MODEM_V27TER) {
                if (count) {
                        strcat(tbuf, ",");
                }
@@ -683,12 +936,8 @@ static int check_modem_rate(enum ast_fax_modems modems, unsigned int rate)
 {
        switch (rate) {
        case 2400:
-               if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
-                       return 1;
-               }
-               break;
        case 4800:
-               if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
+               if (!(modems & (AST_FAX_MODEM_V27TER | AST_FAX_MODEM_V34))) {
                        return 1;
                }
                break;
@@ -778,7 +1027,7 @@ const char *ast_fax_state_to_str(enum ast_fax_state state)
        case AST_FAX_STATE_INACTIVE:
                return "Inactive";
        default:
-               ast_log(LOG_WARNING, "unhandled FAX state: %d\n", state);
+               ast_log(LOG_WARNING, "unhandled FAX state: %u\n", state);
                return "Unknown";
        }
 }
@@ -967,6 +1216,7 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
        if (reserved) {
                s = reserved;
                ao2_ref(reserved, +1);
+               ao2_unlink(faxregistry.container, reserved);
 
                /* NOTE: we don't consume the reference to the reserved
                 * session. The session returned from fax_session_new() is a
@@ -1003,13 +1253,13 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
                ast_dsp_set_threshold(s->debug_info->dsp, 128);
        }
 
-       if (!(s->channame = ast_strdup(chan->name))) {
+       if (!(s->channame = ast_strdup(ast_channel_name(chan)))) {
                fax_session_release(s, token);
                ao2_ref(s, -1);
                return NULL;
        }
 
-       if (!(s->chan_uniqueid = ast_strdup(chan->uniqueid))) {
+       if (!(s->chan_uniqueid = ast_strdup(ast_channel_uniqueid(chan)))) {
                fax_session_release(s, token);
                ao2_ref(s, -1);
                return NULL;
@@ -1032,6 +1282,10 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
                        }
                        ast_debug(4, "Requesting a new FAX session from '%s'.\n", faxmod->tech->description);
                        ast_module_ref(faxmod->tech->module);
+                       if (reserved) {
+                               /* Balance module ref from reserved session */
+                               ast_module_unref(reserved->tech->module);
+                       }
                        s->tech = faxmod->tech;
                        break;
                }
@@ -1052,24 +1306,51 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
        }
        /* link the session to the session container */
        if (!(ao2_link(faxregistry.container, s))) {
-               ast_log(LOG_ERROR, "failed to add FAX session '%d' to container.\n", s->id);
+               ast_log(LOG_ERROR, "failed to add FAX session '%u' to container.\n", s->id);
                ao2_ref(s, -1);
                return NULL;
        }
-       ast_debug(4, "channel '%s' using FAX session '%d'\n", s->channame, s->id);
+       ast_debug(4, "channel '%s' using FAX session '%u'\n", s->channame, s->id);
 
        return s;
 }
 
-static void get_manager_event_info(struct ast_channel *chan, struct manager_event_info *info)
+/*!
+ * \internal
+ * \brief Convert the filenames in a fax session into a JSON array
+ * \retval NULL on error
+ * \retval A \ref ast_json array on success
+ */
+static struct ast_json *generate_filenames_json(struct ast_fax_session_details *details)
 {
-       pbx_substitute_variables_helper(chan, "${CONTEXT}", info->context, sizeof(info->context));
-       pbx_substitute_variables_helper(chan, "${EXTEN}", info->exten, sizeof(info->exten));
-       pbx_substitute_variables_helper(chan, "${CALLERID(num)}", info->cid, sizeof(info->cid));
-}
+       RAII_VAR(struct ast_json *, json_array, ast_json_array_create(), ast_json_unref);
+       struct ast_fax_document *doc;
 
+       if (!details || !json_array) {
+               return NULL;
+       }
 
-/* \brief Generate a string of filenames using the given prefix and separator.
+       /* don't process empty lists */
+       if (AST_LIST_EMPTY(&details->documents)) {
+               return NULL;
+       }
+
+       AST_LIST_TRAVERSE(&details->documents, doc, next) {
+               struct ast_json *entry = ast_json_string_create(doc->filename);
+               if (!entry) {
+                       return NULL;
+               }
+               if (ast_json_array_append(json_array, entry)) {
+                       return NULL;
+               }
+       }
+
+       ast_json_ref(json_array);
+       return json_array;
+}
+
+/*!
+ * \brief Generate a string of filenames using the given prefix and separator.
  * \param details the fax session details
  * \param prefix the prefix to each filename
  * \param separator the separator between filenames
@@ -1089,7 +1370,7 @@ static char *generate_filenames_string(struct ast_fax_session_details *details,
 
        /* don't process empty lists */
        if (AST_LIST_EMPTY(&details->documents)) {
-               return NULL;
+               return ast_strdup("");
        }
 
        /* Calculate the total length of all of the file names */
@@ -1119,39 +1400,38 @@ static char *generate_filenames_string(struct ast_fax_session_details *details,
 /*! \brief send a FAX status manager event */
 static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
 {
-       char *filenames = generate_filenames_string(details, "FileName: ", "\r\n");
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       struct ast_json *json_filenames = NULL;
 
-       ast_channel_lock(chan);
-       if (details->option.statusevents) {
-               struct manager_event_info info;
-
-               get_manager_event_info(chan, &info);
-               manager_event(EVENT_FLAG_CALL,
-                             "FAXStatus",
-                             "Operation: %s\r\n"
-                             "Status: %s\r\n"
-                             "Channel: %s\r\n"
-                             "Context: %s\r\n"
-                             "Exten: %s\r\n"
-                             "CallerID: %s\r\n"
-                             "LocalStationID: %s\r\n"
-                             "%s%s",
-                             (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send",
-                             status,
-                             chan->name,
-                             info.context,
-                             info.exten,
-                             info.cid,
-                             details->localstationid,
-                             S_OR(filenames, ""),
-                             filenames ? "\r\n" : "");
+       if (!details->option.statusevents) {
+               return 0;
        }
-       ast_channel_unlock(chan);
 
-       if (filenames) {
-               ast_free(filenames);
+       json_filenames = generate_filenames_json(details);
+       if (!json_filenames) {
+               return -1;
+       }
+
+       json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: o}",
+                       "type", "status",
+                       "operation", (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send",
+                       "status", status,
+                       "local_station_id", details->localstationid,
+                       "filenames", json_filenames);
+       if (!json_object) {
+               return -1;
        }
 
+       {
+               SCOPED_CHANNELLOCK(lock, chan);
+
+               message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), ast_channel_fax_type(), json_object);
+               if (!message) {
+                       return -1;
+               }
+               stasis_publish(ast_channel_topic(chan), message);
+       }
        return 0;
 }
 
@@ -1167,7 +1447,13 @@ static void set_channel_variables(struct ast_channel *chan, struct ast_fax_sessi
        pbx_builtin_setvar_helper(chan, "FAXBITRATE", S_OR(details->transfer_rate, NULL));
        pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", S_OR(details->resolution, NULL));
 
-       snprintf(buf, sizeof(buf), "%d", details->pages_transferred);
+       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+               pbx_builtin_setvar_helper(chan, "FAXMODE", "T38");
+       } else {
+               pbx_builtin_setvar_helper(chan, "FAXMODE", "audio");
+       }
+
+       snprintf(buf, sizeof(buf), "%u", details->pages_transferred);
        pbx_builtin_setvar_helper(chan, "FAXPAGES", buf);
 }
 
@@ -1185,12 +1471,11 @@ static void set_channel_variables(struct ast_channel *chan, struct ast_fax_sessi
 #define GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason) \
        do {    \
                GENERIC_FAX_EXEC_SET_VARS(fax, chan, errorstr, reason); \
-               res = ms = -1; \
        } while (0)
 
 #define GENERIC_FAX_EXEC_ERROR(fax, chan, errorstr, reason)    \
        do {    \
-               ast_log(LOG_ERROR, "channel '%s' FAX session '%d' failure, reason: '%s' (%s)\n", chan->name, fax->id, reason, errorstr); \
+               ast_log(LOG_ERROR, "channel '%s' FAX session '%u' failure, reason: '%s' (%s)\n", ast_channel_name(chan), fax->id, reason, errorstr); \
                GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \
        } while (0)
 
@@ -1216,14 +1501,14 @@ static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_det
                 */
                struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_PARMS, };
                if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters)) != AST_T38_REQUEST_PARMS) {
-                       ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
+                       ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", ast_channel_name(chan));
                        return -1;
                }
                details->caps |= AST_FAX_TECH_T38;
                break;
        }
        default:
-               ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
+               ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", ast_channel_name(chan));
                return -1;
        }
 
@@ -1232,30 +1517,31 @@ static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_det
 
 static int disable_t38(struct ast_channel *chan)
 {
-       int ms;
+       int timeout_ms;
        struct ast_frame *frame = NULL;
        struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
+       struct timeval start;
+       int ms;
 
-       ast_debug(1, "Shutting down T.38 on %s\n", chan->name);
+       ast_debug(1, "Shutting down T.38 on %s\n", ast_channel_name(chan));
        if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0) {
-               ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name);
+               ast_debug(1, "error while disabling T.38 on channel '%s'\n", ast_channel_name(chan));
                return -1;
        }
 
        /* wait up to five seconds for negotiation to complete */
-       ms = 5000;
-
-       while (ms > 0) {
+       timeout_ms = 5000;
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
                ms = ast_waitfor(chan, ms);
-               if (ms < 0) {
-                       ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name);
-                       return -1;
-               }
 
-               if (ms == 0) { /* all done, nothing happened */
-                       ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", chan->name);
+               if (ms == 0) {
                        break;
                }
+               if (ms < 0) {
+                       ast_debug(1, "error while disabling T.38 on channel '%s'\n", ast_channel_name(chan));
+                       return -1;
+               }
 
                if (!(frame = ast_read(chan))) {
                        return -1;
@@ -1267,14 +1553,14 @@ static int disable_t38(struct ast_channel *chan)
 
                        switch (parameters->request_response) {
                        case AST_T38_TERMINATED:
-                               ast_debug(1, "Shut down T.38 on %s\n", chan->name);
+                               ast_debug(1, "Shut down T.38 on %s\n", ast_channel_name(chan));
                                break;
                        case AST_T38_REFUSED:
-                               ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", chan->name);
+                               ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", ast_channel_name(chan));
                                ast_frfree(frame);
                                return -1;
                        default:
-                               ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", chan->name);
+                               ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", ast_channel_name(chan));
                                ast_frfree(frame);
                                return -1;
                        }
@@ -1284,6 +1570,10 @@ static int disable_t38(struct ast_channel *chan)
                ast_frfree(frame);
        }
 
+       if (ms == 0) { /* all done, nothing happened */
+               ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", ast_channel_name(chan));
+       }
+
        return 0;
 }
 
@@ -1292,20 +1582,20 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 {
        int ms;
        int timeout = RES_FAX_TIMEOUT;
-       int res = 0, chancount;
+       int chancount;
        unsigned int expected_frametype = -1;
-       union ast_frame_subclass expected_framesubclass = { .integer = -1 };
+       struct ast_frame_subclass expected_framesubclass = { .integer = 0, };
        unsigned int t38negotiated = (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED);
        struct ast_control_t38_parameters t38_parameters;
        const char *tempvar;
        struct ast_fax_session *fax = NULL;
        struct ast_frame *frame = NULL;
        struct ast_channel *c = chan;
-       struct ast_format orig_write_format;
-       struct ast_format orig_read_format;
+       RAII_VAR(struct ast_format *, orig_write_format, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_format *, orig_read_format, NULL, ao2_cleanup);
+       int remaining_time;
+       struct timeval start;
 
-       ast_format_clear(&orig_write_format);
-       ast_format_clear(&orig_read_format);
        chancount = 1;
 
        /* create the FAX session */
@@ -1329,26 +1619,20 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
        report_fax_status(chan, details, "Allocating Resources");
 
        if (details->caps & AST_FAX_TECH_AUDIO) {
-               expected_frametype = AST_FRAME_VOICE;;
-               ast_format_set(&expected_framesubclass.format, AST_FORMAT_SLINEAR, 0);
-               ast_format_copy(&orig_write_format, &chan->writeformat);
-               if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
-                       ast_log(LOG_ERROR, "channel '%s' failed to set write format to signed linear'.\n", chan->name);
-                       ao2_lock(faxregistry.container);
-                       ao2_unlink(faxregistry.container, fax);
-                       ao2_unlock(faxregistry.container);
-                       ao2_ref(fax, -1);
-                       ast_channel_unlock(chan);
+               expected_frametype = AST_FRAME_VOICE;
+               expected_framesubclass.format = ast_format_slin;
+               orig_write_format = ao2_bump(ast_channel_writeformat(chan));
+               if (ast_set_write_format(chan, ast_format_slin) < 0) {
+                       ast_log(LOG_ERROR, "channel '%s' failed to set write format to signed linear'.\n", ast_channel_name(chan));
+                       ao2_unlink(faxregistry.container, fax);
+                       ao2_ref(fax, -1);
                        return -1;
                }
-               ast_format_copy(&orig_read_format, &chan->readformat);
-               if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
-                       ast_log(LOG_ERROR, "channel '%s' failed to set read format to signed linear.\n", chan->name);
-                       ao2_lock(faxregistry.container);
-                       ao2_unlink(faxregistry.container, fax);
-                       ao2_unlock(faxregistry.container);
-                       ao2_ref(fax, -1);
-                       ast_channel_unlock(chan);
+               orig_read_format = ao2_bump(ast_channel_readformat(chan));
+               if (ast_set_read_format(chan, ast_format_slin) < 0) {
+                       ast_log(LOG_ERROR, "channel '%s' failed to set read format to signed linear.\n", ast_channel_name(chan));
+                       ao2_unlink(faxregistry.container, fax);
+                       ao2_ref(fax, -1);
                        return -1;
                }
                if (fax->smoother) {
@@ -1356,7 +1640,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                        fax->smoother = NULL;
                }
                if (!(fax->smoother = ast_smoother_new(320))) {
-                       ast_log(LOG_WARNING, "Channel '%s' FAX session '%d' failed to obtain a smoother.\n", chan->name, fax->id);
+                       ast_log(LOG_WARNING, "Channel '%s' FAX session '%u' failed to obtain a smoother.\n", ast_channel_name(chan), fax->id);
                }
        } else {
                expected_frametype = AST_FRAME_MODEM;
@@ -1380,11 +1664,12 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 
        report_fax_status(chan, details, "FAX Transmission In Progress");
 
-       ast_debug(5, "channel %s will wait on FAX fd %d\n", chan->name, fax->fd);
+       ast_debug(5, "channel %s will wait on FAX fd %d\n", ast_channel_name(chan), fax->fd);
 
        /* handle frames for the session */
-       ms = 1000;
-       while ((res > -1) && (ms > -1) && (timeout > 0)) {
+       remaining_time = timeout;
+       start = ast_tvnow();
+       while (remaining_time > 0) {
                struct ast_channel *ready_chan;
                int ofd, exception;
 
@@ -1397,11 +1682,11 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                                 * FAX session complete before we exit the application.  if needed,
                                 * send the FAX stack silence so the modems can finish their session without
                                 * any problems */
-                               ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", chan->name);
+                               ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
                                GENERIC_FAX_EXEC_SET_VARS(fax, chan, "HANGUP", "remote channel hungup");
                                c = NULL;
                                chancount = 0;
-                               timeout -= (1000 - ms);
+                               remaining_time = ast_remaining_ms(start, timeout);
                                fax->tech->cancel_session(fax);
                                if (fax->tech->generate_silence) {
                                        fax->tech->generate_silence(fax);
@@ -1432,7 +1717,10 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                                        break;
                                }
                                if (t38negotiated && !was_t38) {
-                                       fax->tech->switch_to_t38(fax);
+                                       if (fax->tech->switch_to_t38(fax)) {
+                                               GENERIC_FAX_EXEC_ERROR(fax, chan, "UNKNOWN", "T.38 switch failed");
+                                               break;
+                                       }
                                        details->caps &= ~AST_FAX_TECH_AUDIO;
                                        expected_frametype = AST_FRAME_MODEM;
                                        expected_framesubclass.integer = AST_MODEM_T38;
@@ -1443,10 +1731,12 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 
                                        report_fax_status(chan, details, "T.38 Negotiated");
 
-                                       ast_verb(3, "Channel '%s' switched to T.38 FAX session '%d'.\n", chan->name, fax->id);
+                                       ast_verb(3, "Channel '%s' switched to T.38 FAX session '%u'.\n", ast_channel_name(chan), fax->id);
                                }
-                       } else if ((frame->frametype == expected_frametype) &&
-                                  (!memcmp(&frame->subclass, &expected_framesubclass, sizeof(frame->subclass)))) {
+                       } else if ((frame->frametype == expected_frametype) && (expected_framesubclass.integer == frame->subclass.integer) &&
+                               ((!frame->subclass.format && !expected_framesubclass.format) ||
+                               (frame->subclass.format && expected_framesubclass.format &&
+                                       (ast_format_cmp(frame->subclass.format, expected_framesubclass.format) != AST_FORMAT_CMP_NOT_EQUAL)))) {
                                struct ast_frame *f;
 
                                if (fax->smoother) {
@@ -1470,7 +1760,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                                        fax->tech->write(fax, frame);
                                        fax->frames_received++;
                                }
-                               timeout = RES_FAX_TIMEOUT;
+                               start = ast_tvnow();
                        }
                        ast_frfree(frame);
                } else if (ofd == fax->fd) {
@@ -1487,36 +1777,30 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                        ast_write(chan, frame);
                        fax->frames_sent++;
                        ast_frfree(frame);
-                       timeout = RES_FAX_TIMEOUT;
+                       start = ast_tvnow();
                } else {
                        if (ms && (ofd < 0)) {
                                if ((errno == 0) || (errno == EINTR)) {
-                                       timeout -= (1000 - ms);
-                                       if (timeout <= 0)
+                                       remaining_time = ast_remaining_ms(start, timeout);
+                                       if (remaining_time <= 0)
                                                GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
                                        continue;
                                } else {
-                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", chan->name);
+                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(chan));
                                        GENERIC_FAX_EXEC_ERROR(fax, chan, "UNKNOWN", "error polling data");
-                                       res = ms;
                                        break;
                                }
                        } else {
                                /* nothing happened */
-                               if (timeout > 0) {
-                                       timeout -= 1000;
-                                       if (timeout <= 0)
-                                               GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
-                                       continue;
-                               } else {
-                                       ast_log(LOG_WARNING, "channel '%s' timed-out during the FAX transmission.\n", chan->name);
+                               remaining_time = ast_remaining_ms(start, timeout);
+                               if (remaining_time <= 0) {
                                        GENERIC_FAX_EXEC_ERROR(fax, chan, "TIMEOUT", "fax session timed-out");
                                        break;
                                }
                        }
                }
        }
-       ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, ms: %d, res: %d }\n", chan->name, timeout, ms, res);
+       ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
 
        set_channel_variables(chan, details);
 
@@ -1526,9 +1810,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
        }
 
        if (fax) {
-               ao2_lock(faxregistry.container);
                ao2_unlink(faxregistry.container, fax);
-               ao2_unlock(faxregistry.container);
                ao2_ref(fax, -1);
        }
 
@@ -1536,11 +1818,11 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
         * restore them now
         */
        if (chancount) {
-               if (orig_read_format.id) {
-                       ast_set_read_format(chan, &orig_read_format);
+               if (orig_read_format) {
+                       ast_set_read_format(chan, orig_read_format);
                }
-               if (orig_write_format.id) {
-                       ast_set_write_format(chan, &orig_write_format);
+               if (orig_write_format) {
+                       ast_set_write_format(chan, orig_write_format);
                }
        }
 
@@ -1550,23 +1832,27 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
 
 static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
-       int ms;
+       int timeout_ms;
        struct ast_frame *frame = NULL;
        struct ast_control_t38_parameters t38_parameters;
+       struct timeval start;
+       int ms;
 
        /* don't send any audio if we've already received a T.38 reinvite */
        if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
                /* generate 3 seconds of CED */
                if (ast_playtones_start(chan, 1024, "!2100/3000", 1)) {
-                       ast_log(LOG_ERROR, "error generating CED tone on %s\n", chan->name);
+                       ast_log(LOG_ERROR, "error generating CED tone on %s\n", ast_channel_name(chan));
                        return -1;
                }
 
-               ms = 3000;
-               while (ms > 0) {
+               timeout_ms = 3000;
+               start = ast_tvnow();
+               while ((ms = ast_remaining_ms(start, timeout_ms))) {
                        ms = ast_waitfor(chan, ms);
+
                        if (ms < 0) {
-                               ast_log(LOG_ERROR, "error while generating CED tone on %s\n", chan->name);
+                               ast_log(LOG_ERROR, "error while generating CED tone on %s\n", ast_channel_name(chan));
                                ast_playtones_stop(chan);
                                return -1;
                        }
@@ -1576,7 +1862,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                        }
 
                        if (!(frame = ast_read(chan))) {
-                               ast_log(LOG_ERROR, "error reading frame while generating CED tone on %s\n", chan->name);
+                               ast_log(LOG_ERROR, "error reading frame while generating CED tone on %s\n", ast_channel_name(chan));
                                ast_playtones_stop(chan);
                                return -1;
                        }
@@ -1597,7 +1883,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                                        ast_playtones_stop(chan);
                                        break;
                                case AST_T38_NEGOTIATED:
-                                       ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name);
+                                       ast_debug(1, "Negotiated T.38 for receive on %s\n", ast_channel_name(chan));
                                        t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                        details->caps &= ~AST_FAX_TECH_AUDIO;
                                        report_fax_status(chan, details, "T.38 Negotiated");
@@ -1618,10 +1904,10 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
        }
 
        /* request T.38 */
-       ast_debug(1, "Negotiating T.38 for receive on %s\n", chan->name);
+       ast_debug(1, "Negotiating T.38 for receive on %s\n", ast_channel_name(chan));
 
-       /* wait up to five seconds for negotiation to complete */
-       ms = 5000;
+       /* wait for negotiation to complete */
+       timeout_ms = details->t38timeout;
 
        /* set parameters based on the session's parameters */
        t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
@@ -1630,21 +1916,23 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                return -1;
        }
 
-       while (ms > 0) {
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+               int break_loop = 0;
+
                ms = ast_waitfor(chan, ms);
                if (ms < 0) {
-                       ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
+                       ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", ast_channel_name(chan));
                        return -1;
                }
-
                if (ms == 0) { /* all done, nothing happened */
-                       ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
+                       ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(chan));
                        details->caps &= ~AST_FAX_TECH_T38;
                        break;
                }
 
                if (!(frame = ast_read(chan))) {
-                       ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
+                       ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", ast_channel_name(chan));
                        return -1;
                }
 
@@ -1660,25 +1948,28 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
                                ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
                                break;
                        case AST_T38_NEGOTIATED:
-                               ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name);
+                               ast_debug(1, "Negotiated T.38 for receive on %s\n", ast_channel_name(chan));
                                t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                details->caps &= ~AST_FAX_TECH_AUDIO;
                                report_fax_status(chan, details, "T.38 Negotiated");
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        case AST_T38_REFUSED:
-                               ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
+                               ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", ast_channel_name(chan));
                                details->caps &= ~AST_FAX_TECH_T38;
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        default:
-                               ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
+                               ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", ast_channel_name(chan));
                                details->caps &= ~AST_FAX_TECH_T38;
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        }
                }
                ast_frfree(frame);
+               if (break_loop) {
+                       break;
+               }
        }
 
        /* if T.38 was negotiated, we are done initializing */
@@ -1688,7 +1979,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
 
        /* if we made it here, then T.38 failed, check the 'f' flag */
        if (details->option.allow_audio != AST_FAX_OPTFLAG_TRUE) {
-               ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", chan->name);
+               ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", ast_channel_name(chan));
                return -1;
        }
 
@@ -1698,13 +1989,79 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
        return 0;
 }
 
+/*! \brief Report on the final state of a receive fax operation
+ * \note This will lock the \ref ast_channel
+ */
+static int report_receive_fax_status(struct ast_channel *chan, const char *filename)
+{
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_json *, json_array, ast_json_array_create(), ast_json_unref);
+       struct ast_json *json_filename = ast_json_string_create(filename);
+
+       if (!json_array || !json_filename) {
+               ast_json_unref(json_filename);
+               return -1;
+       }
+       ast_json_array_append(json_array, json_filename);
+
+       {
+               const char *remote_station_id;
+               const char *local_station_id;
+               const char *fax_pages;
+               const char *fax_resolution;
+               const char *fax_bitrate;
+               SCOPED_CHANNELLOCK(lock, chan);
+
+               remote_station_id = S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), "");
+               if (!ast_strlen_zero(remote_station_id)) {
+                       remote_station_id = ast_strdupa(remote_station_id);
+               }
+               local_station_id = S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), "");
+               if (!ast_strlen_zero(local_station_id)) {
+                       local_station_id = ast_strdupa(local_station_id);
+               }
+               fax_pages = S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), "");
+               if (!ast_strlen_zero(fax_pages)) {
+                       fax_pages = ast_strdupa(fax_pages);
+               }
+               fax_resolution = S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), "");
+               if (!ast_strlen_zero(fax_resolution)) {
+                       fax_resolution = ast_strdupa(fax_resolution);
+               }
+               fax_bitrate = S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), "");
+               if (!ast_strlen_zero(fax_bitrate)) {
+                       fax_bitrate = ast_strdupa(fax_bitrate);
+               }
+
+               json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: o}",
+                               "type", "receive",
+                               "remote_station_id", S_OR(remote_station_id, ""),
+                               "local_station_id", S_OR(local_station_id, ""),
+                               "fax_pages", S_OR(fax_pages, ""),
+                               "fax_resolution", S_OR(fax_resolution, ""),
+                               "fax_bitrate", S_OR(fax_bitrate, ""),
+                               "filenames", ast_json_ref(json_array));
+               if (!json_object) {
+                       return -1;
+               }
+
+               message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), ast_channel_fax_type(), json_object);
+               if (!message) {
+                       return -1;
+               }
+               stasis_publish(ast_channel_topic(chan), message);
+       }
+       return 0;
+}
+
 /*! \brief initiate a receive FAX session */
 static int receivefax_exec(struct ast_channel *chan, const char *data)
 {
        char *parse, modems[128] = "";
        int channel_alive;
-       struct ast_fax_session_details *details;
-       struct ast_fax_session *s;
+       RAII_VAR(struct ast_fax_session *, s, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
        struct ast_fax_tech_token *token = NULL;
        struct ast_fax_document *doc;
        AST_DECLARE_APP_ARGS(args,
@@ -1712,7 +2069,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(options);
        );
        struct ast_flags opts = { 0, };
-       struct manager_event_info info;
        enum ast_t38_state t38state;
 
        /* initialize output channel variables */
@@ -1721,6 +2077,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
        pbx_builtin_setvar_helper(chan, "FAXPAGES", "0");
        pbx_builtin_setvar_helper(chan, "FAXBITRATE", NULL);
        pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", NULL);
+       pbx_builtin_setvar_helper(chan, "FAXMODE", NULL);
 
        /* Get a FAX session details structure from the channel's FAX datastore and create one if
         * it does not already exist. */
@@ -1740,7 +2097,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1748,28 +2104,25 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "maxrate is less than minrate");
                set_channel_variables(chan, details);
-               ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
-               ao2_ref(details, -1);
+               ast_log(LOG_ERROR, "maxrate %u is less than minrate %u\n", details->maxrate, details->minrate);
                return -1;
        }
 
        if (check_modem_rate(details->modems, details->minrate)) {
                ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
-               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %u\n", modems, details->minrate);
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
        if (check_modem_rate(details->modems, details->maxrate)) {
                ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
-               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %u\n", modems, details->maxrate);
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1778,7 +2131,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax);
-               ao2_ref(details, -1);
                return -1;
        }
        parse = ast_strdupa(data);
@@ -1789,7 +2141,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
        if (ast_strlen_zero(args.filename)) {
@@ -1797,7 +2148,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[,options])\n", app_receivefax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1807,7 +2157,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s does not support polling\n", app_receivefax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -1821,16 +2170,16 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "error allocating memory");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
        strcpy(doc->filename, args.filename);
        AST_LIST_INSERT_TAIL(&details->documents, doc, next);
 
-       ast_verb(3, "Channel '%s' receiving FAX '%s'\n", chan->name, args.filename);
+       ast_verb(3, "Channel '%s' receiving FAX '%s'\n", ast_channel_name(chan), args.filename);
 
        details->caps = AST_FAX_TECH_RECEIVE;
+       details->option.send_ced = AST_FAX_OPTFLAG_TRUE;
 
        /* check for debug */
        if (ast_test_flag(&opts, OPT_DEBUG) || global_fax_debug) {
@@ -1853,19 +2202,16 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "error reserving fax session");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "Unable to reserve FAX session.\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
        /* make sure the channel is up */
-       if (chan->_state != AST_STATE_UP) {
+       if (ast_channel_state(chan) != AST_STATE_UP) {
                if (ast_answer(chan)) {
                        ast_string_field_set(details, resultstr, "error answering channel");
                        set_channel_variables(chan, details);
-                       ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name);
+                       ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", ast_channel_name(chan));
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        }
@@ -1876,8 +2222,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        } else {
@@ -1890,13 +2234,9 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
-                       ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name);
+                       ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", ast_channel_name(chan));
                        return -1;
                }
-       } else {
-               details->option.send_ced = 1;
        }
 
        if ((channel_alive = generic_fax_exec(chan, details, s, token)) < 0) {
@@ -1905,40 +2245,13 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
 
        if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
                if (disable_t38(chan)) {
-                       ast_debug(1, "error disabling T.38 mode on %s\n", chan->name);
+                       ast_debug(1, "error disabling T.38 mode on %s\n", ast_channel_name(chan));
                }
        }
 
-       /* send out the AMI completion event */
-       ast_channel_lock(chan);
-
-       get_manager_event_info(chan, &info);
-       manager_event(EVENT_FLAG_CALL,
-                     "ReceiveFAX",
-                     "Channel: %s\r\n"
-                     "Context: %s\r\n"
-                     "Exten: %s\r\n"
-                     "CallerID: %s\r\n"
-                     "RemoteStationID: %s\r\n"
-                     "LocalStationID: %s\r\n"
-                     "PagesTransferred: %s\r\n"
-                     "Resolution: %s\r\n"
-                     "TransferRate: %s\r\n"
-                     "FileName: %s\r\n",
-                     chan->name,
-                     info.context,
-                     info.exten,
-                     info.cid,
-                     S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
-                     args.filename);
-       ast_channel_unlock(chan);
-
-       ao2_ref(s, -1);
-       ao2_ref(details, -1);
+       if (report_receive_fax_status(chan, args.filename)) {
+               ast_log(AST_LOG_ERROR, "Error publishing ReceiveFax status message\n");
+       }
 
        /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
        return (!channel_alive) ? -1 : 0;
@@ -1946,9 +2259,11 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
 
 static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
-       int ms;
+       int timeout_ms;
        struct ast_frame *frame = NULL;
        struct ast_control_t38_parameters t38_parameters;
+       struct timeval start;
+       int ms;
 
        /* send CNG tone while listening for the receiver to initiate a switch
         * to T.38 mode; if they do, stop sending the CNG tone and proceed with
@@ -1956,20 +2271,23 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
         *
         * 10500 is enough time for 3 CNG tones
         */
-       ms = 10500;
+       timeout_ms = 10500;
 
        /* don't send any audio if we've already received a T.38 reinvite */
        if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
                if (ast_playtones_start(chan, 1024, "!1100/500,!0/3000,!1100/500,!0/3000,!1100/500,!0/3000", 1)) {
-                       ast_log(LOG_ERROR, "error generating CNG tone on %s\n", chan->name);
+                       ast_log(LOG_ERROR, "error generating CNG tone on %s\n", ast_channel_name(chan));
                        return -1;
                }
        }
 
-       while (ms > 0) {
+       start = ast_tvnow();
+       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+               int break_loop = 0;
                ms = ast_waitfor(chan, ms);
+
                if (ms < 0) {
-                       ast_log(LOG_ERROR, "error while generating CNG tone on %s\n", chan->name);
+                       ast_log(LOG_ERROR, "error while generating CNG tone on %s\n", ast_channel_name(chan));
                        ast_playtones_stop(chan);
                        return -1;
                }
@@ -1979,7 +2297,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                }
 
                if (!(frame = ast_read(chan))) {
-                       ast_log(LOG_ERROR, "error reading frame while generating CNG tone on %s\n", chan->name);
+                       ast_log(LOG_ERROR, "error reading frame while generating CNG tone on %s\n", ast_channel_name(chan));
                        ast_playtones_stop(chan);
                        return -1;
                }
@@ -2000,17 +2318,20 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                ast_playtones_stop(chan);
                                break;
                        case AST_T38_NEGOTIATED:
-                               ast_debug(1, "Negotiated T.38 for send on %s\n", chan->name);
+                               ast_debug(1, "Negotiated T.38 for send on %s\n", ast_channel_name(chan));
                                t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                details->caps &= ~AST_FAX_TECH_AUDIO;
                                report_fax_status(chan, details, "T.38 Negotiated");
-                               ms = 0;
+                               break_loop = 1;
                                break;
                        default:
                                break;
                        }
                }
                ast_frfree(frame);
+               if (break_loop) {
+                       break;
+               }
        }
 
        ast_playtones_stop(chan);
@@ -2021,10 +2342,10 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
 
        /* T.38 negotiation did not happen, initiate a switch if requested */
        if (details->option.request_t38 == AST_FAX_OPTFLAG_TRUE) {
-               ast_debug(1, "Negotiating T.38 for send on %s\n", chan->name);
+               ast_debug(1, "Negotiating T.38 for send on %s\n", ast_channel_name(chan));
 
                /* wait up to five seconds for negotiation to complete */
-               ms = 5000;
+               timeout_ms = 5000;
 
                /* set parameters based on the session's parameters */
                t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
@@ -2033,21 +2354,23 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                        return -1;
                }
 
-               while (ms > 0) {
+               start = ast_tvnow();
+               while ((ms = ast_remaining_ms(start, timeout_ms))) {
+                       int break_loop = 0;
+
                        ms = ast_waitfor(chan, ms);
                        if (ms < 0) {
-                               ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
+                               ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", ast_channel_name(chan));
                                return -1;
                        }
-
                        if (ms == 0) { /* all done, nothing happened */
-                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
+                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(chan));
                                details->caps &= ~AST_FAX_TECH_T38;
                                break;
                        }
 
                        if (!(frame = ast_read(chan))) {
-                               ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", chan->name);
+                               ast_log(LOG_WARNING, "error on '%s' while waiting for T.38 negotiation.\n", ast_channel_name(chan));
                                return -1;
                        }
 
@@ -2063,25 +2386,28 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                        ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
                                        break;
                                case AST_T38_NEGOTIATED:
-                                       ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name);
+                                       ast_debug(1, "Negotiated T.38 for receive on %s\n", ast_channel_name(chan));
                                        t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                        details->caps &= ~AST_FAX_TECH_AUDIO;
                                        report_fax_status(chan, details, "T.38 Negotiated");
-                                       ms = 0;
+                                       break_loop = 1;
                                        break;
                                case AST_T38_REFUSED:
-                                       ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
+                                       ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", ast_channel_name(chan));
                                        details->caps &= ~AST_FAX_TECH_T38;
-                                       ms = 0;
+                                       break_loop = 1;
                                        break;
                                default:
-                                       ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
+                                       ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", ast_channel_name(chan));
                                        details->caps &= ~AST_FAX_TECH_T38;
-                                       ms = 0;
+                                       break_loop = 1;
                                        break;
                                }
                        }
                        ast_frfree(frame);
+                       if (break_loop) {
+                               break;
+                       }
                }
 
                /* if T.38 was negotiated, we are done initializing */
@@ -2093,25 +2419,27 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                 * carriers if we are going to fall back to audio mode */
                if (details->option.allow_audio == AST_FAX_OPTFLAG_TRUE) {
                        if (ast_playtones_start(chan, 1024, "!1100/500,!0/3000", 1)) {
-                               ast_log(LOG_ERROR, "error generating second CNG tone on %s\n", chan->name);
+                               ast_log(LOG_ERROR, "error generating second CNG tone on %s\n", ast_channel_name(chan));
                                return -1;
                        }
 
-                       ms = 3500;
-                       while (ms > 0) {
+                       timeout_ms = 3500;
+                       start = ast_tvnow();
+                       while ((ms = ast_remaining_ms(start, timeout_ms))) {
+                               int break_loop = 0;
+
                                ms = ast_waitfor(chan, ms);
                                if (ms < 0) {
-                                       ast_log(LOG_ERROR, "error while generating second CNG tone on %s\n", chan->name);
+                                       ast_log(LOG_ERROR, "error while generating second CNG tone on %s\n", ast_channel_name(chan));
                                        ast_playtones_stop(chan);
                                        return -1;
                                }
-
                                if (ms == 0) { /* all done, nothing happened */
                                        break;
                                }
 
                                if (!(frame = ast_read(chan))) {
-                                       ast_log(LOG_ERROR, "error reading frame while generating second CNG tone on %s\n", chan->name);
+                                       ast_log(LOG_ERROR, "error reading frame while generating second CNG tone on %s\n", ast_channel_name(chan));
                                        ast_playtones_stop(chan);
                                        return -1;
                                }
@@ -2132,17 +2460,20 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
                                                ast_playtones_stop(chan);
                                                break;
                                        case AST_T38_NEGOTIATED:
-                                               ast_debug(1, "Negotiated T.38 for send on %s\n", chan->name);
+                                               ast_debug(1, "Negotiated T.38 for send on %s\n", ast_channel_name(chan));
                                                t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
                                                details->caps &= ~AST_FAX_TECH_AUDIO;
                                                report_fax_status(chan, details, "T.38 Negotiated");
-                                               ms = 0;
+                                               break_loop = 1;
                                                break;
                                        default:
                                                break;
                                        }
                                }
                                ast_frfree(frame);
+                               if (break_loop) {
+                                       break;
+                               }
                        }
 
                        ast_playtones_stop(chan);
@@ -2156,7 +2487,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
 
        /* if we made it here, then T.38 failed, check the 'f' flag */
        if (details->option.allow_audio == AST_FAX_OPTFLAG_FALSE) {
-               ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", chan->name);
+               ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", ast_channel_name(chan));
                return -1;
        }
 
@@ -2166,14 +2497,79 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
        return 0;
 }
 
+/*!
+ * \brief Report on the status of a completed fax send attempt
+ * \note This will lock the \ref ast_channel
+ */
+static int report_send_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details)
+{
+       RAII_VAR(struct ast_json *, json_obj, NULL, ast_json_unref);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+       struct ast_json *json_filenames;
+
+       json_filenames = generate_filenames_json(details);
+       if (!json_filenames) {
+               return -1;
+       }
+
+       {
+               const char *remote_station_id;
+               const char *local_station_id;
+               const char *fax_pages;
+               const char *fax_resolution;
+               const char *fax_bitrate;
+               SCOPED_CHANNELLOCK(lock, chan);
+
+               remote_station_id = S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), "");
+               if (!ast_strlen_zero(remote_station_id)) {
+                       remote_station_id = ast_strdupa(remote_station_id);
+               }
+               local_station_id = S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), "");
+               if (!ast_strlen_zero(local_station_id)) {
+                       local_station_id = ast_strdupa(local_station_id);
+               }
+               fax_pages = S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), "");
+               if (!ast_strlen_zero(fax_pages)) {
+                       fax_pages = ast_strdupa(fax_pages);
+               }
+               fax_resolution = S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), "");
+               if (!ast_strlen_zero(fax_resolution)) {
+                       fax_resolution = ast_strdupa(fax_resolution);
+               }
+               fax_bitrate = S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), "");
+               if (!ast_strlen_zero(fax_bitrate)) {
+                       fax_bitrate = ast_strdupa(fax_bitrate);
+               }
+               json_obj = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: o}",
+                               "type", "send",
+                               "remote_station_id", S_OR(remote_station_id, ""),
+                               "local_station_id", S_OR(local_station_id, ""),
+                               "fax_pages", S_OR(fax_pages, ""),
+                               "fax_resolution", S_OR(fax_resolution, ""),
+                               "fax_bitrate", S_OR(fax_bitrate, ""),
+                               "filenames", json_filenames);
+               if (!json_obj) {
+                       return -1;
+               }
+
+               message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), ast_channel_fax_type(), json_obj);
+               if (!message) {
+                       return -1;
+               }
+               stasis_publish(ast_channel_topic(chan), message);
+       }
+       return 0;
+}
+
+
 
 /*! \brief initiate a send FAX session */
 static int sendfax_exec(struct ast_channel *chan, const char *data)
 {
        char *parse, *filenames, *c, modems[128] = "";
        int channel_alive, file_count;
-       struct ast_fax_session_details *details;
-       struct ast_fax_session *s;
+       RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_fax_session *, s, NULL, ao2_cleanup);
        struct ast_fax_tech_token *token = NULL;
        struct ast_fax_document *doc;
        AST_DECLARE_APP_ARGS(args,
@@ -2181,7 +2577,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(options);
        );
        struct ast_flags opts = { 0, };
-       struct manager_event_info info;
        enum ast_t38_state t38state;
 
        /* initialize output channel variables */
@@ -2190,6 +2585,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
        pbx_builtin_setvar_helper(chan, "FAXPAGES", "0");
        pbx_builtin_setvar_helper(chan, "FAXBITRATE", NULL);
        pbx_builtin_setvar_helper(chan, "FAXRESOLUTION", NULL);
+       pbx_builtin_setvar_helper(chan, "FAXMODE", NULL);
 
        /* Get a requirement structure and set it.  This structure is used
         * to tell the FAX technology module about the higher level FAX session */
@@ -2209,7 +2605,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2217,28 +2612,25 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "maxrate is less than minrate");
                set_channel_variables(chan, details);
-               ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
-               ao2_ref(details, -1);
+               ast_log(LOG_ERROR, "maxrate %u is less than minrate %u\n", details->maxrate, details->minrate);
                return -1;
        }
 
        if (check_modem_rate(details->modems, details->minrate)) {
                ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
-               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %u\n", modems, details->minrate);
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
        if (check_modem_rate(details->modems, details->maxrate)) {
                ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
-               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %u\n", modems, details->maxrate);
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2247,7 +2639,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]][,options])\n", app_sendfax);
-               ao2_ref(details, -1);
                return -1;
        }
        parse = ast_strdupa(data);
@@ -2255,11 +2646,10 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
 
 
        if (!ast_strlen_zero(args.options) &&
-           ast_app_parse_options(fax_exec_options, &opts, NULL, args.options)) {
+               ast_app_parse_options(fax_exec_options, &opts, NULL, args.options)) {
                ast_string_field_set(details, error, "INVALID_ARGUMENTS");
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
-               ao2_ref(details, -1);
                return -1;
        }
        if (ast_strlen_zero(args.filenames)) {
@@ -2267,7 +2657,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s requires an argument (filename[&filename[&filename]],options])\n", app_sendfax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2277,7 +2666,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "invalid arguments");
                set_channel_variables(chan, details);
                ast_log(LOG_WARNING, "%s does not support polling\n", app_sendfax);
-               ao2_ref(details, -1);
                return -1;
        }
 
@@ -2291,7 +2679,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error reading file");
                        set_channel_variables(chan, details);
                        ast_log(LOG_ERROR, "access failure.  Verify '%s' exists and check permissions.\n", args.filenames);
-                       ao2_ref(details, -1);
                        return -1;
                }
 
@@ -2300,7 +2687,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error allocating memory");
                        set_channel_variables(chan, details);
                        ast_log(LOG_ERROR, "System cannot provide memory for session requirements.\n");
-                       ao2_ref(details, -1);
                        return -1;
                }
 
@@ -2309,7 +2695,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                file_count++;
        }
 
-       ast_verb(3, "Channel '%s' sending FAX:\n", chan->name);
+       ast_verb(3, "Channel '%s' sending FAX:\n", ast_channel_name(chan));
        AST_LIST_TRAVERSE(&details->documents, doc, next) {
                ast_verb(3, "   %s\n", doc->filename);
        }
@@ -2345,19 +2731,16 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                ast_string_field_set(details, resultstr, "error reserving fax session");
                set_channel_variables(chan, details);
                ast_log(LOG_ERROR, "Unable to reserve FAX session.\n");
-               ao2_ref(details, -1);
                return -1;
        }
 
        /* make sure the channel is up */
-       if (chan->_state != AST_STATE_UP) {
+       if (ast_channel_state(chan) != AST_STATE_UP) {
                if (ast_answer(chan)) {
                        ast_string_field_set(details, resultstr, "error answering channel");
                        set_channel_variables(chan, details);
-                       ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name);
+                       ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", ast_channel_name(chan));
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        }
@@ -2368,8 +2751,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
                        return -1;
                }
        } else {
@@ -2382,9 +2763,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                        ast_string_field_set(details, resultstr, "error negotiating T.38");
                        set_channel_variables(chan, details);
                        fax_session_release(s, token);
-                       ao2_ref(s, -1);
-                       ao2_ref(details, -1);
-                       ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name);
+                       ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", ast_channel_name(chan));
                        return -1;
                }
        } else {
@@ -2397,48 +2776,19 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
 
        if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
                if (disable_t38(chan)) {
-                       ast_debug(1, "error disabling T.38 mode on %s\n", chan->name);
+                       ast_debug(1, "error disabling T.38 mode on %s\n", ast_channel_name(chan));
                }
        }
 
        if (!(filenames = generate_filenames_string(details, "FileName: ", "\r\n"))) {
                ast_log(LOG_ERROR, "Error generating SendFAX manager event\n");
-               ao2_ref(s, -1);
-               ao2_ref(details, -1);
                return (!channel_alive) ? -1 : 0;
        }
 
        /* send out the AMI completion event */
-       ast_channel_lock(chan);
-       get_manager_event_info(chan, &info);
-       manager_event(EVENT_FLAG_CALL,
-                     "SendFAX",
-                     "Channel: %s\r\n"
-                     "Context: %s\r\n"
-                     "Exten: %s\r\n"
-                     "CallerID: %s\r\n"
-                     "RemoteStationID: %s\r\n"
-                     "LocalStationID: %s\r\n"
-                     "PagesTransferred: %s\r\n"
-                     "Resolution: %s\r\n"
-                     "TransferRate: %s\r\n"
-                     "%s\r\n",
-                     chan->name,
-                     info.context,
-                     info.exten,
-                     info.cid,
-                     S_OR(pbx_builtin_getvar_helper(chan, "REMOTESTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "LOCALSTATIONID"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXPAGES"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXRESOLUTION"), ""),
-                     S_OR(pbx_builtin_getvar_helper(chan, "FAXBITRATE"), ""),
-                     filenames);
-       ast_channel_unlock(chan);
-
-       ast_free(filenames);
-
-       ao2_ref(s, -1);
-       ao2_ref(details, -1);
+       if (report_send_fax_status(chan, details)) {
+               ast_log(AST_LOG_ERROR, "Error publishing SendFAX status message\n");
+       }
 
        /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */
        return (!channel_alive) ? -1 : 0;
@@ -2448,18 +2798,14 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
 static void destroy_v21_sessions(struct fax_gateway *gateway)
 {
        if (gateway->chan_v21_session) {
-               ao2_lock(faxregistry.container);
                ao2_unlink(faxregistry.container, gateway->chan_v21_session);
-               ao2_unlock(faxregistry.container);
 
                ao2_ref(gateway->chan_v21_session, -1);
                gateway->chan_v21_session = NULL;
        }
 
        if (gateway->peer_v21_session) {
-               ao2_lock(faxregistry.container);
                ao2_unlink(faxregistry.container, gateway->peer_v21_session);
-               ao2_unlock(faxregistry.container);
 
                ao2_ref(gateway->peer_v21_session, -1);
                gateway->peer_v21_session = NULL;
@@ -2477,13 +2823,30 @@ static void destroy_gateway(void *data)
                fax_session_release(gateway->s, gateway->token);
                gateway->token = NULL;
 
-               ao2_lock(faxregistry.container);
                ao2_unlink(faxregistry.container, gateway->s);
-               ao2_unlock(faxregistry.container);
 
                ao2_ref(gateway->s, -1);
                gateway->s = NULL;
        }
+
+       ao2_cleanup(gateway->chan_read_format);
+       ao2_cleanup(gateway->chan_write_format);
+       ao2_cleanup(gateway->peer_read_format);
+       ao2_cleanup(gateway->peer_write_format);
+}
+
+static struct ast_fax_session *fax_v21_session_new (struct ast_channel *chan) {
+       struct ast_fax_session_details *v21_details;
+       struct ast_fax_session *v21_session;
+
+       if (!chan || !(v21_details = session_details_new())) {
+               return NULL;
+       }
+
+       v21_details->caps = AST_FAX_TECH_V21_DETECT;
+       v21_session = fax_session_new(v21_details, chan, NULL, NULL);
+       ao2_ref(v21_details, -1);
+       return v21_session;
 }
 
 /*! \brief Create a new fax gateway object.
@@ -2494,29 +2857,15 @@ static void destroy_gateway(void *data)
 static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
        struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
-       struct ast_fax_session_details *v21_details;
        if (!gateway) {
                return NULL;
        }
 
-       if (!(v21_details = session_details_new())) {
-               ao2_ref(gateway, -1);
-               return NULL;
-       }
-
-       v21_details->caps = AST_FAX_TECH_V21_DETECT;
-       if (!(gateway->chan_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
-               ao2_ref(v21_details, -1);
-               ao2_ref(gateway, -1);
-               return NULL;
-       }
-
-       if (!(gateway->peer_v21_session = fax_session_new(v21_details, chan, NULL, NULL))) {
-               ao2_ref(v21_details, -1);
+       if (!(gateway->chan_v21_session = fax_v21_session_new(chan))) {
+               ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(chan));
                ao2_ref(gateway, -1);
                return NULL;
        }
-       ao2_ref(v21_details, -1);
 
        gateway->framehook = -1;
 
@@ -2531,14 +2880,21 @@ static struct fax_gateway *fax_gateway_new(struct ast_channel *chan, struct ast_
        return gateway;
 }
 
-/*! \brief Create a fax session and start T.30<->T.38 gateway mode
+/*!
+ * \brief Create a fax session and start T.30<->T.38 gateway mode
+ *
  * \param gateway a fax gateway object
  * \param details fax session details
  * \param chan active channel
- * \return 0 on error 1 on success*/
+ *
+ * \pre chan is locked on entry
+ *
+ * \return 0 on error 1 on success
+ */
 static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan)
 {
        struct ast_fax_session *s;
+       int start_res;
 
        /* create the FAX session */
        if (!(s = fax_session_new(details, chan, gateway->s, gateway->token))) {
@@ -2553,11 +2909,16 @@ static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session
        }
        /* release the reference for the reserved session and replace it with
         * the real session */
-       ao2_ref(gateway->s, -1);
+       if (gateway->s) {
+               ao2_ref(gateway->s, -1);
+       }
        gateway->s = s;
        gateway->token = NULL;
 
-       if (gateway->s->tech->start_session(gateway->s) < 0) {
+       ast_channel_unlock(chan);
+       start_res = gateway->s->tech->start_session(gateway->s);
+       ast_channel_lock(chan);
+       if (start_res < 0) {
                ast_string_field_set(details, result, "FAILED");
                ast_string_field_set(details, resultstr, "error starting gateway session");
                ast_string_field_set(details, error, "INIT_ERROR");
@@ -2573,6 +2934,7 @@ static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session
        return 0;
 }
 
+/*! \pre chan is locked on entry */
 static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_frame *f)
 {
        struct ast_frame *fp;
@@ -2590,7 +2952,7 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
        struct ast_fax_session_details *details = find_details(chan);
 
        if (!details) {
-               ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+               ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", ast_channel_name(chan));
                ast_framehook_detach(chan, gateway->framehook);
                return f;
        }
@@ -2599,7 +2961,7 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
        ao2_ref(details, -1);
 
        if (!(fp = ast_frisolate(&control_frame))) {
-               ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name);
+               ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", ast_channel_name(chan));
                return f;
        }
 
@@ -2607,10 +2969,11 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
        gateway->timeout_start = ast_tvnow();
        details->gateway_timeout = FAX_GATEWAY_TIMEOUT;
 
-       ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name);
+       ast_debug(1, "requesting T.38 for gateway session for %s\n", ast_channel_name(chan));
        return fp;
 }
 
+/*! \pre chan is locked on entry */
 static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
 {
        struct ast_channel *other = (active == chan) ? peer : chan;
@@ -2628,10 +2991,10 @@ static struct ast_frame *fax_gateway_detect_v21(struct fax_gateway *gateway, str
        if (gateway->detected_v21) {
                destroy_v21_sessions(gateway);
                if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
-                       ast_debug(1, "detected v21 preamble from %s\n", active->name);
+                       ast_debug(1, "detected v21 preamble from %s\n", ast_channel_name(active));
                        return fax_gateway_request_t38(gateway, chan, f);
                } else {
-                       ast_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
+                       ast_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", ast_channel_name(active), ast_channel_name(other));
                }
        }
 
@@ -2647,12 +3010,17 @@ static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel
        }
 }
 
-/*! \brief T38 Gateway Negotiate t38 parameters
+/*!
+ * \brief T38 Gateway Negotiate t38 parameters
+ *
  * \param gateway gateway object
  * \param chan channel running the gateway
  * \param peer channel im bridged too
  * \param active channel the frame originated on
  * \param f the control frame to process
+ *
+ * \pre chan is locked on entry
+ *
  * \return processed control frame or null frame
  */
 static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
@@ -2676,7 +3044,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
        }
 
        if (!(details = find_details(chan))) {
-               ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+               ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", ast_channel_name(chan));
                ast_framehook_detach(chan, gateway->framehook);
                return f;
        }
@@ -2689,7 +3057,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                         * other channel appears to support T.38, we'll pass
                         * the request through and only step in if the other
                         * channel rejects the request */
-                       ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", active->name, other->name);
+                       ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", ast_channel_name(active), ast_channel_name(other));
                        t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
                        gateway->t38_state = T38_STATE_UNKNOWN;
                        gateway->timeout_start = ast_tvnow();
@@ -2699,14 +3067,14 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                } else if (state == T38_STATE_UNAVAILABLE || state == T38_STATE_REJECTED) {
                        /* the other channel does not support T.38, we need to
                         * step in here */
-                       ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", active->name, other->name);
-                       ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+                       ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", ast_channel_name(active), ast_channel_name(other));
+                       ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", ast_channel_name(active), ast_channel_name(other));
 
                        t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
                        t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
 
                        if (fax_gateway_start(gateway, details, chan)) {
-                               ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+                               ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", ast_channel_name(active), ast_channel_name(other));
                                gateway->t38_state = T38_STATE_REJECTED;
                                control_params->request_response = AST_T38_REFUSED;
 
@@ -2733,7 +3101,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                        gateway->timeout_start = ast_tvnow();
                        details->gateway_timeout = FAX_GATEWAY_TIMEOUT;
 
-                       ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble detection\n", active->name);
+                       ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble detection\n", ast_channel_name(active));
                        ao2_ref(details, -1);
                        return &ast_null_frame;
                } else if (gateway->t38_state == T38_STATE_NEGOTIATED) {
@@ -2755,18 +3123,18 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                        ast_string_field_set(details, error, "NATIVE_T38");
                        set_channel_variables(chan, details);
 
-                       ast_debug(1, "%s is attempting to negotiate T.38 after we already negotiated T.38 with %s, disabling the gateway\n", active->name, other->name);
+                       ast_debug(1, "%s is attempting to negotiate T.38 after we already negotiated T.38 with %s, disabling the gateway\n", ast_channel_name(active), ast_channel_name(other));
                        ao2_ref(details, -1);
                        return &ast_null_frame;
                } else {
-                       ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", active->name, other->name);
+                       ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", ast_channel_name(active), ast_channel_name(other));
                        ao2_ref(details, -1);
                        return f;
                }
        } else if (gateway->t38_state == T38_STATE_NEGOTIATING
                && control_params->request_response == AST_T38_REFUSED) {
 
-               ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", active->name);
+               ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", ast_channel_name(active));
 
                /* our request to negotiate T.38 was refused, if the other
                 * channel supports T.38, they might still reinvite and save
@@ -2788,12 +3156,12 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
        } else if (gateway->t38_state == T38_STATE_NEGOTIATING
                && control_params->request_response == AST_T38_NEGOTIATED) {
 
-               ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+               ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", ast_channel_name(active), ast_channel_name(other));
 
                t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
 
                if (fax_gateway_start(gateway, details, chan)) {
-                       ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+                       ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", ast_channel_name(active), ast_channel_name(other));
                        gateway->t38_state = T38_STATE_NEGOTIATING;
                        control_params->request_response = AST_T38_REQUEST_TERMINATE;
 
@@ -2809,13 +3177,13 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                /* the other channel refused the request to negotiate T.38,
                 * we'll step in here and pretend the request was accepted */
 
-               ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", other->name, active->name);
-               ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", other->name, active->name);
+               ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", ast_channel_name(other), ast_channel_name(active));
+               ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", ast_channel_name(other), ast_channel_name(active));
 
                t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
 
                if (fax_gateway_start(gateway, details, chan)) {
-                       ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+                       ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", ast_channel_name(active), ast_channel_name(other));
                        gateway->t38_state = T38_STATE_REJECTED;
                        control_params->request_response = AST_T38_REFUSED;
 
@@ -2832,7 +3200,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                /* the channel wishes to end our short relationship, we shall
                 * oblige */
 
-               ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", active->name);
+               ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", ast_channel_name(active));
 
                ast_framehook_detach(chan, details->gateway_id);
                details->gateway_id = -1;
@@ -2845,7 +3213,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                ao2_ref(details, -1);
                return &ast_null_frame;
        } else if (control_params->request_response == AST_T38_NEGOTIATED) {
-               ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", active->name, other->name);
+               ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", ast_channel_name(active), ast_channel_name(other));
 
                ast_framehook_detach(chan, details->gateway_id);
                details->gateway_id = -1;
@@ -2858,7 +3226,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                ao2_ref(details, -1);
                return f;
        } else if (control_params->request_response == AST_T38_TERMINATED) {
-               ast_debug(1, "T.38 disabled on channel %s\n", active->name);
+               ast_debug(1, "T.38 disabled on channel %s\n", ast_channel_name(active));
 
                ast_framehook_detach(chan, details->gateway_id);
                details->gateway_id = -1;
@@ -2873,7 +3241,8 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
 
 /*! \brief Destroy the gateway data structure when the framehook is detached
  * \param data framehook data (gateway data)*/
-static void fax_gateway_framehook_destroy(void *data) {
+static void fax_gateway_framehook_destroy(void *data)
+{
        struct fax_gateway *gateway = data;
 
        if (gateway->s) {
@@ -2894,7 +3263,8 @@ static void fax_gateway_framehook_destroy(void *data) {
        ao2_ref(gateway, -1);
 }
 
-/*! \brief T.30<->T.38 gateway framehook.
+/*!
+ * \brief T.30<->T.38 gateway framehook.
  *
  * Intercept packets on bridged channels and determine if a T.38 gateway is
  * required. If a gateway is required, start a gateway and handle T.38
@@ -2905,21 +3275,28 @@ static void fax_gateway_framehook_destroy(void *data) {
  * \param event framehook event
  * \param data framehook data (struct fax_gateway *)
  *
+ * \pre chan is locked on entry
+ *
  * \return processed frame or NULL when f is NULL or a null frame
  */
-static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
+{
        struct fax_gateway *gateway = data;
-       struct ast_channel *peer, *active;
-       struct ast_fax_session_details *details;
+       struct ast_channel *active;
+       RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_channel *, peer, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_channel *, chan_ref, chan, ao2_cleanup);
+
+       /* Ref bump channel for when we have to unlock it */
+       ao2_ref(chan_ref, 1);
 
        if (gateway->s) {
                details = gateway->s->details;
                ao2_ref(details, 1);
        } else {
                if (!(details = find_details(chan))) {
-                       ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+                       ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", ast_channel_name(chan));
                        ast_framehook_detach(chan, gateway->framehook);
-                       details->gateway_id = -1;
                        return f;
                }
        }
@@ -2929,41 +3306,42 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                set_channel_variables(chan, details);
 
                if (gateway->bridged) {
-                       ast_set_read_format(chan, &gateway->chan_read_format);
-                       ast_set_read_format(chan, &gateway->chan_write_format);
+                       ast_set_read_format(chan, gateway->chan_read_format);
+                       ast_set_write_format(chan, gateway->chan_write_format);
 
-                       if ((peer = ast_bridged_channel(chan))) {
-                               ast_set_read_format(peer, &gateway->peer_read_format);
-                               ast_set_read_format(peer, &gateway->peer_write_format);
+                       ast_channel_unlock(chan);
+                       peer = ast_channel_bridge_peer(chan);
+                       if (peer) {
+                               ast_set_read_format(peer, gateway->peer_read_format);
+                               ast_set_write_format(peer, gateway->peer_write_format);
                                ast_channel_make_compatible(chan, peer);
                        }
+                       ast_channel_lock(chan);
                }
-
-               ao2_ref(details, -1);
                return NULL;
        }
 
        if (!f || (event == AST_FRAMEHOOK_EVENT_ATTACHED)) {
-               ao2_ref(details, -1);
                return NULL;
        };
 
        /* this frame was generated by the fax gateway, pass it on */
        if (ast_test_flag(f, AST_FAX_FRFLAG_GATEWAY)) {
-               ao2_ref(details, -1);
                return f;
        }
 
-       if (!(peer = ast_bridged_channel(chan))) {
-               /* not bridged, don't do anything */
-               ao2_ref(details, -1);
+       /* If we aren't bridged or we don't have a peer, don't do anything */
+       ast_channel_unlock(chan);
+       peer = ast_channel_bridge_peer(chan);
+       ast_channel_lock(chan);
+       if (!peer) {
                return f;
        }
 
-       if (!gateway->bridged && peer) {
+       if (!gateway->bridged) {
                /* don't start a gateway if neither channel can handle T.38 */
                if (ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE && ast_channel_get_t38_state(peer) == T38_STATE_UNAVAILABLE) {
-                       ast_debug(1, "not starting gateway for %s and %s; neither channel supports T.38\n", chan->name, peer->name);
+                       ast_debug(1, "not starting gateway for %s and %s; neither channel supports T.38\n", ast_channel_name(chan), ast_channel_name(peer));
                        ast_framehook_detach(chan, gateway->framehook);
                        details->gateway_id = -1;
 
@@ -2971,7 +3349,6 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                        ast_string_field_set(details, resultstr, "neither channel supports T.38");
                        ast_string_field_set(details, error, "T38_NEG_ERROR");
                        set_channel_variables(chan, details);
-                       ao2_ref(details, -1);
                        return f;
                }
 
@@ -2979,27 +3356,36 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                        gateway->timeout_start = ast_tvnow();
                }
 
+               ast_channel_unlock(chan);
+               ast_channel_lock_both(chan, peer);
+
                /* we are bridged, change r/w formats to SLIN for v21 preamble
                 * detection and T.30 */
-               ast_format_copy(&gateway->chan_read_format, &chan->readformat);
-               ast_format_copy(&gateway->chan_write_format, &chan->readformat);
+               ao2_replace(gateway->chan_read_format, ast_channel_readformat(chan));
+               ao2_replace(gateway->chan_write_format, ast_channel_writeformat(chan));
+
+               ao2_replace(gateway->peer_read_format, ast_channel_readformat(peer));
+               ao2_replace(gateway->peer_write_format, ast_channel_writeformat(peer));
 
-               ast_format_copy(&gateway->peer_read_format, &peer->readformat);
-               ast_format_copy(&gateway->peer_write_format, &peer->readformat);
+               ast_set_read_format(chan, ast_format_slin);
+               ast_set_write_format(chan, ast_format_slin);
 
-               ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
-               ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR);
+               ast_set_read_format(peer, ast_format_slin);
+               ast_set_write_format(peer, ast_format_slin);
 
-               ast_set_read_format_by_id(peer, AST_FORMAT_SLINEAR);
-               ast_set_write_format_by_id(peer, AST_FORMAT_SLINEAR);
+               ast_channel_unlock(peer);
 
-               ast_channel_make_compatible(chan, peer);
                gateway->bridged = 1;
+               if (!(gateway->peer_v21_session = fax_v21_session_new(peer))) {
+                       ast_log(LOG_ERROR, "Can't create V21 session on chan %s for T.38 gateway session\n", ast_channel_name(peer));
+                       ast_framehook_detach(chan, gateway->framehook);
+                       return f;
+               }
        }
 
        if (gateway->bridged && !ast_tvzero(gateway->timeout_start)) {
                if (ast_tvdiff_ms(ast_tvnow(), gateway->timeout_start) > details->gateway_timeout) {
-                       ast_debug(1, "no fax activity between %s and %s after %d ms, disabling gateway\n", chan->name, peer->name, details->gateway_timeout);
+                       ast_debug(1, "no fax activity between %s and %s after %d ms, disabling gateway\n", ast_channel_name(chan), ast_channel_name(peer), details->gateway_timeout);
                        ast_framehook_detach(chan, gateway->framehook);
                        details->gateway_id = -1;
 
@@ -3007,7 +3393,6 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                        ast_string_field_build(details, resultstr, "no fax activity after %d ms", details->gateway_timeout);
                        ast_string_field_set(details, error, "TIMEOUT");
                        set_channel_variables(chan, details);
-                       ao2_ref(details, -1);
                        return f;
                }
        }
@@ -3015,13 +3400,9 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
        /* only handle VOICE, MODEM, and CONTROL frames*/
        switch (f->frametype) {
        case AST_FRAME_VOICE:
-               switch (f->subclass.format.id) {
-               case AST_FORMAT_SLINEAR:
-               case AST_FORMAT_ALAW:
-               case AST_FORMAT_ULAW:
-                       break;
-               default:
-                       ao2_ref(details, -1);
+               if ((ast_format_cmp(f->subclass.format, ast_format_slin) != AST_FORMAT_CMP_EQUAL) &&
+                       (ast_format_cmp(f->subclass.format, ast_format_alaw) != AST_FORMAT_CMP_EQUAL) &&
+                       (ast_format_cmp(f->subclass.format, ast_format_ulaw) != AST_FORMAT_CMP_EQUAL)) {
                        return f;
                }
                break;
@@ -3029,16 +3410,13 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                if (f->subclass.integer == AST_MODEM_T38) {
                        break;
                }
-               ao2_ref(details, -1);
                return f;
        case AST_FRAME_CONTROL:
                if (f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
                        break;
                }
-               ao2_ref(details, -1);
                return f;
        default:
-               ao2_ref(details, -1);
                return f;
        }
 
@@ -3051,73 +3429,65 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                active = chan;
                break;
        default:
-               ast_log(LOG_WARNING, "unhandled framehook event %i\n", event);
-               ao2_ref(details, -1);
+               ast_log(LOG_WARNING, "unhandled framehook event %u\n", event);
                return f;
        }
 
        /* handle control frames */
        if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_T38_PARAMETERS) {
-               ao2_ref(details, -1);
                return fax_gateway_detect_t38(gateway, chan, peer, active, f);
        }
 
        if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
                /* not in gateway mode and have not detected v21 yet, listen
                 * for v21 */
-               ao2_ref(details, -1);
                return fax_gateway_detect_v21(gateway, chan, peer, active, f);
        }
 
        /* in gateway mode, gateway some packets */
        if (gateway->t38_state == T38_STATE_NEGOTIATED) {
+               struct ast_trans_pvt *readtrans;
                /* framehooks are called in __ast_read() before frame format
                 * translation is done, so we need to translate here */
-               if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id != AST_FORMAT_SLINEAR)) {
-                       if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) {
+               if ((f->frametype == AST_FRAME_VOICE) && (ast_format_cmp(f->subclass.format, ast_format_slin) != AST_FORMAT_CMP_EQUAL)
+                       && (readtrans = ast_channel_readtrans(active))) {
+                       if ((f = ast_translate(readtrans, f, 1)) == NULL) {
                                f = &ast_null_frame;
-                               ao2_ref(details, -1);
                                return f;
                        }
-               }
-
-               /* XXX we ignore the return value here, perhaps we should
-                * disable the gateway if a write fails. I am not sure how a
-                * write would fail, or even if a failure would be fatal so for
-                * now we'll just ignore the return value. */
-               gateway->s->tech->write(gateway->s, f);
-               if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id != AST_FORMAT_SLINEAR) && active->readtrans) {
-                       /* Only free the frame if we translated / duplicated it - otherwise,
-                        * let whatever is outside the frame hook do it */
+                       /* XXX we ignore the return value here, perhaps we should
+                        * disable the gateway if a write fails. I am not sure how a
+                        * write would fail, or even if a failure would be fatal so for
+                        * now we'll just ignore the return value. */
+                       gateway->s->tech->write(gateway->s, f);
                        ast_frfree(f);
+               } else {
+                       gateway->s->tech->write(gateway->s, f);
                }
+
                f = &ast_null_frame;
-               ao2_ref(details, -1);
                return f;
        }
 
        /* force silence on the line if T.38 negotiation might be taking place */
        if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) {
-               if (f->frametype == AST_FRAME_VOICE && f->subclass.format.id == AST_FORMAT_SLINEAR) {
+               if (f->frametype == AST_FRAME_VOICE &&
+                       (ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) {
                        short silence_buf[f->samples];
                        struct ast_frame silence_frame = {
                                .frametype = AST_FRAME_VOICE,
+                               .subclass.format = ast_format_slin,
                                .data.ptr = silence_buf,
                                .samples = f->samples,
                                .datalen = sizeof(silence_buf),
                        };
-                       ast_format_set(&silence_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
                        memset(silence_buf, 0, sizeof(silence_buf));
-
-                       ao2_ref(details, -1);
                        return ast_frisolate(&silence_frame);
                } else {
-                       ao2_ref(details, -1);
                        return &ast_null_frame;
                }
        }
 
-       ao2_ref(details, -1);
        return f;
 }
 
@@ -3134,8 +3504,13 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d
                .version = AST_FRAMEHOOK_INTERFACE_VERSION,
                .event_cb = fax_gateway_framehook,
                .destroy_cb = fax_gateway_framehook_destroy,
+               .disable_inheritance = 1, /* Masquerade inheritance is handled through the datastore fixup */
        };
 
+       if (global_fax_debug) {
+               details->option.debug = AST_FAX_OPTFLAG_TRUE;
+       }
+
        ast_string_field_set(details, result, "SUCCESS");
        ast_string_field_set(details, resultstr, "gateway operation started successfully");
        ast_string_field_set(details, error, "NO_ERROR");
@@ -3178,12 +3553,13 @@ static void destroy_faxdetect(void *data)
                ast_dsp_free(faxdetect->dsp);
                faxdetect->dsp = NULL;
        }
-       ao2_ref(faxdetect->details, -1);
+       ao2_cleanup(faxdetect->details);
+       ao2_cleanup(faxdetect->orig_format);
 }
 
 /*! \brief Create a new fax detect object.
  * \param chan the channel attaching to
- * \param timeout remove framehook in this time if set
+ * \param timeout in ms to remove framehook in this time if not zero
  * \param flags required options
  * \return NULL or a fax gateway object
  */
@@ -3220,7 +3596,8 @@ static struct fax_detect *fax_detect_new(struct ast_channel *chan, int timeout,
 
 /*! \brief Deref the faxdetect data structure when the faxdetect framehook is detached
  * \param data framehook data (faxdetect data)*/
-static void fax_detect_framehook_destroy(void *data) {
+static void fax_detect_framehook_destroy(void *data)
+{
        struct fax_detect *faxdetect = data;
 
        ao2_ref(faxdetect, -1);
@@ -3237,38 +3614,45 @@ static void fax_detect_framehook_destroy(void *data) {
  *
  * \return processed frame or NULL when f is NULL or a null frame
  */
-static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data)
+{
        struct fax_detect *faxdetect = data;
        struct ast_fax_session_details *details;
        struct ast_control_t38_parameters *control_params;
-       struct ast_channel *peer;
+       RAII_VAR(struct ast_channel *, peer, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_channel *, chan_ref, chan, ao2_cleanup);
        int result = 0;
 
+       /* Ref bump the channel for when we have to unlock it */
+       ao2_ref(chan, 1);
+
        details = faxdetect->details;
 
        switch (event) {
        case AST_FRAMEHOOK_EVENT_ATTACHED:
                /* Setup format for DSP on ATTACH*/
-               ast_format_copy(&faxdetect->orig_format, &chan->readformat);
-               switch (chan->readformat.id) {
-                       case AST_FORMAT_SLINEAR:
-                       case AST_FORMAT_ALAW:
-                       case AST_FORMAT_ULAW:
-                               break;
-                       default:
-                               if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR)) {
-                                       ast_framehook_detach(chan, details->faxdetect_id);
-                                       details->faxdetect_id = -1;
-                                       return f;
-                               }
+               ao2_replace(faxdetect->orig_format, ast_channel_readformat(chan));
+
+               if ((ast_format_cmp(ast_channel_readformat(chan), ast_format_slin) != AST_FORMAT_CMP_EQUAL) &&
+                       (ast_format_cmp(ast_channel_readformat(chan), ast_format_alaw) != AST_FORMAT_CMP_EQUAL) &&
+                       (ast_format_cmp(ast_channel_readformat(chan), ast_format_ulaw) != AST_FORMAT_CMP_EQUAL)) {
+                       if (ast_set_read_format(chan, ast_format_slin)) {
+                               ast_framehook_detach(chan, details->faxdetect_id);
+                               details->faxdetect_id = -1;
+                               return f;
+                       }
                }
+
                return NULL;
        case AST_FRAMEHOOK_EVENT_DETACHED:
                /* restore audio formats when we are detached */
-               ast_set_read_format(chan, &faxdetect->orig_format);
-               if ((peer = ast_bridged_channel(chan))) {
+               ast_set_read_format(chan, faxdetect->orig_format);
+               ast_channel_unlock(chan);
+               peer = ast_channel_bridge_peer(chan);
+               if (peer) {
                        ast_channel_make_compatible(chan, peer);
                }
+               ast_channel_lock(chan);
                return NULL;
        case AST_FRAMEHOOK_EVENT_READ:
                if (f) {
@@ -3282,8 +3666,9 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
                return f;
        }
 
-       if ((!ast_tvzero(faxdetect->timeout_start) &&
-           (ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > faxdetect->timeout))) {
+       if (!ast_tvzero(faxdetect->timeout_start)
+               && ast_tvdiff_ms(ast_tvnow(), faxdetect->timeout_start) > details->faxdetect_timeout) {
+               ast_debug(1, "FAXOPT(faxdetect) timeout on %s\n", ast_channel_name(chan));
                ast_framehook_detach(chan, details->faxdetect_id);
                details->faxdetect_id = -1;
                return f;
@@ -3294,16 +3679,13 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
        case AST_FRAME_VOICE:
                /* we have no DSP this means we not detecting CNG */
                if (!faxdetect->dsp) {
-                       break;
+                       return f;
                }
                /* We can only process some formats*/
-               switch (f->subclass.format.id) {
-                       case AST_FORMAT_SLINEAR:
-                       case AST_FORMAT_ALAW:
-                       case AST_FORMAT_ULAW:
-                               break;
-                       default:
-                               return f;
+               if ((ast_format_cmp(f->subclass.format, ast_format_slin) != AST_FORMAT_CMP_EQUAL) &&
+                       (ast_format_cmp(f->subclass.format, ast_format_alaw) != AST_FORMAT_CMP_EQUAL) &&
+                       (ast_format_cmp(f->subclass.format, ast_format_ulaw) != AST_FORMAT_CMP_EQUAL)) {
+                       return f;
                }
                break;
        case AST_FRAME_CONTROL:
@@ -3334,30 +3716,36 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
        }
 
        if (result) {
-               const char *target_context = S_OR(chan->macrocontext, chan->context);
+               const char *target_context;
+
                switch (result) {
                case 'f':
                case 't':
+                       target_context = S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan));
+
                        ast_channel_unlock(chan);
+                       ast_frfree(f);
+                       f = &ast_null_frame;
                        if (ast_exists_extension(chan, target_context, "fax", 1,
-                           S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
-                               ast_channel_lock(chan);
+                           S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
                                ast_verb(2, "Redirecting '%s' to fax extension due to %s detection\n",
-                                       chan->name, (result == 'f') ? "CNG" : "T38");
-                               pbx_builtin_setvar_helper(chan, "FAXEXTEN", chan->exten);
+                                       ast_channel_name(chan), (result == 'f') ? "CNG" : "T38");
+                               pbx_builtin_setvar_helper(chan, "FAXEXTEN", ast_channel_exten(chan));
                                if (ast_async_goto(chan, target_context, "fax", 1)) {
-                                       ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", chan->name, target_context);
+                                       ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(chan), target_context);
                                }
-                               ast_frfree(f);
-                               f = &ast_null_frame;
                        } else {
-                               ast_channel_lock(chan);
                                ast_log(LOG_NOTICE, "FAX %s detected but no fax extension in context (%s)\n",
                                        (result == 'f') ? "CNG" : "T38", target_context);
                        }
+                       ast_channel_lock(chan);
+
+                       ast_framehook_detach(chan, details->faxdetect_id);
+                       details->faxdetect_id = -1;
+                       break;
+               default:
+                       break;
                }
-               ast_framehook_detach(chan, details->faxdetect_id);
-               details->faxdetect_id = -1;
        }
 
        return f;
@@ -3365,7 +3753,7 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
 
 /*! \brief Attach a faxdetect framehook object to a channel.
  * \param chan the channel to attach to
- * \param timeout remove framehook in this time if set
+ * \param timeout in ms to remove framehook in this time if not zero
  * \return the faxdetect structure or NULL on error
  * \param flags required options
  * \retval -1 error
@@ -3396,6 +3784,8 @@ static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags)
        faxdetect->details = details;
        ast_channel_lock(chan);
        details->faxdetect_id = ast_framehook_attach(chan, &fr_hook);
+       details->faxdetect_timeout = timeout;
+       details->faxdetect_flags = flags;
        ast_channel_unlock(chan);
 
        if (details->faxdetect_id < 0) {
@@ -3438,7 +3828,7 @@ static char *fax_session_tab_complete(struct ast_cli_args *a)
        tklen = strlen(a->word);
        i = ao2_iterator_init(faxregistry.container, 0);
        while ((s = ao2_iterator_next(&i))) {
-               snprintf(tbuf, sizeof(tbuf), "%d", s->id);
+               snprintf(tbuf, sizeof(tbuf), "%u", s->id);
                if (!strncasecmp(a->word, tbuf, tklen) && ++wordnum > a->n) {
                        name = ast_strdup(tbuf);
                        ao2_ref(s, -1);
@@ -3541,7 +3931,7 @@ static char *cli_fax_show_capabilities(struct ast_cli_entry *e, int cmd, struct
                num_modules++;
        }
        AST_RWLIST_UNLOCK(&faxmodules);
-       ast_cli(a->fd, "%d registered modules\n\n", num_modules);
+       ast_cli(a->fd, "%u registered modules\n\n", num_modules);
 
        return CLI_SUCCESS;
 }
@@ -3551,6 +3941,7 @@ static char *cli_fax_show_settings(struct ast_cli_entry *e, int cmd, struct ast_
 {
        struct fax_module *fax;
        char modems[128] = "";
+       struct fax_options options;
 
        switch (cmd) {
        case CLI_INIT:
@@ -3563,13 +3954,16 @@ static char *cli_fax_show_settings(struct ast_cli_entry *e, int cmd, struct ast_
                return NULL;
        }
 
+       get_general_options(&options);
+
        ast_cli(a->fd, "FAX For Asterisk Settings:\n");
-       ast_cli(a->fd, "\tECM: %s\n", general_options.ecm ? "Enabled" : "Disabled");
-       ast_cli(a->fd, "\tStatus Events: %s\n",  general_options.statusevents ? "On" : "Off");
-       ast_cli(a->fd, "\tMinimum Bit Rate: %d\n", general_options.minrate);
-       ast_cli(a->fd, "\tMaximum Bit Rate: %d\n", general_options.maxrate);
-       ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
+       ast_cli(a->fd, "\tECM: %s\n", options.ecm ? "Enabled" : "Disabled");
+       ast_cli(a->fd, "\tStatus Events: %s\n",  options.statusevents ? "On" : "Off");
+       ast_cli(a->fd, "\tMinimum Bit Rate: %u\n", options.minrate);
+       ast_cli(a->fd, "\tMaximum Bit Rate: %u\n", options.maxrate);
+       ast_fax_modem_to_str(options.modems, modems, sizeof(modems));
        ast_cli(a->fd, "\tModem Modulations Allowed: %s\n", modems);
+       ast_cli(a->fd, "\tT.38 Negotiation Timeout: %u\n", options.t38timeout);
        ast_cli(a->fd, "\n\nFAX Technology Modules:\n\n");
        AST_RWLIST_RDLOCK(&faxmodules);
        AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
@@ -3601,7 +3995,7 @@ static char *cli_fax_show_session(struct ast_cli_entry *e, int cmd, struct ast_c
                return CLI_SHOWUSAGE;
        }
 
-       if (sscanf(a->argv[3], "%d", &tmp.id) != 1) {
+       if (sscanf(a->argv[3], "%u", &tmp.id) != 1) {
                ast_log(LOG_ERROR, "invalid session id: '%s'\n", a->argv[3]);
                return RESULT_SUCCESS;
        }
@@ -3617,6 +4011,43 @@ static char *cli_fax_show_session(struct ast_cli_entry *e, int cmd, struct ast_c
        return CLI_SUCCESS;
 }
 
+static int manager_fax_session(struct mansession *s, const struct message *m)
+{
+       const char *action_id = astman_get_header(m, "ActionID");
+       const char *session_number = astman_get_header(m, "SessionNumber");
+       char id_text[256] = "";
+       struct ast_fax_session *session;
+       struct ast_fax_session find_session;
+
+       if (sscanf(session_number, "%30u", &find_session.id) != 1) {
+               astman_send_error(s, m, "Invalid session ID");
+               return 0;
+       }
+
+       session = ao2_find(faxregistry.container, &find_session, OBJ_POINTER);
+       if (!session) {
+               astman_send_error(s, m, "Session not found");
+               return 0;
+       }
+
+       if (!session->tech->manager_fax_session) {
+               astman_send_error(s, m, "Fax technology doesn't provide a handler for FAXSession");
+               ao2_ref(session, -1);
+               return 0;
+       }
+
+       if (!ast_strlen_zero(action_id)) {
+               snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
+       }
+
+       astman_send_ack(s, m, "FAXSession event will follow");
+
+       session->tech->manager_fax_session(s, id_text, session);
+       ao2_ref(session, -1);
+
+       return 0;
+}
+
 /*! \brief display fax stats */
 static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -3650,7 +4081,36 @@ static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli
        return CLI_SUCCESS;
 }
 
-static const char *cli_session_type(struct ast_fax_session *s)
+static int manager_fax_stats(struct mansession *s, const struct message *m)
+{
+       const char *action_id = astman_get_header(m, "ActionID");
+
+       char id_text[256] = "";
+
+       astman_send_ack(s, m, "FAXStats event will follow");
+
+       if (!ast_strlen_zero(action_id)) {
+               snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
+       }
+
+       astman_append(s, "Event: FAXStats\r\n"
+               "%s"
+               "CurrentSessions: %d\r\n"
+               "ReservedSessions: %d\r\n"
+               "TransmitAttempts: %d\r\n"
+               "ReceiveAttempts: %d\r\n"
+               "CompletedFAXes: %d\r\n"
+               "FailedFAXes: %d\r\n"
+               "\r\n",
+               id_text,
+               faxregistry.active_sessions, faxregistry.reserved_sessions,
+               faxregistry.fax_tx_attempts, faxregistry.fax_rx_attempts,
+               faxregistry.fax_complete, faxregistry.fax_failures);
+
+       return 0;
+}
+
+static const char *fax_session_type(struct ast_fax_session *s)
 {
        if (s->details->caps & AST_FAX_TECH_AUDIO) {
                return "G.711";
@@ -3662,7 +4122,7 @@ static const char *cli_session_type(struct ast_fax_session *s)
        return "none";
 }
 
-static const char *cli_session_operation(struct ast_fax_session *s)
+const char *ast_fax_session_operation_str(struct ast_fax_session *s)
 {
        if (s->details->caps & AST_FAX_TECH_GATEWAY) {
                return "gateway";
@@ -3708,10 +4168,10 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
 
                filenames = generate_filenames_string(s->details, "", ", ");
 
-               ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30s\n",
+               ast_cli(a->fd, "%-20.20s %-10.10s %-10u %-5.5s %-10.10s %-15.15s %-30s\n",
                        s->channame, s->tech->type, s->id,
-                       cli_session_type(s),
-                       cli_session_operation(s),
+                       fax_session_type(s),
+                       ast_fax_session_operation_str(s),
                        ast_fax_state_to_str(s->state), S_OR(filenames, ""));
 
                ast_free(filenames);
@@ -3725,6 +4185,69 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
        return CLI_SUCCESS;
 }
 
+static int manager_fax_sessions_entry(struct mansession *s,
+       struct ast_fax_session *session, const char *id_text)
+{
+       char *filenames;
+
+       ao2_lock(session);
+       filenames = generate_filenames_string(session->details, "", ",");
+
+       if (!filenames) {
+               ast_log(LOG_ERROR, "Error generating Files string");
+               ao2_unlock(session);
+               return -1;
+       }
+
+       astman_append(s, "Event: FAXSessionsEntry\r\n"
+               "%s" /* ActionID if present */
+               "Channel: %s\r\n" /* Channel name */
+               "Technology: %s\r\n" /* Fax session technology */
+               "SessionNumber: %u\r\n" /* Session ID */
+               "SessionType: %s\r\n" /* G711 or T38 */
+               "Operation: %s\r\n"
+               "State: %s\r\n"
+               "Files: %s\r\n"
+               "\r\n",
+               id_text, session->channame, session->tech->type, session->id,
+               fax_session_type(session), ast_fax_session_operation_str(session),
+               ast_fax_state_to_str(session->state), S_OR(filenames, ""));
+       ast_free(filenames);
+       ao2_unlock(session);
+       return 0;
+}
+
+static int manager_fax_sessions(struct mansession *s, const struct message *m)
+{
+       const char *action_id = astman_get_header(m, "ActionID");
+       char id_text[256];
+       struct ast_fax_session *session;
+       struct ao2_iterator iter;
+       int session_count = 0;
+
+       id_text[0] = '\0';
+       if (!ast_strlen_zero(action_id)) {
+               snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
+       }
+
+       astman_send_listack(s, m, "FAXSessionsEntry event list will follow", "Start");
+
+       iter = ao2_iterator_init(faxregistry.container, 0);
+       while ((session = ao2_iterator_next(&iter))) {
+               if (!manager_fax_sessions_entry(s, session, id_text)) {
+                       session_count++;
+               }
+               ao2_ref(session, -1);
+       }
+       ao2_iterator_destroy(&iter);
+
+       astman_send_list_complete_start(s, m, "FAXSessionsComplete", session_count);
+       astman_append(s, "Total: %d\r\n", session_count);
+       astman_send_list_complete_end(s);
+
+       return 0;
+}
+
 static struct ast_cli_entry fax_cli[] = {
        AST_CLI_DEFINE(cli_fax_show_version, "Show versions of FAX For Asterisk components"),
        AST_CLI_DEFINE(cli_fax_set_debug, "Enable/Disable FAX debugging on new FAX sessions"),
@@ -3735,35 +4258,77 @@ static struct ast_cli_entry fax_cli[] = {
        AST_CLI_DEFINE(cli_fax_show_stats, "Summarize FAX session history"),
 };
 
+static void set_general_options(const struct fax_options *options)
+{
+       ast_rwlock_wrlock(&options_lock);
+       general_options = *options;
+       ast_rwlock_unlock(&options_lock);
+}
+
+static void get_general_options(struct fax_options *options)
+{
+       ast_rwlock_rdlock(&options_lock);
+       *options = general_options;
+       ast_rwlock_unlock(&options_lock);
+}
+
+static int set_t38timeout(const char *value, unsigned int *t38timeout)
+{
+       unsigned int timeout;
+
+       if (sscanf(value, "%u", &timeout) != 1) {
+               ast_log(LOG_ERROR, "Unable to get timeout from '%s'\n", value);
+               return -1;
+       } else if (timeout) {
+               *t38timeout = timeout;
+       } else {
+               ast_log(LOG_ERROR, "T.38 negotiation timeout must be non-zero\n");
+               return -1;
+       }
+
+       return 0;
+}
+
 /*! \brief configure res_fax */
-static int set_config(const char *config_file)
+static int set_config(int reload)
 {
        struct ast_config *cfg;
        struct ast_variable *v;
-       struct ast_flags config_flags = { 0 };
+       struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
        char modems[128] = "";
+       struct fax_options options;
+       int res = 0;
 
-       /* set defaults */
-       general_options.minrate = RES_FAX_MINRATE;
-       general_options.maxrate = RES_FAX_MAXRATE;
-       general_options.statusevents = RES_FAX_STATUSEVENTS;
-       general_options.modems = RES_FAX_MODEM;
-       general_options.ecm = AST_FAX_OPTFLAG_TRUE;
+       options = default_options;
+
+       /* When we're not reloading, we have to be certain to set the general options
+        * to the defaults in case config loading goes wrong at some point. On a reload,
+        * the general options need to stay the same as what they were prior to the
+        * reload rather than being reset to the defaults.
+        */
+       if (!reload) {
+               set_general_options(&options);
+       }
 
        /* read configuration */
-       if (!(cfg = ast_config_load2(config_file, "res_fax", config_flags))) {
-               ast_log(LOG_NOTICE, "Configuration file '%s' not found, using default options.\n", config_file);
+       if (!(cfg = ast_config_load2(config, "res_fax", config_flags))) {
+               ast_log(LOG_NOTICE, "Configuration file '%s' not found, %s options.\n",
+                               config, reload ? "not changing" : "using default");
                return 0;
        }
 
        if (cfg == CONFIG_STATUS_FILEINVALID) {
-               ast_log(LOG_NOTICE, "Configuration file '%s' is invalid, using default options.\n", config_file);
+               ast_log(LOG_NOTICE, "Configuration file '%s' is invalid, %s options.\n",
+                               config, reload ? "not changing" : "using default");
                return 0;
        }
 
        if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
-               ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
-               cfg = ast_config_load2(config_file, "res_fax", config_flags);
+               return 0;
+       }
+
+       if (reload) {
+               options = default_options;
        }
 
        /* create configuration */
@@ -3773,49 +4338,59 @@ static int set_config(const char *config_file)
                if (!strcasecmp(v->name, "minrate")) {
                        ast_debug(3, "reading minrate '%s' from configuration file\n", v->value);
                        if ((rate = fax_rate_str_to_int(v->value)) == 0) {
-                               ast_config_destroy(cfg);
-                               return -1;
+                               res = -1;
+                               goto end;
                        }
-                       general_options.minrate = rate;
+                       options.minrate = rate;
                } else if (!strcasecmp(v->name, "maxrate")) {
                        ast_debug(3, "reading maxrate '%s' from configuration file\n", v->value);
                        if ((rate = fax_rate_str_to_int(v->value)) == 0) {
-                               ast_config_destroy(cfg);
-                               return -1;
+                               res = -1;
+                               goto end;
                        }
-                       general_options.maxrate = rate;
+                       options.maxrate = rate;
                } else if (!strcasecmp(v->name, "statusevents")) {
                        ast_debug(3, "reading statusevents '%s' from configuration file\n", v->value);
-                       general_options.statusevents = ast_true(v->value);
+                       options.statusevents = ast_true(v->value);
                } else if (!strcasecmp(v->name, "ecm")) {
                        ast_debug(3, "reading ecm '%s' from configuration file\n", v->value);
-                       general_options.ecm = ast_true(v->value);
+                       options.ecm = ast_true(v->value);
                } else if ((!strcasecmp(v->name, "modem")) || (!strcasecmp(v->name, "modems"))) {
-                       general_options.modems = 0;
-                       update_modem_bits(&general_options.modems, v->value);
+                       options.modems = 0;
+                       update_modem_bits(&options.modems, v->value);
+               } else if (!strcasecmp(v->name, "t38timeout")) {
+                       if (set_t38timeout(v->value, &options.t38timeout)) {
+                               res = -1;
+                               goto end;
+                       }
                }
        }
 
-       ast_config_destroy(cfg);
-
-       if (general_options.maxrate < general_options.minrate) {
-               ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", general_options.maxrate, general_options.minrate);
-               return -1;
+       if (options.maxrate < options.minrate) {
+               ast_log(LOG_ERROR, "maxrate %u is less than minrate %u\n", options.maxrate, options.minrate);
+               res = -1;
+               goto end;
        }
 
-       if (check_modem_rate(general_options.modems, general_options.minrate)) {
-               ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
-               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, general_options.minrate);
-               return -1;
+       if (check_modem_rate(options.modems, options.minrate)) {
+               ast_fax_modem_to_str(options.modems, modems, sizeof(modems));
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %u\n", modems, options.minrate);
+               res = -1;
+               goto end;
        }
 
-       if (check_modem_rate(general_options.modems, general_options.maxrate)) {
-               ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
-               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, general_options.maxrate);
-               return -1;
+       if (check_modem_rate(options.modems, options.maxrate)) {
+               ast_fax_modem_to_str(options.modems, modems, sizeof(modems));
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %u\n", modems, options.maxrate);
+               res = -1;
+               goto end;
        }
 
-       return 0;
+       set_general_options(&options);
+
+end:
+       ast_config_destroy(cfg);
+       return res;
 }
 
 /*! \brief FAXOPT read function returns the contents of a FAX option */
@@ -3826,7 +4401,7 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
        char *filenames;
 
        if (!details) {
-               ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
+               ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", ast_channel_name(chan), data);
                return -1;
        }
        if (!strcasecmp(data, "ecm")) {
@@ -3840,20 +4415,20 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
                ast_copy_string(buf, details->error, len);
        } else if (!strcasecmp(data, "filename")) {
                if (AST_LIST_EMPTY(&details->documents)) {
-                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
+                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", ast_channel_name(chan), data);
                        res = -1;
                } else {
                        ast_copy_string(buf, AST_LIST_FIRST(&details->documents)->filename, len);
                }
        } else if (!strcasecmp(data, "filenames")) {
                if (AST_LIST_EMPTY(&details->documents)) {
-                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", chan->name, data);
+                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s) because it has never been written.\n", ast_channel_name(chan), data);
                        res = -1;
                } else if ((filenames = generate_filenames_string(details, "", ","))) {
                        ast_copy_string(buf, filenames, len);
                        ast_free(filenames);
                } else {
-                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s), there was an error generating the filenames list.\n", chan->name, data);
+                       ast_log(LOG_ERROR, "channel '%s' can't read FAXOPT(%s), there was an error generating the filenames list.\n", ast_channel_name(chan), data);
                        res = -1;
                }
        } else if (!strcasecmp(data, "headerinfo")) {
@@ -3861,11 +4436,11 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
        } else if (!strcasecmp(data, "localstationid")) {
                ast_copy_string(buf, details->localstationid, len);
        } else if (!strcasecmp(data, "maxrate")) {
-               snprintf(buf, len, "%d", details->maxrate);
+               snprintf(buf, len, "%u", details->maxrate);
        } else if (!strcasecmp(data, "minrate")) {
-               snprintf(buf, len, "%d", details->minrate);
+               snprintf(buf, len, "%u", details->minrate);
        } else if (!strcasecmp(data, "pages")) {
-               snprintf(buf, len, "%d", details->pages_transferred);
+               snprintf(buf, len, "%u", details->pages_transferred);
        } else if (!strcasecmp(data, "rate")) {
                ast_copy_string(buf, details->transfer_rate, len);
        } else if (!strcasecmp(data, "remotestationid")) {
@@ -3873,15 +4448,17 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
        } else if (!strcasecmp(data, "resolution")) {
                ast_copy_string(buf, details->resolution, len);
        } else if (!strcasecmp(data, "sessionid")) {
-               snprintf(buf, len, "%d", details->id);
+               snprintf(buf, len, "%u", details->id);
        } else if (!strcasecmp(data, "status")) {
                ast_copy_string(buf, details->result, len);
        } else if (!strcasecmp(data, "statusstr")) {
                ast_copy_string(buf, details->resultstr, len);
        } else if ((!strcasecmp(data, "modem")) || (!strcasecmp(data, "modems"))) {
                ast_fax_modem_to_str(details->modems, buf, len);
+       } else if (!strcasecmp(data, "t38timeout")) {
+               snprintf(buf, len, "%u", details->t38timeout);
        } else {
-               ast_log(LOG_WARNING, "channel '%s' can't read FAXOPT(%s) because it is unhandled!\n", chan->name, data);
+               ast_log(LOG_WARNING, "channel '%s' can't read FAXOPT(%s) because it is unhandled!\n", ast_channel_name(chan), data);
                res = -1;
        }
        ao2_ref(details, -1);
@@ -3896,10 +4473,10 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
        struct ast_fax_session_details *details;
 
        if (!(details = find_or_create_details(chan))) {
-               ast_log(LOG_WARNING, "channel '%s' can't set FAXOPT(%s) to '%s' because it failed to create a datastore.\n", chan->name, data, value);
+               ast_log(LOG_WARNING, "channel '%s' can't set FAXOPT(%s) to '%s' because it failed to create a datastore.\n", ast_channel_name(chan), data, value);
                return -1;
        }
-       ast_debug(3, "channel '%s' setting FAXOPT(%s) to '%s'\n", chan->name, data, value);
+       ast_debug(3, "channel '%s' setting FAXOPT(%s) to '%s'\n", ast_channel_name(chan), data, value);
 
        if (!strcasecmp(data, "ecm")) {
                const char *val = ast_skip_blanks(value);
@@ -3924,8 +4501,14 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                                details->gateway_timeout = 0;
                                if (timeout) {
                                        unsigned int gwtimeout;
-                                       if (sscanf(timeout, "%u", &gwtimeout) == 1) {
-                                               details->gateway_timeout = gwtimeout * 1000;
+
+                                       if (sscanf(timeout, "%30u", &gwtimeout) == 1) {
+                                               if (gwtimeout >= 0) {
+                                                       details->gateway_timeout = gwtimeout * 1000;
+                                               } else {
+                                                       ast_log(LOG_WARNING, "%s(%s) timeout cannot be negative.  Ignoring timeout\n",
+                                                               cmd, data);
+                                               }
                                        } else {
                                                ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n", timeout, data);
                                        }
@@ -3933,16 +4516,18 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
 
                                details->gateway_id = fax_gateway_attach(chan, details);
                                if (details->gateway_id < 0) {
-                                       ast_log(LOG_ERROR, "Error attaching T.38 gateway to channel %s.\n", chan->name);
+                                       ast_log(LOG_ERROR, "Error attaching T.38 gateway to channel %s.\n", ast_channel_name(chan));
                                        res = -1;
                                } else {
-                                       ast_debug(1, "Attached T.38 gateway to channel %s.\n", chan->name);
+                                       ast_debug(1, "Attached T.38 gateway to channel %s.\n", ast_channel_name(chan));
                                }
                        } else {
-                               ast_log(LOG_WARNING, "Attempt to attach a T.38 gateway on channel (%s) with gateway already running.\n", chan->name);
+                               ast_log(LOG_WARNING, "Attempt to attach a T.38 gateway on channel (%s) with gateway already running.\n", ast_channel_name(chan));
                        }
                } else if (ast_false(val)) {
+                       ast_channel_lock(chan);
                        ast_framehook_detach(chan, details->gateway_id);
+                       ast_channel_unlock(chan);
                        details->gateway_id = -1;
                } else {
                        ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);
@@ -3960,11 +4545,18 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
 
                if (ast_true(val) || !strcasecmp(val, "t38") || !strcasecmp(val, "cng")) {
                        if (details->faxdetect_id < 0) {
-                               if (timeout && (sscanf(timeout, "%u", &fdtimeout) == 1)) {
-                                       if (fdtimeout > 0) {
-                                               fdtimeout = fdtimeout * 1000;
+                               if (timeout) {
+                                       if (sscanf(timeout, "%30u", &fdtimeout) == 1) {
+                                               if (fdtimeout >= 0) {
+                                                       fdtimeout *= 1000;
+                                               } else {
+                                                       ast_log(LOG_WARNING, "%s(%s) timeout cannot be negative.  Ignoring timeout\n",
+                                                               cmd, data);
+                                                       fdtimeout = 0;
+                                               }
                                        } else {
-                                               ast_log(LOG_WARNING, "Timeout cannot be negative ignoring timeout\n");
+                                               ast_log(LOG_WARNING, "Unsupported timeout '%s' passed to FAXOPT(%s).\n",
+                                                       timeout, data);
                                        }
                                }
 
@@ -3978,16 +4570,18 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
 
                                faxdetect = fax_detect_attach(chan, fdtimeout, flags);
                                if (faxdetect < 0) {
-                                       ast_log(LOG_ERROR, "Error attaching FAX detect to channel %s.\n", chan->name);
+                                       ast_log(LOG_ERROR, "Error attaching FAX detect to channel %s.\n", ast_channel_name(chan));
                                        res = -1;
                                } else {
-                                       ast_debug(1, "Attached FAX detect to channel %s.\n", chan->name);
+                                       ast_debug(1, "Attached FAX detect to channel %s.\n", ast_channel_name(chan));
                                }
                        } else {
-                               ast_log(LOG_WARNING, "Attempt to attach a FAX detect on channel (%s) with FAX detect already running.\n", chan->name);
+                               ast_log(LOG_WARNING, "Attempt to attach a FAX detect on channel (%s) with FAX detect already running.\n", ast_channel_name(chan));
                        }
                } else if (ast_false(val)) {
+                       ast_channel_lock(chan);
                        ast_framehook_detach(chan, details->faxdetect_id);
+                       ast_channel_unlock(chan);
                        details->faxdetect_id = -1;
                } else {
                        ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(%s).\n", value, data);
@@ -4006,10 +4600,14 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                if (!details->minrate) {
                        details->minrate = ast_fax_minrate();
                }
+       } else if (!strcasecmp(data, "t38timeout")) {
+               if (set_t38timeout(value, &details->t38timeout)) {
+                       res = -1;
+               }
        } else if ((!strcasecmp(data, "modem")) || (!strcasecmp(data, "modems"))) {
                update_modem_bits(&details->modems, value);
        } else {
-               ast_log(LOG_WARNING, "channel '%s' set FAXOPT(%s) to '%s' is unhandled!\n", chan->name, data, value);
+               ast_log(LOG_WARNING, "channel '%s' set FAXOPT(%s) to '%s' is unhandled!\n", ast_channel_name(chan), data, value);
                res = -1;
        }
 
@@ -4042,6 +4640,10 @@ static int unload_module(void)
                ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_receivefax);
        }
 
+       ast_manager_unregister("FAXSessions");
+       ast_manager_unregister("FAXSession");
+       ast_manager_unregister("FAXStats");
+
        if (fax_logger_level != -1) {
                ast_logger_unregister_level("FAX");
        }
@@ -4051,7 +4653,16 @@ static int unload_module(void)
        return 0;
 }
 
-/*! \brief load res_fax */
+/*!
+ * \brief Load the module
+ *
+ * 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
+ * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
+ */
 static int load_module(void)
 {
        int res;
@@ -4063,7 +4674,7 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
        }
 
-       if (set_config(config) < 0) {
+       if (set_config(0) < 0) {
                ast_log(LOG_ERROR, "failed to load configuration file '%s'\n", config);
                ao2_ref(faxregistry.container, -1);
                return AST_MODULE_LOAD_DECLINE;
@@ -4082,6 +4693,33 @@ static int load_module(void)
                return AST_MODULE_LOAD_DECLINE;
        }
 
+       if (ast_manager_register_xml("FAXSessions", EVENT_FLAG_CALL, manager_fax_sessions)) {
+               ast_log(LOG_WARNING, "failed to register 'FAXSessions' AMI command.\n");
+               ast_unregister_application(app_receivefax);
+               ast_unregister_application(app_sendfax);
+               ao2_ref(faxregistry.container, -1);
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       if (ast_manager_register_xml("FAXSession", EVENT_FLAG_CALL, manager_fax_session)) {
+               ast_log(LOG_WARNING, "failed to register 'FAXSession' AMI command.\n");
+               ast_manager_unregister("FAXSession");
+               ast_unregister_application(app_receivefax);
+               ast_unregister_application(app_sendfax);
+               ao2_ref(faxregistry.container, -1);
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       if (ast_manager_register_xml("FAXStats", EVENT_FLAG_REPORTING, manager_fax_stats)) {
+               ast_log(LOG_WARNING, "failed to register 'FAXStats' AMI command.\n");
+               ast_manager_unregister("FAXSession");
+               ast_manager_unregister("FAXSessions");
+               ast_unregister_application(app_receivefax);
+               ast_unregister_application(app_sendfax);
+               ao2_ref(faxregistry.container, -1);
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
        ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli));
        res = ast_custom_function_register(&acf_faxopt);
        fax_logger_level = ast_logger_register_level("FAX");
@@ -4089,9 +4727,17 @@ static int load_module(void)
        return res;
 }
 
+static int reload_module(void)
+{
+       set_config(1);
+       return 0;
+}
+
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Generic FAX Applications",
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_APP_DEPEND,
-              );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload_module,
+       .load_pri = AST_MODPRI_APP_DEPEND,
+);