Update res_fax and res_fax_spandsp to be compatible with Fax For Asterisk 1.2.
authorMatthew Nicholson <mnicholson@digium.com>
Mon, 26 Apr 2010 14:18:15 +0000 (14:18 +0000)
committerMatthew Nicholson <mnicholson@digium.com>
Mon, 26 Apr 2010 14:18:15 +0000 (14:18 +0000)
The fax session initilization code for T.38 faxes has been rewritten. T.38 session initialization was removed from generic_fax_exec, and split into two different code paths for receive and send.  Also the 'z' option (to send a T.38 reinvite if we do not receive one) was added to sendfax.

In the output of 'fax show sessions', the 'Type' column has been renamed to 'Tech' and replaced with a new 'Tech' column that will report 'G.711' or 'T.38'.

Control of ECM defaults has been added to res_fax

A 'fax show settings' CLI command has been added.

Support of the new AST_T38_REQUEST_PARMS control method request to handle channels that have already received a T.38 reinvite before the FAX application is start has been added.

Support for the 'fax show settings' command has been added to res_fax_spandsp and handling of the ECM flag has been slightly altered.

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

include/asterisk/res_fax.h
res/res_fax.c
res/res_fax_spandsp.c

index 414ba38..9d83f6c 100644 (file)
@@ -139,13 +139,19 @@ struct ast_fax_session_details {
                        /*! flag to send debug manager events */
                        uint32_t debug:2;
                        /*! flag indicating the use of Error Correction Mode (ECM) */
-                       uint32_t ecm:2;
+                       uint32_t ecm:1;
                        /*! flag indicating the sending of status manager events */
                        uint32_t statusevents:2;
                        /*! allow audio mode FAX on T.38-capable channels */
                        uint32_t allow_audio:2;
                        /*! indicating the session switched to T38 */
                        uint32_t switch_to_t38:1;
+                       /*! flag indicating whether CED should be sent (for receive mode) */
+                       uint32_t send_ced:1;
+                       /*! flag indicating whether CNG should be sent (for send mode) */
+                       uint32_t send_cng:1;
+                       /*! send a T.38 reinvite */
+                       uint32_t request_t38:1;
                };
        } option;
        /*! override the minimum transmission rate with a channel variable */
@@ -181,6 +187,8 @@ struct ast_fax_session {
        enum ast_fax_state state;
        /*! name of the Asterisk channel using the fax session */
        char *channame;
+       /*! unique ID of the Asterisk channel using the fax session */
+       char *chan_uniqueid;
        /*! Asterisk channel using the fax session */
        struct ast_channel *chan;
        /*! fax debugging structure */
@@ -229,6 +237,8 @@ struct ast_fax_tech {
        char * (* const cli_show_session)(struct ast_fax_session *, int);
        /*! displays statistics from the fax technology module */
        char * (* const cli_show_stats)(int);
+       /*! displays settings from the fax technology module */
+       char * (* const cli_show_settings)(int);
 };
   
 /*! \brief register a fax technology */
index 0b04b1b..04c426a 100644 (file)
@@ -50,12 +50,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/config.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/res_fax.h"
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
 #include "asterisk/manager.h"
 #include "asterisk/dsp.h"
-#include "asterisk/res_fax.h"
+#include "asterisk/indications.h"
+
+#include "asterisk/version.h"
 
 static const char app_receivefax[] = "ReceiveFAX";
 static const char synopsis_receivefax[] = "Receive a FAX and save as a TIFF/F file.";
@@ -78,6 +81,7 @@ static const char descrip_sendfax[] = "SendFAX(filename[,options]):\n"
  " The application arguments are:\n"
  "    'd' - enables FAX debugging\n"
  "    'f' - allow audio fallback FAX transfer on T.38 capable channels\n"
+ "    'z' - initiate a T.38 reinvite on the channel if the remote end does not\n"
  "    's' - send progress Manager events (overrides statusevents setting in res_fax.conf)\n"
  "\n"
  " Use the FAXOPT function to specify session arguments prior to calling SendFAX()\n"
@@ -135,6 +139,7 @@ static AST_RWLIST_HEAD_STATIC(faxmodules, fax_module);
 static struct {
        enum ast_fax_modems modems;
        uint32_t statusevents:1;
+       uint32_t ecm:1;
        unsigned int minrate;   
        unsigned int maxrate;
 } general_options;
@@ -144,11 +149,12 @@ static const char *config = "res_fax.conf";
 static int global_fax_debug = 0;
 
 enum {
-       OPT_CALLEDMODE = (1 << 0),
-       OPT_CALLERMODE = (1 << 1),
-       OPT_DEBUG      = (1 << 2),
-       OPT_STATUS     = (1 << 3),
-       OPT_ALLOWAUDIO = (1 << 5),
+       OPT_CALLEDMODE  = (1 << 0),
+       OPT_CALLERMODE  = (1 << 1),
+       OPT_DEBUG       = (1 << 2),
+       OPT_STATUS      = (1 << 3),
+       OPT_ALLOWAUDIO  = (1 << 5),
+       OPT_REQUEST_T38 = (1 << 6),
 };
 
 AST_APP_OPTIONS(fax_exec_options, BEGIN_OPTIONS
@@ -157,6 +163,7 @@ AST_APP_OPTIONS(fax_exec_options, BEGIN_OPTIONS
        AST_APP_OPTION('d', OPT_DEBUG),
        AST_APP_OPTION('f', OPT_ALLOWAUDIO),
        AST_APP_OPTION('s', OPT_STATUS),
+       AST_APP_OPTION('z', OPT_REQUEST_T38),
 END_OPTIONS);
 
 struct manager_event_info {
@@ -262,7 +269,10 @@ static struct ast_fax_session_details *session_details_new(void)
 
        /* These options need to be set to the configured default and may be overridden by
         * SendFAX, ReceiveFAX, or FAXOPT */
-       d->option.ecm = AST_FAX_OPTFLAG_DEFAULT;
+       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;
@@ -491,6 +501,7 @@ static void destroy_session(void *session)
        ast_atomic_fetchadd_int(&faxregistry.active_sessions, -1);
 
        ast_free(s->channame);
+       ast_free(s->chan_uniqueid);
 }
 
 /*! \brief create a FAX session */
@@ -523,6 +534,12 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d
                ao2_ref(s, -1);
                return NULL;
        }
+
+       if (!(s->chan_uniqueid = ast_strdup(chan->uniqueid))) {
+               ao2_ref(s, -1);
+               return NULL;
+       }
+
        s->chan = chan;
        s->details = details;
        ao2_ref(s->details, 1);
@@ -634,55 +651,10 @@ static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, co
        dst->transcoding_jbig = src->transcoding_jbig;
 }
 
