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/stasis.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/format_cache.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, .support_level = AST_MODULE_SUPPORT_EXTENDED, .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 150 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 159 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().

#define WATCHDOG_TOTAL_TIMEOUT   30 * 60

Definition at line 158 of file app_fax.c.

Referenced by transmit_audio(), and transmit_t38().


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 1002 of file app_fax.c.

static void __unreg_module ( void   )  [static]

Definition at line 1002 of file app_fax.c.

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

Definition at line 323 of file app_fax.c.

00324 {
00325    return params;
00326 }

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

Definition at line 328 of file app_fax.c.

References ast_channel_name(), ast_format_slin, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log, ast_write(), buf, errno, ast_frame::frametype, LOG_WARNING, MAX_SAMPLES, and ast_frame::samples.

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

static int load_module ( void   )  [static]

Definition at line 984 of file app_fax.c.

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

00985 {
00986    int res ;
00987 
00988    res = ast_register_application_xml(app_sndfax_name, sndfax_exec);
00989    res |= ast_register_application_xml(app_rcvfax_name, rcvfax_exec);
00990 
00991    /* The default SPAN message handler prints to stderr. It is something we do not want */
00992    span_set_message_handler(NULL);
00993 
00994    return res;
00995 }

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

Definition at line 205 of file app_fax.c.

References ao2_cleanup, ast_channel_blob_create_from_cache(), ast_channel_fax_type(), ast_channel_topic(), ast_channel_uniqueid(), ast_debug, ast_json_pack(), ast_json_ref(), ast_json_unref(), ast_log, buf, fax_session::chan, fax_session::direction, fax_session::file_name, fax_session::finished, LOG_WARNING, NULL, pbx_builtin_setvar_helper(), RAII_VAR, S_OR, and stasis_publish().

Referenced by transmit_audio(), and transmit_t38().

00206 {
00207    RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
00208    RAII_VAR(struct ast_json *, json_filenames, NULL, ast_json_unref);
00209    RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
00210    const char *local_ident;
00211    const char *far_ident;
00212    char buf[20];
00213    fax_session *s = (fax_session *) user_data;
00214    t30_stats_t stat;
00215    int pages_transferred;
00216 
00217    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00218 
00219    t30_get_transfer_statistics(f, &stat);
00220 
00221    s = (fax_session *) user_data;
00222 
00223    if (result != T30_ERR_OK) {
00224       s->finished = -1;
00225 
00226       /* FAXSTATUS is already set to FAILED */
00227       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00228 
00229       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00230 
00231       return;
00232    }
00233 
00234    s->finished = 1;
00235 
00236    local_ident = S_OR(t30_get_tx_ident(f), "");
00237    far_ident = S_OR(t30_get_rx_ident(f), "");
00238    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS");
00239    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL);
00240    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00241 #if SPANDSP_RELEASE_DATE >= 20090220
00242    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00243 #else
00244    pages_transferred = stat.pages_transferred;
00245 #endif
00246    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00247    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00248    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00249    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00250    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00251    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf);
00252 
00253    ast_debug(1, "Fax transmitted successfully.\n");
00254    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00255    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00256    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00257    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00258 
00259    json_filenames = ast_json_pack("[s]", s->file_name);
00260    if (!json_filenames) {
00261       return;
00262    }
00263    ast_json_ref(json_filenames);
00264    json_object = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: i, s: o}",
00265          "type", s->direction ? "send" : "receive",
00266          "remote_station_id", far_ident,
00267          "local_station_id", local_ident,
00268          "fax_pages", pages_transferred,
00269          "fax_resolution", stat.y_resolution,
00270          "fax_bitrate", stat.bit_rate,
00271          "filenames", json_filenames);
00272    message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(s->chan), ast_channel_fax_type(), json_object);
00273    if (!message) {
00274       return;
00275    }
00276    stasis_publish(ast_channel_topic(s->chan), message);
00277 }

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

Definition at line 905 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, NULL, parse(), session, transmit(), and TRUE.

Referenced by load_module().

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

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

Definition at line 313 of file app_fax.c.

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

00314 {
00315    t30_set_ecm_capability(state, ecm);
00316    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00317 }

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

Definition at line 305 of file app_fax.c.

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

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

00306 {
00307    if (s->direction)
00308       t30_set_tx_file(state, s->file_name, -1, -1);
00309    else
00310       t30_set_rx_file(state, s->file_name, -1);
00311 }

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

