res_fax.c: Add chan locked precondition comments.
[asterisk/asterisk.git] / res / res_fax.c
index e05b101..94c512d 100644 (file)
@@ -65,7 +65,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
 
 #include "asterisk/io.h"
 #include "asterisk/file.h"
@@ -89,6 +89,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #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">
@@ -223,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";
@@ -271,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 */
@@ -336,7 +521,8 @@ static AST_RWLIST_HEAD_STATIC(faxmodules, fax_module);
 #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
 
 struct fax_options {
        enum ast_fax_modems modems;
@@ -344,6 +530,7 @@ struct fax_options {
        uint32_t ecm:1;
        unsigned int minrate;
        unsigned int maxrate;
+       unsigned int t38timeout;
 };
 
 static struct fax_options general_options;
@@ -354,6 +541,7 @@ static const struct fax_options default_options = {
        .statusevents = RES_FAX_STATUSEVENTS,
        .modems = RES_FAX_MODEM,
        .ecm = AST_FAX_OPTFLAG_TRUE,
+       .t38timeout = RES_FAX_T38TIMEOUT,
 };
 
 AST_RWLOCK_DEFINE_STATIC(options_lock);
@@ -407,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");
@@ -425,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)
 {
@@ -493,6 +719,7 @@ static struct ast_fax_session_details *session_details_new(void)
        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;
@@ -589,7 +816,7 @@ static int update_modem_bits(enum ast_fax_modems *bits, const char *value)
                m[i] = NULL;
        } else {
                tok = strtok(v, ", ");
-               while (tok && (i < 5)) {
+               while (tok && i < ARRAY_LEN(m) - 1) {
                        m[i++] = tok;
                        tok = strtok(NULL, ", ");
                }
@@ -601,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")) {
@@ -612,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;
@@ -679,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, ",");
                }
@@ -708,22 +936,14 @@ static int check_modem_rate(enum ast_fax_modems modems, unsigned int rate)
 {
        switch (rate) {
        case 2400:
-               if (!(modems & (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;
        case 7200:
-               if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
-                       return 1;
-               }
-               break;
        case 9600:
-               if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V27 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
+               if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
                        return 1;
                }
                break;
@@ -807,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";
        }
 }
@@ -996,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
@@ -1061,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;
                }
@@ -1081,11 +1306,11 @@ 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;
 }
@@ -1124,7 +1349,8 @@ static struct ast_json *generate_filenames_json(struct ast_fax_session_details *
        return json_array;
 }
 
-/* \brief Generate a string of filenames using the given prefix and separator.
+/*!
+ * \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
@@ -1144,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 */
@@ -1175,7 +1401,6 @@ static char *generate_filenames_string(struct ast_fax_session_details *details,
 static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
 {
        RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
-       RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
        RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
        struct ast_json *json_filenames = NULL;
 
@@ -1222,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);
 }
 
@@ -1244,7 +1475,7 @@ static void set_channel_variables(struct ast_channel *chan, struct ast_fax_sessi
 
 #define GENERIC_FAX_EXEC_ERROR(fax, chan, errorstr, reason)    \
        do {    \
-               ast_log(LOG_ERROR, "channel '%s' FAX session '%d' failure, reason: '%s' (%s)\n", ast_channel_name(chan), 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)
 
@@ -1353,20 +1584,18 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
        int timeout = RES_FAX_TIMEOUT;
        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 */
@@ -1390,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, ast_channel_writeformat(chan));
-               if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
+               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_lock(faxregistry.container);
-                       ao2_unlink(faxregistry.container, fax);
-                       ao2_unlock(faxregistry.container);
-                       ao2_ref(fax, -1);
-                       ast_channel_unlock(chan);
+                       ao2_unlink(faxregistry.container, fax);
+                       ao2_ref(fax, -1);
                        return -1;
                }
-               ast_format_copy(&orig_read_format, ast_channel_readformat(chan));
-               if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
+               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_lock(faxregistry.container);
-                       ao2_unlink(faxregistry.container, fax);
-                       ao2_unlock(faxregistry.container);
-                       ao2_ref(fax, -1);
-                       ast_channel_unlock(chan);
+                       ao2_unlink(faxregistry.container, fax);
+                       ao2_ref(fax, -1);
                        return -1;
                }
                if (fax->smoother) {
@@ -1417,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", ast_channel_name(chan), 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;
@@ -1494,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;
@@ -1505,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", ast_channel_name(chan), 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) {
@@ -1582,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);
        }
 
@@ -1592,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);
                }
        }
 
@@ -1680,8 +1906,8 @@ 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", ast_channel_name(chan));
 
-       /* wait up to five seconds for negotiation to complete */
-       timeout_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);
@@ -1808,14 +2034,14 @@ static int report_receive_fax_status(struct ast_channel *chan, const char *filen
                        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}",
+               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", json_array);
+                               "filenames", ast_json_ref(json_array));
                if (!json_object) {
                        return -1;
                }
@@ -1851,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. */
@@ -1877,13 +2104,13 @@ 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);
+               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);
@@ -1892,7 +2119,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
 
        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);
@@ -2358,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 */
@@ -2384,13 +2612,13 @@ 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);
+               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);
@@ -2399,7 +2627,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
 
        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);
@@ -2570,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;
@@ -2599,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.
@@ -2616,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;
 
