Thu Apr 28 2011 17:13:37

Asterisk developer's documentation


app_fax.c File Reference

#include "asterisk.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <pthread.h>
#include <errno.h>
#include <tiffio.h>
#include <spandsp.h>
#include <spandsp/version.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/dsp.h"
#include "asterisk/module.h"
#include "asterisk/manager.h"
Include dependency graph for app_fax.c:

Go to the source code of this file.

Data Structures

struct  fax_session

Defines

#define MAX_SAMPLES   240
#define WATCHDOG_STATE_TIMEOUT   5 * 60
#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void * fax_generator_alloc (struct ast_channel *chan, void *params)
static int fax_generator_generate (struct ast_channel *chan, void *data, int len, int samples)
static int load_module (void)
static void phase_e_handler (t30_state_t *f, void *user_data, int result)
static int rcvfax_exec (struct ast_channel *chan, void *data)
static void set_ecm (t30_state_t *state, int ecm)
static void set_file (t30_state_t *state, fax_session *s)
static void set_local_info (t30_state_t *state, fax_session *s)
static int set_logging (logging_state_t *state)
static int sndfax_exec (struct ast_channel *chan, void *data)
static void span_message (int level, const char *msg)
static int t38_tx_packet_handler (t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
static int transmit (fax_session *s)
static int transmit_audio (fax_session *s)
static int transmit_t38 (fax_session *s)
static int unload_module (void)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static char * app_rcvfax_name = "ReceiveFAX"
static char * app_sndfax_name = "SendFAX"
static struct ast_module_infoast_module_info = &__mod_info
struct ast_generator generator

Define Documentation

#define MAX_SAMPLES   240

Definition at line 147 of file app_fax.c.

Referenced by fax_generator_generate().

#define WATCHDOG_STATE_TIMEOUT   5 * 60

Definition at line 156 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 155 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 945 of file app_fax.c.

static void __unreg_module ( void  ) [static]

Definition at line 945 of file app_fax.c.

static void* fax_generator_alloc ( struct ast_channel chan,
void *  params 
) [static]

Definition at line 319 of file app_fax.c.

{
   return params;
}
static int fax_generator_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 324 of file app_fax.c.

References AST_FORMAT_SLINEAR, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_write(), buf, errno, ast_frame::frametype, len(), LOG_WARNING, MAX_SAMPLES, ast_channel::name, and ast_frame::samples.

{
   fax_state_t *fax = (fax_state_t*) data;
   uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
   int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
    
   struct ast_frame outf = {
      .frametype = AST_FRAME_VOICE,
      .subclass = AST_FORMAT_SLINEAR,
      .src = __FUNCTION__,
   };

   if (samples > MAX_SAMPLES) {
      ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
      samples = MAX_SAMPLES;
   }
   
   if ((len = fax_tx(fax, buf, samples)) > 0) {
      outf.samples = len;
      AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));

      if (ast_write(chan, &outf) < 0) {
         ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
         return -1;
      }
   }

   return 0;
}
static int load_module ( void  ) [static]

Definition at line 928 of file app_fax.c.

References ast_register_application_xml, rcvfax_exec(), and sndfax_exec().

{
   int res ;

   res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
   res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);

   /* The default SPAN message handler prints to stderr. It is something we do not want */
   span_set_message_handler(NULL);

   return res;
}
static void phase_e_handler ( t30_state_t *  f,
void *  user_data,
int  result 
) [static]

Definition at line 202 of file app_fax.c.

References ast_debug, ast_log(), buf, fax_session::chan, ast_channel::cid, ast_callerid::cid_num, fax_session::direction, EVENT_FLAG_CALL, ast_channel::exten, fax_session::file_name, fax_session::finished, LOG_WARNING, manager_event, ast_channel::name, pbx_builtin_setvar_helper(), s, and S_OR.

Referenced by transmit_audio(), and transmit_t38().

