Wed Aug 18 22:33:58 2010

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/expose.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"

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 __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, }
static char * app_rcvfax_desc
static char * app_rcvfax_name = "ReceiveFAX"
static char * app_rcvfax_synopsis = "Receive a FAX"
static char * app_sndfax_desc
static char * app_sndfax_name = "SendFAX"
static char * app_sndfax_synopsis = "Send a FAX"
static struct ast_module_infoast_module_info = &__mod_info
ast_generator generator


Define Documentation

#define MAX_SAMPLES   240

Definition at line 102 of file app_fax.c.

Referenced by fax_generator_generate().

#define WATCHDOG_STATE_TIMEOUT   5 * 60

Definition at line 111 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 110 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 897 of file app_fax.c.

static void __unreg_module ( void   )  [static]

Definition at line 897 of file app_fax.c.

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

Definition at line 274 of file app_fax.c.

00275 {
00276    return params;
00277 }

static int fax_generator_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

Definition at line 279 of file app_fax.c.

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

00280 {
00281    fax_state_t *fax = (fax_state_t*) data;
00282    uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
00283    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00284     
00285    struct ast_frame outf = {
00286       .frametype = AST_FRAME_VOICE,
00287       .subclass = AST_FORMAT_SLINEAR,
00288       .src = __FUNCTION__,
00289    };
00290 
00291    if (samples > MAX_SAMPLES) {
00292       ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
00293       samples = MAX_SAMPLES;
00294    }
00295    
00296    if ((len = fax_tx(fax, buf, samples)) > 0) {
00297       outf.samples = len;
00298       AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
00299 
00300       if (ast_write(chan, &outf) < 0) {
00301          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00302          return -1;
00303       }
00304    }
00305 
00306    return 0;
00307 }

static int load_module ( void   )  [static]

Definition at line 880 of file app_fax.c.

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

00881 {
00882    int res ;
00883 
00884    res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc);
00885    res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc);
00886 
00887    /* The default SPAN message handler prints to stderr. It is something we do not want */
00888    span_set_message_handler(NULL);
00889 
00890    return res;
00891 }

static void phase_e_handler ( t30_state_t *  f,
void *  user_data,
int  result 
) [static]

Definition at line 157 of file app_fax.c.

References ast_debug, ast_log(), buf, EVENT_FLAG_CALL, LOG_WARNING, manager_event, pbx_builtin_setvar_helper(), s, and S_OR.

Referenced by transmit_audio(), and transmit_t38().

00158 {
00159    const char *local_ident;
00160    const char *far_ident;
00161    char buf[20];
00162    fax_session *s = (fax_session *) user_data;
00163    t30_stats_t stat;
00164    int pages_transferred;
00165 
00166    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00167 
00168    t30_get_transfer_statistics(f, &stat);
00169 
00170    s = (fax_session *) user_data;
00171 
00172    if (result != T30_ERR_OK) {
00173       s->finished = -1;
00174 
00175       /* FAXSTATUS is already set to FAILED */
00176       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00177 
00178       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00179 
00180       return;
00181    }
00182    
00183    s->finished = 1; 
00184    
00185    local_ident = t30_get_tx_ident(f);
00186    far_ident = t30_get_rx_ident(f);
00187    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
00188    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
00189    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00190 #if SPANDSP_RELEASE_DATE >= 20090220
00191    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00192 #else
00193    pages_transferred = stat.pages_transferred;
00194 #endif
00195    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00196    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00197    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00198    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00199    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00200    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
00201    
00202    ast_debug(1, "Fax transmitted successfully.\n");
00203    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00204    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00205    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00206    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00207    
00208    manager_event(EVENT_FLAG_CALL,
00209             s->direction ? "FaxSent" : "FaxReceived", 
00210             "Channel: %s\r\n"
00211             "Exten: %s\r\n"
00212             "CallerID: %s\r\n"
00213             "RemoteStationID: %s\r\n"
00214             "LocalStationID: %s\r\n"
00215             "PagesTransferred: %d\r\n"
00216             "Resolution: %d\r\n"
00217             "TransferRate: %d\r\n"
00218             "FileName: %s\r\n",
00219             s->chan->name,
00220             s->chan->exten,
00221             S_OR(s->chan->cid.cid_num, ""),
00222             far_ident,
00223             local_ident,
00224             pages_transferred,
00225             stat.y_resolution,
00226             stat.bit_rate,
00227             s->file_name);
00228 }