-/*! \brief this is the generic FAX session handling function */
-static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details)
+static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
-       int ms;
-       int timeout = RES_FAX_TIMEOUT;
-       int res = 0, chancount;
-       unsigned int expected_frametype = -1;
-       union ast_frame_subclass expected_framesubclass = { .integer = -1 };
-       char tbuf[10];
-       unsigned int t38negotiated = 0;
-       enum ast_t38_state t38_state;
-       struct ast_control_t38_parameters t38_parameters;
-       int send_cng = -1;
-       unsigned int disable_t38 = 0;
-       const char *tempvar;
-       struct ast_fax_session *fax = NULL;
-       struct ast_frame *frame = NULL;
-       struct ast_channel *c = chan;
-       unsigned int orig_write_format = 0, orig_read_format = 0;
-       unsigned int request_t38 = 0;
-       unsigned int send_audio = 1;
-
-       details->our_t38_parameters.version = 0;
-       details->our_t38_parameters.max_ifp = 400;
-       details->our_t38_parameters.rate = AST_T38_RATE_14400;
-       details->our_t38_parameters.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF;
-
-       chancount = 1;
-
-       switch ((t38_state = ast_channel_get_t38_state(chan))) {
+       switch (ast_channel_get_t38_state(chan)) {
        case T38_STATE_UNKNOWN:
-               if (details->caps & AST_FAX_TECH_SEND) {
-                       if (details->option.allow_audio) {
-                               details->caps |= AST_FAX_TECH_AUDIO;
-                       } else {
-                               /* we are going to send CNG to attempt to stimulate the receiver
-                                * into switching to T.38, since audio mode is not allowed
-                                */
-                               send_cng = 0;
-                       }
-               } else {
-                       /* we *always* request a switch to T.38 if allowed; if audio is also
-                        * allowed, then we will allow the switch to happen later if needed
-                        */
-                       if (details->option.allow_audio) {
-                               details->caps |= AST_FAX_TECH_AUDIO;
-                       }
-                       request_t38 = 1;
-               }
                details->caps |= AST_FAX_TECH_T38;
                break;
        case T38_STATE_UNAVAILABLE:
@@ -697,14 +669,11 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                 * that gets called after this one completes
                 */
                struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_PARMS, };
-               ast_log(LOG_NOTICE, "Channel is already in T.38 negotiation state; retrieving remote parameters.\n");
                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);
                        return -1;
                }
                details->caps |= AST_FAX_TECH_T38;
-               details->option.allow_audio = 0;
-               send_audio = 0;
                break;
        }
        default:
@@ -712,199 +681,97 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
                return -1;
        }
 