{
   const char *local_ident;
   const char *far_ident;
   char buf[20];
   fax_session *s = (fax_session *) user_data;
   t30_stats_t stat;
   int pages_transferred;

   ast_debug(1, "Fax phase E handler. result=%d\n", result);

   t30_get_transfer_statistics(f, &stat);

   s = (fax_session *) user_data;

   if (result != T30_ERR_OK) {
      s->finished = -1;

      /* FAXSTATUS is already set to FAILED */
      pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));

      ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));

      return;
   }
   
   s->finished = 1; 
   
   local_ident = S_OR(t30_get_tx_ident(f), "");
   far_ident = S_OR(t30_get_rx_ident(f), "");
   pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
   pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
   pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
#if SPANDSP_RELEASE_DATE >= 20090220
   pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
#else
   pages_transferred = stat.pages_transferred;
#endif
   snprintf(buf, sizeof(buf), "%d", pages_transferred);
   pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
   snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
   pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
   snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
   pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
   
   ast_debug(1, "Fax transmitted successfully.\n");
   ast_debug(1, "  Remote station ID: %s\n", far_ident);
   ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
   ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
   ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
   
   manager_event(EVENT_FLAG_CALL,
            s->direction ? "FaxSent" : "FaxReceived", 
            "Channel: %s\r\n"
            "Exten: %s\r\n"
            "CallerID: %s\r\n"
            "RemoteStationID: %s\r\n"
            "LocalStationID: %s\r\n"
            "PagesTransferred: %d\r\n"
            "Resolution: %d\r\n"
            "TransferRate: %d\r\n"
            "FileName: %s\r\n",
            s->chan->name,
            s->chan->exten,
            S_OR(s->chan->cid.cid_num, ""),
            far_ident,
            local_ident,
            pages_transferred,
            stat.y_resolution,
            stat.bit_rate,
            s->file_name);
}
static int rcvfax_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 874 of file app_fax.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, chan, fax_session::chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.

Referenced by load_module().

{
   int res = 0;
   char *parse;
   fax_session session;

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(file_name);
      AST_APP_ARG(options);
   );

   if (chan == NULL) {
      ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
      return -1;
   }

   /* The next few lines of code parse out the filename and header from the input string */
   if (ast_strlen_zero(data)) {
      /* No data implies no filename or anything is present */
      ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
      return -1;
   }

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);
   
   session.caller_mode = FALSE;

   if (args.options) {
      if (strchr(args.options, 'c'))
         session.caller_mode = TRUE;
   }

   /* Done parsing */
   session.direction = 0;
   session.file_name = args.file_name;
   session.chan = chan;
   session.finished = 0;

   res = transmit(&session);

   return res;
}
static void set_ecm ( t30_state_t *  state,
int  ecm 
) [static]

Definition at line 309 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

{
   t30_set_ecm_capability(state, ecm);
   t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
}
static void set_file ( t30_state_t *  state,
fax_session s 
) [static]

Definition at line 301 of file app_fax.c.

References fax_session::direction, and fax_session::file_name.

Referenced by transmit_audio(), and transmit_t38().

{
   if (s->direction)
      t30_set_tx_file(state, s->file_name, -1, -1);
   else
      t30_set_rx_file(state, s->file_name, -1);
}
static void set_local_info ( t30_state_t *  state,
fax_session s 
) [static]

Definition at line 288 of file app_fax.c.

References ast_strlen_zero(), fax_session::chan, and pbx_builtin_getvar_helper().

Referenced by transmit_audio(), and transmit_t38().

{
   const char *x;

   x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
   if (!ast_strlen_zero(x))
      t30_set_tx_ident(state, x);

   x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
   if (!ast_strlen_zero(x))
      t30_set_tx_page_header_info(state, x);
}
static int set_logging ( logging_state_t *  state) [static]

Definition at line 278 of file app_fax.c.

References option_debug, and span_message().

Referenced by transmit_audio(), and transmit_t38().

{
   int level = SPAN_LOG_WARNING + option_debug;

   span_log_set_message_handler(state, span_message);
   span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 

   return 0;
}
static int sndfax_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 830 of file app_fax.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, chan, fax_session::chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.

Referenced by load_module().

