Fri Nov 12 11:49:25 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"
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 __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 = "0901e4e500243c855563a2d78b0c03e4" , .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.

00320 {
00321    return params;
00322 }

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, LOG_WARNING, MAX_SAMPLES, ast_channel::name, and ast_frame::samples.

00325 {
00326    fax_state_t *fax = (fax_state_t*) data;
00327    uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
00328    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00329     
00330    struct ast_frame outf = {
00331       .frametype = AST_FRAME_VOICE,
00332       .subclass = AST_FORMAT_SLINEAR,
00333       .src = __FUNCTION__,
00334    };
00335 
00336    if (samples > MAX_SAMPLES) {
00337       ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
00338       samples = MAX_SAMPLES;
00339    }
00340    
00341    if ((len = fax_tx(fax, buf, samples)) > 0) {
00342       outf.samples = len;
00343       AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
00344 
00345       if (ast_write(chan, &outf) < 0) {
00346          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00347          return -1;
00348       }
00349    }
00350 
00351    return 0;
00352 }

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

00929 {
00930    int res ;
00931 
00932    res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
00933    res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
00934 
00935    /* The default SPAN message handler prints to stderr. It is something we do not want */
00936    span_set_message_handler(NULL);
00937 
00938    return res;
00939 }

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

00203 {
00204    const char *local_ident;
00205    const char *far_ident;
00206    char buf[20];
00207    fax_session *s = (fax_session *) user_data;
00208    t30_stats_t stat;
00209    int pages_transferred;
00210 
00211    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00212 
00213    t30_get_transfer_statistics(f, &stat);
00214 
00215    s = (fax_session *) user_data;
00216 
00217    if (result != T30_ERR_OK) {
00218       s->finished = -1;
00219 
00220       /* FAXSTATUS is already set to FAILED */
00221       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00222 
00223       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00224 
00225       return;
00226    }
00227    
00228    s->finished = 1; 
00229    
00230    local_ident = t30_get_tx_ident(f);
00231    far_ident = t30_get_rx_ident(f);
00232    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
00233    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
00234    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00235 #if SPANDSP_RELEASE_DATE >= 20090220
00236    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00237 #else
00238    pages_transferred = stat.pages_transferred;
00239 #endif
00240    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00241    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00242    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00243    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00244    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00245    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
00246    
00247    ast_debug(1, "Fax transmitted successfully.\n");
00248    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00249    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00250    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00251    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00252    
00253    manager_event(EVENT_FLAG_CALL,
00254             s->direction ? "FaxSent" : "FaxReceived", 
00255             "Channel: %s\r\n"
00256             "Exten: %s\r\n"
00257             "CallerID: %s\r\n"
00258             "RemoteStationID: %s\r\n"
00259             "LocalStationID: %s\r\n"
00260             "PagesTransferred: %d\r\n"
00261             "Resolution: %d\r\n"
00262             "TransferRate: %d\r\n"
00263             "FileName: %s\r\n",
00264             s->chan->name,
00265             s->chan->exten,
00266             S_OR(s->chan->cid.cid_num, ""),
00267             far_ident,
00268             local_ident,
00269             pages_transferred,
00270             stat.y_resolution,
00271             stat.bit_rate,
00272             s->file_name);
00273 }

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

Referenced by load_module().

00875 {
00876    int res = 0;
00877    char *parse;
00878    fax_session session;
00879 
00880    AST_DECLARE_APP_ARGS(args,
00881       AST_APP_ARG(file_name);
00882       AST_APP_ARG(options);
00883    );
00884 
00885    if (chan == NULL) {
00886       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00887       return -1;
00888    }
00889 
00890    /* The next few lines of code parse out the filename and header from the input string */
00891    if (ast_strlen_zero(data)) {
00892       /* No data implies no filename or anything is present */
00893       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00894       return -1;
00895    }
00896 
00897    parse = ast_strdupa(data);
00898    AST_STANDARD_APP_ARGS(args, parse);
00899    
00900    session.caller_mode = FALSE;
00901 
00902    if (args.options) {
00903       if (strchr(args.options, 'c'))
00904          session.caller_mode = TRUE;
00905    }
00906 
00907    /* Done parsing */
00908    session.direction = 0;
00909    session.file_name = args.file_name;
00910    session.chan = chan;
00911    session.finished = 0;
00912 
00913    res = transmit(&session);
00914 
00915    return res;
00916 }

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

