Fri Feb 10 06:34:27 2012

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 SPANDSP_EXPOSE_INTERNAL_STRUCTURES
#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, const char *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, const char *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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static const char app_rcvfax_name [] = "ReceiveFAX"
static const char app_sndfax_name [] = "SendFAX"
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_generator generator


Define Documentation

#define MAX_SAMPLES   240

Definition at line 148 of file app_fax.c.

Referenced by fax_generator_generate().

#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES

Definition at line 34 of file app_fax.c.

#define WATCHDOG_STATE_TIMEOUT   5 * 60

Definition at line 157 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 156 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1008 of file app_fax.c.

static void __unreg_module ( void   )  [static]

Definition at line 1008 of file app_fax.c.

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

Definition at line 326 of file app_fax.c.

00327 {
00328    return params;
00329 }

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

Definition at line 331 of file app_fax.c.

References ast_channel_name(), ast_format_set(), AST_FORMAT_SLINEAR, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_write(), errno, ast_frame_subclass::format, ast_frame::frametype, LOG_WARNING, MAX_SAMPLES, ast_frame::samples, and ast_frame::subclass.

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

static int load_module ( void   )  [static]

Definition at line 991 of file app_fax.c.

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

00992 {
00993    int res ;
00994 
00995    res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
00996    res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
00997 
00998    /* The default SPAN message handler prints to stderr. It is something we do not want */
00999    span_set_message_handler(NULL);
01000 
01001    return res;
01002 }

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

Definition at line 203 of file app_fax.c.

References ast_channel_name(), ast_debug, ast_log(), ast_manager_event, ast_channel::caller, fax_session::chan, ast_channel::connected, fax_session::direction, EVENT_FLAG_CALL, ast_channel::exten, fax_session::file_name, fax_session::finished, ast_party_connected_line::id, ast_party_caller::id, LOG_WARNING, ast_party_id::name, ast_party_id::number, pbx_builtin_setvar_helper(), S_COR, S_OR, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.

Referenced by transmit_audio(), and transmit_t38().

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

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

Definition at line 912 of file app_fax.c.

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

Referenced by load_module().

00913 {
00914    int res = 0;
00915    char *parse;
00916    fax_session session;
00917    char restore_digit_detect = 0;
00918 
00919    AST_DECLARE_APP_ARGS(args,
00920       AST_APP_ARG(file_name);
00921       AST_APP_ARG(options);
00922    );
00923 
00924    if (chan == NULL) {
00925       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00926       return -1;
00927    }
00928 
00929    /* The next few lines of code parse out the filename and header from the input string */
00930    if (ast_strlen_zero(data)) {
00931       /* No data implies no filename or anything is present */
00932       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00933       return -1;
00934    }
00935 
00936    parse = ast_strdupa(data);
00937    AST_STANDARD_APP_ARGS(args, parse);
00938    
00939    session.caller_mode = FALSE;
00940 
00941    if (args.options) {
00942       if (strchr(args.options, 'c'))
00943          session.caller_mode = TRUE;
00944    }
00945 
00946    /* Done parsing */
00947    session.direction = 0;
00948    session.file_name = args.file_name;
00949    session.chan = chan;
00950    session.finished = 0;
00951 
00952    /* get current digit detection mode, then disable digit detection if enabled */
00953    {
00954       int dummy = sizeof(restore_digit_detect);
00955 
00956       ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
00957    }
00958 
00959    if (restore_digit_detect) {
00960       char new_digit_detect = 0;
00961 
00962       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
00963    }
00964 
00965    /* disable FAX tone detection if enabled */
00966    {
00967       char new_fax_detect = 0;
00968 
00969       ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
00970    }
00971 
00972    res = transmit(&session);
00973 
00974    if (restore_digit_detect) {
00975       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
00976    }
00977 
00978    return res;
00979 }

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

Definition at line 316 of file app_fax.c.

Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().

00317 {
00318    t30_set_ecm_capability(state, ecm);
00319    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00320 }

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

Definition at line 308 of file app_fax.c.

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

Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().

00309 {
00310    if (s->direction)
00311       t30_set_tx_file(state, s->file_name, -1, -1);
00312    else
00313       t30_set_rx_file(state, s->file_name, -1);
00314 }

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

Definition at line 295 of file app_fax.c.

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

Referenced by spandsp_fax_start(), transmit_audio(), and transmit_t38().