-       /* generate 3 seconds of CED if we are in receive mode and not already negotiating T.38 */
-       if (send_audio && (details->caps & AST_FAX_TECH_RECEIVE)) {
-               ms = 3000;
-               if (ast_tonepair_start(chan, 2100, 0, ms, 0)) {
-                       ast_log(LOG_ERROR, "error generating CED tone on %s\n", chan->name);
-                       return -1;
-               }
-
-               do {
-                       ms = ast_waitfor(chan, ms);
-                       if (ms < 0) {
-                               ast_log(LOG_ERROR, "error while generating CED tone on %s\n", chan->name);
-                               ast_tonepair_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 CED tone on %s\n", chan->name);
-                               ast_tonepair_stop(chan);
-                               return -1;
-                       }
+       return 0;
+}
 
-                       if ((frame->frametype == AST_FRAME_CONTROL) &&
-                                       (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
-                                       (frame->datalen == sizeof(t38_parameters))) {
-                               struct ast_control_t38_parameters *parameters = frame->data.ptr;
+static int disable_t38(struct ast_channel *chan)
+{
+       int ms;
+       struct ast_frame *frame = NULL;
+       struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
 
-                               switch (parameters->request_response) {
-                               case AST_T38_REQUEST_NEGOTIATE:
-                                       /* the other end has requested a switch to T.38, so reply that we are willing, if we can
-                                        * do T.38 as well
-                                        */
-                                       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
-                                       t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
-                                       ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
-                                       break;
-                               case AST_T38_NEGOTIATED:
-                                       ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name);
-                                       t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
-                                       details->caps &= ~AST_FAX_TECH_AUDIO;
-                                       report_fax_status(chan, details, "T.38 Negotiated");
-                                       t38negotiated = 1;
-                                       ms = 0;
-                                       break;
-                               default:
-                                       break;
-                               }
-                       }
-                       ast_frfree(frame);
-               } while (ms > 0);
-               ast_tonepair_stop(chan);
+       ast_debug(1, "Shutting down T.38 on %s\n", chan->name);
+       if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0) {
+               ast_log(LOG_WARNING, "error while disabling T.38 on channel '%s'\n", chan->name);
+               return -1;
        }
 