00310 {
00311    t30_set_ecm_capability(state, ecm);
00312    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00313 }

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

00302 {
00303    if (s->direction)
00304       t30_set_tx_file(state, s->file_name, -1, -1);
00305    else
00306       t30_set_rx_file(state, s->file_name, -1);
00307 }

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

00289 {
00290    const char *x;
00291 
00292    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00293    if (!ast_strlen_zero(x))
00294       t30_set_tx_ident(state, x);
00295 
00296    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00297    if (!ast_strlen_zero(x))
00298       t30_set_tx_page_header_info(state, x);
00299 }

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

00279 {
00280    int level = SPAN_LOG_WARNING + option_debug;
00281 
00282    span_log_set_message_handler(state, span_message);
00283    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00284 
00285    return 0;
00286 }

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

Referenced by load_module().

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

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

00169 {
00170    if (level == SPAN_LOG_ERROR) {
00171       ast_log(LOG_ERROR, "%s", msg);
00172    } else if (level == SPAN_LOG_WARNING) {
00173       ast_log(LOG_WARNING, "%s", msg);
00174    } else {
00175       ast_log(LOG_DEBUG, "%s", msg);
00176    }
00177 }

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

00180 {
00181    struct ast_channel *chan = (struct ast_channel *) user_data;
00182 
00183    struct ast_frame outf = {
00184       .frametype = AST_FRAME_MODEM,
00185       .subclass = AST_MODEM_T38,
00186       .src = __FUNCTION__,
00187    };
00188 
00189    /* TODO: Asterisk does not provide means of resending the same packet multiple
00190      times so count is ignored at the moment */
00191 
00192    AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
00193 
00194    if (ast_write(chan, &outf) < 0) {
00195       ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
00196       return -1;
00197    }
00198 
00199    return 0;
00200 }

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

00773 {
00774    int res = 0;
00775 
00776    /* Clear all channel variables which to be set by the application.
00777       Pre-set status to error so in case of any problems we can just leave */
00778    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
00779    pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
00780 
00781    pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
00782    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
00783    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL);
00784    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
00785    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
00786 
00787    if (s->chan->_state != AST_STATE_UP) {
00788       /* Shouldn't need this, but checking to see if channel is already answered
00789        * Theoretically asterisk should already have answered before running the app */
00790       res = ast_answer(s->chan);
00791       if (res) {
00792          ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
00793          return res;
00794       }
00795    }
00796 
00797    s->t38state = ast_channel_get_t38_state(s->chan);
00798    if (s->t38state != T38_STATE_NEGOTIATED) {
00799       /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
00800       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
00801       res = transmit_audio(s);
00802       if (res > 0) {
00803          /* transmit_audio reports switchover to T38. Update t38state */
00804          s->t38state = ast_channel_get_t38_state(s->chan);
00805          if (s->t38state != T38_STATE_NEGOTIATED) {
00806             ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
00807          }
00808       }
00809    }
00810 
00811    if (s->t38state == T38_STATE_NEGOTIATED) {
00812       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
00813       res = transmit_t38(s);
00814    }
00815 
00816    if (res) {
00817       ast_log(LOG_WARNING, "Transmission error\n");
00818       res = -1;
00819    } else if (s->finished < 0) {
00820       ast_log(LOG_WARNING, "Transmission failed\n");
00821    } else if (s->finished > 0) {
00822       ast_debug(1, "Transmission finished Ok\n");
00823    }
00824 
00825    return res;
00826 }

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

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

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