Definition at line 292 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().

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

static int set_logging ( logging_state_t *  state  )  [static]

Definition at line 282 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().

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

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

Definition at line 836 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, NULL, parse(), session, transmit(), and TRUE.

Referenced by load_module().

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

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

Definition at line 171 of file app_fax.c.

References ast_debug, ast_log, LOG_ERROR, and LOG_WARNING.

Referenced by set_logging().

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

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

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

static int transmit ( fax_session s  )  [static]

Definition at line 778 of file app_fax.c.

References 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, NULL, pbx_builtin_setvar_helper(), T38_STATE_NEGOTIATED, fax_session::t38state, transmit_audio(), and transmit_t38().

Referenced by rcvfax_exec(), and sndfax_exec().

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

static int transmit_audio ( fax_session s  )  [static]

Definition at line 366 of file app_fax.c.

References ao2_bump, ao2_ref, ast_activate_generator(), ast_channel_get_t38_state(), ast_channel_name(), ast_channel_readformat(), ast_channel_writeformat(), AST_CONTROL_T38_PARAMETERS, ast_deactivate_generator(), ast_debug, ast_format_cmp(), AST_FORMAT_CMP_EQUAL, ast_format_get_name(), ast_format_slin, 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_subclass::format, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, NULL, phase_e_handler(), ast_frame::ptr, 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, timeout, TRUE, ast_control_t38_parameters::version, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