00296 {
00297    const char *x;
00298 
00299    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00300    if (!ast_strlen_zero(x))
00301       t30_set_tx_ident(state, x);
00302 
00303    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00304    if (!ast_strlen_zero(x))
00305       t30_set_tx_page_header_info(state, x);
00306 }

static int set_logging ( logging_state_t *  state  )  [static]

Definition at line 285 of file app_fax.c.

References option_debug, and span_message().

Referenced by spandsp_fax_gateway_start(), spandsp_fax_new(), spandsp_fax_start(), transmit_audio(), and transmit_t38().

00286 {
00287    int level = SPAN_LOG_WARNING + option_debug;
00288 
00289    span_log_set_message_handler(state, span_message);
00290    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00291 
00292    return 0;
00293 }

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

Definition at line 843 of file app_fax.c.

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

Referenced by load_module().

00844 {
00845    int res = 0;
00846    char *parse;
00847    fax_session session = { 0, };
00848    char restore_digit_detect = 0;
00849 
00850    AST_DECLARE_APP_ARGS(args,
00851       AST_APP_ARG(file_name);
00852       AST_APP_ARG(options);
00853    );
00854 
00855    if (chan == NULL) {
00856       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00857       return -1;
00858    }
00859 
00860    /* The next few lines of code parse out the filename and header from the input string */
00861    if (ast_strlen_zero(data)) {
00862       /* No data implies no filename or anything is present */
00863       ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
00864       return -1;
00865    }
00866 
00867    parse = ast_strdupa(data);
00868    AST_STANDARD_APP_ARGS(args, parse);
00869    
00870    session.caller_mode = TRUE;
00871 
00872    if (args.options) {
00873       if (strchr(args.options, 'a'))
00874          session.caller_mode = FALSE;
00875    }
00876 
00877    /* Done parsing */
00878    session.direction = 1;
00879    session.file_name = args.file_name;
00880    session.chan = chan;
00881    session.finished = 0;
00882 
00883    /* get current digit detection mode, then disable digit detection if enabled */
00884    {
00885       int dummy = sizeof(restore_digit_detect);
00886 
00887       ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
00888    }
00889 
00890    if (restore_digit_detect) {
00891       char new_digit_detect = 0;
00892 
00893       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
00894    }
00895 
00896    /* disable FAX tone detection if enabled */
00897    {
00898       char new_fax_detect = 0;
00899 
00900       ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
00901    }
00902 
00903    res = transmit(&session);
00904 
00905    if (restore_digit_detect) {
00906       ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
00907    }
00908 
00909    return res;
00910 }

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

Definition at line 169 of file app_fax.c.

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

Referenced by set_logging().

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

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 180 of file app_fax.c.

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

Referenced by spandsp_fax_gateway_start(), spandsp_fax_new(), and transmit_t38().

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

static int transmit ( fax_session s  )  [static]

Definition at line 785 of file app_fax.c.

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

Referenced by rcvfax_exec(), and sndfax_exec().

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

static int transmit_audio ( fax_session s  )  [static]

Definition at line 369 of file app_fax.c.

References ast_activate_generator(), ast_channel_get_t38_state(), ast_channel_name(), AST_CONTROL_T38_PARAMETERS, ast_deactivate_generator(), ast_debug, ast_format_clear(), ast_format_copy(), AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_read_format_by_id(), ast_set_write_format(), ast_set_write_format_by_id(), 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_subclass::format, ast_frame::frametype, ast_format::id, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, 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().

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

static int transmit_t38 ( fax_session s  )  [static]

Definition at line 609 of file app_fax.c.

References ast_channel_get_t38_state(), ast_channel_name(), 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, disable_t38(), FALSE, ast_control_t38_parameters::fill_bit_removal, fax_session::finished, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_control_t38_parameters::max_ifp, 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, 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().

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

static int unload_module ( void   )  [static]

Definition at line 981 of file app_fax.c.

References ast_unregister_application().

00982 {
00983    int res;
00984 
00985    res = ast_unregister_application(app_sndfax_name); 
00986    res |= ast_unregister_application(app_rcvfax_name);   
00987 
00988    return res;
00989 }


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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 1008 of file app_fax.c.

const char app_rcvfax_name[] = "ReceiveFAX" [static]

Definition at line 146 of file app_fax.c.

const char app_sndfax_name[] = "SendFAX" [static]

Definition at line 145 of file app_fax.c.

Definition at line 1008 of file app_fax.c.

struct ast_generator generator [static]

Initial value:

Definition at line 361 of file app_fax.c.

Referenced by cli_alias_passthrough().


Generated on Fri Feb 10 06:34:28 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6