{
   int res = 0;
   char *parse;
   fax_session session = { 0, };

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(file_name);
      AST_APP_ARG(options);
   );

   if (chan == NULL) {
      ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
      return -1;
   }

   /* The next few lines of code parse out the filename and header from the input string */
   if (ast_strlen_zero(data)) {
      /* No data implies no filename or anything is present */
      ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
      return -1;
   }

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);
   
   session.caller_mode = TRUE;

   if (args.options) {
      if (strchr(args.options, 'a'))
         session.caller_mode = FALSE;
   }

   /* Done parsing */
   session.direction = 1;
   session.file_name = args.file_name;
   session.chan = chan;
   session.finished = 0;

   res = transmit(&session);

   return res;
}
static void span_message ( int  level,
const char *  msg 
) [static]

Definition at line 168 of file app_fax.c.

References ast_log(), LOG_DEBUG, LOG_ERROR, and LOG_WARNING.

Referenced by set_logging().

{
   if (level == SPAN_LOG_ERROR) {
      ast_log(LOG_ERROR, "%s", msg);
   } else if (level == SPAN_LOG_WARNING) {
      ast_log(LOG_WARNING, "%s", msg);
   } else {
      ast_log(LOG_DEBUG, "%s", msg);
   }
}
static int t38_tx_packet_handler ( t38_core_state_t *  s,
void *  user_data,
const uint8_t *  buf,
int  len,
int  count 
) [static]

Definition at line 179 of file app_fax.c.

References AST_FRAME_MODEM, AST_FRAME_SET_BUFFER, ast_log(), AST_MODEM_T38, ast_write(), chan, errno, ast_frame::frametype, and LOG_WARNING.

Referenced by transmit_t38().

{
   struct ast_channel *chan = (struct ast_channel *) user_data;

   struct ast_frame outf = {
      .frametype = AST_FRAME_MODEM,
      .subclass = AST_MODEM_T38,
      .src = __FUNCTION__,
   };

   /* TODO: Asterisk does not provide means of resending the same packet multiple
     times so count is ignored at the moment */

   AST_FRAME_SET_BUFFER(&outf, buf, 0, len);

   if (ast_write(chan, &outf) < 0) {
      ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
      return -1;
   }

   return 0;
}
static int transmit ( fax_session s) [static]

Definition at line 772 of file app_fax.c.

References ast_channel::_state, ast_answer(), ast_channel_get_t38_state(), ast_debug, ast_log(), AST_STATE_UP, fax_session::chan, fax_session::finished, LOG_ERROR, LOG_WARNING, ast_channel::name, pbx_builtin_setvar_helper(), T38_STATE_NEGOTIATED, fax_session::t38state, transmit_audio(), and transmit_t38().

Referenced by rcvfax_exec(), and sndfax_exec().

{
   int res = 0;

   /* Clear all channel variables which to be set by the application.
      Pre-set status to error so in case of any problems we can just leave */
   pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
   pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 

   pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
   pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
   pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL);
   pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
   pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 

   if (s->chan->_state != AST_STATE_UP) {
      /* Shouldn't need this, but checking to see if channel is already answered
       * Theoretically asterisk should already have answered before running the app */
      res = ast_answer(s->chan);
      if (res) {
         ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
         return res;
      }
   }

   s->t38state = ast_channel_get_t38_state(s->chan);
   if (s->t38state != T38_STATE_NEGOTIATED) {
      /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
      pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
      res = transmit_audio(s);
      if (res > 0) {
         /* transmit_audio reports switchover to T38. Update t38state */
         s->t38state = ast_channel_get_t38_state(s->chan);
         if (s->t38state != T38_STATE_NEGOTIATED) {
            ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
         }
      }
   }

   if (s->t38state == T38_STATE_NEGOTIATED) {
      pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
      res = transmit_t38(s);
   }

   if (res) {
      ast_log(LOG_WARNING, "Transmission error\n");
      res = -1;
   } else if (s->finished < 0) {
      ast_log(LOG_WARNING, "Transmission failed\n");
   } else if (s->finished > 0) {
      ast_debug(1, "Transmission finished Ok\n");
   }

   return res;
}
static int transmit_audio ( fax_session s) [static]