@@ -2653,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))) {
@@ -2681,7 +2915,10 @@ static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session
        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");
@@ -2697,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;
@@ -2735,6 +2973,7 @@ static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, st
        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;
@@ -2771,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)
@@ -2997,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) {
@@ -3018,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
@@ -3029,9 +3275,12 @@ 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 *active;
        RAII_VAR(struct ast_fax_session_details *, details, NULL, ao2_cleanup);
@@ -3057,14 +3306,14 @@ 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);
 
                        ast_channel_unlock(chan);
                        peer = ast_channel_bridge_peer(chan);
                        if (peer) {
-                               ast_set_read_format(peer, &gateway->peer_read_format);
-                               ast_set_read_format(peer, &gateway->peer_write_format);
+                               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);
@@ -3107,24 +3356,31 @@ 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, ast_channel_readformat(chan));
-               ast_format_copy(&gateway->chan_write_format, ast_channel_readformat(chan));
+               ao2_replace(gateway->chan_read_format, ast_channel_readformat(chan));
+               ao2_replace(gateway->chan_write_format, ast_channel_writeformat(chan));
 
-               ast_format_copy(&gateway->peer_read_format, ast_channel_readformat(peer));
-               ast_format_copy(&gateway->peer_write_format, ast_channel_readformat(peer));
+               ao2_replace(gateway->peer_read_format, ast_channel_readformat(peer));
+               ao2_replace(gateway->peer_write_format, ast_channel_writeformat(peer));
 
-               ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
-               ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR);
+               ast_set_read_format(chan, ast_format_slin);
+               ast_set_write_format(chan, ast_format_slin);
 
-               ast_channel_unlock(chan);
-               ast_set_read_format_by_id(peer, AST_FORMAT_SLINEAR);
-               ast_set_write_format_by_id(peer, AST_FORMAT_SLINEAR);
+               ast_set_read_format(peer, ast_format_slin);
+               ast_set_write_format(peer, ast_format_slin);
+
+               ast_channel_unlock(peer);
 
-               ast_channel_make_compatible(chan, peer);
-               ast_channel_lock(chan);
                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)) {
@@ -3144,12 +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:
+               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;
@@ -3176,7 +3429,7 @@ 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);
+               ast_log(LOG_WARNING, "unhandled framehook event %u\n", event);
                return f;
        }
 
@@ -3193,40 +3446,41 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
 
        /* 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 (ast_channel_readtrans(active) && (f = ast_translate(ast_channel_readtrans(active), 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;
                                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) && ast_channel_readtrans(active)) {
-                       /* 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;
                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));
                        return ast_frisolate(&silence_frame);
                } else {
@@ -3250,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");
@@ -3294,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
  */
@@ -3336,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);
@@ -3353,7 +3614,8 @@ 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;
@@ -3369,23 +3631,22 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
        switch (event) {
        case AST_FRAMEHOOK_EVENT_ATTACHED:
                /* Setup format for DSP on ATTACH*/
-               ast_format_copy(&faxdetect->orig_format, ast_channel_readformat(chan));
-               switch (ast_channel_readformat(chan)->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);
+               ast_set_read_format(chan, faxdetect->orig_format);
                ast_channel_unlock(chan);
                peer = ast_channel_bridge_peer(chan);
                if (peer) {
@@ -3405,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;
@@ -3420,13 +3682,10 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
                        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:
@@ -3457,30 +3716,36 @@ static struct ast_frame *fax_detect_framehook(struct ast_channel *chan, struct a
        }
 
        if (result) {
-               const char *target_context = S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan));
+               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(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
-                               ast_channel_lock(chan);
                                ast_verb(2, "Redirecting '%s' to fax extension due to %s detection\n",
                                        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", 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;
@@ -3488,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
@@ -3519,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) {
@@ -3561,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);
@@ -3664,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;
 }
@@ -3692,10 +3959,11 @@ static char *cli_fax_show_settings(struct ast_cli_entry *e, int cmd, struct ast_
        ast_cli(a->fd, "FAX For Asterisk Settings:\n");
        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: %d\n", options.minrate);
-       ast_cli(a->fd, "\tMaximum Bit Rate: %d\n", options.maxrate);
+       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) {
@@ -3727,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;
        }
@@ -3743,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)
 {
@@ -3776,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";
@@ -3788,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";
@@ -3834,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);
@@ -3851,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"),
@@ -3875,6 +4272,23 @@ static void get_general_options(struct fax_options *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(int reload)
 {
@@ -3944,25 +4358,30 @@ static int set_config(int reload)
                } else if ((!strcasecmp(v->name, "modem")) || (!strcasecmp(v->name, "modems"))) {
                        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;
+                       }
                }
        }
 
        if (options.maxrate < options.minrate) {
-               ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", 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(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 %d\n", modems, options.minrate);
+               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(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 %d\n", modems, options.maxrate);
+               ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %u\n", modems, options.maxrate);
                res = -1;
                goto end;
        }
@@ -4017,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")) {
@@ -4029,13 +4448,15 @@ 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", ast_channel_name(chan), data);
                res = -1;
@@ -4080,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);
                                        }
@@ -4098,7 +4525,9 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                                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);
@@ -4116,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);
                                        }
                                }
 
@@ -4143,7 +4579,9 @@ static int acf_faxopt_write(struct ast_channel *chan, const char *cmd, char *dat
                                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);
@@ -4162,6 +4600,10 @@ 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 {
@@ -4198,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");
        }
@@ -4247,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");
@@ -4262,8 +4735,9 @@ static int reload_module(void)
 
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Generic FAX Applications",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload_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,
+);