2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2009-2010, Digium, Inc.
6 * Matthew Nicholson <mnicholson@digium.com>
8 * Initial T.38-gateway code
9 * 2008, Daniel Ferenci <daniel.ferenci@nethemba.com>
10 * Created by Nethemba s.r.o. http://www.nethemba.com
11 * Sponsored by IPEX a.s. http://www.ipex.cz
13 * T.38-gateway integration into asterisk app_fax and rework
14 * 2008, Gregory Hinton Nietsky <gregory@dnstelecom.co.za>
15 * dns Telecom http://www.dnstelecom.co.za
17 * Modified to make T.38-gateway compatible with Asterisk 1.6.2
18 * 2010, Anton Verevkin <mymail@verevkin.it>
19 * ViaNetTV http://www.vianettv.com
21 * Modified to make T.38-gateway work
22 * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
24 * See http://www.asterisk.org for more information about
25 * the Asterisk project. Please do not directly contact
26 * any of the maintainers of this project for assistance;
27 * the project provides a web site, mailing lists and IRC
28 * channels for your use.
30 * This program is free software, distributed under the terms of
31 * the GNU General Public License Version 2. See the LICENSE file
32 * at the top of the source tree.
37 * \brief Spandsp T.38 and G.711 FAX Resource
39 * \author Matthew Nicholson <mnicholson@digium.com>
40 * \author Gregory H. Nietsky <gregory@distrotech.co.za>
42 * This module registers the Spandsp FAX technology with the res_fax module.
46 <depend>spandsp</depend>
47 <depend>res_fax</depend>
48 <support_level>extended</support_level>
51 /* Include spandsp headers before asterisk.h so the inline functions can continue using
52 * malloc and free, even with MALLOC_DEBUG enabled. */
53 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
55 #include <spandsp/version.h>
59 ASTERISK_REGISTER_FILE()
61 #include "asterisk/logger.h"
62 #include "asterisk/module.h"
63 #include "asterisk/strings.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/utils.h"
66 #include "asterisk/timing.h"
67 #include "asterisk/astobj2.h"
68 #include "asterisk/res_fax.h"
69 #include "asterisk/channel.h"
70 #include "asterisk/format_cache.h"
72 #define SPANDSP_FAX_SAMPLES 160
73 #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */
74 #define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
76 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
77 static void spandsp_fax_destroy(struct ast_fax_session *s);
78 static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s);
79 static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f);
80 static int spandsp_fax_start(struct ast_fax_session *s);
81 static int spandsp_fax_cancel(struct ast_fax_session *s);
82 static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
83 static int spandsp_fax_gateway_start(struct ast_fax_session *s);
84 static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
85 static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s);
86 static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f);
87 static void spandsp_v21_cleanup(struct ast_fax_session *s);
88 static void spandsp_v21_tone(void *data, int code, int level, int delay);
90 static char *spandsp_fax_cli_show_capabilities(int fd);
91 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
92 static void spandsp_manager_fax_session(struct mansession *s,
93 const char *id_text, struct ast_fax_session *session);
94 static char *spandsp_fax_cli_show_stats(int fd);
95 static char *spandsp_fax_cli_show_settings(int fd);
97 static struct ast_fax_tech spandsp_fax_tech = {
99 .description = "Spandsp FAX Driver",
100 #if SPANDSP_RELEASE_DATE >= 20090220
102 .version = SPANDSP_RELEASE_DATETIME_STRING,
105 * TODO: maybe we should determine the version better way
107 .version = "pre-20090220",
109 .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND
110 | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY
111 | AST_FAX_TECH_V21_DETECT,
112 .new_session = spandsp_fax_new,
113 .destroy_session = spandsp_fax_destroy,
114 .read = spandsp_fax_read,
115 .write = spandsp_fax_write,
116 .start_session = spandsp_fax_start,
117 .cancel_session = spandsp_fax_cancel,
118 .switch_to_t38 = spandsp_fax_switch_to_t38,
119 .cli_show_capabilities = spandsp_fax_cli_show_capabilities,
120 .cli_show_session = spandsp_fax_cli_show_session,
121 .manager_fax_session = spandsp_manager_fax_session,
122 .cli_show_stats = spandsp_fax_cli_show_stats,
123 .cli_show_settings = spandsp_fax_cli_show_settings,
126 struct spandsp_fax_stats {
131 int rx_protocol_error;
132 int tx_protocol_error;
134 int retries_exceeded;
144 struct spandsp_fax_stats g711;
145 struct spandsp_fax_stats t38;
146 } spandsp_global_stats;
149 unsigned int ist38:1;
150 unsigned int isdone:1;
151 enum ast_t38_state ast_t38_state;
152 fax_state_t fax_state;
153 t38_terminal_state_t t38_state;
154 t30_state_t *t30_state;
155 t38_core_state_t *t38_core_state;
157 struct spandsp_fax_stats *stats;
159 struct spandsp_fax_gw_stats *t38stats;
160 t38_gateway_state_t t38_gw_state;
162 struct ast_timer *timer;
163 AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
166 modem_connect_tones_rx_state_t *tone_state;
169 static int spandsp_v21_new(struct spandsp_pvt *p);
170 static void session_destroy(struct spandsp_pvt *p);
171 static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count);
172 static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code);
173 static void spandsp_log(int level, const char *msg);
174 static int update_stats(struct spandsp_pvt *p, int completion_code);
175 static int spandsp_modems(struct ast_fax_session_details *details);
177 static void set_logging(logging_state_t *state, struct ast_fax_session_details *details);
178 static void set_local_info(t30_state_t *t30_state, struct ast_fax_session_details *details);
179 static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *details);
180 static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details);
182 static void session_destroy(struct spandsp_pvt *p)
185 t30_state_t *t30_to_terminate;
188 t30_to_terminate = p->t30_state;
189 } else if (p->ist38) {
190 #if SPANDSP_RELEASE_DATE >= 20080725
191 t30_to_terminate = &p->t38_state.t30;
193 t30_to_terminate = &p->t38_state.t30_state;
196 #if SPANDSP_RELEASE_DATE >= 20080725
197 t30_to_terminate = &p->fax_state.t30;
199 t30_to_terminate = &p->fax_state.t30_state;
203 t30_terminate(t30_to_terminate);
206 ast_timer_close(p->timer);
208 fax_release(&p->fax_state);
209 t38_terminal_release(&p->t38_state);
211 while ((f = AST_LIST_REMOVE_HEAD(&p->read_frames, frame_list))) {
219 static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
222 struct ast_fax_session *s = data;
223 struct spandsp_pvt *p = s->tech_pvt;
224 struct ast_frame fax_frame = {
225 .frametype = AST_FRAME_MODEM,
226 .subclass.integer = AST_MODEM_T38,
227 .src = "res_fax_spandsp_t38",
230 struct ast_frame *f = &fax_frame;
233 /* TODO: Asterisk does not provide means of resending the same packet multiple
234 times so count is ignored at the moment */
236 AST_FRAME_SET_BUFFER(f, buf, 0, len);
238 if (!(f = ast_frisolate(f))) {
242 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
243 ast_set_flag(f, AST_FAX_FRFLAG_GATEWAY);
244 if (p->ast_t38_state == T38_STATE_NEGOTIATED) {
245 res = ast_write(s->chan, f);
247 res = ast_queue_frame(s->chan, f);
251 /* no need to lock, this all runs in the same thread */
252 AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
259 static int update_stats(struct spandsp_pvt *p, int completion_code)
261 switch (completion_code) {
263 ast_atomic_fetchadd_int(&p->stats->success, 1);
267 case T30_ERR_CEDTONE: /*! The CED tone exceeded 5s */
268 case T30_ERR_T0_EXPIRED: /*! Timed out waiting for initial communication */
269 case T30_ERR_T1_EXPIRED: /*! Timed out waiting for the first message */
270 case T30_ERR_T3_EXPIRED: /*! Timed out waiting for procedural interrupt */
271 case T30_ERR_HDLC_CARRIER: /*! The HDLC carrier did not stop in a timely manner */
272 case T30_ERR_CANNOT_TRAIN: /*! Failed to train with any of the compatible modems */
273 ast_atomic_fetchadd_int(&p->stats->failed_to_train, 1);
276 case T30_ERR_OPER_INT_FAIL: /*! Operator intervention failed */
277 case T30_ERR_INCOMPATIBLE: /*! Far end is not compatible */
278 case T30_ERR_RX_INCAPABLE: /*! Far end is not able to receive */
279 case T30_ERR_TX_INCAPABLE: /*! Far end is not able to transmit */
280 case T30_ERR_NORESSUPPORT: /*! Far end cannot receive at the resolution of the image */
281 case T30_ERR_NOSIZESUPPORT: /*! Far end cannot receive at the size of image */
282 ast_atomic_fetchadd_int(&p->stats->neg_failed, 1);
285 case T30_ERR_UNEXPECTED: /*! Unexpected message received */
286 ast_atomic_fetchadd_int(&p->stats->protocol_error, 1);
289 /* Phase E status values returned to a transmitter */
290 case T30_ERR_TX_BADDCS: /*! Received bad response to DCS or training */
291 case T30_ERR_TX_BADPG: /*! Received a DCN from remote after sending a page */
292 case T30_ERR_TX_ECMPHD: /*! Invalid ECM response received from receiver */
293 case T30_ERR_TX_GOTDCN: /*! Received a DCN while waiting for a DIS */
294 case T30_ERR_TX_INVALRSP: /*! Invalid response after sending a page */
295 case T30_ERR_TX_NODIS: /*! Received other than DIS while waiting for DIS */
296 case T30_ERR_TX_PHBDEAD: /*! Received no response to DCS, training or TCF */
297 case T30_ERR_TX_PHDDEAD: /*! No response after sending a page */
298 case T30_ERR_TX_T5EXP: /*! Timed out waiting for receiver ready (ECM mode) */
299 ast_atomic_fetchadd_int(&p->stats->tx_protocol_error, 1);
302 /* Phase E status values returned to a receiver */
303 case T30_ERR_RX_ECMPHD: /*! Invalid ECM response received from transmitter */
304 case T30_ERR_RX_GOTDCS: /*! DCS received while waiting for DTC */
305 case T30_ERR_RX_INVALCMD: /*! Unexpected command after page received */
306 case T30_ERR_RX_NOCARRIER: /*! Carrier lost during fax receive */
307 case T30_ERR_RX_NOEOL: /*! Timed out while waiting for EOL (end Of line) */
308 ast_atomic_fetchadd_int(&p->stats->rx_protocol_error, 1);
310 case T30_ERR_RX_NOFAX: /*! Timed out while waiting for first line */
311 ast_atomic_fetchadd_int(&p->stats->nofax, 1);
313 case T30_ERR_RX_T2EXPDCN: /*! Timer T2 expired while waiting for DCN */
314 case T30_ERR_RX_T2EXPD: /*! Timer T2 expired while waiting for phase D */
315 case T30_ERR_RX_T2EXPFAX: /*! Timer T2 expired while waiting for fax page */
316 case T30_ERR_RX_T2EXPMPS: /*! Timer T2 expired while waiting for next fax page */
317 case T30_ERR_RX_T2EXPRR: /*! Timer T2 expired while waiting for RR command */
318 case T30_ERR_RX_T2EXP: /*! Timer T2 expired while waiting for NSS, DCS or MCF */
319 case T30_ERR_RX_DCNWHY: /*! Unexpected DCN while waiting for DCS or DIS */
320 case T30_ERR_RX_DCNDATA: /*! Unexpected DCN while waiting for image data */
321 case T30_ERR_RX_DCNFAX: /*! Unexpected DCN while waiting for EOM, EOP or MPS */
322 case T30_ERR_RX_DCNPHD: /*! Unexpected DCN after EOM or MPS sequence */
323 case T30_ERR_RX_DCNRRD: /*! Unexpected DCN after RR/RNR sequence */
324 case T30_ERR_RX_DCNNORTN: /*! Unexpected DCN after requested retransmission */
325 ast_atomic_fetchadd_int(&p->stats->rx_protocol_error, 1);
328 /* TIFF file problems */
329 case T30_ERR_FILEERROR: /*! TIFF/F file cannot be opened */
330 case T30_ERR_NOPAGE: /*! TIFF/F page not found */
331 case T30_ERR_BADTIFF: /*! TIFF/F format is not compatible */
332 case T30_ERR_BADPAGE: /*! TIFF/F page number tag missing */
333 case T30_ERR_BADTAG: /*! Incorrect values for TIFF/F tags */
334 case T30_ERR_BADTIFFHDR: /*! Bad TIFF/F header - incorrect values in fields */
335 ast_atomic_fetchadd_int(&p->stats->file_error, 1);
337 case T30_ERR_NOMEM: /*! Cannot allocate memory for more pages */
338 ast_atomic_fetchadd_int(&p->stats->mem_error, 1);
341 /* General problems */
342 case T30_ERR_RETRYDCN: /*! Disconnected after permitted retries */
343 ast_atomic_fetchadd_int(&p->stats->retries_exceeded, 1);
345 case T30_ERR_CALLDROPPED: /*! The call dropped prematurely */
346 ast_atomic_fetchadd_int(&p->stats->call_dropped, 1);
349 /* Feature negotiation issues */
350 case T30_ERR_NOPOLL: /*! Poll not accepted */
351 case T30_ERR_IDENT_UNACCEPTABLE: /*! Far end's ident is not acceptable */
352 case T30_ERR_SUB_UNACCEPTABLE: /*! Far end's sub-address is not acceptable */
353 case T30_ERR_SEP_UNACCEPTABLE: /*! Far end's selective polling address is not acceptable */
354 case T30_ERR_PSA_UNACCEPTABLE: /*! Far end's polled sub-address is not acceptable */
355 case T30_ERR_SID_UNACCEPTABLE: /*! Far end's sender identification is not acceptable */
356 case T30_ERR_PWD_UNACCEPTABLE: /*! Far end's password is not acceptable */
357 case T30_ERR_TSA_UNACCEPTABLE: /*! Far end's transmitting subscriber internet address is not acceptable */
358 case T30_ERR_IRA_UNACCEPTABLE: /*! Far end's internet routing address is not acceptable */
359 case T30_ERR_CIA_UNACCEPTABLE: /*! Far end's calling subscriber internet address is not acceptable */
360 case T30_ERR_ISP_UNACCEPTABLE: /*! Far end's internet selective polling address is not acceptable */
361 case T30_ERR_CSA_UNACCEPTABLE: /*! Far end's called subscriber internet address is not acceptable */
362 ast_atomic_fetchadd_int(&p->stats->neg_failed, 1);
365 ast_atomic_fetchadd_int(&p->stats->unknown_error, 1);
366 ast_log(LOG_WARNING, "unknown FAX session result '%d' (%s)\n", completion_code, t30_completion_code_to_str(completion_code));
372 /*! \brief Phase E handler callback.
373 * \param t30_state the span t30 state
374 * \param data this will be the ast_fax_session
375 * \param completion_code the result of the fax session
377 * This function pulls stats from the spandsp stack and stores them for res_fax
380 static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code)
382 struct ast_fax_session *s = data;
383 struct spandsp_pvt *p = s->tech_pvt;
384 char headerinfo[T30_MAX_PAGE_HEADER_INFO + 1];
388 ast_debug(5, "FAX session '%u' entering phase E\n", s->id);
392 update_stats(p, completion_code);
394 t30_get_transfer_statistics(t30_state, &stats);
396 if (completion_code == T30_ERR_OK) {
397 ast_string_field_set(s->details, result, "SUCCESS");
399 ast_string_field_set(s->details, result, "FAILED");
400 ast_string_field_set(s->details, error, t30_completion_code_to_str(completion_code));
403 ast_string_field_set(s->details, resultstr, t30_completion_code_to_str(completion_code));
405 ast_debug(5, "FAX session '%u' completed with result: %s (%s)\n", s->id, s->details->result, s->details->resultstr);
407 if ((c = t30_get_tx_ident(t30_state))) {
408 ast_string_field_set(s->details, localstationid, c);
411 if ((c = t30_get_rx_ident(t30_state))) {
412 ast_string_field_set(s->details, remotestationid, c);
415 #if SPANDSP_RELEASE_DATE >= 20090220
416 s->details->pages_transferred = (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx;
418 s->details->pages_transferred = stats.pages_transferred;
421 ast_string_field_build(s->details, transfer_rate, "%d", stats.bit_rate);
423 ast_string_field_build(s->details, resolution, "%dx%d", stats.x_resolution, stats.y_resolution);
425 t30_get_tx_page_header_info(t30_state, headerinfo);
426 ast_string_field_set(s->details, headerinfo, headerinfo);
429 /*! \brief Send spandsp log messages to asterisk.
430 * \param level the spandsp logging level
431 * \param msg the log message
433 * \note This function is a callback function called by spandsp.
435 static void spandsp_log(int level, const char *msg)
437 if (level == SPAN_LOG_ERROR) {
438 ast_log(LOG_ERROR, "%s", msg);
439 } else if (level == SPAN_LOG_WARNING) {
440 ast_log(LOG_WARNING, "%s", msg);
442 ast_fax_log(LOG_DEBUG, msg);
446 static void set_logging(logging_state_t *state, struct ast_fax_session_details *details)
448 int level = SPAN_LOG_WARNING;
450 if (details->option.debug) {
451 level = SPAN_LOG_DEBUG_3;
454 span_log_set_message_handler(state, spandsp_log);
455 span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level);
458 static void set_local_info(t30_state_t *t30_state, struct ast_fax_session_details *details)
460 if (!ast_strlen_zero(details->localstationid)) {
461 t30_set_tx_ident(t30_state, details->localstationid);
464 if (!ast_strlen_zero(details->headerinfo)) {
465 t30_set_tx_page_header_info(t30_state, details->headerinfo);
469 static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *details)
471 if (details->caps & AST_FAX_TECH_RECEIVE) {
472 t30_set_rx_file(t30_state, AST_LIST_FIRST(&details->documents)->filename, -1);
474 /* if not AST_FAX_TECH_RECEIVE, assume AST_FAX_TECH_SEND, this
475 * should be safe because we ensure either RECEIVE or SEND is
476 * indicated in spandsp_fax_new() */
477 t30_set_tx_file(t30_state, AST_LIST_FIRST(&details->documents)->filename, -1, -1);
481 static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details)
483 t30_set_ecm_capability(t30_state, details->option.ecm);
484 t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
487 static int spandsp_v21_new(struct spandsp_pvt *p)
489 /* XXX Here we use MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE even though
490 * we don't care about CED tones. Using MODEM_CONNECT_TONES_PREAMBLE
491 * doesn't seem to work right all the time.
493 p->tone_state = modem_connect_tones_rx_init(NULL, MODEM_CONNECT_TONES_FAX_CED_OR_PREAMBLE, spandsp_v21_tone, p);
494 if (!p->tone_state) {
501 static int spandsp_modems(struct ast_fax_session_details *details)
504 if (AST_FAX_MODEM_V17 & details->modems) {
505 modems |= T30_SUPPORT_V17;
507 if (AST_FAX_MODEM_V27TER & details->modems) {
508 modems |= T30_SUPPORT_V27TER;
510 if (AST_FAX_MODEM_V29 & details->modems) {
511 modems |= T30_SUPPORT_V29;
513 if (AST_FAX_MODEM_V34 & details->modems) {
514 #if defined(T30_SUPPORT_V34)
515 modems |= T30_SUPPORT_V34;
516 #elif defined(T30_SUPPORT_V34HDX)
517 modems |= T30_SUPPORT_V34HDX;
519 ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n");
526 /*! \brief create an instance of the spandsp tech_pvt for a fax session */
527 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token)
529 struct spandsp_pvt *p;
532 if ((!(p = ast_calloc(1, sizeof(*p))))) {
533 ast_log(LOG_ERROR, "Cannot initialize the spandsp private FAX technology structure.\n");
537 if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
538 if (spandsp_v21_new(p)) {
539 ast_log(LOG_ERROR, "Cannot initialize the spandsp private v21 technology structure.\n");
542 s->state = AST_FAX_STATE_ACTIVE;
546 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
547 s->state = AST_FAX_STATE_INITIALIZED;
551 AST_LIST_HEAD_INIT(&p->read_frames);
553 if (s->details->caps & AST_FAX_TECH_RECEIVE) {
555 } else if (s->details->caps & AST_FAX_TECH_SEND) {
558 ast_log(LOG_ERROR, "Are we sending or receiving? The FAX requirements (capabilities: 0x%X) were not properly set.\n", s->details->caps);
562 if (!(p->timer = ast_timer_open())) {
563 ast_log(LOG_ERROR, "Channel '%s' FAX session '%u' failed to create timing source.\n", s->channame, s->id);
567 s->fd = ast_timer_fd(p->timer);
569 p->stats = &spandsp_global_stats.g711;
571 if (s->details->caps & (AST_FAX_TECH_T38 | AST_FAX_TECH_AUDIO)) {
572 if ((s->details->caps & AST_FAX_TECH_AUDIO) == 0) {
573 /* audio mode was not requested, start in T.38 mode */
575 p->stats = &spandsp_global_stats.t38;
579 t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, s);
580 set_logging(&p->t38_state.logging, s->details);
582 /* init audio stuff */
583 fax_init(&p->fax_state, caller_mode);
584 set_logging(&p->fax_state.logging, s->details);
587 s->state = AST_FAX_STATE_INITIALIZED;
596 static void spandsp_v21_cleanup(struct ast_fax_session *s)
598 struct spandsp_pvt *p = s->tech_pvt;
600 modem_connect_tones_rx_free(p->tone_state);
603 /*! \brief Destroy a spandsp fax session.
605 static void spandsp_fax_destroy(struct ast_fax_session *s)
607 struct spandsp_pvt *p = s->tech_pvt;
609 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
610 spandsp_fax_gateway_cleanup(s);
611 } else if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
612 spandsp_v21_cleanup(s);
622 /*! \brief Read a frame from the spandsp fax stack.
624 static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s)
626 struct spandsp_pvt *p = s->tech_pvt;
627 uint8_t buffer[AST_FRIENDLY_OFFSET + SPANDSP_FAX_SAMPLES * sizeof(uint16_t)];
628 int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
631 struct ast_frame fax_frame = {
632 .frametype = AST_FRAME_VOICE,
633 .src = "res_fax_spandsp_g711",
634 .subclass.format = ast_format_slin,
636 struct ast_frame *f = &fax_frame;
638 if (ast_timer_ack(p->timer, 1) < 0) {
639 ast_log(LOG_ERROR, "Failed to acknowledge timer for FAX session '%u'\n", s->id);
643 /* XXX do we need to lock here? */
645 s->state = AST_FAX_STATE_COMPLETE;
646 ast_debug(5, "FAX session '%u' is complete.\n", s->id);
651 t38_terminal_send_timeout(&p->t38_state, SPANDSP_FAX_SAMPLES);
652 if ((f = AST_LIST_REMOVE_HEAD(&p->read_frames, frame_list))) {
656 if ((samples = fax_tx(&p->fax_state, buf, SPANDSP_FAX_SAMPLES)) > 0) {
657 f->samples = samples;
658 AST_FRAME_SET_BUFFER(f, buffer, AST_FRIENDLY_OFFSET, samples * sizeof(int16_t));
659 return ast_frisolate(f);
663 return &ast_null_frame;
666 static void spandsp_v21_tone(void *data, int code, int level, int delay)
668 struct spandsp_pvt *p = data;
670 if (code == MODEM_CONNECT_TONES_FAX_PREAMBLE) {
675 static int spandsp_v21_detect(struct ast_fax_session *s, const struct ast_frame *f)
677 struct spandsp_pvt *p = s->tech_pvt;
679 g711_state_t *decoder;
681 if (p->v21_detected) {
686 if (!f->data.ptr || !f->datalen) {
690 ast_debug(5, "frame={ datalen=%d, samples=%d, mallocd=%d, src=%s, flags=%u, ts=%ld, len=%ld, seqno=%d, data.ptr=%p, subclass.format=%s }\n", f->datalen, f->samples, f->mallocd, f->src, f->flags, f->ts, f->len, f->seqno, f->data.ptr, ast_format_get_name(f->subclass.format));
692 /* slinear frame can be passed to spandsp */
693 if (ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL) {
694 modem_connect_tones_rx(p->tone_state, f->data.ptr, f->samples);
696 /* alaw/ulaw frame must be converted to slinear before passing to spandsp */
697 } else if (ast_format_cmp(f->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL ||
698 ast_format_cmp(f->subclass.format, ast_format_ulaw) == AST_FORMAT_CMP_EQUAL) {
699 if (!(slndata = ast_malloc(sizeof(*slndata) * f->samples))) {
702 decoder = g711_init(NULL, (ast_format_cmp(f->subclass.format, ast_format_alaw) == AST_FORMAT_CMP_EQUAL ? G711_ALAW : G711_ULAW));
703 g711_decode(decoder, slndata, f->data.ptr, f->samples);
704 ast_debug(5, "spandsp transcoding frame from %s to slinear for v21 detection\n", ast_format_get_name(f->subclass.format));
705 modem_connect_tones_rx(p->tone_state, slndata, f->samples);
706 g711_release(decoder);
707 #if SPANDSP_RELEASE_DATE >= 20090220
712 /* frame in other formats cannot be passed to spandsp, it could cause segfault */
714 ast_log(LOG_WARNING, "Frame format %s not supported, v.21 detection skipped\n", ast_format_get_name(f->subclass.format));
718 if (p->v21_detected) {
719 s->details->option.v21_detected = 1;
720 ast_debug(5, "v.21 detected\n");
726 /*! \brief Write a frame to the spandsp fax stack.
727 * \param s a fax session
728 * \param f the frame to write
730 * \note res_fax does not currently use the return value of this function.
731 * Also the fax_rx() function never fails.
736 static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f)
738 struct spandsp_pvt *p = s->tech_pvt;
740 if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
741 return spandsp_v21_detect(s, f);
744 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
745 return spandsp_fax_gateway_process(s, f);
748 /* XXX do we need to lock here? */
749 if (s->state == AST_FAX_STATE_COMPLETE) {
750 ast_log(LOG_WARNING, "FAX session '%u' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
755 return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
757 return fax_rx(&p->fax_state, f->data.ptr, f->samples);
761 /*! \brief generate T.30 packets sent to the T.30 leg of gateway
762 * \param chan T.30 channel
763 * \param data fax session structure
764 * \param len not used
765 * \param samples no of samples generated
766 * \return -1 on failure or 0 on sucess*/
767 static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len, int samples)
770 struct ast_fax_session *s = data;
771 struct spandsp_pvt *p = s->tech_pvt;
772 uint8_t buffer[AST_FRIENDLY_OFFSET + samples * sizeof(uint16_t)];
774 struct ast_frame t30_frame = {
775 .frametype = AST_FRAME_VOICE,
776 .subclass.format = ast_format_slin,
777 .src = "res_fax_spandsp_g711",
779 .flags = AST_FAX_FRFLAG_GATEWAY,
782 AST_FRAME_SET_BUFFER(&t30_frame, buffer, AST_FRIENDLY_OFFSET, t30_frame.samples * sizeof(int16_t));
784 if (!(f = ast_frisolate(&t30_frame))) {
785 return p->isdone ? -1 : res;
788 /* generate a T.30 packet */
789 if ((f->samples = t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples))) {
790 f->datalen = f->samples * sizeof(int16_t);
791 res = ast_write(chan, f);
794 return p->isdone ? -1 : res;
797 /*! \brief simple routine to allocate data to generator
798 * \param chan channel
799 * \param params generator data
800 * \return data to use in generator call*/
801 static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params)
807 static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data)
812 /*! \brief activate a spandsp gateway based on the information in the given fax session
813 * \param s fax session
814 * \return -1 on error 0 on sucess*/
815 static int spandsp_fax_gateway_start(struct ast_fax_session *s)
817 struct spandsp_pvt *p = s->tech_pvt;
818 struct ast_fax_t38_parameters *t38_param;
820 RAII_VAR(struct ast_channel *, peer, NULL, ao2_cleanup);
821 static struct ast_generator t30_gen = {
822 .alloc = spandsp_fax_gw_gen_alloc,
823 .release = spandsp_fax_gw_gen_release,
824 .generate = spandsp_fax_gw_t30_gen,
827 #if SPANDSP_RELEASE_DATE >= 20081012
828 /* for spandsp shaphots 0.0.6 and higher */
829 p->t38_core_state=&p->t38_gw_state.t38x.t38;
831 /* for spandsp release 0.0.5 */
832 p->t38_core_state=&p->t38_gw_state.t38;
835 if (!t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, s)) {
840 p->ast_t38_state = ast_channel_get_t38_state(s->chan);
841 peer = ast_channel_bridge_peer(s->chan);
846 /* we can be in T38_STATE_NEGOTIATING or T38_STATE_NEGOTIATED when the
847 * gateway is started. We treat both states the same. */
848 if (p->ast_t38_state == T38_STATE_NEGOTIATING) {
849 p->ast_t38_state = T38_STATE_NEGOTIATED;
852 ast_activate_generator(p->ast_t38_state == T38_STATE_NEGOTIATED ? peer : s->chan, &t30_gen , s);
854 set_logging(&p->t38_gw_state.logging, s->details);
855 set_logging(&p->t38_core_state->logging, s->details);
857 t38_param = (p->ast_t38_state == T38_STATE_NEGOTIATED) ? &s->details->our_t38_parameters : &s->details->their_t38_parameters;
858 t38_set_t38_version(p->t38_core_state, t38_param->version);
859 t38_gateway_set_ecm_capability(&p->t38_gw_state, s->details->option.ecm);
860 t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp);
861 t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal);
862 t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr);
863 t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig);
864 t38_set_data_rate_management_method(p->t38_core_state,
865 (t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2);
867 t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE);
868 t38_set_sequence_number_handling(p->t38_core_state, TRUE);
871 t38_gateway_set_supported_modems(&p->t38_gw_state, spandsp_modems(s->details));
873 /* engage udptl nat on other side of T38 line
874 * (Asterisk changes media ports thus we send a few packets to reinitialize
875 * pinholes in NATs and FWs
877 for (i=0; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) {
878 #if SPANDSP_RELEASE_DATE >= 20091228
879 t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL);
880 #elif SPANDSP_RELEASE_DATE >= 20081012
881 t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38x.t38.indicator_tx_count);
883 t38_core_send_indicator(&p->t38_gw_state.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38.indicator_tx_count);
887 s->state = AST_FAX_STATE_ACTIVE;
892 /*! \brief process a frame from the bridge
893 * \param s fax session
894 * \param f frame to process
895 * \return 1 on sucess 0 on incorect packet*/
896 static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f)
898 struct spandsp_pvt *p = s->tech_pvt;
901 if (!f->data.ptr || !f->datalen) {
905 /* Process a IFP packet */
906 if ((f->frametype == AST_FRAME_MODEM) && (f->subclass.integer == AST_MODEM_T38)) {
907 return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
908 } else if ((f->frametype == AST_FRAME_VOICE) &&
909 (ast_format_cmp(f->subclass.format, ast_format_slin) == AST_FORMAT_CMP_EQUAL)) {
910 return t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples);
916 /*! \brief gather data and clean up after gateway ends
917 * \param s fax session*/
918 static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s)
920 struct spandsp_pvt *p = s->tech_pvt;
921 t38_stats_t t38_stats;
923 t38_gateway_get_transfer_statistics(&p->t38_gw_state, &t38_stats);
925 s->details->option.ecm = t38_stats.error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE;
926 s->details->pages_transferred = t38_stats.pages_transferred;
927 ast_string_field_build(s->details, transfer_rate, "%d", t38_stats.bit_rate);
931 static int spandsp_fax_start(struct ast_fax_session *s)
933 struct spandsp_pvt *p = s->tech_pvt;
935 s->state = AST_FAX_STATE_OPEN;
937 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
938 return spandsp_fax_gateway_start(s);
942 #if SPANDSP_RELEASE_DATE >= 20080725
943 /* for spandsp shaphots 0.0.6 and higher */
944 p->t30_state = &p->t38_state.t30;
945 p->t38_core_state = &p->t38_state.t38_fe.t38;
947 /* for spandsp releases 0.0.5 */
948 p->t30_state = &p->t38_state.t30_state;
949 p->t38_core_state = &p->t38_state.t38;
952 #if SPANDSP_RELEASE_DATE >= 20080725
953 /* for spandsp shaphots 0.0.6 and higher */
954 p->t30_state = &p->fax_state.t30;
956 /* for spandsp release 0.0.5 */
957 p->t30_state = &p->fax_state.t30_state;
961 set_logging(&p->t30_state->logging, s->details);
963 /* set some parameters */
964 set_local_info(p->t30_state, s->details);
965 set_file(p->t30_state, s->details);
966 set_ecm(p->t30_state, s->details);
967 t30_set_supported_modems(p->t30_state, spandsp_modems(s->details));
969 /* perhaps set_transmit_on_idle() should be called */
971 t30_set_phase_e_handler(p->t30_state, t30_phase_e_handler, s);
973 /* set T.38 parameters */
975 set_logging(&p->t38_core_state->logging, s->details);
977 t38_set_max_datagram_size(p->t38_core_state, s->details->their_t38_parameters.max_ifp);
979 if (s->details->their_t38_parameters.fill_bit_removal) {
980 t38_set_fill_bit_removal(p->t38_core_state, TRUE);
983 if (s->details->their_t38_parameters.transcoding_mmr) {
984 t38_set_mmr_transcoding(p->t38_core_state, TRUE);
987 if (s->details->their_t38_parameters.transcoding_jbig) {
988 t38_set_jbig_transcoding(p->t38_core_state, TRUE);
991 /* have the fax stack generate silence if it has no data to send */
992 fax_set_transmit_on_idle(&p->fax_state, 1);
996 /* start the timer */
997 if (ast_timer_set_rate(p->timer, SPANDSP_FAX_TIMER_RATE)) {
998 ast_log(LOG_ERROR, "FAX session '%u' error setting rate on timing source.\n", s->id);
1002 s->state = AST_FAX_STATE_ACTIVE;
1008 static int spandsp_fax_cancel(struct ast_fax_session *s)
1010 struct spandsp_pvt *p = s->tech_pvt;
1012 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
1017 t30_terminate(p->t30_state);
1023 static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
1025 struct spandsp_pvt *p = s->tech_pvt;
1027 /* prevent the phase E handler from running, this is not a real termination */
1028 t30_set_phase_e_handler(p->t30_state, NULL, NULL);
1030 t30_terminate(p->t30_state);
1032 s->details->option.switch_to_t38 = 1;
1033 ast_atomic_fetchadd_int(&p->stats->switched, 1);
1036 p->stats = &spandsp_global_stats.t38;
1037 spandsp_fax_start(s);
1043 static char *spandsp_fax_cli_show_capabilities(int fd)
1045 ast_cli(fd, "SEND RECEIVE T.38 G.711 GATEWAY\n\n");
1050 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
1053 if (s->details->caps & AST_FAX_TECH_GATEWAY) {
1054 struct spandsp_pvt *p = s->tech_pvt;
1056 ast_cli(fd, "%-22s : %u\n", "session", s->id);
1057 ast_cli(fd, "%-22s : %s\n", "operation", "Gateway");
1058 ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
1059 if (s->state != AST_FAX_STATE_UNINITIALIZED) {
1061 t38_gateway_get_transfer_statistics(&p->t38_gw_state, &stats);
1062 ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
1063 ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
1064 ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
1066 } else if (s->details->caps & AST_FAX_TECH_V21_DETECT) {
1067 ast_cli(fd, "%-22s : %u\n", "session", s->id);
1068 ast_cli(fd, "%-22s : %s\n", "operation", "V.21 Detect");
1069 ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
1071 struct spandsp_pvt *p = s->tech_pvt;
1073 ast_cli(fd, "%-22s : %u\n", "session", s->id);
1074 ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
1075 ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
1076 if (s->state != AST_FAX_STATE_UNINITIALIZED) {
1078 t30_get_transfer_statistics(p->t30_state, &stats);
1079 ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
1080 ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
1081 ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
1082 ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
1083 #if SPANDSP_RELEASE_DATE >= 20090220
1084 ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
1086 ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
1088 ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
1090 ast_cli(fd, "\nData Statistics:\n");
1091 #if SPANDSP_RELEASE_DATE >= 20090220
1092 ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
1093 ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
1095 ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
1096 ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
1098 ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
1099 ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
1103 ast_cli(fd, "\n\n");
1107 static void spandsp_manager_fax_session(struct mansession *s,
1108 const char *id_text, struct ast_fax_session *session)
1110 struct ast_str *message_string;
1111 struct spandsp_pvt *span_pvt = session->tech_pvt;
1114 message_string = ast_str_create(128);
1116 if (!message_string) {
1121 res = ast_str_append(&message_string, 0, "SessionNumber: %u\r\n", session->id);
1122 res |= ast_str_append(&message_string, 0, "Operation: %s\r\n", ast_fax_session_operation_str(session));
1123 res |= ast_str_append(&message_string, 0, "State: %s\r\n", ast_fax_state_to_str(session->state));
1125 if (session->details->caps & AST_FAX_TECH_GATEWAY) {
1128 if (session->state == AST_FAX_STATE_UNINITIALIZED) {
1129 goto skip_cap_additions;
1132 t38_gateway_get_transfer_statistics(&span_pvt->t38_gw_state, &stats);
1133 res |= ast_str_append(&message_string, 0, "ErrorCorrectionMode: %s\r\n",
1134 stats.error_correcting_mode ? "yes" : "no");
1135 res |= ast_str_append(&message_string, 0, "DataRate: %d\r\n",
1137 res |= ast_str_append(&message_string, 0, "PageNumber: %d\r\n",
1138 stats.pages_transferred + 1);
1139 } else if (!(session->details->caps & AST_FAX_TECH_V21_DETECT)) { /* caps is SEND/RECEIVE */
1142 if (session->state == AST_FAX_STATE_UNINITIALIZED) {
1143 goto skip_cap_additions;
1146 t30_get_transfer_statistics(span_pvt->t30_state, &stats);
1147 res |= ast_str_append(&message_string, 0, "ErrorCorrectionMode: %s\r\n",
1148 stats.error_correcting_mode ? "Yes" : "No");
1149 res |= ast_str_append(&message_string, 0, "DataRate: %d\r\n",
1151 res |= ast_str_append(&message_string, 0, "ImageResolution: %dx%d\r\n",
1152 stats.x_resolution, stats.y_resolution);
1153 #if SPANDSP_RELEASE_DATE >= 20090220
1154 res |= ast_str_append(&message_string, 0, "PageNumber: %d\r\n",
1155 ((session->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
1157 res |= ast_str_append(&message_string, 0, "PageNumber: %d\r\n",
1158 stats.pages_transferred + 1);
1160 res |= ast_str_append(&message_string, 0, "FileName: %s\r\n",
1161 session->details->caps & AST_FAX_TECH_RECEIVE ? span_pvt->t30_state->rx_file :
1162 span_pvt->t30_state->tx_file);
1163 #if SPANDSP_RELEASE_DATE >= 20090220
1164 res |= ast_str_append(&message_string, 0, "PagesTransmitted: %d\r\n",
1166 res |= ast_str_append(&message_string, 0, "PagesReceived: %d\r\n",
1169 res |= ast_str_append(&message_string, 0, "PagesTransmitted: %d\r\n",
1170 (session->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
1171 res |= ast_str_append(&message_string, 0, "PagesReceived: %d\r\n",
1172 (session->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
1174 res |= ast_str_append(&message_string, 0, "TotalBadLines: %d\r\n",
1180 ao2_unlock(session);
1183 /* One or more of the ast_str_append attempts failed, cancel the message */
1184 ast_free(message_string);
1188 astman_append(s, "Event: FAXSession\r\n"
1193 ast_str_buffer(message_string));
1195 ast_free(message_string);
1199 static char *spandsp_fax_cli_show_stats(int fd)
1201 ast_mutex_lock(&spandsp_global_stats.lock);
1202 ast_cli(fd, "\n%-20.20s\n", "Spandsp G.711");
1203 ast_cli(fd, "%-20.20s : %d\n", "Success", spandsp_global_stats.g711.success);
1204 ast_cli(fd, "%-20.20s : %d\n", "Switched to T.38", spandsp_global_stats.g711.switched);
1205 ast_cli(fd, "%-20.20s : %d\n", "Call Dropped", spandsp_global_stats.g711.call_dropped);
1206 ast_cli(fd, "%-20.20s : %d\n", "No FAX", spandsp_global_stats.g711.nofax);
1207 ast_cli(fd, "%-20.20s : %d\n", "Negotiation Failed", spandsp_global_stats.g711.neg_failed);
1208 ast_cli(fd, "%-20.20s : %d\n", "Train Failure", spandsp_global_stats.g711.failed_to_train);
1209 ast_cli(fd, "%-20.20s : %d\n", "Retries Exceeded", spandsp_global_stats.g711.retries_exceeded);
1210 ast_cli(fd, "%-20.20s : %d\n", "Protocol Error", spandsp_global_stats.g711.protocol_error);
1211 ast_cli(fd, "%-20.20s : %d\n", "TX Protocol Error", spandsp_global_stats.g711.tx_protocol_error);
1212 ast_cli(fd, "%-20.20s : %d\n", "RX Protocol Error", spandsp_global_stats.g711.rx_protocol_error);
1213 ast_cli(fd, "%-20.20s : %d\n", "File Error", spandsp_global_stats.g711.file_error);
1214 ast_cli(fd, "%-20.20s : %d\n", "Memory Error", spandsp_global_stats.g711.mem_error);
1215 ast_cli(fd, "%-20.20s : %d\n", "Unknown Error", spandsp_global_stats.g711.unknown_error);
1217 ast_cli(fd, "\n%-20.20s\n", "Spandsp T.38");
1218 ast_cli(fd, "%-20.20s : %d\n", "Success", spandsp_global_stats.t38.success);
1219 ast_cli(fd, "%-20.20s : %d\n", "Call Dropped", spandsp_global_stats.t38.call_dropped);
1220 ast_cli(fd, "%-20.20s : %d\n", "No FAX", spandsp_global_stats.t38.nofax);
1221 ast_cli(fd, "%-20.20s : %d\n", "Negotiation Failed", spandsp_global_stats.t38.neg_failed);
1222 ast_cli(fd, "%-20.20s : %d\n", "Train Failure", spandsp_global_stats.t38.failed_to_train);
1223 ast_cli(fd, "%-20.20s : %d\n", "Retries Exceeded", spandsp_global_stats.t38.retries_exceeded);
1224 ast_cli(fd, "%-20.20s : %d\n", "Protocol Error", spandsp_global_stats.t38.protocol_error);
1225 ast_cli(fd, "%-20.20s : %d\n", "TX Protocol Error", spandsp_global_stats.t38.tx_protocol_error);
1226 ast_cli(fd, "%-20.20s : %d\n", "RX Protocol Error", spandsp_global_stats.t38.rx_protocol_error);
1227 ast_cli(fd, "%-20.20s : %d\n", "File Error", spandsp_global_stats.t38.file_error);
1228 ast_cli(fd, "%-20.20s : %d\n", "Memory Error", spandsp_global_stats.t38.mem_error);
1229 ast_cli(fd, "%-20.20s : %d\n", "Unknown Error", spandsp_global_stats.t38.unknown_error);
1230 ast_mutex_unlock(&spandsp_global_stats.lock);
1235 /*! \brief Show res_fax_spandsp settings */
1236 static char *spandsp_fax_cli_show_settings(int fd)
1238 /* no settings at the moment */
1242 /*! \brief unload res_fax_spandsp */
1243 static int unload_module(void)
1245 ast_fax_tech_unregister(&spandsp_fax_tech);
1246 ast_mutex_destroy(&spandsp_global_stats.lock);
1247 return AST_MODULE_LOAD_SUCCESS;
1250 /*! \brief load res_fax_spandsp */
1251 static int load_module(void)
1253 ast_mutex_init(&spandsp_global_stats.lock);
1254 spandsp_fax_tech.module = ast_module_info->self;
1255 if (ast_fax_tech_register(&spandsp_fax_tech) < 0) {
1256 ast_log(LOG_ERROR, "failed to register FAX technology\n");
1257 return AST_MODULE_LOAD_DECLINE;
1260 /* prevent logging to stderr */
1261 span_set_message_handler(NULL);
1263 return AST_MODULE_LOAD_SUCCESS;
1267 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Spandsp G.711 and T.38 FAX Technologies",
1268 .support_level = AST_MODULE_SUPPORT_EXTENDED,
1269 .load = load_module,
1270 .unload = unload_module,