-       if (request_t38) {
-               /* wait up to five seconds for negotiation to complete */
-               timeout = 5000;
+       /* wait up to five seconds for negotiation to complete */
+       ms = 5000;
 
-               /* set parameters based on the session's parameters */
-               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
-               t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
-               if ((ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0)) {
-                       res = -1;
-                       goto t38done;
+       while (ms > 0) {
+               ms = ast_waitfor(chan, ms);
+               if (ms < 0) {
+                       ast_log(LOG_WARNING, "error while disabling T.38 on channel '%s'\n", chan->name);
+                       return -1;
                }
 
-               ast_log(LOG_NOTICE, "Negotiating T.38 for %s on %s\n", (details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", chan->name);
-       } else if (!details->option.allow_audio) {
-               /* wait up to sixty seconds for negotiation to complete */
-               timeout = 60000;
-
-               ast_log(LOG_NOTICE, "Waiting for T.38 negotiation for %s on %s\n", (details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", chan->name);
-       }
-
-       if (request_t38 || !details->option.allow_audio) {
-               struct ast_silence_generator *silence_gen = NULL;
-
-               if (send_audio && (send_cng != -1)) {
-                       silence_gen = ast_channel_start_silence_generator(chan);
+               if (ms == 0) { /* all done, nothing happened */
+                       ast_log(LOG_WARNING, "channel '%s' timed-out during T.38 shutdown\n", chan->name);
+                       break;
                }
 
-               while (timeout > 0) {
-                       if (send_cng > 3000) {
-                               if (send_audio) {
-                                       ast_channel_stop_silence_generator(chan, silence_gen);
-                                       silence_gen = NULL;
-                                       ast_tonepair_start(chan, 1100, 0, 500, 0);
-                               }
-                               send_cng = 0;
-                       } else if (!chan->generator && (send_cng != -1)) {
-                               if (send_audio) {
-                                       /* The CNG tone is done so restart silence generation. */
-                                       silence_gen = ast_channel_start_silence_generator(chan);
-                               }
-                       }
-                       /* this timeout *MUST* be 500ms, in order to keep the spacing
-                        * of CNG tones correct when this loop is sending them
-                        */
-                       ms = ast_waitfor(chan, 500);
-                       if (ms < 0) {
-                               ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", chan->name);
-                               res = -1;
+               if (!(frame = ast_read(chan))) {
+                       return -1;
+               }
+               if ((frame->frametype == AST_FRAME_CONTROL) &&
+                   (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                   (frame->datalen == sizeof(t38_parameters))) {
+                       struct ast_control_t38_parameters *parameters = frame->data.ptr;
+
+                       switch (parameters->request_response) {
+                       case AST_T38_TERMINATED:
+                               ast_debug(1, "Shut down T.38 on %s\n", chan->name);
                                break;
-                       }
-                       if (send_cng != -1) {
-                               send_cng += 500 - ms;
-                       }
-                       if (!ms) {
-                               /* nothing happened */
-                               if (timeout > 0) {
-                                       timeout -= 500;
-                                       continue;
-                               } else {
-                                       ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", chan->name);
-                                       res = -1;
-                                       break;
-                               }
-                       }
-                       if (!(frame = ast_read(chan))) {
-                               if (silence_gen) {
-                                       ast_channel_stop_silence_generator(chan, silence_gen);
-                                       silence_gen = NULL;
-                               }
+                       case AST_T38_REFUSED:
+                               ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", chan->name);
+                               ast_frfree(frame);
+                               return -1;
+                       default:
+                               ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", chan->name);
+                               ast_frfree(frame);
                                return -1;
-                       }
-                       if ((frame->frametype == AST_FRAME_CONTROL) &&
-                           (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
-                           (frame->datalen == sizeof(t38_parameters))) {
-                               struct ast_control_t38_parameters *parameters = frame->data.ptr;
-                               int stop = 1;
-                               
-                               switch (parameters->request_response) {
-                               case AST_T38_REQUEST_NEGOTIATE:
-                                       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
-                                       t38_parameters.request_response = AST_T38_NEGOTIATED;
-                                       ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
-                                       stop = 0;
-                                       send_audio = 0;
-                                       break;
-                               case AST_T38_NEGOTIATED:
-                                       ast_log(LOG_NOTICE, "Negotiated T.38 for %s on %s\n", (details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", chan->name);
-                                       t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
-                                       details->caps &= ~AST_FAX_TECH_AUDIO;
-                                       t38negotiated = 1;
-                                       disable_t38 = 1;
-                                       break;
-                               case AST_T38_REFUSED:
-                                       ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
-                                       res = -1;
-                                       break;
-                               default:
-                                       ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
-                                       res = -1;
-                                       break;
-                               }
-                               if (stop) {
-                                       ast_frfree(frame);
-                                       break;
-                               }
                        }
                        ast_frfree(frame);
+                       break;
                }
-
-               if (silence_gen) {
-                       ast_channel_stop_silence_generator(chan, silence_gen);
-                       silence_gen = NULL;
-               }
+               ast_frfree(frame);
        }
 
-t38done:
-       /* if any failures occurred during T.38 negotiation, handle them here */
-       if (res) {
-               /* if audio is allowed, then drop the T.38 session requirement
-                * and proceed, otherwise the request has failed
-                */
-               if (details->option.allow_audio) {
-                       details->caps &= ~AST_FAX_TECH_T38;
-                       res = 0;
-               } else {
-                       ast_log(LOG_WARNING, "Audio FAX not allowed on channel '%s' and T.38 negotiation failed; aborting.\n", chan->name);
-                       return -1;
-               }
-       }
+       return 0;
+}
+
+static struct ast_control_t38_parameters our_t38_parameters = {
+       .version = 0,
+       .max_ifp = 400,
+       .rate = AST_T38_RATE_14400,
+       .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
+};
+
+/*! \brief this is the generic FAX session handling function */
+static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details)
+{
+       int ms;
+       int timeout = RES_FAX_TIMEOUT;
+       int res = 0, chancount;
+       unsigned int expected_frametype = -1;
+       union ast_frame_subclass expected_framesubclass = { .integer = -1 };
+       char tbuf[10];
+       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;
+       unsigned int orig_write_format = 0, orig_read_format = 0;
+
+       chancount = 1;
 
        /* create the FAX session */
        if (!(fax = fax_session_new(details, chan))) {
                ast_log(LOG_ERROR, "Can't create a FAX session, FAX attempt failed.\n");
                report_fax_status(chan, details, "No Available Resource");
-               chancount = -1;
-               goto disable_t38;
+               return -1;
        }
 
        ast_channel_lock(chan);
@@ -1132,62 +999,160 @@ t38done:
                }
        }
 
-disable_t38:
-       if (disable_t38 &&
-           (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED)) {
-               struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
+       /* return the chancount so the calling function can determine if the channel hungup during this FAX session or not */
+       return chancount;
+}
 
-               if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) {
-                       /* wait up to five seconds for negotiation to complete */
-                       unsigned int timeout = 5000;
-                       int ms;
-                       
-                       ast_debug(1, "Shutting down T.38 on %s\n", chan->name);
-                       while (timeout > 0) {
-                               ms = ast_waitfor(chan, 1000);
-                               if (ms < 0) {
-                                       ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", chan->name);
-                                       return -1;
-                               }
-                               if (!ms) {
-                                       /* nothing happened */
-                                       if (timeout > 0) {
-                                               timeout -= 1000;
-                                               continue;
-                                       } else {
-                                               ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", chan->name);
-                                               break;
-                                       }
-                               }
-                               if (!(frame = ast_read(chan))) {
-                                       return -1;
-                               }
-                               if ((frame->frametype == AST_FRAME_CONTROL) &&
-                                   (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
-                                   (frame->datalen == sizeof(t38_parameters))) {
-                                       struct ast_control_t38_parameters *parameters = frame->data.ptr;
-                                       
-                                       switch (parameters->request_response) {
-                                       case AST_T38_TERMINATED:
-                                               ast_debug(1, "Shut down T.38 on %s\n", chan->name);
-                                               break;
-                                       case AST_T38_REFUSED:
-                                               ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", chan->name);
-                                               break;
-                                       default:
-                                               ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", chan->name);
-                                               break;
-                                       }
-                                       ast_frfree(frame);
+static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_details *details)
+{
+       int ms;
+       struct ast_frame *frame = NULL;
+       struct ast_control_t38_parameters t38_parameters;
+
+       t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
+
+       /* 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);
+                       return -1;
+               }
+
+               ms = 3000;
+               while (ms > 0) {
+                       ms = ast_waitfor(chan, ms);
+                       if (ms < 0) {
+                               ast_log(LOG_ERROR, "error while generating CED tone on %s\n", chan->name);
+                               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 CED tone on %s\n", chan->name);
+                               ast_playtones_stop(chan);
+                               return -1;
+                       }
+
+                       if ((frame->frametype == AST_FRAME_CONTROL) &&
+                           (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                           (frame->datalen == sizeof(t38_parameters))) {
+                               struct ast_control_t38_parameters *parameters = frame->data.ptr;
+
+                               switch (parameters->request_response) {
+                               case AST_T38_REQUEST_NEGOTIATE:
+                                       /* the other end has requested a switch to T.38, so reply that we are willing, if we can
+                                        * do T.38 as well
+                                        */
+                                       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+                                       t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
+                                       ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+                                       ast_playtones_stop(chan);
+                                       break;
+                               case AST_T38_NEGOTIATED:
+                                       ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name);
+                                       t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters);
+                                       details->caps &= ~AST_FAX_TECH_AUDIO;
+                                       report_fax_status(chan, details, "T.38 Negotiated");
+                                       break;
+                               default:
                                        break;
                                }
-                               ast_frfree(frame);
                        }
+                       ast_frfree(frame);
                }
+
+               ast_playtones_stop(chan);
        }
 
-       /* return the chancount so the calling function can determine if the channel hungup during this FAX session or not */
-       return chancount;
+       /* if T.38 was negotiated, we are done initializing */
+       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+               return 0;
+       }
+
+       /* request T.38 */
+       ast_log(LOG_NOTICE, "Negotiating T.38 for receive on %s\n", chan->name);
+
+       /* wait up to five seconds for negotiation to complete */
+       ms = 5000;
+
+       /* set parameters based on the session's parameters */
+       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+       t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
+       if ((ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0)) {
+               return -1;
+       }
+
+       while (ms > 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);
+                       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);
+                       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);
+                       return -1;
+               }
+
+               if ((frame->frametype == AST_FRAME_CONTROL) &&
+                               (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                               (frame->datalen == sizeof(t38_parameters))) {
+                       struct ast_control_t38_parameters *parameters = frame->data.ptr;
+
+                       switch (parameters->request_response) {
+                       case AST_T38_REQUEST_NEGOTIATE:
+                               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+                               t38_parameters.request_response = AST_T38_NEGOTIATED;
+                               ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+                               break;
+                       case AST_T38_NEGOTIATED:
+                               ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name);
+                               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;
+                       case AST_T38_REFUSED:
+                               ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
+                               details->caps &= ~AST_FAX_TECH_T38;
+                               ms = 0;
+                               break;
+                       default:
+                               ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
+                               details->caps &= ~AST_FAX_TECH_T38;
+                               ms = 0;
+                               break;
+                       }
+               }
+               ast_frfree(frame);
+       }
+
+       /* if T.38 was negotiated, we are done initializing */
+       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+               return 0;
+       }
+
+       /* 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);
+               return -1;
+       }
+
+       /* ok, audio fallback is allowed */
+       details->caps |= AST_FAX_TECH_AUDIO;
+
+       return 0;
 }
 
 /*! \brief initiate a receive FAX session */