00367 {
00368    int res = -1;
00369    struct ast_format *original_read_fmt;
00370    struct ast_format *original_write_fmt = NULL;
00371    fax_state_t fax;
00372    t30_state_t *t30state;
00373    struct ast_frame *inf = NULL;
00374    int last_state = 0;
00375    struct timeval now, start, state_change;
00376    enum ast_t38_state t38_state;
00377    struct ast_control_t38_parameters t38_parameters = { .version = 0,
00378                           .max_ifp = 800,
00379                           .rate = AST_T38_RATE_14400,
00380                           .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
00381                           .fill_bit_removal = 1,
00382 /*
00383  * spandsp has API calls to support MMR and JBIG transcoding, but they aren't
00384  * implemented quite yet... so don't offer them to the remote endpoint
00385  *                        .transcoding_mmr = 1,
00386  *                        .transcoding_jbig = 1,
00387 */
00388    };
00389 
00390    /* if in called party mode, try to use T.38 */
00391    if (s->caller_mode == FALSE) {
00392       /* check if we are already in T.38 mode (unlikely), or if we can request
00393        * a switch... if so, request it now and wait for the result, rather
00394        * than starting an audio FAX session that will have to be cancelled
00395        */
00396       if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
00397          return 1;
00398       } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
00399             (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
00400              (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
00401          /* wait up to five seconds for negotiation to complete */
00402          unsigned int timeout = 5000;
00403          int ms;
00404 
00405          ast_debug(1, "Negotiating T.38 for receive on %s\n", ast_channel_name(s->chan));
00406          while (timeout > 0) {
00407             ms = ast_waitfor(s->chan, 1000);
00408             if (ms < 0) {
00409                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", ast_channel_name(s->chan));
00410                return -1;
00411             }
00412             if (!ms) {
00413                /* nothing happened */
00414                if (timeout > 0) {
00415                   timeout -= 1000;
00416                   continue;
00417                } else {
00418                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", ast_channel_name(s->chan));
00419                   break;
00420                }
00421             }
00422             if (!(inf = ast_read(s->chan))) {
00423                return -1;
00424             }
00425             if ((inf->frametype == AST_FRAME_CONTROL) &&
00426                 (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS) &&
00427                 (inf->datalen == sizeof(t38_parameters))) {
00428                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00429 
00430                switch (parameters->request_response) {
00431                case AST_T38_NEGOTIATED:
00432                   ast_debug(1, "Negotiated T.38 for receive on %s\n", ast_channel_name(s->chan));
00433                   res = 1;
00434                   break;
00435                case AST_T38_REFUSED:
00436                   ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", ast_channel_name(s->chan));
00437                   break;
00438                default:
00439                   ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", ast_channel_name(s->chan));
00440                   break;
00441                }
00442                ast_frfree(inf);
00443                if (res == 1) {
00444                   return 1;
00445                } else {
00446                   break;
00447                }
00448             }
00449             ast_frfree(inf);
00450          }
00451       }
00452    }
00453 
00454 #if SPANDSP_RELEASE_DATE >= 20080725
00455         /* for spandsp shaphots 0.0.6 and higher */
00456         t30state = &fax.t30;
00457 #else
00458         /* for spandsp release 0.0.5 */
00459         t30state = &fax.t30_state;
00460 #endif
00461 
00462     original_read_fmt = ao2_bump(ast_channel_readformat(s->chan));
00463    res = ast_set_read_format(s->chan, ast_format_slin);
00464    if (res < 0) {
00465       ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
00466       goto done;
00467    }
00468 
00469    original_write_fmt = ao2_bump(ast_channel_writeformat(s->chan));
00470    res = ast_set_write_format(s->chan, ast_format_slin);
00471    if (res < 0) {
00472       ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
00473       goto done;
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/%s, len=%d\n", inf->frametype, ast_format_get_name(inf->subclass.format), 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 &&
00532          (ast_format_cmp(inf->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) {
00533          if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
00534             /* I know fax_rx never returns errors. The check here is for good style only */
00535             ast_log(LOG_WARNING, "fax_rx returned error\n");
00536             res = -1;
00537             break;
00538          }
00539          if (last_state != t30state->state) {
00540             state_change = ast_tvnow();
00541             last_state = t30state->state;
00542          }
00543       } else if ((inf->frametype == AST_FRAME_CONTROL) &&
00544             (inf->subclass.integer == AST_CONTROL_T38_PARAMETERS)) {
00545          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00546 
00547          if (parameters->request_response == AST_T38_NEGOTIATED) {
00548             /* T38 switchover completed */
00549             s->t38parameters = *parameters;
00550             ast_debug(1, "T38 negotiated, finishing audio loop\n");
00551             res = 1;
00552             break;
00553          } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
00554             t38_parameters.request_response = AST_T38_NEGOTIATED;
00555             ast_debug(1, "T38 request received, accepting\n");
00556             /* Complete T38 switchover */
00557             ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
00558             /* Do not break audio loop, wait until channel driver finally acks switchover
00559              * with AST_T38_NEGOTIATED
00560              */
00561          }
00562       }
00563 
00564       ast_frfree(inf);
00565       inf = NULL;
00566    }
00567 
00568    ast_debug(1, "Loop finished, res=%d\n", res);
00569 
00570    if (inf)
00571       ast_frfree(inf);
00572 
00573    ast_deactivate_generator(s->chan);
00574 
00575    /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
00576       by t30_terminate, display diagnostics and set status variables although no transmittion
00577       has taken place yet. */
00578    if (res > 0) {
00579       t30_set_phase_e_handler(t30state, NULL, NULL);
00580    }
00581 
00582    t30_terminate(t30state);
00583    fax_release(&fax);
00584 
00585 done:
00586    if (original_write_fmt) {
00587       if (ast_set_write_format(s->chan, original_write_fmt) < 0)
00588          ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", ast_channel_name(s->chan));
00589       ao2_ref(original_write_fmt, -1);
00590    }
00591 
00592    if (original_read_fmt) {
00593       if (ast_set_read_format(s->chan, original_read_fmt) < 0)
00594          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", ast_channel_name(s->chan));
00595       ao2_ref(original_read_fmt, -1);
00596    }
00597 
00598    return res;
00599 
00600 }

static int transmit_t38 ( fax_session s  )  [static]

Definition at line 602 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, NULL, 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, timeout, ast_control_t38_parameters::transcoding_jbig, ast_control_t38_parameters::transcoding_mmr, TRUE, WATCHDOG_STATE_TIMEOUT, and WATCHDOG_TOTAL_TIMEOUT.

Referenced by transmit().

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

static int unload_module ( void   )  [static]

Definition at line 974 of file app_fax.c.

References ast_unregister_application().

00975 {
00976    int res;
00977 
00978    res = ast_unregister_application(app_sndfax_name); 
00979    res |= ast_unregister_application(app_rcvfax_name);   
00980 
00981    return res;
00982 }


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

Definition at line 1002 of file app_fax.c.

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

Definition at line 148 of file app_fax.c.

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

Definition at line 147 of file app_fax.c.

Definition at line 1002 of file app_fax.c.

struct ast_generator generator [static]


Generated on Thu Apr 16 06:28:27 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6