#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"
Go to the source code of this file.
Data Structures | |
| struct | fax_session |
Defines | |
| #define | MAX_SAMPLES 240 |
| #define | WATCHDOG_STATE_TIMEOUT 5 * 60 |
| #define | WATCHDOG_TOTAL_TIMEOUT 30 * 60 |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static void * | fax_generator_alloc (struct ast_channel *chan, void *params) |
| static int | fax_generator_generate (struct ast_channel *chan, void *data, int len, int samples) |
| static int | load_module (void) |
| static void | phase_e_handler (t30_state_t *f, void *user_data, int result) |
| static int | rcvfax_exec (struct ast_channel *chan, void *data) |
| static void | set_ecm (t30_state_t *state, int ecm) |
| static void | set_file (t30_state_t *state, fax_session *s) |
| static void | set_local_info (t30_state_t *state, fax_session *s) |
| static int | set_logging (logging_state_t *state) |
| static int | sndfax_exec (struct ast_channel *chan, void *data) |
| static void | span_message (int level, const char *msg) |
| static int | t38_tx_packet_handler (t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count) |
| static int | transmit (fax_session *s) |
| static int | transmit_audio (fax_session *s) |
| static int | transmit_t38 (fax_session *s) |
| static int | unload_module (void) |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } |
| static char * | app_rcvfax_desc |
| static char * | app_rcvfax_name = "ReceiveFAX" |
| static char * | app_rcvfax_synopsis = "Receive a FAX" |
| static char * | app_sndfax_desc |
| static char * | app_sndfax_name = "SendFAX" |
| static char * | app_sndfax_synopsis = "Send a FAX" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| struct ast_generator | generator |
| #define MAX_SAMPLES 240 |
Definition at line 102 of file app_fax.c.
Referenced by fax_generator_generate().
| #define WATCHDOG_STATE_TIMEOUT 5 * 60 |
Definition at line 111 of file app_fax.c.
Referenced by transmit_audio(), and transmit_t38().
| #define WATCHDOG_TOTAL_TIMEOUT 30 * 60 |
Definition at line 110 of file app_fax.c.
Referenced by transmit_audio(), and transmit_t38().
| static void* fax_generator_alloc | ( | struct ast_channel * | chan, | |
| void * | params | |||
| ) | [static] |
| static int fax_generator_generate | ( | struct ast_channel * | chan, | |
| void * | data, | |||
| int | len, | |||
| int | samples | |||
| ) | [static] |
Definition at line 279 of file app_fax.c.
References AST_FORMAT_SLINEAR, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_write(), buf, errno, ast_frame::frametype, LOG_WARNING, MAX_SAMPLES, and ast_frame::samples.
00280 { 00281 fax_state_t *fax = (fax_state_t*) data; 00282 uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)]; 00283 int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET); 00284 00285 struct ast_frame outf = { 00286 .frametype = AST_FRAME_VOICE, 00287 .subclass = AST_FORMAT_SLINEAR, 00288 .src = __FUNCTION__, 00289 }; 00290 00291 if (samples > MAX_SAMPLES) { 00292 ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples); 00293 samples = MAX_SAMPLES; 00294 } 00295 00296 if ((len = fax_tx(fax, buf, samples)) > 0) { 00297 outf.samples = len; 00298 AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t)); 00299 00300 if (ast_write(chan, &outf) < 0) { 00301 ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno)); 00302 return -1; 00303 } 00304 } 00305 00306 return 0; 00307 }
| static int load_module | ( | void | ) | [static] |
Definition at line 875 of file app_fax.c.
References ast_register_application, rcvfax_exec(), and sndfax_exec().
00876 { 00877 int res ; 00878 00879 res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc); 00880 res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc); 00881 00882 /* The default SPAN message handler prints to stderr. It is something we do not want */ 00883 span_set_message_handler(NULL); 00884 00885 return res; 00886 }
| static void phase_e_handler | ( | t30_state_t * | f, | |
| void * | user_data, | |||
| int | result | |||
| ) | [static] |
Definition at line 157 of file app_fax.c.
References ast_debug, ast_log(), buf, 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, pbx_builtin_setvar_helper(), s, and S_OR.
Referenced by transmit_audio(), and transmit_t38().
00158 { 00159 const char *local_ident; 00160 const char *far_ident; 00161 char buf[20]; 00162 fax_session *s = (fax_session *) user_data; 00163 t30_stats_t stat; 00164 int pages_transferred; 00165 00166 ast_debug(1, "Fax phase E handler. result=%d\n", result); 00167 00168 t30_get_transfer_statistics(f, &stat); 00169 00170 s = (fax_session *) user_data; 00171 00172 if (result != T30_ERR_OK) { 00173 s->finished = -1; 00174 00175 /* FAXSTATUS is already set to FAILED */ 00176 pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result)); 00177 00178 ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result)); 00179 00180 return; 00181 } 00182 00183 s->finished = 1; 00184 00185 local_ident = t30_get_tx_ident(f); 00186 far_ident = t30_get_rx_ident(f); 00187 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 00188 pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 00189 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident); 00190 #if SPANDSP_RELEASE_DATE >= 20090220 00191 pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx; 00192 #else 00193 pages_transferred = stat.pages_transferred; 00194 #endif 00195 snprintf(buf, sizeof(buf), "%d", pages_transferred); 00196 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf); 00197 snprintf(buf, sizeof(buf), "%d", stat.y_resolution); 00198 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf); 00199 snprintf(buf, sizeof(buf), "%d", stat.bit_rate); 00200 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 00201 00202 ast_debug(1, "Fax transmitted successfully.\n"); 00203 ast_debug(1, " Remote station ID: %s\n", far_ident); 00204 ast_debug(1, " Pages transferred: %d\n", pages_transferred); 00205 ast_debug(1, " Image resolution: %d x %d\n", stat.x_resolution, stat.y_resolution); 00206 ast_debug(1, " Transfer Rate: %d\n", stat.bit_rate); 00207 00208 manager_event(EVENT_FLAG_CALL, 00209 s->direction ? "FaxSent" : "FaxReceived", 00210 "Channel: %s\r\n" 00211 "Exten: %s\r\n" 00212 "CallerID: %s\r\n" 00213 "RemoteStationID: %s\r\n" 00214 "LocalStationID: %s\r\n" 00215 "PagesTransferred: %d\r\n" 00216 "Resolution: %d\r\n" 00217 "TransferRate: %d\r\n" 00218 "FileName: %s\r\n", 00219 s->chan->name, 00220 s->chan->exten, 00221 S_OR(s->chan->cid.cid_num, ""), 00222 far_ident, 00223 local_ident, 00224 pages_transferred, 00225 stat.y_resolution, 00226 stat.bit_rate, 00227 s->file_name); 00228 }
| static int rcvfax_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 821 of file app_fax.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.
Referenced by load_module().
00822 { 00823 int res = 0; 00824 char *parse; 00825 fax_session session; 00826 00827 AST_DECLARE_APP_ARGS(args, 00828 AST_APP_ARG(file_name); 00829 AST_APP_ARG(options); 00830 ); 00831 00832 if (chan == NULL) { 00833 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00834 return -1; 00835 } 00836 00837 /* The next few lines of code parse out the filename and header from the input string */ 00838 if (ast_strlen_zero(data)) { 00839 /* No data implies no filename or anything is present */ 00840 ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n"); 00841 return -1; 00842 } 00843 00844 parse = ast_strdupa(data); 00845 AST_STANDARD_APP_ARGS(args, parse); 00846 00847 session.caller_mode = FALSE; 00848 00849 if (args.options) { 00850 if (strchr(args.options, 'c')) 00851 session.caller_mode = TRUE; 00852 } 00853 00854 /* Done parsing */ 00855 session.direction = 0; 00856 session.file_name = args.file_name; 00857 session.chan = chan; 00858 session.finished = 0; 00859 00860 res = transmit(&session); 00861 00862 return res; 00863 }
| static void set_ecm | ( | t30_state_t * | state, | |
| int | ecm | |||
| ) | [static] |
Definition at line 264 of file app_fax.c.
Referenced by transmit_audio(), and transmit_t38().
| static void set_file | ( | t30_state_t * | state, | |
| fax_session * | s | |||
| ) | [static] |
Definition at line 256 of file app_fax.c.
References fax_session::direction, and fax_session::file_name.
Referenced by transmit_audio(), and transmit_t38().
| static void set_local_info | ( | t30_state_t * | state, | |
| fax_session * | s | |||
| ) | [static] |
Definition at line 243 of file app_fax.c.
References ast_strlen_zero(), fax_session::chan, and pbx_builtin_getvar_helper().
Referenced by transmit_audio(), and transmit_t38().
00244 { 00245 const char *x; 00246 00247 x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID"); 00248 if (!ast_strlen_zero(x)) 00249 t30_set_tx_ident(state, x); 00250 00251 x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO"); 00252 if (!ast_strlen_zero(x)) 00253 t30_set_tx_page_header_info(state, x); 00254 }
| static int set_logging | ( | logging_state_t * | state | ) | [static] |
Definition at line 233 of file app_fax.c.
References option_debug, and span_message().
Referenced by transmit_audio(), and transmit_t38().
00234 { 00235 int level = SPAN_LOG_WARNING + option_debug; 00236 00237 span_log_set_message_handler(state, span_message); 00238 span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 00239 00240 return 0; 00241 }
| static int sndfax_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
Definition at line 777 of file app_fax.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), fax_session::caller_mode, fax_session::chan, fax_session::direction, FALSE, fax_session::file_name, fax_session::finished, LOG_ERROR, parse(), transmit(), and TRUE.
Referenced by load_module().
00778 { 00779 int res = 0; 00780 char *parse; 00781 fax_session session = { 0, }; 00782 00783 AST_DECLARE_APP_ARGS(args, 00784 AST_APP_ARG(file_name); 00785 AST_APP_ARG(options); 00786 ); 00787 00788 if (chan == NULL) { 00789 ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n"); 00790 return -1; 00791 } 00792 00793 /* The next few lines of code parse out the filename and header from the input string */ 00794 if (ast_strlen_zero(data)) { 00795 /* No data implies no filename or anything is present */ 00796 ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n"); 00797 return -1; 00798 } 00799 00800 parse = ast_strdupa(data); 00801 AST_STANDARD_APP_ARGS(args, parse); 00802 00803 session.caller_mode = TRUE; 00804 00805 if (args.options) { 00806 if (strchr(args.options, 'a')) 00807 session.caller_mode = FALSE; 00808 } 00809 00810 /* Done parsing */ 00811 session.direction = 1; 00812 session.file_name = args.file_name; 00813 session.chan = chan; 00814 session.finished = 0; 00815 00816 res = transmit(&session); 00817 00818 return res; 00819 }
| static void span_message | ( | int | level, | |
| const char * | msg | |||
| ) | [static] |
Definition at line 123 of file app_fax.c.
References ast_log(), LOG_DEBUG, LOG_ERROR, and LOG_WARNING.
Referenced by set_logging().
| static int t38_tx_packet_handler | ( | t38_core_state_t * | s, | |
| void * | user_data, | |||
| const uint8_t * | buf, | |||
| int | len, | |||
| int | count | |||
| ) | [static] |
Definition at line 134 of file app_fax.c.
References AST_FRAME_MODEM, AST_FRAME_SET_BUFFER, ast_log(), AST_MODEM_T38, ast_write(), chan, errno, ast_frame::frametype, and LOG_WARNING.
Referenced by transmit_t38().
00135 { 00136 struct ast_channel *chan = (struct ast_channel *) user_data; 00137 00138 struct ast_frame outf = { 00139 .frametype = AST_FRAME_MODEM, 00140 .subclass = AST_MODEM_T38, 00141 .src = __FUNCTION__, 00142 }; 00143 00144 /* TODO: Asterisk does not provide means of resending the same packet multiple 00145 times so count is ignored at the moment */ 00146 00147 AST_FRAME_SET_BUFFER(&outf, buf, 0, len); 00148 00149 if (ast_write(chan, &outf) < 0) { 00150 ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno)); 00151 return -1; 00152 } 00153 00154 return 0; 00155 }
| static int transmit | ( | fax_session * | s | ) | [static] |
Definition at line 719 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, pbx_builtin_setvar_helper(), T38_STATE_NEGOTIATED, fax_session::t38state, transmit_audio(), and transmit_t38().
Referenced by rcvfax_exec(), and sndfax_exec().
00720 { 00721 int res = 0; 00722 00723 /* Clear all channel variables which to be set by the application. 00724 Pre-set status to error so in case of any problems we can just leave */ 00725 pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 00726 pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 00727 00728 pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL); 00729 pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL); 00730 pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL); 00731 pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL); 00732 pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 00733 00734 if (s->chan->_state != AST_STATE_UP) { 00735 /* Shouldn't need this, but checking to see if channel is already answered 00736 * Theoretically asterisk should already have answered before running the app */ 00737 res = ast_answer(s->chan); 00738 if (res) { 00739 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name); 00740 return res; 00741 } 00742 } 00743 00744 s->t38state = ast_channel_get_t38_state(s->chan); 00745 if (s->t38state != T38_STATE_NEGOTIATED) { 00746 /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */ 00747 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 00748 res = transmit_audio(s); 00749 if (res > 0) { 00750 /* transmit_audio reports switchover to T38. Update t38state */ 00751 s->t38state = ast_channel_get_t38_state(s->chan); 00752 if (s->t38state != T38_STATE_NEGOTIATED) { 00753 ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n"); 00754 } 00755 } 00756 } 00757 00758 if (s->t38state == T38_STATE_NEGOTIATED) { 00759 pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 00760 res = transmit_t38(s); 00761 } 00762 00763 if (res) { 00764 ast_log(LOG_WARNING, "Transmission error\n"); 00765 res = -1; 00766 } else if (s->finished < 0) { 00767 ast_log(LOG_WARNING, "Transmission failed\n"); 00768 } else if (s->finished > 0) { 00769 ast_debug(1, "Transmission finished Ok\n"); 00770 } 00771 00772 return res; 00773 }
| static int transmit_audio | ( | fax_session * | s | ) | [static] |
Definition at line 317 of file app_fax.c.
References ast_activate_generator(), ast_channel_get_t38_state(), AST_CONTROL_T38_PARAMETERS, ast_deactivate_generator(), ast_debug, AST_FORMAT_SLINEAR, AST_FRAME_CONTROL, AST_FRAME_VOICE, ast_frfree, ast_indicate_data(), ast_log(), ast_read(), ast_set_read_format(), ast_set_write_format(), AST_T38_NEGOTIATED, AST_T38_RATE_14400, AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, AST_T38_REFUSED, AST_T38_REQUEST_NEGOTIATE, ast_tvdiff_sec(), ast_tvnow(), ast_waitfor(), fax_session::caller_mode, fax_session::chan, ast_frame::data, ast_frame::datalen, FALSE, fax_session::finished, ast_frame::frametype, 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().
00318 { 00319 int res = -1; 00320 int original_read_fmt = AST_FORMAT_SLINEAR; 00321 int original_write_fmt = AST_FORMAT_SLINEAR; 00322 fax_state_t fax; 00323 t30_state_t *t30state; 00324 struct ast_frame *inf = NULL; 00325 int last_state = 0; 00326 struct timeval now, start, state_change; 00327 enum ast_t38_state t38_state; 00328 struct ast_control_t38_parameters t38_parameters = { .version = 0, 00329 .max_ifp = 800, 00330 .rate = AST_T38_RATE_14400, 00331 .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, 00332 .fill_bit_removal = 1, 00333 .transcoding_mmr = 1, 00334 .transcoding_jbig = 1, 00335 }; 00336 00337 /* if in called party mode, try to use T.38 */ 00338 if (s->caller_mode == FALSE) { 00339 /* check if we are already in T.38 mode (unlikely), or if we can request 00340 * a switch... if so, request it now and wait for the result, rather 00341 * than starting an audio FAX session that will have to be cancelled 00342 */ 00343 if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) { 00344 return 1; 00345 } else if ((t38_state != T38_STATE_UNAVAILABLE) && 00346 (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE, 00347 (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) { 00348 /* wait up to five seconds for negotiation to complete */ 00349 unsigned int timeout = 5000; 00350 int ms; 00351 00352 ast_debug(1, "Negotiating T.38 for receive on %s\n", s->chan->name); 00353 while (timeout > 0) { 00354 ms = ast_waitfor(s->chan, 1000); 00355 if (ms < 0) { 00356 ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name); 00357 return -1; 00358 } 00359 if (!ms) { 00360 /* nothing happened */ 00361 if (timeout > 0) { 00362 timeout -= 1000; 00363 continue; 00364 } else { 00365 ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name); 00366 break; 00367 } 00368 } 00369 if (!(inf = ast_read(s->chan))) { 00370 return -1; 00371 } 00372 if ((inf->frametype == AST_FRAME_CONTROL) && 00373 (inf->subclass == AST_CONTROL_T38_PARAMETERS) && 00374 (inf->datalen == sizeof(t38_parameters))) { 00375 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00376 00377 switch (parameters->request_response) { 00378 case AST_T38_NEGOTIATED: 00379 ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name); 00380 res = 1; 00381 break; 00382 case AST_T38_REFUSED: 00383 ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name); 00384 break; 00385 default: 00386 ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name); 00387 break; 00388 } 00389 ast_frfree(inf); 00390 if (res == 1) { 00391 return 1; 00392 } else { 00393 break; 00394 } 00395 } 00396 ast_frfree(inf); 00397 } 00398 } 00399 } 00400 00401 #if SPANDSP_RELEASE_DATE >= 20080725 00402 /* for spandsp shaphots 0.0.6 and higher */ 00403 t30state = &fax.t30; 00404 #else 00405 /* for spandsp release 0.0.5 */ 00406 t30state = &fax.t30_state; 00407 #endif 00408 00409 original_read_fmt = s->chan->readformat; 00410 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00411 res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR); 00412 if (res < 0) { 00413 ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n"); 00414 goto done; 00415 } 00416 } 00417 00418 original_write_fmt = s->chan->writeformat; 00419 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00420 res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR); 00421 if (res < 0) { 00422 ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n"); 00423 goto done; 00424 } 00425 } 00426 00427 /* Initialize T30 terminal */ 00428 fax_init(&fax, s->caller_mode); 00429 00430 /* Setup logging */ 00431 set_logging(&fax.logging); 00432 set_logging(&t30state->logging); 00433 00434 /* Configure terminal */ 00435 set_local_info(t30state, s); 00436 set_file(t30state, s); 00437 set_ecm(t30state, TRUE); 00438 00439 fax_set_transmit_on_idle(&fax, TRUE); 00440 00441 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00442 00443 start = state_change = ast_tvnow(); 00444 00445 ast_activate_generator(s->chan, &generator, &fax); 00446 00447 while (!s->finished) { 00448 inf = NULL; 00449 00450 if ((res = ast_waitfor(s->chan, 25)) < 0) { 00451 ast_debug(1, "Error waiting for a frame\n"); 00452 break; 00453 } 00454 00455 /* Watchdog */ 00456 now = ast_tvnow(); 00457 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00458 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00459 res = -1; 00460 break; 00461 } 00462 00463 if (!res) { 00464 /* There was timeout waiting for a frame. Loop around and wait again */ 00465 continue; 00466 } 00467 00468 /* There is a frame available. Get it */ 00469 res = 0; 00470 00471 if (!(inf = ast_read(s->chan))) { 00472 ast_debug(1, "Channel hangup\n"); 00473 res = -1; 00474 break; 00475 } 00476 00477 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen); 00478 00479 /* Check the frame type. Format also must be checked because there is a chance 00480 that a frame in old format was already queued before we set channel format 00481 to slinear so it will still be received by ast_read */ 00482 if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) { 00483 if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) { 00484 /* I know fax_rx never returns errors. The check here is for good style only */ 00485 ast_log(LOG_WARNING, "fax_rx returned error\n"); 00486 res = -1; 00487 break; 00488 } 00489 if (last_state != t30state->state) { 00490 state_change = ast_tvnow(); 00491 last_state = t30state->state; 00492 } 00493 } else if ((inf->frametype == AST_FRAME_CONTROL) && 00494 (inf->subclass == AST_CONTROL_T38_PARAMETERS)) { 00495 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00496 00497 if (parameters->request_response == AST_T38_NEGOTIATED) { 00498 /* T38 switchover completed */ 00499 s->t38parameters = *parameters; 00500 ast_debug(1, "T38 negotiated, finishing audio loop\n"); 00501 res = 1; 00502 break; 00503 } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) { 00504 t38_parameters.request_response = AST_T38_NEGOTIATED; 00505 ast_debug(1, "T38 request received, accepting\n"); 00506 /* Complete T38 switchover */ 00507 ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)); 00508 /* Do not break audio loop, wait until channel driver finally acks switchover 00509 * with AST_T38_NEGOTIATED 00510 */ 00511 } 00512 } 00513 00514 ast_frfree(inf); 00515 } 00516 00517 ast_debug(1, "Loop finished, res=%d\n", res); 00518 00519 if (inf) 00520 ast_frfree(inf); 00521 00522 ast_deactivate_generator(s->chan); 00523 00524 /* If we are switching to T38, remove phase E handler. Otherwise it will be executed 00525 by t30_terminate, display diagnostics and set status variables although no transmittion 00526 has taken place yet. */ 00527 if (res > 0) { 00528 t30_set_phase_e_handler(t30state, NULL, NULL); 00529 } 00530 00531 t30_terminate(t30state); 00532 fax_release(&fax); 00533 00534 done: 00535 if (original_write_fmt != AST_FORMAT_SLINEAR) { 00536 if (ast_set_write_format(s->chan, original_write_fmt) < 0) 00537 ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name); 00538 } 00539 00540 if (original_read_fmt != AST_FORMAT_SLINEAR) { 00541 if (ast_set_read_format(s->chan, original_read_fmt) < 0) 00542 ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name); 00543 } 00544 00545 return res; 00546 00547 }
| static int transmit_t38 | ( | fax_session * | s | ) | [static] |
Definition at line 549 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, 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().
00550 { 00551 int res = 0; 00552 t38_terminal_state_t t38; 00553 struct ast_frame *inf = NULL; 00554 int last_state = 0; 00555 struct timeval now, start, state_change, last_frame; 00556 t30_state_t *t30state; 00557 t38_core_state_t *t38state; 00558 00559 #if SPANDSP_RELEASE_DATE >= 20080725 00560 /* for spandsp shaphots 0.0.6 and higher */ 00561 t30state = &t38.t30; 00562 t38state = &t38.t38_fe.t38; 00563 #else 00564 /* for spandsp releases 0.0.5 */ 00565 t30state = &t38.t30_state; 00566 t38state = &t38.t38; 00567 #endif 00568 00569 /* Initialize terminal */ 00570 memset(&t38, 0, sizeof(t38)); 00571 if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) { 00572 ast_log(LOG_WARNING, "Unable to start T.38 termination.\n"); 00573 res = -1; 00574 goto disable_t38; 00575 } 00576 00577 t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp); 00578 00579 if (s->t38parameters.fill_bit_removal) { 00580 t38_set_fill_bit_removal(t38state, TRUE); 00581 } 00582 if (s->t38parameters.transcoding_mmr) { 00583 t38_set_mmr_transcoding(t38state, TRUE); 00584 } 00585 if (s->t38parameters.transcoding_jbig) { 00586 t38_set_jbig_transcoding(t38state, TRUE); 00587 } 00588 00589 /* Setup logging */ 00590 set_logging(&t38.logging); 00591 set_logging(&t30state->logging); 00592 set_logging(&t38state->logging); 00593 00594 /* Configure terminal */ 00595 set_local_info(t30state, s); 00596 set_file(t30state, s); 00597 set_ecm(t30state, TRUE); 00598 00599 t30_set_phase_e_handler(t30state, phase_e_handler, s); 00600 00601 now = start = state_change = ast_tvnow(); 00602 00603 while (!s->finished) { 00604 00605 res = ast_waitfor(s->chan, 20); 00606 if (res < 0) 00607 break; 00608 else if (res > 0) 00609 res = 0; 00610 00611 last_frame = now; 00612 now = ast_tvnow(); 00613 t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000)); 00614 00615 inf = ast_read(s->chan); 00616 if (inf == NULL) { 00617 ast_debug(1, "Channel hangup\n"); 00618 res = -1; 00619 break; 00620 } 00621 00622 ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen); 00623 00624 if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) { 00625 t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno); 00626 00627 /* Watchdog */ 00628 if (last_state != t30state->state) { 00629 state_change = ast_tvnow(); 00630 last_state = t30state->state; 00631 } 00632 } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) { 00633 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00634 if (parameters->request_response == AST_T38_TERMINATED) { 00635 ast_debug(1, "T38 down, finishing\n"); 00636 break; 00637 } 00638 } 00639 00640 ast_frfree(inf); 00641 inf = NULL; 00642 00643 /* Watchdog */ 00644 if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) { 00645 ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n"); 00646 res = -1; 00647 break; 00648 } 00649 } 00650 00651 ast_debug(1, "Loop finished, res=%d\n", res); 00652 00653 if (inf) 00654 ast_frfree(inf); 00655 00656 t30_terminate(t30state); 00657 t38_terminal_release(&t38); 00658 00659 disable_t38: 00660 /* if we are not the caller, it's our job to shut down the T.38 00661 * session when the FAX transmisson is complete. 00662 */ 00663 if ((s->caller_mode == FALSE) && 00664 (ast_channel_get_t38_state(s->chan) == T38_STATE_NEGOTIATED)) { 00665 struct ast_control_t38_parameters t38_parameters = { .request_response = AST_T38_REQUEST_TERMINATE, }; 00666 00667 if (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0) { 00668 /* wait up to five seconds for negotiation to complete */ 00669 unsigned int timeout = 5000; 00670 int ms; 00671 00672 ast_debug(1, "Shutting down T.38 on %s\n", s->chan->name); 00673 while (timeout > 0) { 00674 ms = ast_waitfor(s->chan, 1000); 00675 if (ms < 0) { 00676 ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name); 00677 return -1; 00678 } 00679 if (!ms) { 00680 /* nothing happened */ 00681 if (timeout > 0) { 00682 timeout -= 1000; 00683 continue; 00684 } else { 00685 ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 shutdown.\n", s->chan->name); 00686 break; 00687 } 00688 } 00689 if (!(inf = ast_read(s->chan))) { 00690 return -1; 00691 } 00692 if ((inf->frametype == AST_FRAME_CONTROL) && 00693 (inf->subclass == AST_CONTROL_T38_PARAMETERS) && 00694 (inf->datalen == sizeof(t38_parameters))) { 00695 struct ast_control_t38_parameters *parameters = inf->data.ptr; 00696 00697 switch (parameters->request_response) { 00698 case AST_T38_TERMINATED: 00699 ast_debug(1, "Shut down T.38 on %s\n", s->chan->name); 00700 break; 00701 case AST_T38_REFUSED: 00702 ast_log(LOG_WARNING, "channel '%s' refused to disable T.38\n", s->chan->name); 00703 break; 00704 default: 00705 ast_log(LOG_ERROR, "channel '%s' failed to disable T.38\n", s->chan->name); 00706 break; 00707 } 00708 ast_frfree(inf); 00709 break; 00710 } 00711 ast_frfree(inf); 00712 } 00713 } 00714 } 00715 00716 return res; 00717 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 865 of file app_fax.c.
References ast_unregister_application().
00866 { 00867 int res; 00868 00869 res = ast_unregister_application(app_sndfax_name); 00870 res |= ast_unregister_application(app_rcvfax_name); 00871 00872 return res; 00873 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Simple FAX Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static] |
char* app_rcvfax_desc [static] |
char* app_rcvfax_name = "ReceiveFAX" [static] |
char* app_rcvfax_synopsis = "Receive a FAX" [static] |
char* app_sndfax_desc [static] |
char* app_sndfax_name = "SendFAX" [static] |
char* app_sndfax_synopsis = "Send a FAX" [static] |
struct ast_module_info* ast_module_info = &__mod_info [static] |
| struct ast_generator generator |
{
alloc: fax_generator_alloc,
generate: fax_generator_generate,
}
1.6.1