static int rcvfax_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 826 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, fax_session::chan, chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.

Referenced by load_module().

00827 {
00828    int res = 0;
00829    char *parse;
00830    fax_session session;
00831 
00832    AST_DECLARE_APP_ARGS(args,
00833       AST_APP_ARG(file_name);
00834       AST_APP_ARG(options);
00835    );
00836 
00837    if (chan == NULL) {
00838       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00839       return -1;
00840    }
00841 
00842    /* The next few lines of code parse out the filename and header from the input string */
00843    if (ast_strlen_zero(data)) {
00844       /* No data implies no filename or anything is present */
00845       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00846       return -1;
00847    }
00848 
00849    parse = ast_strdupa(data);
00850    AST_STANDARD_APP_ARGS(args, parse);
00851    
00852    session.caller_mode = FALSE;
00853 
00854    if (args.options) {
00855       if (strchr(args.options, 'c'))
00856          session.caller_mode = TRUE;
00857    }
00858 
00859    /* Done parsing */
00860    session.direction = 0;
00861    session.file_name = args.file_name;
00862    session.chan = chan;
00863    session.finished = 0;
00864 
00865    res = transmit(&session);
00866 
00867    return res;
00868 }

static void set_ecm ( t30_state_t *  state,
int  ecm 
) [static]

Definition at line 264 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

00265 {
00266    t30_set_ecm_capability(state, ecm);
00267    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00268 }

static void set_file ( t30_state_t *  state,
fax_session s 
) [static]

Definition at line 256 of file app_fax.c.

References s.

Referenced by transmit_audio(), and transmit_t38().

00257 {
00258    if (s->direction)
00259       t30_set_tx_file(state, s->file_name, -1, -1);
00260    else
00261       t30_set_rx_file(state, s->file_name, -1);
00262 }

static void set_local_info ( t30_state_t *  state,
fax_session s 
) [static]

Definition at line 243 of file app_fax.c.

References ast_strlen_zero(), pbx_builtin_getvar_helper(), and s.

Referenced by transmit_audio(), and transmit_t38().

00244 {
00245    const char *x;
00246 
00247    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00248    if (!ast_strlen_zero(x))
00249       t30_set_tx_ident(state, x);
00250 
00251    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00252    if (!ast_strlen_zero(x))
00253       t30_set_tx_page_header_info(state, x);
00254 }

static int set_logging ( logging_state_t *  state  )  [static]

Definition at line 233 of file app_fax.c.

References option_debug, and span_message().

Referenced by transmit_audio(), and transmit_t38().

00234 {
00235    int level = SPAN_LOG_WARNING + option_debug;
00236 
00237    span_log_set_message_handler(state, span_message);
00238    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00239 
00240    return 0;
00241 }

static int sndfax_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 782 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, fax_session::chan, chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.

Referenced by load_module().

00783 {
00784    int res = 0;
00785    char *parse;
00786    fax_session session = { 0, };
00787 
00788    AST_DECLARE_APP_ARGS(args,
00789       AST_APP_ARG(file_name);
00790       AST_APP_ARG(options);
00791    );
00792 
00793    if (chan == NULL) {
00794       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00795       return -1;
00796    }
00797 
00798    /* The next few lines of code parse out the filename and header from the input string */
00799    if (ast_strlen_zero(data)) {
00800       /* No data implies no filename or anything is present */
00801       ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
00802       return -1;
00803    }
00804 
00805    parse = ast_strdupa(data);
00806    AST_STANDARD_APP_ARGS(args, parse);
00807    
00808    session.caller_mode = TRUE;
00809 
00810    if (args.options) {
00811       if (strchr(args.options, 'a'))
00812          session.caller_mode = FALSE;
00813    }
00814 
00815    /* Done parsing */
00816    session.direction = 1;
00817    session.file_name = args.file_name;
00818    session.chan = chan;
00819    session.finished = 0;
00820 
00821    res = transmit(&session);
00822 
00823    return res;
00824 }

static void span_message ( int  level,
const char *  msg 
) [static]

Definition at line 123 of file app_fax.c.

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

Referenced by set_logging().