@@ -1283,10 +1248,31 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
                details->option.allow_audio = AST_FAX_OPTFLAG_TRUE;
        }
 
+       if (set_fax_t38_caps(chan, details)) {
+               ao2_ref(details, -1);
+               return -1;
+       }
+
+       if (details->caps & AST_FAX_TECH_T38) {
+               if (receivefax_t38_init(chan, details)) {
+                       ao2_ref(details, -1);
+                       ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name);
+                       return -1;
+               }
+       } else {
+               details->option.send_ced = 1;
+       }
+
        if ((channel_alive = generic_fax_exec(chan, details)) < 0) {
                ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
        }
-       
+
+       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+               if (disable_t38(chan)) {
+                       ast_log(LOG_WARNING, "error disabling T.38 mode on %s\n", chan->name);
+               }
+       }
+
        /* send out the AMI completion event */
        ast_channel_lock(chan);
 
@@ -1321,6 +1307,231 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
        return (!channel_alive) ? -1 : 0;
 }
 
+static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_details *details)
+{
+       int ms;
+       struct ast_frame *frame = NULL;
+       struct ast_control_t38_parameters t38_parameters;
+
+       t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
+
+       /* 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
+        * the switch.
+        *
+        * 10500 is enough time for 3 CNG tones
+        */
+       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);
+                       return -1;
+               }
+       }
+
+       while (ms > 0) {
+               ms = ast_waitfor(chan, ms);
+               if (ms < 0) {
+                       ast_log(LOG_ERROR, "error while generating CNG tone on %s\n", chan->name);
+                       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 CNG tone on %s\n", chan->name);
+                       ast_playtones_stop(chan);
+                       return -1;
+               }
+
+               if ((frame->frametype == AST_FRAME_CONTROL) &&
+                               (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                               (frame->datalen == sizeof(t38_parameters))) {
+                       struct ast_control_t38_parameters *parameters = frame->data.ptr;
+
+                       switch (parameters->request_response) {
+                       case AST_T38_REQUEST_NEGOTIATE:
+                               /* the other end has requested a switch to T.38, so reply that we are willing, if we can
+                                * do T.38 as well
+                                */
+                               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+                               t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
+                               ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+                               ast_playtones_stop(chan);
+                               break;
+                       case AST_T38_NEGOTIATED:
+                               ast_log(LOG_NOTICE, "Negotiated T.38 for send on %s\n", chan->name);
+                               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;
+                       default:
+                               break;
+                       }
+               }
+               ast_frfree(frame);
+       }
+
+       ast_playtones_stop(chan);
+
+       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+               return 0;
+       }
+
+       /* T.38 negotiation did not happen, initiate a switch if requested */
+       if (details->option.request_t38 == AST_FAX_OPTFLAG_TRUE) {
+               ast_log(LOG_NOTICE, "Negotiating T.38 for send on %s\n", chan->name);
+
+               /* wait up to five seconds for negotiation to complete */
+               ms = 5000;
+
+               /* set parameters based on the session's parameters */
+               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+               t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
+               if ((ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0)) {
+                       return -1;
+               }
+
+               while (ms > 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);
+                               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);
+                               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);
+                               return -1;
+                       }
+
+                       if ((frame->frametype == AST_FRAME_CONTROL) &&
+                                       (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                                       (frame->datalen == sizeof(t38_parameters))) {
+                               struct ast_control_t38_parameters *parameters = frame->data.ptr;
+
+                               switch (parameters->request_response) {
+                               case AST_T38_REQUEST_NEGOTIATE:
+                                       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+                                       t38_parameters.request_response = AST_T38_NEGOTIATED;
+                                       ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+                                       break;
+                               case AST_T38_NEGOTIATED:
+                                       ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name);
+                                       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;
+                               case AST_T38_REFUSED:
+                                       ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", chan->name);
+                                       details->caps &= ~AST_FAX_TECH_T38;
+                                       ms = 0;
+                                       break;
+                               default:
+                                       ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", chan->name);
+                                       details->caps &= ~AST_FAX_TECH_T38;
+                                       ms = 0;
+                                       break;
+                               }
+                       }
+                       ast_frfree(frame);
+               }
+
+               /* if T.38 was negotiated, we are done initializing */
+               if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+                       return 0;
+               }
+
+               /* send one more CNG tone to get audio going again for some
+                * 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);
+                               return -1;
+                       }
+
+                       ms = 3500;
+                       while (ms > 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_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_playtones_stop(chan);
+                                       return -1;
+                               }
+
+                               if ((frame->frametype == AST_FRAME_CONTROL) &&
+                                               (frame->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
+                                               (frame->datalen == sizeof(t38_parameters))) {
+                                       struct ast_control_t38_parameters *parameters = frame->data.ptr;
+
+                                       switch (parameters->request_response) {
+                                       case AST_T38_REQUEST_NEGOTIATE:
+                                               /* the other end has requested a switch to T.38, so reply that we are willing, if we can
+                                                * do T.38 as well
+                                                */
+                                               t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+                                               t38_parameters.request_response = (details->caps & AST_FAX_TECH_T38) ? AST_T38_NEGOTIATED : AST_T38_REFUSED;
+                                               ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
+                                               ast_playtones_stop(chan);
+                                               break;
+                                       case AST_T38_NEGOTIATED:
+                                               ast_log(LOG_NOTICE, "Negotiated T.38 for send on %s\n", chan->name);
+                                               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;
+                                       default:
+                                               break;
+                                       }
+                               }
+                               ast_frfree(frame);
+                       }
+
+                       ast_playtones_stop(chan);
+
+                       /* if T.38 was negotiated, we are done initializing */
+                       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+                               return 0;
+                       }
+               }
+       }
+
+       /* 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);
+               return -1;
+       }
+
+       /* ok, audio fallback is allowed */
+       details->caps |= AST_FAX_TECH_AUDIO;
+
+       return 0;
+}
+
+
 /*! \brief initiate a send FAX session */
 static int sendfax_exec(struct ast_channel *chan, const char *data)
 {
@@ -1419,10 +1630,35 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
                details->option.allow_audio = AST_FAX_OPTFLAG_TRUE;
        }
 
