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