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