00124 {
00125    if (level == SPAN_LOG_ERROR) {
00126       ast_log(LOG_ERROR, "%s", msg);
00127    } else if (level == SPAN_LOG_WARNING) {
00128       ast_log(LOG_WARNING, "%s", msg);
00129    } else {
00130       ast_log(LOG_DEBUG, "%s", msg);
00131    }
00132 }

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 134 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().

00135 {
00136    struct ast_channel *chan = (struct ast_channel *) user_data;
00137 
00138    struct ast_frame outf = {
00139       .frametype = AST_FRAME_MODEM,
00140       .subclass = AST_MODEM_T38,
00141       .src = __FUNCTION__,
00142    };
00143 
00144    /* TODO: Asterisk does not provide means of resending the same packet multiple
00145      times so count is ignored at the moment */
00146 
00147    AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
00148 
00149    if (ast_write(chan, &outf) < 0) {
00150       ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
00151       return -1;
00152    }
00153 
00154    return 0;
00155 }

static int transmit ( fax_session s  )  [static]

Definition at line 724 of file app_fax.c.

References ast_answer(), ast_channel_get_t38_state(), ast_debug, ast_log(), AST_STATE_UP, LOG_ERROR, LOG_WARNING, pbx_builtin_setvar_helper(), s, T38_STATE_NEGOTIATED, transmit_audio(), and transmit_t38().

Referenced by rcvfax_exec(), and sndfax_exec().

00725 {
00726    int res = 0;
00727 
00728    /* Clear all channel variables which to be set by the application.
00729       Pre-set status to error so in case of any problems we can just leave */
00730    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
00731    pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
00732 
00733    pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
00734    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
00735    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL);
00736    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
00737    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
00738 
00739    if (s->chan->_state != AST_STATE_UP) {
00740       /* Shouldn't need this, but checking to see if channel is already answered
00741        * Theoretically asterisk should already have answered before running the app */
00742       res = ast_answer(s->chan);
00743       if (res) {
00744          ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
00745          return res;
00746       }
00747    }
00748 
00749    s->t38state = ast_channel_get_t38_state(s->chan);
00750    if (s->t38state != T38_STATE_NEGOTIATED) {
00751       /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
00752       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
00753       res = transmit_audio(s);
00754       if (res > 0) {
00755          /* transmit_audio reports switchover to T38. Update t38state */
00756          s->t38state = ast_channel_get_t38_state(s->chan);
00757          if (s->t38state != T38_STATE_NEGOTIATED) {
00758             ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
00759          }
00760       }
00761    }
00762 
00763    if (s->t38state == T38_STATE_NEGOTIATED) {
00764       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
00765       res = transmit_t38(s);
00766    }
00767 
00768    if (res) {
00769       ast_log(LOG_WARNING, "Transmission error\n");
00770       res = -1;
00771    } else if (s->finished < 0) {
00772       ast_log(LOG_WARNING, "Transmission failed\n");
00773    } else if (s->finished > 0) {
00774       ast_debug(1, "Transmission finished Ok\n");
00775    }
00776 
00777    return res;
00778 }

static int transmit_audio ( fax_session s  )  [static]

Definition at line 317 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(), ast_frame::data, ast_frame::datalen, FALSE, ast_frame::frametype, generator, LOG_ERROR, LOG_WARNING, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, s, ast_frame::samples, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, T38_STATE_UNAVAILABLE, TRUE, ast_control_t38_parameters::version, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