+       if (ast_test_flag(&opts, OPT_REQUEST_T38)) {
+               details->option.request_t38 = AST_FAX_OPTFLAG_TRUE;
+       }
+
+       if (set_fax_t38_caps(chan, details)) {
+               ao2_ref(details, -1);
+               return -1;
+       }
+
+       if (details->caps & AST_FAX_TECH_T38) {
+               if (sendfax_t38_init(chan, details)) {
+                       ao2_ref(details, -1);
+                       ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name);
+                       return -1;
+               }
+       } else {
+               details->option.send_cng = 1;
+       }
+
        if ((channel_alive = generic_fax_exec(chan, details)) < 0) {
                ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
        }
-       
+
+       if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) {
+               if (disable_t38(chan)) {
+                       ast_log(LOG_WARNING, "error disabling T.38 mode on %s\n", chan->name);
+               }
+       }
+
        /* send out the AMI completion event */
        ast_channel_lock(chan);
        get_manager_event_info(chan, &info);
@@ -1472,8 +1708,8 @@ static int session_cmp_cb(void *obj, void *arg, int flags)
        return (lhs->id == rhs->id) ? CMP_MATCH | CMP_STOP : 0;
 }
 
-/*! \brief FAX session tab completion */
-static char *fax_session_tab_complete(struct ast_cli_args *a) 
+/*! \brief fax session tab completion */
+static char *fax_session_tab_complete(struct ast_cli_args *a)
 {
        int tklen;
        int wordnum = 0;
@@ -1503,6 +1739,37 @@ static char *fax_session_tab_complete(struct ast_cli_args *a)
        return name;
 }
 