00600 {
00601    int res = 0;
00602    t38_terminal_state_t t38;
00603    struct ast_frame *inf = NULL;
00604    int last_state = 0;
00605    struct timeval now, start, state_change, last_frame;
00606    t30_state_t *t30state;
00607    t38_core_state_t *t38state;
00608 
00609 #if SPANDSP_RELEASE_DATE >= 20080725
00610    /* for spandsp shaphots 0.0.6 and higher */
00611    t30state = &t38.t30;
00612    t38state = &t38.t38_fe.t38;
00613 #else
00614    /* for spandsp releases 0.0.5 */
00615    t30state = &t38.t30_state;
00616    t38state = &t38.t38;
00617 #endif
00618 
00619    /* Initialize terminal */
00620    memset(&t38, 0, sizeof(t38));
00621    if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
00622       ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
00623       res = -1;
00624       goto disable_t38;
00625    }
00626 
00627    t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
00628 
00629    if (s->t38parameters.fill_bit_removal) {
00630       t38_set_fill_bit_removal(t38state, TRUE);
00631    }
00632    if (s->t38parameters.transcoding_mmr) {
00633       t38_set_mmr_transcoding(t38state, TRUE);
00634    }
00635    if (s->t38parameters.transcoding_jbig) {
00636       t38_set_jbig_transcoding(t38state, TRUE);
00637    }
00638 
00639    /* Setup logging */
00640    set_logging(&t38.logging);
00641    set_logging(&t30state->logging);
00642    set_logging(&t38state->logging);
00643 
00644    /* Configure terminal */
00645    set_local_info(t30state, s);
00646    set_file(t30state, s);
00647    set_ecm(t30state, TRUE);
00648 
00649    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00650 
00651    now = start = state_change = ast_tvnow();
00652 
00653    while (!s->finished) {
00654       inf = NULL;
00655       if ((res = ast_waitfor(s->chan, 20)) < 0) {
00656          break;
00657       }
00658 
00659       last_frame = now;
00660       now = ast_tvnow();
00661       /* if nothing arrived, check the watchdog timers */
00662       if (res == 0) {
00663          if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00664             ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00665             res = -1;
00666             break;
00667          } else {
00668             /* timers have not triggered, loop around to wait
00669              * again
00670              */
00671             t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00672             continue;
00673          }
00674       }
00675 
00676       t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00677 
00678       if (!(inf = ast_read(s->chan))) {
00679          ast_debug(1, "Channel hangup\n");
00680          res = -1;
00681          break;
00682       }
00683 
00684       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00685 
00686       if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) {
00687          t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
00688          if (last_state != t30state->state) {
00689             state_change = ast_tvnow();
00690             last_state = t30state->state;
00691          }
00692       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
00693          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00694          if (parameters->request_response == AST_T38_TERMINATED) {
00695             ast_debug(1, "T38 down, finishing\n");
00696             break;
00697          }
00698       }
00699 
00700       ast_frfree(inf);
00701       inf = NULL;
00702    }
00703 
00704    ast_debug(1, "Loop finished, res=%d\n", res);
00705 
00706    if (inf)
00707       ast_frfree(inf);
00708 
00709    t30_terminate(t30state);
00710    t38_terminal_release(&t38);
00711 
00712 disable_t38:
00713    /* if we are not the caller, it's our job to shut down the T.38
00714     * session when the FAX transmisson is complete.
00715     */
00716    if ((s->caller_mode == FALSE) &&
00717        (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) {
00718       struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, };
00719 
00720       if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) {
00721          /* wait up to five seconds for negotiation to complete */
00722          unsigned int timeout = 5000;
00723          int ms;
00724          
00725          ast_debug(1, "Shutting down T.38 on %s\n", s->chan->name);
00726          while (timeout > 0) {
00727             ms = ast_waitfor(s->chan, 1000);
00728             if (ms < 0) {
00729                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
00730                return -1;
00731             }
00732             if (!ms) {
00733                /* nothing happened */
00734                if (timeout > 0) {
00735                   timeout -= 1000;
00736                   continue;
00737                } else {
00738                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name);
00739                   break;
00740                }
00741             }
00742             if (!(inf = ast_read(s->chan))) {
00743                return -1;
00744             }
00745             if ((inf->frametype == AST_FRAME_CONTROL) &&
00746                 (inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
00747                 (inf->datalen == sizeof(t38_parameters))) {
00748                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00749                
00750                switch (parameters->request_response) {
00751                case AST_T38_TERMINATED:
00752                   ast_debug(1, "Shut down T.38 on %s\n", s->chan->name);
00753                   break;
00754                case AST_T38_REFUSED:
00755                   ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name);
00756                   break;
00757                default:
00758                   ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name);
00759                   break;
00760                }
00761                ast_frfree(inf);
00762                break;
00763             }
00764             ast_frfree(inf);
00765          }
00766       }
00767    }
00768 
00769    return res;
00770 }

static int unload_module ( void   )  [static]

Definition at line 918 of file app_fax.c.

References ast_unregister_application().

00919 {
00920    int res;
00921 
00922    res = ast_unregister_application(app_sndfax_name); 
00923    res |= ast_unregister_application(app_rcvfax_name);   
00924 
00925    return res;
00926 }


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 = "0901e4e500243c855563a2d78b0c03e4" , .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().


Generated by  doxygen 1.6.2