00318 {
00319    int res = -1;
00320    int original_read_fmt = AST_FORMAT_SLINEAR;
00321    int original_write_fmt = AST_FORMAT_SLINEAR;
00322    fax_state_t fax;
00323    t30_state_t *t30state;
00324    struct ast_frame *inf = NULL;
00325    int last_state = 0;
00326    struct timeval now, start, state_change;
00327    enum ast_t38_state t38_state;
00328    struct ast_control_t38_parameters t38_parameters = { .version = 0,
00329                           .max_ifp = 800,
00330                           .rate = AST_T38_RATE_14400,
00331                           .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
00332                           .fill_bit_removal = 1,
00333 /*
00334  * spandsp has API calls to support MMR and JBIG transcoding, but they aren't
00335  * implemented quite yet... so don't offer them to the remote endpoint
00336  *                        .transcoding_mmr = 1,
00337  *                        .transcoding_jbig = 1,
00338 */
00339    };
00340 
00341    /* if in called party mode, try to use T.38 */
00342    if (s->caller_mode == FALSE) {
00343       /* check if we are already in T.38 mode (unlikely), or if we can request
00344        * a switch... if so, request it now and wait for the result, rather
00345        * than starting an audio FAX session that will have to be cancelled
00346        */
00347       if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
00348          return 1;
00349       } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
00350             (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
00351              (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
00352          /* wait up to five seconds for negotiation to complete */
00353          unsigned int timeout = 5000;
00354          int ms;
00355          
00356          ast_debug(1, "Negotiating T.38 for receive on %s\n", s->chan->name);
00357          while (timeout > 0) {
00358             ms = ast_waitfor(s->chan, 1000);
00359             if (ms < 0) {
00360                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
00361                return -1;
00362             }
00363             if (!ms) {
00364                /* nothing happened */
00365                if (timeout > 0) {
00366                   timeout -= 1000;
00367                   continue;
00368                } else {
00369                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name);
00370                   break;
00371                }
00372             }
00373             if (!(inf = ast_read(s->chan))) {
00374                return -1;
00375             }
00376             if ((inf->frametype == AST_FRAME_CONTROL) &&
00377                 (inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
00378                 (inf->datalen == sizeof(t38_parameters))) {
00379                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00380                
00381                switch (parameters->request_response) {
00382                case AST_T38_NEGOTIATED:
00383                   ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name);
00384                   res = 1;
00385                   break;
00386                case AST_T38_REFUSED:
00387                   ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name);
00388                   break;
00389                default:
00390                   ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name);
00391                   break;
00392                }
00393                ast_frfree(inf);
00394                if (res == 1) {
00395                   return 1;
00396                } else {
00397                   break;
00398                }
00399             }
00400             ast_frfree(inf);
00401          }
00402       }
00403    }
00404 
00405 #if SPANDSP_RELEASE_DATE >= 20080725
00406         /* for spandsp shaphots 0.0.6 and higher */
00407         t30state = &fax.t30;
00408 #else
00409         /* for spandsp release 0.0.5 */
00410         t30state = &fax.t30_state;
00411 #endif
00412 
00413    original_read_fmt = s->chan->readformat;
00414    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00415       res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
00416       if (res < 0) {
00417          ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
00418          goto done;
00419       }
00420    }
00421 
00422    original_write_fmt = s->chan->writeformat;
00423    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00424       res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
00425       if (res < 0) {
00426          ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
00427          goto done;
00428       }
00429    }
00430 
00431    /* Initialize T30 terminal */
00432    fax_init(&fax, s->caller_mode);
00433 
00434    /* Setup logging */
00435    set_logging(&fax.logging);
00436    set_logging(&t30state->logging);
00437 
00438    /* Configure terminal */
00439    set_local_info(t30state, s);
00440    set_file(t30state, s);
00441    set_ecm(t30state, TRUE);
00442 
00443    fax_set_transmit_on_idle(&fax, TRUE);
00444 
00445    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00446 
00447    start = state_change = ast_tvnow();
00448 
00449    ast_activate_generator(s->chan, &generator, &fax);
00450 
00451    while (!s->finished) {
00452       inf = NULL;
00453 
00454       if ((res = ast_waitfor(s->chan, 25)) < 0) {
00455          ast_debug(1, "Error waiting for a frame\n");
00456          break;
00457       }
00458 
00459       /* Watchdog */
00460       now = ast_tvnow();
00461       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00462          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00463          res = -1;
00464          break;
00465       }
00466 
00467       if (!res) {
00468          /* There was timeout waiting for a frame. Loop around and wait again */
00469          continue;
00470       }
00471 
00472       /* There is a frame available. Get it */
00473       res = 0;
00474 
00475       if (!(inf = ast_read(s->chan))) {
00476          ast_debug(1, "Channel hangup\n");
00477          res = -1;
00478          break;
00479       }
00480 
00481       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00482 
00483       /* Check the frame type. Format also must be checked because there is a chance
00484          that a frame in old format was already queued before we set channel format
00485          to slinear so it will still be received by ast_read */
00486       if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) {
00487          if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
00488             /* I know fax_rx never returns errors. The check here is for good style only */
00489             ast_log(LOG_WARNING, "fax_rx returned error\n");
00490             res = -1;
00491             break;
00492          }
00493          if (last_state != t30state->state) {
00494             state_change = ast_tvnow();
00495             last_state = t30state->state;
00496          }
00497       } else if ((inf->frametype == AST_FRAME_CONTROL) &&
00498             (inf->subclass == AST_CONTROL_T38_PARAMETERS)) {
00499          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00500 
00501          if (parameters->request_response == AST_T38_NEGOTIATED) {
00502             /* T38 switchover completed */
00503             s->t38parameters = *parameters;
00504             ast_debug(1, "T38 negotiated, finishing audio loop\n");
00505             res = 1;
00506             break;
00507          } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
00508             t38_parameters.request_response = AST_T38_NEGOTIATED;
00509             ast_debug(1, "T38 request received, accepting\n");
00510             /* Complete T38 switchover */
00511             ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
00512             /* Do not break audio loop, wait until channel driver finally acks switchover
00513              * with AST_T38_NEGOTIATED
00514              */
00515          }
00516       }
00517 
00518       ast_frfree(inf);
00519       inf = NULL;
00520    }
00521 
00522    ast_debug(1, "Loop finished, res=%d\n", res);
00523 
00524    if (inf)
00525       ast_frfree(inf);
00526 
00527    ast_deactivate_generator(s->chan);
00528 
00529    /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
00530       by t30_terminate, display diagnostics and set status variables although no transmittion
00531       has taken place yet. */
00532    if (res > 0) {
00533       t30_set_phase_e_handler(t30state, NULL, NULL);
00534    }
00535 
00536    t30_terminate(t30state);
00537    fax_release(&fax);
00538 
00539 done:
00540    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00541       if (ast_set_write_format(s->chan, original_write_fmt) < 0)
00542          ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name);
00543    }
00544 
00545    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00546       if (ast_set_read_format(s->chan, original_read_fmt) < 0)
00547          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name);
00548    }
00549 
00550    return res;
00551 
00552 }