+static char *cli_fax_show_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct fax_module *fax;
+
+       switch(cmd) {
+       case CLI_INIT:
+               e->command = "fax show version";
+               e->usage =
+                       "Usage: fax show version\n"
+                       "       Show versions of FAX For Asterisk components.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc != 3) {
+               return CLI_SHOWUSAGE;
+       }
+
+       ast_cli(a->fd, "FAX For Asterisk Components:\n");
+       ast_cli(a->fd, "\tApplications: " ASTERISK_VERSION "\n");
+       AST_RWLIST_RDLOCK(&faxmodules);
+       AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
+               ast_cli(a->fd, "\t%s: %s\n", fax->tech->description, fax->tech->version);
+       }
+       AST_RWLIST_UNLOCK(&faxmodules);
+       ast_cli(a->fd, "\n");
+
+       return CLI_SUCCESS;
+}
+
 /*! \brief enable FAX debugging */
 static char *cli_fax_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
@@ -1568,7 +1835,42 @@ static char *cli_fax_show_capabilities(struct ast_cli_entry *e, int cmd, struct
        return CLI_SUCCESS;
 }
 
-/*! \brief display details of a specified FAX session */
+/*! \brief display global defaults and settings */
+static char *cli_fax_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct fax_module *fax;
+       char modems[128] = "";
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "fax show settings";
+               e->usage =
+                       "Usage: fax show settings\n"
+                       "       Show the global settings and defaults of both the FAX core and technology modules\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       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, "\tModem Modulations Allowed: %s\n", modems);
+       ast_cli(a->fd, "\n\nFAX Technology Modules:\n\n");
+       AST_RWLIST_RDLOCK(&faxmodules);
+       AST_RWLIST_TRAVERSE(&faxmodules, fax, list) {
+               ast_cli(a->fd, "%s (%s) Settings:\n", fax->tech->type, fax->tech->description);
+               fax->tech->cli_show_settings(a->fd);
+       }
+       AST_RWLIST_UNLOCK(&faxmodules);
+
+       return CLI_SUCCESS;
+}
+
+/*! \brief display details of a specified fax session */
 static char *cli_fax_show_session(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        struct ast_fax_session *s, tmp;
@@ -1655,13 +1957,15 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
        }
 
        ast_cli(a->fd, "\nCurrent FAX Sessions:\n\n");