Definition at line 362 of file app_fax.c.

References ast_activate_generator(), ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_deactivate_generator(), ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_write_format(), AST_T38_NEGOTIATED, AST_T38_RATE_14400, AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_tvdiff_sec(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame::data, ast_frame::datalen, FALSE, fax_session::finished, ast_frame::frametype, LOG_ERROR, LOG_WARNING, ast_channel::name, phase_e_handler(), ast_frame::ptr, ast_channel::readformat, ast_control_t38_parameters::request_response, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, T38_STATE_UNAVAILABLE, fax_session::t38parameters, TRUE, ast_control_t38_parameters::version, WATCHDOG_STATE_TIMEOUT, WATCHDOG_TOTAL_TIMEOUT, and ast_channel::writeformat.

Referenced by transmit().

{
   int res = -1;
   int original_read_fmt = AST_FORMAT_SLINEAR;
   int original_write_fmt = AST_FORMAT_SLINEAR;
   fax_state_t fax;
   t30_state_t *t30state;
   struct ast_frame *inf = NULL;
   int last_state = 0;
   struct timeval now, start, state_change;
   enum ast_t38_state t38_state;
   struct ast_control_t38_parameters t38_parameters = { .version = 0,
                          .max_ifp = 800,
                          .rate = AST_T38_RATE_14400,
                          .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
                          .fill_bit_removal = 1,
/*
 * spandsp has API calls to support MMR and JBIG transcoding, but they aren't
 * implemented quite yet... so don't offer them to the remote endpoint
 *                        .transcoding_mmr = 1,
 *                        .transcoding_jbig = 1,
*/
   };

   /* if in called party mode, try to use T.38 */
   if (s->caller_mode == FALSE) {
      /* check if we are already in T.38 mode (unlikely), or if we can request
       * a switch... if so, request it now and wait for the result, rather
       * than starting an audio FAX session that will have to be cancelled
       */
      if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
         return 1;
      } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
            (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
             (ast_indicate_data(s->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, "Negotiating T.38 for receive on %s\n", s->chan->name);
         while (timeout > 0) {
            ms = ast_waitfor(s->chan, 1000);
            if (ms < 0) {
               ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->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 negotiation.\n", s->chan->name);
                  break;
               }
            }
            if (!(inf = ast_read(s->chan))) {
               return -1;
            }
            if ((inf->frametype == AST_FRAME_CONTROL) &&
                (inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
                (inf->datalen == sizeof(t38_parameters))) {
               struct ast_control_t38_parameters *parameters = inf->data.ptr;
               
               switch (parameters->request_response) {
               case AST_T38_NEGOTIATED:
                  ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name);
                  res = 1;
                  break;
               case AST_T38_REFUSED:
                  ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name);
                  break;
               default:
                  ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name);
                  break;
               }
               ast_frfree(inf);
               if (res == 1) {
                  return 1;
               } else {
                  break;
               }
            }
            ast_frfree(inf);
         }
      }
   }

#if SPANDSP_RELEASE_DATE >= 20080725
        /* for spandsp shaphots 0.0.6 and higher */
        t30state = &fax.t30;
#else
        /* for spandsp release 0.0.5 */
        t30state = &fax.t30_state;
