Tue Aug 20 16:34:37 2013

Asterisk developer's documentation


res_fax_spandsp.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009-2010, Digium, Inc.
00005  *
00006  * Matthew Nicholson <mnicholson@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Spandsp T.38 and G.711 FAX Resource
00022  *
00023  * \author Matthew Nicholson <mnicholson@digium.com>
00024  *
00025  * This module registers the Spandsp FAX technology with the res_fax module.
00026  */
00027 
00028 /*** MODULEINFO
00029    <depend>spandsp</depend>
00030    <depend>res_fax</depend>
00031    <support_level>extended</support_level>
00032 ***/
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 375893 $")
00037 
00038 #define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
00039 #include <spandsp.h>
00040 #include <spandsp/version.h>
00041 
00042 #include "asterisk/logger.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/strings.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/timing.h"
00048 #include "asterisk/astobj2.h"
00049 #include "asterisk/res_fax.h"
00050 
00051 #define SPANDSP_FAX_SAMPLES 160
00052 #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES   /* 50 ticks per second, 20ms, 160 samples per second */
00053 
00054 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
00055 static void spandsp_fax_destroy(struct ast_fax_session *s);
00056 static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s);
00057 static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f);
00058 static int spandsp_fax_start(struct ast_fax_session *s);
00059 static int spandsp_fax_cancel(struct ast_fax_session *s);
00060 static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
00061 
00062 static char *spandsp_fax_cli_show_capabilities(int fd);
00063 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
00064 static char *spandsp_fax_cli_show_stats(int fd);
00065 static char *spandsp_fax_cli_show_settings(int fd);
00066 
00067 static struct ast_fax_tech spandsp_fax_tech = {
00068    .type = "Spandsp",
00069    .description = "Spandsp FAX Driver",
00070 #if SPANDSP_RELEASE_DATE >= 20090220
00071    /* spandsp 0.0.6 */
00072    .version = SPANDSP_RELEASE_DATETIME_STRING,
00073 #else
00074    /* spandsp 0.0.5
00075     * TODO: maybe we should determine the version better way
00076     */
00077    .version = "pre-20090220",
00078 #endif
00079    .caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,
00080    .new_session = spandsp_fax_new,
00081    .destroy_session = spandsp_fax_destroy,
00082    .read = spandsp_fax_read,
00083    .write = spandsp_fax_write,
00084    .start_session = spandsp_fax_start,
00085    .cancel_session = spandsp_fax_cancel,
00086    .switch_to_t38 = spandsp_fax_switch_to_t38,
00087    .cli_show_capabilities = spandsp_fax_cli_show_capabilities,
00088    .cli_show_session = spandsp_fax_cli_show_session,
00089    .cli_show_stats = spandsp_fax_cli_show_stats,
00090    .cli_show_settings = spandsp_fax_cli_show_settings,
00091 };
00092 
00093 struct spandsp_fax_stats {
00094    int success;
00095    int nofax;
00096    int neg_failed;
00097    int failed_to_train;
00098    int rx_protocol_error;
00099    int tx_protocol_error;
00100    int protocol_error;
00101    int retries_exceeded;
00102    int file_error;
00103    int mem_error;
00104    int call_dropped;
00105    int unknown_error;
00106    int switched;
00107 };
00108 
00109 static struct {
00110    ast_mutex_t lock;
00111    struct spandsp_fax_stats g711;
00112    struct spandsp_fax_stats t38;
00113 } spandsp_global_stats;
00114 
00115 struct spandsp_pvt {
00116    unsigned int ist38:1;
00117    unsigned int isdone:1;
00118    fax_state_t fax_state;
00119    t38_terminal_state_t t38_state;
00120    t30_state_t *t30_state;
00121    t38_core_state_t *t38_core_state;
00122 
00123    struct spandsp_fax_stats *stats;
00124 
00125    struct ast_timer *timer;
00126    AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
00127 };
00128 
00129 static void session_destroy(struct spandsp_pvt *p);
00130 static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count);
00131 static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code);
00132 static void spandsp_log(int level, const char *msg);
00133 static int update_stats(struct spandsp_pvt *p, int completion_code);
00134 static int spandsp_modems(struct ast_fax_session_details *details);
00135 
00136 static void set_logging(logging_state_t *state, struct ast_fax_session_details *details);
00137 static void set_local_info(t30_state_t *t30_state, struct ast_fax_session_details *details);
00138 static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *details);
00139 static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details);
00140 
00141 static void session_destroy(struct spandsp_pvt *p)
00142 {
00143    struct ast_frame *f;
00144 
00145    t30_terminate(p->t30_state);
00146    p->isdone = 1;
00147 
00148    ast_timer_close(p->timer);
00149    p->timer = NULL;
00150    fax_release(&p->fax_state);
00151    t38_terminal_release(&p->t38_state);
00152 
00153    while ((f = AST_LIST_REMOVE_HEAD(&p->read_frames, frame_list))) {
00154       ast_frfree(f);
00155    }
00156 }
00157 
00158 /*! \brief
00159  *
00160  */
00161 static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
00162 {
00163    struct spandsp_pvt *p = data;
00164    struct ast_frame fax_frame = {
00165       .frametype = AST_FRAME_MODEM,
00166       .subclass.integer = AST_MODEM_T38,
00167       .src = "res_fax_spandsp_t38",
00168    };
00169 
00170    struct ast_frame *f = &fax_frame;
00171 
00172 
00173    /* TODO: Asterisk does not provide means of resending the same packet multiple
00174      times so count is ignored at the moment */
00175 
00176    AST_FRAME_SET_BUFFER(f, buf, 0, len);
00177 
00178    if (!(f = ast_frisolate(f))) {
00179       return -1;
00180    }
00181 
00182    /* no need to lock, this all runs in the same thread */
00183    AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
00184 
00185    return 0;
00186 }
00187 
00188 static int update_stats(struct spandsp_pvt *p, int completion_code)
00189 {
00190    switch (completion_code) {
00191    case T30_ERR_OK:
00192       ast_atomic_fetchadd_int(&p->stats->success, 1);
00193       break;
00194 
00195    /* Link problems */
00196    case T30_ERR_CEDTONE:            /*! The CED tone exceeded 5s */
00197    case T30_ERR_T0_EXPIRED:         /*! Timed out waiting for initial communication */
00198    case T30_ERR_T1_EXPIRED:         /*! Timed out waiting for the first message */
00199    case T30_ERR_T3_EXPIRED:         /*! Timed out waiting for procedural interrupt */
00200    case T30_ERR_HDLC_CARRIER:       /*! The HDLC carrier did not stop in a timely manner */
00201    case T30_ERR_CANNOT_TRAIN:       /*! Failed to train with any of the compatible modems */
00202       ast_atomic_fetchadd_int(&p->stats->failed_to_train, 1);
00203       break;
00204 
00205    case T30_ERR_OPER_INT_FAIL:      /*! Operator intervention failed */
00206    case T30_ERR_INCOMPATIBLE:       /*! Far end is not compatible */
00207    case T30_ERR_RX_INCAPABLE:       /*! Far end is not able to receive */
00208    case T30_ERR_TX_INCAPABLE:       /*! Far end is not able to transmit */
00209    case T30_ERR_NORESSUPPORT:       /*! Far end cannot receive at the resolution of the image */
00210    case T30_ERR_NOSIZESUPPORT:      /*! Far end cannot receive at the size of image */
00211       ast_atomic_fetchadd_int(&p->stats->neg_failed, 1);
00212       break;
00213 
00214    case T30_ERR_UNEXPECTED:         /*! Unexpected message received */
00215       ast_atomic_fetchadd_int(&p->stats->protocol_error, 1);
00216       break;
00217 
00218    /* Phase E status values returned to a transmitter */
00219    case T30_ERR_TX_BADDCS:          /*! Received bad response to DCS or training */
00220    case T30_ERR_TX_BADPG:           /*! Received a DCN from remote after sending a page */
00221    case T30_ERR_TX_ECMPHD:          /*! Invalid ECM response received from receiver */
00222    case T30_ERR_TX_GOTDCN:          /*! Received a DCN while waiting for a DIS */
00223    case T30_ERR_TX_INVALRSP:        /*! Invalid response after sending a page */
00224    case T30_ERR_TX_NODIS:           /*! Received other than DIS while waiting for DIS */
00225    case T30_ERR_TX_PHBDEAD:         /*! Received no response to DCS, training or TCF */
00226    case T30_ERR_TX_PHDDEAD:         /*! No response after sending a page */
00227    case T30_ERR_TX_T5EXP:           /*! Timed out waiting for receiver ready (ECM mode) */
00228       ast_atomic_fetchadd_int(&p->stats->tx_protocol_error, 1);
00229       break;
00230 
00231    /* Phase E status values returned to a receiver */
00232    case T30_ERR_RX_ECMPHD:          /*! Invalid ECM response received from transmitter */
00233    case T30_ERR_RX_GOTDCS:          /*! DCS received while waiting for DTC */
00234    case T30_ERR_RX_INVALCMD:        /*! Unexpected command after page received */
00235    case T30_ERR_RX_NOCARRIER:       /*! Carrier lost during fax receive */
00236    case T30_ERR_RX_NOEOL:           /*! Timed out while waiting for EOL (end Of line) */
00237       ast_atomic_fetchadd_int(&p->stats->rx_protocol_error, 1);
00238       break;
00239    case T30_ERR_RX_NOFAX:           /*! Timed out while waiting for first line */
00240       ast_atomic_fetchadd_int(&p->stats->nofax, 1);
00241       break;
00242    case T30_ERR_RX_T2EXPDCN:        /*! Timer T2 expired while waiting for DCN */
00243    case T30_ERR_RX_T2EXPD:          /*! Timer T2 expired while waiting for phase D */
00244    case T30_ERR_RX_T2EXPFAX:        /*! Timer T2 expired while waiting for fax page */
00245    case T30_ERR_RX_T2EXPMPS:        /*! Timer T2 expired while waiting for next fax page */
00246    case T30_ERR_RX_T2EXPRR:         /*! Timer T2 expired while waiting for RR command */
00247    case T30_ERR_RX_T2EXP:           /*! Timer T2 expired while waiting for NSS, DCS or MCF */
00248    case T30_ERR_RX_DCNWHY:          /*! Unexpected DCN while waiting for DCS or DIS */
00249    case T30_ERR_RX_DCNDATA:         /*! Unexpected DCN while waiting for image data */
00250    case T30_ERR_RX_DCNFAX:          /*! Unexpected DCN while waiting for EOM, EOP or MPS */
00251    case T30_ERR_RX_DCNPHD:          /*! Unexpected DCN after EOM or MPS sequence */
00252    case T30_ERR_RX_DCNRRD:          /*! Unexpected DCN after RR/RNR sequence */
00253    case T30_ERR_RX_DCNNORTN:        /*! Unexpected DCN after requested retransmission */
00254       ast_atomic_fetchadd_int(&p->stats->rx_protocol_error, 1);
00255       break;
00256 
00257    /* TIFF file problems */
00258    case T30_ERR_FILEERROR:          /*! TIFF/F file cannot be opened */
00259    case T30_ERR_NOPAGE:             /*! TIFF/F page not found */
00260    case T30_ERR_BADTIFF:            /*! TIFF/F format is not compatible */
00261    case T30_ERR_BADPAGE:            /*! TIFF/F page number tag missing */
00262    case T30_ERR_BADTAG:             /*! Incorrect values for TIFF/F tags */
00263    case T30_ERR_BADTIFFHDR:         /*! Bad TIFF/F header - incorrect values in fields */
00264       ast_atomic_fetchadd_int(&p->stats->file_error, 1);
00265       break;
00266    case T30_ERR_NOMEM:              /*! Cannot allocate memory for more pages */
00267       ast_atomic_fetchadd_int(&p->stats->mem_error, 1);
00268       break;
00269 
00270    /* General problems */
00271    case T30_ERR_RETRYDCN:           /*! Disconnected after permitted retries */
00272       ast_atomic_fetchadd_int(&p->stats->retries_exceeded, 1);
00273       break;
00274    case T30_ERR_CALLDROPPED:        /*! The call dropped prematurely */
00275       ast_atomic_fetchadd_int(&p->stats->call_dropped, 1);
00276       break;
00277 
00278    /* Feature negotiation issues */
00279    case T30_ERR_NOPOLL:             /*! Poll not accepted */
00280    case T30_ERR_IDENT_UNACCEPTABLE: /*! Far end's ident is not acceptable */
00281    case T30_ERR_SUB_UNACCEPTABLE:   /*! Far end's sub-address is not acceptable */
00282    case T30_ERR_SEP_UNACCEPTABLE:   /*! Far end's selective polling address is not acceptable */
00283    case T30_ERR_PSA_UNACCEPTABLE:   /*! Far end's polled sub-address is not acceptable */
00284    case T30_ERR_SID_UNACCEPTABLE:   /*! Far end's sender identification is not acceptable */
00285    case T30_ERR_PWD_UNACCEPTABLE:   /*! Far end's password is not acceptable */
00286    case T30_ERR_TSA_UNACCEPTABLE:   /*! Far end's transmitting subscriber internet address is not acceptable */
00287    case T30_ERR_IRA_UNACCEPTABLE:   /*! Far end's internet routing address is not acceptable */
00288    case T30_ERR_CIA_UNACCEPTABLE:   /*! Far end's calling subscriber internet address is not acceptable */
00289    case T30_ERR_ISP_UNACCEPTABLE:   /*! Far end's internet selective polling address is not acceptable */
00290    case T30_ERR_CSA_UNACCEPTABLE:   /*! Far end's called subscriber internet address is not acceptable */
00291       ast_atomic_fetchadd_int(&p->stats->neg_failed, 1);
00292       break;
00293    default:
00294       ast_atomic_fetchadd_int(&p->stats->unknown_error, 1);
00295       ast_log(LOG_WARNING, "unknown FAX session result '%d' (%s)\n", completion_code, t30_completion_code_to_str(completion_code));
00296       return -1;
00297    }
00298    return 0;
00299 }
00300 
00301 /*! \brief Phase E handler callback.
00302  * \param t30_state the span t30 state
00303  * \param data this will be the ast_fax_session
00304  * \param completion_code the result of the fax session
00305  *
00306  * This function pulls stats from the spandsp stack and stores them for res_fax
00307  * to use later.
00308  */
00309 static void t30_phase_e_handler(t30_state_t *t30_state, void *data, int completion_code)
00310 {
00311    struct ast_fax_session *s = data;
00312    struct spandsp_pvt *p = s->tech_pvt;
00313    char headerinfo[T30_MAX_PAGE_HEADER_INFO + 1];
00314    const char *c;
00315    t30_stats_t stats;
00316 
00317    ast_debug(5, "FAX session '%d' entering phase E\n", s->id);
00318 
00319    p->isdone = 1;
00320 
00321    update_stats(p, completion_code);
00322 
00323    t30_get_transfer_statistics(t30_state, &stats);
00324 
00325    if (completion_code == T30_ERR_OK) {
00326       ast_string_field_set(s->details, result, "SUCCESS");
00327    } else {
00328       ast_string_field_set(s->details, result, "FAILED");
00329       ast_string_field_set(s->details, error, t30_completion_code_to_str(completion_code));
00330    }
00331 
00332    ast_string_field_set(s->details, resultstr, t30_completion_code_to_str(completion_code));
00333 
00334    ast_debug(5, "FAX session '%d' completed with result: %s (%s)\n", s->id, s->details->result, s->details->resultstr);
00335 
00336    if ((c = t30_get_tx_ident(t30_state))) {
00337       ast_string_field_set(s->details, localstationid, c);
00338    }
00339 
00340    if ((c = t30_get_rx_ident(t30_state))) {
00341       ast_string_field_set(s->details, remotestationid, c);
00342    }
00343 
00344 #if SPANDSP_RELEASE_DATE >= 20090220
00345    s->details->pages_transferred = (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx;
00346 #else
00347    s->details->pages_transferred = stats.pages_transferred;
00348 #endif
00349 
00350    ast_string_field_build(s->details, transfer_rate, "%d", stats.bit_rate);
00351 
00352    ast_string_field_build(s->details, resolution, "%dx%d", stats.x_resolution, stats.y_resolution);
00353 
00354    t30_get_tx_page_header_info(t30_state, headerinfo);
00355    ast_string_field_set(s->details, headerinfo, headerinfo);
00356 }
00357 
00358 /*! \brief Send spandsp log messages to asterisk.
00359  * \param level the spandsp logging level
00360  * \param msg the log message
00361  *
00362  * \note This function is a callback function called by spandsp.
00363  */
00364 static void spandsp_log(int level, const char *msg)
00365 {
00366    if (level == SPAN_LOG_ERROR) {
00367       ast_log(LOG_ERROR, "%s", msg);
00368    } else if (level == SPAN_LOG_WARNING) {
00369       ast_log(LOG_WARNING, "%s", msg);
00370    } else {
00371       ast_fax_log(LOG_DEBUG, msg);
00372    }
00373 }
00374 
00375 static void set_logging(logging_state_t *state, struct ast_fax_session_details *details)
00376 {
00377    int level = SPAN_LOG_WARNING;
00378 
00379         if (details->option.debug) {
00380       level = SPAN_LOG_DEBUG_3;
00381    }
00382 
00383    span_log_set_message_handler(state, spandsp_log);
00384    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level);
00385 }
00386 
00387 static void set_local_info(t30_state_t *t30_state, struct ast_fax_session_details *details)
00388 {
00389    if (!ast_strlen_zero(details->localstationid)) {
00390       t30_set_tx_ident(t30_state, details->localstationid);
00391    }
00392 
00393    if (!ast_strlen_zero(details->headerinfo)) {
00394       t30_set_tx_page_header_info(t30_state, details->headerinfo);
00395    }
00396 }
00397 
00398 static void set_file(t30_state_t *t30_state, struct ast_fax_session_details *details)
00399 {
00400    if (details->caps & AST_FAX_TECH_RECEIVE) {
00401       t30_set_rx_file(t30_state, AST_LIST_FIRST(&details->documents)->filename, -1);
00402    } else {
00403       /* if not AST_FAX_TECH_RECEIVE, assume AST_FAX_TECH_SEND, this
00404        * should be safe because we ensure either RECEIVE or SEND is
00405        * indicated in spandsp_fax_new() */
00406       t30_set_tx_file(t30_state, AST_LIST_FIRST(&details->documents)->filename, -1, -1);
00407    }
00408 }
00409 
00410 static void set_ecm(t30_state_t *t30_state, struct ast_fax_session_details *details)
00411 {
00412    t30_set_ecm_capability(t30_state, details->option.ecm);
00413    t30_set_supported_compressions(t30_state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00414 }
00415 
00416 static int spandsp_modems(struct ast_fax_session_details *details)
00417 {
00418    int modems = 0;
00419    if (AST_FAX_MODEM_V17 & details->modems) {
00420       modems |= T30_SUPPORT_V17;
00421    }
00422    if (AST_FAX_MODEM_V27 & details->modems) {
00423       modems |= T30_SUPPORT_V27TER;
00424    }
00425    if (AST_FAX_MODEM_V29 & details->modems) {
00426       modems |= T30_SUPPORT_V29;
00427    }
00428    if (AST_FAX_MODEM_V34 & details->modems) {
00429 #if defined(T30_SUPPORT_V34)
00430       modems |= T30_SUPPORT_V34;
00431 #elif defined(T30_SUPPORT_V34HDX)
00432       modems |= T30_SUPPORT_V34HDX;
00433 #else
00434       ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n");
00435 #endif
00436    }
00437 
00438    return modems;
00439 }
00440 
00441 /*! \brief create an instance of the spandsp tech_pvt for a fax session */
00442 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token)
00443 {
00444    struct spandsp_pvt *p;
00445    int caller_mode;
00446 
00447    if ((!(p = ast_calloc(1, sizeof(*p))))) {
00448       ast_log(LOG_ERROR, "Cannot initialize the spandsp private FAX technology structure.\n");
00449       goto e_return;
00450    }
00451 
00452    AST_LIST_HEAD_INIT(&p->read_frames);
00453 
00454    if (s->details->caps & AST_FAX_TECH_RECEIVE) {
00455       caller_mode = 0;
00456    } else if (s->details->caps & AST_FAX_TECH_SEND) {
00457       caller_mode = 1;
00458    } else {
00459       ast_log(LOG_ERROR, "Are we sending or receiving? The FAX requirements (capabilities: 0x%X) were not properly set.\n", s->details->caps);
00460       goto e_free;
00461    }
00462 
00463    if (!(p->timer = ast_timer_open())) {
00464       ast_log(LOG_ERROR, "Channel '%s' FAX session '%d' failed to create timing source.\n", s->channame, s->id);
00465       goto e_free;
00466    }
00467 
00468    s->fd = ast_timer_fd(p->timer);
00469 
00470    p->stats = &spandsp_global_stats.g711;
00471 
00472    if (s->details->caps & AST_FAX_TECH_T38) {
00473       if ((s->details->caps & AST_FAX_TECH_AUDIO) == 0) {
00474          /* audio mode was not requested, start in T.38 mode */
00475          p->ist38 = 1;
00476          p->stats = &spandsp_global_stats.t38;
00477       }
00478 
00479       /* init t38 stuff */
00480       t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p);
00481       set_logging(&p->t38_state.logging, s->details);
00482    }
00483 
00484    if (s->details->caps & AST_FAX_TECH_AUDIO) {
00485       /* init audio stuff */
00486       fax_init(&p->fax_state, caller_mode);
00487       set_logging(&p->fax_state.logging, s->details);
00488    }
00489 
00490    s->state = AST_FAX_STATE_INITIALIZED;
00491    return p;
00492 
00493 e_free:
00494    ast_free(p);
00495 e_return:
00496    return NULL;
00497 }
00498 
00499 /*! \brief Destroy a spandsp fax session.
00500  */
00501 static void spandsp_fax_destroy(struct ast_fax_session *s)
00502 {
00503    struct spandsp_pvt *p = s->tech_pvt;
00504 
00505    session_destroy(p);
00506    ast_free(p);
00507    s->tech_pvt = NULL;
00508    s->fd = -1;
00509 }
00510 
00511 /*! \brief Read a frame from the spandsp fax stack.
00512  */
00513 static struct ast_frame *spandsp_fax_read(struct ast_fax_session *s)
00514 {
00515    struct spandsp_pvt *p = s->tech_pvt;
00516    uint8_t buffer[AST_FRIENDLY_OFFSET + SPANDSP_FAX_SAMPLES * sizeof(uint16_t)];
00517    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00518    int samples;
00519 
00520    struct ast_frame fax_frame = {
00521       .frametype = AST_FRAME_VOICE,
00522       .subclass.codec = AST_FORMAT_SLINEAR,
00523       .src = "res_fax_spandsp_g711",
00524    };
00525 
00526    struct ast_frame *f = &fax_frame;
00527 
00528    if (ast_timer_ack(p->timer, 1) < 0) {
00529       ast_log(LOG_ERROR, "Failed to acknowledge timer for FAX session '%d'\n", s->id);
00530       return NULL;
00531    }
00532 
00533    /* XXX do we need to lock here? */
00534    if (p->isdone) {
00535       s->state = AST_FAX_STATE_COMPLETE;
00536       ast_debug(5, "FAX session '%d' is complete.\n", s->id);
00537       return NULL;
00538    }
00539 
00540    if (p->ist38) {
00541       t38_terminal_send_timeout(&p->t38_state, SPANDSP_FAX_SAMPLES);
00542       if ((f = AST_LIST_REMOVE_HEAD(&p->read_frames, frame_list))) {
00543          return f;
00544       }
00545    } else {
00546       if ((samples = fax_tx(&p->fax_state, buf, SPANDSP_FAX_SAMPLES)) > 0) {
00547          f->samples = samples;
00548          AST_FRAME_SET_BUFFER(f, buffer, AST_FRIENDLY_OFFSET, samples * sizeof(int16_t));
00549          return ast_frisolate(f);
00550       }
00551    }
00552 
00553    return &ast_null_frame;
00554 }
00555 
00556 /*! \brief Write a frame to the spandsp fax stack.
00557  * \param s a fax session
00558  * \param f the frame to write
00559  *
00560  * \note res_fax does not currently use the return value of this function.
00561  * Also the fax_rx() function never fails.
00562  *
00563  * \retval 0 success
00564  * \retval -1 failure
00565  */
00566 static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *f)
00567 {
00568    struct spandsp_pvt *p = s->tech_pvt;
00569 
00570    /* XXX do we need to lock here? */
00571    if (s->state == AST_FAX_STATE_COMPLETE) {
00572       ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
00573       return -1;
00574    }
00575 
00576    if (p->ist38) {
00577       return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
00578    } else {
00579       return fax_rx(&p->fax_state, f->data.ptr, f->samples);
00580    }
00581 }
00582 
00583 /*! \brief */
00584 static int spandsp_fax_start(struct ast_fax_session *s)
00585 {
00586    struct spandsp_pvt *p = s->tech_pvt;
00587 
00588    s->state = AST_FAX_STATE_OPEN;
00589 
00590    if (p->ist38) {
00591 #if SPANDSP_RELEASE_DATE >= 20080725
00592       /* for spandsp shaphots 0.0.6 and higher */
00593       p->t30_state = &p->t38_state.t30;
00594       p->t38_core_state = &p->t38_state.t38_fe.t38;
00595 #else
00596       /* for spandsp releases 0.0.5 */
00597       p->t30_state = &p->t38_state.t30_state;
00598       p->t38_core_state = &p->t38_state.t38;
00599 #endif
00600    } else {
00601 #if SPANDSP_RELEASE_DATE >= 20080725
00602       /* for spandsp shaphots 0.0.6 and higher */
00603       p->t30_state = &p->fax_state.t30;
00604 #else
00605       /* for spandsp release 0.0.5 */
00606       p->t30_state = &p->fax_state.t30_state;
00607 #endif
00608    }
00609 
00610    set_logging(&p->t30_state->logging, s->details);
00611 
00612    /* set some parameters */
00613    set_local_info(p->t30_state, s->details);
00614    set_file(p->t30_state, s->details);
00615    set_ecm(p->t30_state, s->details);
00616    t30_set_supported_modems(p->t30_state, spandsp_modems(s->details));
00617 
00618    /* perhaps set_transmit_on_idle() should be called */
00619 
00620    t30_set_phase_e_handler(p->t30_state, t30_phase_e_handler, s);
00621 
00622    /* set T.38 parameters */
00623    if (p->ist38) {
00624       set_logging(&p->t38_core_state->logging, s->details);
00625 
00626       t38_set_max_datagram_size(p->t38_core_state, s->details->their_t38_parameters.max_ifp);
00627 
00628       if (s->details->their_t38_parameters.fill_bit_removal) {
00629          t38_set_fill_bit_removal(p->t38_core_state, TRUE);
00630       }
00631 
00632       if (s->details->their_t38_parameters.transcoding_mmr) {
00633          t38_set_mmr_transcoding(p->t38_core_state, TRUE);
00634       }
00635 
00636       if (s->details->their_t38_parameters.transcoding_jbig) {
00637          t38_set_jbig_transcoding(p->t38_core_state, TRUE);
00638       }
00639    } else {
00640       /* have the fax stack generate silence if it has no data to send */
00641       fax_set_transmit_on_idle(&p->fax_state, 1);
00642    }
00643 
00644 
00645    /* start the timer */
00646    if (ast_timer_set_rate(p->timer, SPANDSP_FAX_TIMER_RATE)) {
00647       ast_log(LOG_ERROR, "FAX session '%d' error setting rate on timing source.\n", s->id);
00648       return -1;
00649    }
00650 
00651    s->state = AST_FAX_STATE_ACTIVE;
00652 
00653    return 0;
00654 }
00655 
00656 /*! \brief */
00657 static int spandsp_fax_cancel(struct ast_fax_session *s)
00658 {
00659    struct spandsp_pvt *p = s->tech_pvt;
00660    t30_terminate(p->t30_state);
00661    p->isdone = 1;
00662    return 0;
00663 }
00664 
00665 /*! \brief */
00666 static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
00667 {
00668    struct spandsp_pvt *p = s->tech_pvt;
00669 
00670    /* prevent the phase E handler from running, this is not a real termination */
00671    t30_set_phase_e_handler(p->t30_state, NULL, NULL);
00672 
00673    t30_terminate(p->t30_state);
00674 
00675    s->details->option.switch_to_t38 = 1;
00676    ast_atomic_fetchadd_int(&p->stats->switched, 1);
00677 
00678    p->ist38 = 1;
00679    p->stats = &spandsp_global_stats.t38;
00680    spandsp_fax_start(s);
00681 
00682    return 0;
00683 }
00684 
00685 /*! \brief */
00686 static char *spandsp_fax_cli_show_capabilities(int fd)
00687 {
00688    ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n");
00689    return  CLI_SUCCESS;
00690 }
00691 
00692 /*! \brief */
00693 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
00694 {
00695    struct spandsp_pvt *p = s->tech_pvt;
00696    t30_stats_t stats;
00697 
00698    ao2_lock(s);
00699    ast_cli(fd, "%-22s : %d\n", "session", s->id);
00700    ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
00701    ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
00702    if (s->state != AST_FAX_STATE_UNINITIALIZED) {
00703       t30_get_transfer_statistics(p->t30_state, &stats);
00704       ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
00705       ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
00706       ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
00707       ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
00708 #if SPANDSP_RELEASE_DATE >= 20090220
00709       ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
00710 #else
00711       ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
00712 #endif
00713       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);
00714 
00715       ast_cli(fd, "\nData Statistics:\n");
00716 #if SPANDSP_RELEASE_DATE >= 20090220
00717       ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
00718       ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
00719 #else
00720       ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
00721       ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
00722 #endif
00723       ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
00724       ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
00725    }
00726    ao2_unlock(s);
00727    ast_cli(fd, "\n\n");
00728    return CLI_SUCCESS;
00729 }
00730 
00731 /*! \brief */
00732 static char *spandsp_fax_cli_show_stats(int fd)
00733 {
00734    ast_mutex_lock(&spandsp_global_stats.lock);
00735    ast_cli(fd, "\n%-20.20s\n", "Spandsp G.711");
00736    ast_cli(fd, "%-20.20s : %d\n", "Success", spandsp_global_stats.g711.success);
00737    ast_cli(fd, "%-20.20s : %d\n", "Switched to T.38", spandsp_global_stats.g711.switched);
00738    ast_cli(fd, "%-20.20s : %d\n", "Call Dropped", spandsp_global_stats.g711.call_dropped);
00739    ast_cli(fd, "%-20.20s : %d\n", "No FAX", spandsp_global_stats.g711.nofax);
00740    ast_cli(fd, "%-20.20s : %d\n", "Negotiation Failed", spandsp_global_stats.g711.neg_failed);
00741    ast_cli(fd, "%-20.20s : %d\n", "Train Failure", spandsp_global_stats.g711.failed_to_train);
00742    ast_cli(fd, "%-20.20s : %d\n", "Retries Exceeded", spandsp_global_stats.g711.retries_exceeded);
00743    ast_cli(fd, "%-20.20s : %d\n", "Protocol Error", spandsp_global_stats.g711.protocol_error);
00744    ast_cli(fd, "%-20.20s : %d\n", "TX Protocol Error", spandsp_global_stats.g711.tx_protocol_error);
00745    ast_cli(fd, "%-20.20s : %d\n", "RX Protocol Error", spandsp_global_stats.g711.rx_protocol_error);
00746    ast_cli(fd, "%-20.20s : %d\n", "File Error", spandsp_global_stats.g711.file_error);
00747    ast_cli(fd, "%-20.20s : %d\n", "Memory Error", spandsp_global_stats.g711.mem_error);
00748    ast_cli(fd, "%-20.20s : %d\n", "Unknown Error", spandsp_global_stats.g711.unknown_error);
00749 
00750    ast_cli(fd, "\n%-20.20s\n", "Spandsp T.38");
00751    ast_cli(fd, "%-20.20s : %d\n", "Success", spandsp_global_stats.t38.success);
00752    ast_cli(fd, "%-20.20s : %d\n", "Call Dropped", spandsp_global_stats.t38.call_dropped);
00753    ast_cli(fd, "%-20.20s : %d\n", "No FAX", spandsp_global_stats.t38.nofax);
00754    ast_cli(fd, "%-20.20s : %d\n", "Negotiation Failed", spandsp_global_stats.t38.neg_failed);
00755    ast_cli(fd, "%-20.20s : %d\n", "Train Failure", spandsp_global_stats.t38.failed_to_train);
00756    ast_cli(fd, "%-20.20s : %d\n", "Retries Exceeded", spandsp_global_stats.t38.retries_exceeded);
00757    ast_cli(fd, "%-20.20s : %d\n", "Protocol Error", spandsp_global_stats.t38.protocol_error);
00758    ast_cli(fd, "%-20.20s : %d\n", "TX Protocol Error", spandsp_global_stats.t38.tx_protocol_error);
00759    ast_cli(fd, "%-20.20s : %d\n", "RX Protocol Error", spandsp_global_stats.t38.rx_protocol_error);
00760    ast_cli(fd, "%-20.20s : %d\n", "File Error", spandsp_global_stats.t38.file_error);
00761    ast_cli(fd, "%-20.20s : %d\n", "Memory Error", spandsp_global_stats.t38.mem_error);
00762    ast_cli(fd, "%-20.20s : %d\n", "Unknown Error", spandsp_global_stats.t38.unknown_error);
00763    ast_mutex_unlock(&spandsp_global_stats.lock);
00764 
00765    return CLI_SUCCESS;
00766 }
00767 
00768 /*! \brief Show res_fax_spandsp settings */
00769 static char *spandsp_fax_cli_show_settings(int fd)
00770 {
00771    /* no settings at the moment */
00772    return CLI_SUCCESS;
00773 }
00774 
00775 /*! \brief unload res_fax_spandsp */
00776 static int unload_module(void)
00777 {
00778    ast_fax_tech_unregister(&spandsp_fax_tech);
00779    ast_mutex_destroy(&spandsp_global_stats.lock);
00780    return AST_MODULE_LOAD_SUCCESS;
00781 }
00782 
00783 /*! \brief load res_fax_spandsp */
00784 static int load_module(void)
00785 {
00786    ast_mutex_init(&spandsp_global_stats.lock);
00787    spandsp_fax_tech.module = ast_module_info->self;
00788    if (ast_fax_tech_register(&spandsp_fax_tech) < 0) {
00789       ast_log(LOG_ERROR, "failed to register FAX technology\n");
00790       return AST_MODULE_LOAD_DECLINE;
00791    }
00792 
00793    /* prevent logging to stderr */
00794    span_set_message_handler(NULL);
00795 
00796    return AST_MODULE_LOAD_SUCCESS;
00797 }
00798 
00799 
00800 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Spandsp G.711 and T.38 FAX Technologies",
00801       .load = load_module,
00802       .unload = unload_module,
00803           );

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1