-       ast_cli(a->fd, "%-20.20s %-10.10s %-10.10s %-10.10s %-15.15s %-30.30s\n",
-               "Channel", "ID", "Type", "Operation", "State", "File");
+       ast_cli(a->fd, "%-20.20s %-10.10s %-10.10s %-5.5s %-10.10s %-15.15s %-30.30s\n",
+               "Channel", "Tech", "FAXID", "Type", "Operation", "State", "File");
        i = ao2_iterator_init(faxregistry.container, 0);
        while ((s = ao2_iterator_next(&i))) {
                ao2_lock(s);
-               ast_cli(a->fd, "%-20.20s %-10d %-10.10s %-10.10s %-15.15s %-30.30s\n",
-                       s->channame, s->id, s->tech->type, (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
+               ast_cli(a->fd, "%-20.20s %-10.10s %-10d %-5.5s %-10.10s %-15.15s %-30.30s\n",
+                       s->channame, s->tech->type, s->id,
+                       (s->details->caps & AST_FAX_TECH_AUDIO) ? "G.711" : "T.38",
+                       (s->details->caps & AST_FAX_TECH_SEND) ? "send" : "receive",
                        ast_fax_state_to_str(s->state), AST_LIST_FIRST(&s->details->documents)->filename);
                ao2_unlock(s);
                ao2_ref(s, -1);
@@ -1676,8 +1980,10 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
 }
 
 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"),
        AST_CLI_DEFINE(cli_fax_show_capabilities, "Show the capabilities of the registered FAX technology modules"),
+       AST_CLI_DEFINE(cli_fax_show_settings, "Show the global settings and defaults of both the FAX core and technology modules"),
        AST_CLI_DEFINE(cli_fax_show_session, "Show the status of the named FAX sessions"),
        AST_CLI_DEFINE(cli_fax_show_sessions, "Show the current FAX sessions"),
        AST_CLI_DEFINE(cli_fax_show_stats, "Summarize FAX session history"),
@@ -1695,6 +2001,7 @@ static int set_config(const char *config_file)
        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;
 
        /* read configuration */
        if (!(cfg = ast_config_load2(config_file, "res_fax", config_flags))) {
@@ -1727,6 +2034,9 @@ static int set_config(const char *config_file)
                } 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);
+               } 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);
                } else if ((!strcasecmp(v->name, "modem")) || (!strcasecmp(v->name, "modems"))) {
                        general_options.modems = 0;
                        update_modem_bits(&general_options.modems, v->value);
@@ -1753,7 +2063,12 @@ static int acf_faxopt_read(struct ast_channel *chan, const char *cmd, char *data
        } else if (!strcasecmp(data, "error")) {
                ast_copy_string(buf, details->error, len);
        } else if (!strcasecmp(data, "filename")) {
-               ast_copy_string(buf, AST_LIST_FIRST(&details->documents)->filename, len);
+               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);
+                       res = -1;
+               } else {
+                       ast_copy_string(buf, AST_LIST_FIRST(&details->documents)->filename, len);
+               }
        } else if (!strcasecmp(data, "headerinfo")) {
                ast_copy_string(buf, details->headerinfo, len);
        } else if (!strcasecmp(data, "localstationid")) {
index 9a84d28..d29cf79 100644 (file)
@@ -61,6 +61,7 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
 static char *spandsp_fax_cli_show_capabilities(int fd);
 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
 static char *spandsp_fax_cli_show_stats(int fd);
+static char *spandsp_fax_cli_show_settings(int fd);
 
 static struct ast_fax_tech spandsp_fax_tech = {
        .type = "Spandsp",
@@ -85,6 +86,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
        .cli_show_capabilities = spandsp_fax_cli_show_capabilities,
        .cli_show_session = spandsp_fax_cli_show_session,
        .cli_show_stats = spandsp_fax_cli_show_stats,
+       .cli_show_settings = spandsp_fax_cli_show_settings,
 };
 
 struct spandsp_fax_stats {
@@ -405,7 +407,7 @@ static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *det
 
 static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details)
 {
-       t30_set_ecm_capability(t30_state, (details->option.ecm == AST_FAX_OPTFLAG_DEFAULT) ? 1 : details->option.ecm );
+       t30_set_ecm_capability(t30_state, details->option.ecm);
        t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
 }
 
@@ -732,6 +734,13 @@ static char *spandsp_fax_cli_show_stats(int fd)
        return CLI_SUCCESS;
 }
 
+/*! \brief Show res_fax_spandsp settings */
+static char *spandsp_fax_cli_show_settings(int fd)
+{
+       /* no settings at the moment */
+       return CLI_SUCCESS;
+}
+
 /*! \brief unload res_fax_spandsp */
 static int unload_module(void)
 {