static int transmit_t38 ( fax_session s  )  [static]

Definition at line 554 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(), ast_frame::data, ast_frame::datalen, FALSE, ast_frame::frametype, LOG_ERROR, LOG_WARNING, phase_e_handler(), ast_frame::ptr, ast_control_t38_parameters::request_response, s, ast_frame::seqno, set_ecm(), set_file(), set_local_info(), set_logging(), ast_frame::subclass, T38_STATE_NEGOTIATED, t38_tx_packet_handler(), TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

00555 {
00556    int res = 0;
00557    t38_terminal_state_t t38;
00558    struct ast_frame *inf = NULL;
00559    int last_state = 0;
00560    struct timeval now, start, state_change, last_frame;
00561    t30_state_t *t30state;
00562    t38_core_state_t *t38state;
00563 
00564 #if SPANDSP_RELEASE_DATE >= 20080725
00565    /* for spandsp shaphots 0.0.6 and higher */
00566    t30state = &t38.t30;
00567    t38state = &t38.t38_fe.t38;
00568 #else
00569    /* for spandsp releases 0.0.5 */
00570    t30state = &t38.t30_state;
00571    t38state = &t38.t38;
00572 #endif
00573 
00574    /* Initialize terminal */
00575    memset(&t38, 0, sizeof(t38));
00576    if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
00577       ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
00578       res = -1;
00579       goto disable_t38;
00580    }
00581 
00582    t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
00583 
00584    if (s->t38parameters.fill_bit_removal) {
00585       t38_set_fill_bit_removal(t38state, TRUE);
00586    }
00587    if (s->t38parameters.transcoding_mmr) {
00588       t38_set_mmr_transcoding(t38state, TRUE);
00589    }
00590    if (s->t38parameters.transcoding_jbig) {
00591       t38_set_jbig_transcoding(t38state, TRUE);
00592    }
00593 
00594    /* Setup logging */
00595    set_logging(&t38.logging);
00596    set_logging(&t30state->logging);
00597    set_logging(&t38state->logging);
00598 
00599    /* Configure terminal */
00600    set_local_info(t30state, s);
00601    set_file(t30state, s);
00602    set_ecm(t30state, TRUE);
00603 
00604    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00605 
00606    now = start = state_change = ast_tvnow();
00607 
00608    while (!s->finished) {
00609 
00610       res = ast_waitfor(s->chan, 20);
00611       if (res < 0)
00612          break;
00613       else if (res > 0)
00614          res = 0;
00615 
00616       last_frame = now;
00617       now = ast_tvnow();
00618       t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00619 
00620       inf = ast_read(s->chan);
00621       if (inf == NULL) {
00622          ast_debug(1, "Channel hangup\n");
00623          res = -1;
00624          break;
00625       }
00626 
00627       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00628 
00629       if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) {
00630          t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
00631 
00632          /* Watchdog */
00633          if (last_state != t30state->state) {
00634             state_change = ast_tvnow();
00635             last_state = t30state->state;
00636          }
00637       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
00638          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00639          if (parameters->request_response == AST_T38_TERMINATED) {
00640             ast_debug(1, "T38 down, finishing\n");
00641             break;
00642          }
00643       }
00644 
00645       ast_frfree(inf);
00646       inf = NULL;
00647 
00648       /* Watchdog */
00649       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00650          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00651          res = -1;
00652          break;
00653       }
00654    }
00655 
00656    ast_debug(1, "Loop finished, res=%d\n", res);
00657 
00658    if (inf)
00659       ast_frfree(inf);
00660 
00661    t30_terminate(t30state);
00662    t38_terminal_release(&t38);
00663 
00664 disable_t38:
00665    /* if we are not the caller, it's our job to shut down the T.38
00666     * session when the FAX transmisson is complete.
00667     */
00668    if ((s->caller_mode == FALSE) &&
00669        (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) {
00670       struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
00671 
00672       if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) {
00673          /* wait up to five seconds for negotiation to complete */
00674          unsigned int timeout = 5000;
00675          int ms;
00676          
00677          ast_debug(1, "Shutting down T.38 on %s\n", s->chan->name);
00678          while (timeout > 0) {
00679             ms = ast_waitfor(s->chan, 1000);
00680             if (ms < 0) {
00681                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
00682                return -1;
00683             }
00684             if (!ms) {
00685                /* nothing happened */
00686                if (timeout > 0) {
00687                   timeout -= 1000;
00688                   continue;
00689                } else {
00690                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name);
00691                   break;
00692                }
00693             }
00694             if (!(inf = ast_read(s->chan))) {
00695                return -1;
00696             }
00697             if ((inf->frametype == AST_FRAME_CONTROL) &&
00698                 (inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
00699                 (inf->datalen == sizeof(t38_parameters))) {
00700                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00701                
00702                switch (parameters->request_response) {
00703                case AST_T38_TERMINATED:
00704                   ast_debug(1, "Shut down T.38 on %s\n", s->chan->name);
00705                   break;
00706                case AST_T38_REFUSED:
00707                   ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name);
00708                   break;
00709                default:
00710                   ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name);
00711                   break;
00712                }
00713                ast_frfree(inf);
00714                break;
00715             }
00716             ast_frfree(inf);
00717          }
00718       }
00719    }
00720 
00721    return res;
00722 }

static int unload_module ( void   )  [static]

Definition at line 870 of file app_fax.c.

References ast_unregister_application().

00871 {
00872    int res;
00873 
00874    res = ast_unregister_application(app_sndfax_name); 
00875    res |= ast_unregister_application(app_rcvfax_name);   
00876 
00877    return res;
00878 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static]

Definition at line 897 of file app_fax.c.

char* app_rcvfax_desc [static]

Definition at line 76 of file app_fax.c.

char* app_rcvfax_name = "ReceiveFAX" [static]

Definition at line 74 of file app_fax.c.

char* app_rcvfax_synopsis = "Receive a FAX" [static]

Definition at line 75 of file app_fax.c.

char* app_sndfax_desc [static]

Definition at line 49 of file app_fax.c.

char* app_sndfax_name = "SendFAX" [static]

Definition at line 47 of file app_fax.c.

char* app_sndfax_synopsis = "Send a FAX" [static]

Definition at line 48 of file app_fax.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 897 of file app_fax.c.

struct ast_generator generator

Initial value:

Definition at line 309 of file app_fax.c.

Referenced by transmit_audio().


Generated on Wed Aug 18 22:33:58 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7