#endif

   original_read_fmt = s->chan->readformat;
   if (original_read_fmt != AST_FORMAT_SLINEAR) {
      res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
      if (res < 0) {
         ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
         goto done;
      }
   }

   original_write_fmt = s->chan->writeformat;
   if (original_write_fmt != AST_FORMAT_SLINEAR) {
      res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
      if (res < 0) {
         ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
         goto done;
      }
   }

   /* Initialize T30 terminal */
   fax_init(&fax, s->caller_mode);

   /* Setup logging */
   set_logging(&fax.logging);
   set_logging(&t30state->logging);

   /* Configure terminal */
   set_local_info(t30state, s);
   set_file(t30state, s);
   set_ecm(t30state, TRUE);

   fax_set_transmit_on_idle(&fax, TRUE);

   t30_set_phase_e_handler(t30state, phase_e_handler, s);

   start = state_change = ast_tvnow();

   ast_activate_generator(s->chan, &generator, &fax);

   while (!s->finished) {
      inf = NULL;

      if ((res = ast_waitfor(s->chan, 25)) < 0) {
         ast_debug(1, "Error waiting for a frame\n");
         break;
      }

      /* Watchdog */
      now = ast_tvnow();
      if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
         ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
         res = -1;
         break;
      }

      if (!res) {
         /* There was timeout waiting for a frame. Loop around and wait again */
         continue;
      }

      /* There is a frame available. Get it */
      res = 0;

      if (!(inf = ast_read(s->chan))) {
         ast_debug(1, "Channel hangup\n");
         res = -1;
         break;
      }

      ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);

      /* Check the frame type. Format also must be checked because there is a chance
         that a frame in old format was already queued before we set channel format
         to slinear so it will still be received by ast_read */
      if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) {
         if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
            /* I know fax_rx never returns errors. The check here is for good style only */
            ast_log(LOG_WARNING, "fax_rx returned error\n");
            res = -1;
            break;
         }
         if (last_state != t30state->state) {
            state_change = ast_tvnow();
            last_state = t30state->state;
         }
      } else if ((inf->frametype == AST_FRAME_CONTROL) &&
            (inf->subclass == AST_CONTROL_T38_PARAMETERS)) {
         struct ast_control_t38_parameters *parameters = inf->data.ptr;

         if (parameters->request_response == AST_T38_NEGOTIATED) {
            /* T38 switchover completed */
            s->t38parameters = *parameters;
            ast_debug(1, "T38 negotiated, finishing audio loop\n");
            res = 1;
            break;
         } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
            t38_parameters.request_response = AST_T38_NEGOTIATED;
            ast_debug(1, "T38 request received, accepting\n");
            /* Complete T38 switchover */
            ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
            /* Do not break audio loop, wait until channel driver finally acks switchover
             * with AST_T38_NEGOTIATED
             */
         }
      }

      ast_frfree(inf);
      inf = NULL;
   }

   ast_debug(1, "Loop finished, res=%d\n", res);

   if (inf)
      ast_frfree(inf);

   ast_deactivate_generator(s->chan);

   /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
      by t30_terminate, display diagnostics and set status variables although no transmittion
      has taken place yet. */
   if (res > 0) {
      t30_set_phase_e_handler(t30state, NULL, NULL);
   }

   t30_terminate(t30state);
   fax_release(&fax);

done:
   if (original_write_fmt != AST_FORMAT_SLINEAR) {
      if (ast_set_write_format(s->chan, original_write_fmt) < 0)
         ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name);
   }

   if (original_read_fmt != AST_FORMAT_SLINEAR) {
      if (ast_set_read_format(s->chan, original_read_fmt) < 0)
         ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name);
   }

   return res;

}
static int transmit_t38 ( fax_session s) [static]

Definition at line 599 of file app_fax.c.

References ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_debug, AST_FRAME_CONTROL, AST_FRAME_MODEM, ast_frfree, ast_indicate_data(), ast_log(), AST_MODEM_T38, ast_read(), AST_T38_REFUSED, AST_T38_REQUEST_TERMINATE, AST_T38_TERMINATED, ast_tvdiff_sec(), ast_tvdiff_us(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame::data, ast_frame::datalen, FALSE, ast_control_t38_parameters::fill_bit_removal, fax_session::finished, ast_frame::frametype, LOG_ERROR, LOG_WARNING, ast_control_t38_parameters::max_ifp, ast_channel::name, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, t38_tx_packet_handler(), fax_session::t38parameters, ast_control_t38_parameters::transcoding_jbig, ast_control_t38_parameters::transcoding_mmr, TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

{
   int res = 0;
   t38_terminal_state_t t38;
   struct ast_frame *inf = NULL;
   int last_state = 0;
   struct timeval now, start, state_change, last_frame;
   t30_state_t *t30state;
   t38_core_state_t *t38state;

#if SPANDSP_RELEASE_DATE >= 20080725
   /* for spandsp shaphots 0.0.6 and higher */
   t30state = &t38.t30;
   t38state = &t38.t38_fe.t38;
#else
   /* for spandsp releases 0.0.5 */
   t30state = &t38.t30_state;
   t38state = &t38.t38;
#endif

   /* Initialize terminal */
   memset(&t38, 0, sizeof(t38));
   if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
      ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
      res = -1;
      goto disable_t38;
   }

   t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);

   if (s->t38parameters.fill_bit_removal) {
      t38_set_fill_bit_removal(t38state, TRUE);
   }
   if (s->t38parameters.transcoding_mmr) {
      t38_set_mmr_transcoding(t38state, TRUE);
   }
   if (s->t38parameters.transcoding_jbig) {
      t38_set_jbig_transcoding(t38state, TRUE);
   }

   /* Setup logging */
   set_logging(&t38.logging);
   set_logging(&t30state->logging);
   set_logging(&t38state->logging);

   /* Configure terminal */
   set_local_info(t30state, s);
   set_file(t30state, s);
   set_ecm(t30state, TRUE);

   t30_set_phase_e_handler(t30state, phase_e_handler, s);

   now = start = state_change = ast_tvnow();

   while (!s->finished) {
      inf = NULL;
      if ((res = ast_waitfor(s->chan, 20)) < 0) {
         break;
      }

      last_frame = now;
      now = ast_tvnow();
      /* if nothing arrived, check the watchdog timers */
      if (res == 0) {
         if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
            ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
            res = -1;
            break;
         } else {
            /* timers have not triggered, loop around to wait
             * again
             */
            t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
            continue;
         }
      }

      t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));

      if (!(inf = ast_read(s->chan))) {
         ast_debug(1, "Channel hangup\n");
         res = -1;
         break;
      }

      ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);

      if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) {
         t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
         if (last_state != t30state->state) {
            state_change = ast_tvnow();
            last_state = t30state->state;
         }
      } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
         struct ast_control_t38_parameters *parameters = inf->data.ptr;
         if (parameters->request_response == AST_T38_TERMINATED) {
            ast_debug(1, "T38 down, finishing\n");
            break;
         }
      }

      ast_frfree(inf);
      inf = NULL;
   }

   ast_debug(1, "Loop finished, res=%d\n", res);

   if (inf)
      ast_frfree(inf);

   t30_terminate(t30state);
   t38_terminal_release(&t38);

disable_t38:
   /* if we are not the caller, it's our job to shut down the T.38
    * session when the FAX transmisson is complete.
    */
   if ((s->caller_mode == FALSE) &&
       (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) {
      struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };

      if (ast_indicate_data(s->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", s->chan->name);
         while (timeout > 0) {
            ms = ast_waitfor(s->chan, 1000);
            if (ms < 0) {
               ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->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", s->chan->name);
                  break;
               }
            }
            if (!(inf = ast_read(s->chan))) {
               return -1;
            }
            if ((inf->frametype == AST_FRAME_CONTROL) &&
                (inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
                (inf->datalen == sizeof(t38_parameters))) {
               struct ast_control_t38_parameters *parameters = inf->data.ptr;
               
               switch (parameters->request_response) {
               case AST_T38_TERMINATED:
                  ast_debug(1, "Shut down T.38 on %s\n", s->chan->name);
                  break;
               case AST_T38_REFUSED:
                  ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name);
                  break;
               default:
                  ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name);
                  break;
               }
               ast_frfree(inf);
               break;
            }
            ast_frfree(inf);
         }
      }
   }

   return res;
}
static int unload_module ( void  ) [static]

Definition at line 918 of file app_fax.c.

References ast_unregister_application().


Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 945 of file app_fax.c.

char* app_rcvfax_name = "ReceiveFAX" [static]

Definition at line 145 of file app_fax.c.

char* app_sndfax_name = "SendFAX" [static]

Definition at line 144 of file app_fax.c.

Definition at line 945 of file app_fax.c.

Initial value:

Definition at line 354 of file app_fax.c.

Referenced by cli_alias_passthrough().