Wed Mar 3 22:35:40 2010

Asterisk developer's documentation


app_fax.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Simple fax applications
00005  * 
00006  * 2007-2008, Dmitry Andrianov <asterisk@dima.spb.ru>
00007  *
00008  * Code based on original implementation by Steve Underwood <steveu@coppice.org>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License
00012  *
00013  */
00014 
00015 /*** MODULEINFO
00016     <depend>spandsp</depend>
00017 ***/
00018  
00019 #include "asterisk.h"
00020 
00021 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 209281 $")
00022 
00023 #include <string.h>
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <inttypes.h>
00027 #include <pthread.h>
00028 #include <errno.h>
00029 #include <tiffio.h>
00030 
00031 #include <spandsp.h>
00032 #ifdef HAVE_SPANDSP_EXPOSE_H
00033 #include <spandsp/expose.h>
00034 #endif
00035 #include <spandsp/version.h>
00036 
00037 #include "asterisk/lock.h"
00038 #include "asterisk/file.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/app.h"
00043 #include "asterisk/dsp.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/manager.h"
00046 
00047 static char *app_sndfax_name = "SendFAX";
00048 static char *app_sndfax_synopsis = "Send a FAX";
00049 static char *app_sndfax_desc = 
00050 "  SendFAX(filename[|options]):\n"
00051 "Send a given TIFF file to the channel as a FAX.\n"
00052 "The option string may contain zero or more of the following characters:\n"
00053 "     'a' - makes the application behave as an answering machine\n"
00054 "           The default behaviour is to behave as a calling machine.\n"
00055 "\n"
00056 "This application uses following variables:\n"
00057 "     LOCALSTATIONID to identify itself to the remote end.\n"
00058 "     LOCALHEADERINFO to generate a header line on each page.\n"
00059 "\n"
00060 "This application sets the following channel variables upon completion:\n"
00061 "     FAXSTATUS       - status of operation:\n"
00062 "                           SUCCESS | FAILED\n"
00063 "     FAXERROR        - Error when FAILED\n"
00064 "     FAXMODE         - Mode used:\n"
00065 "                           audio | T38\n"
00066 "     REMOTESTATIONID - CSID of the remote side.\n"
00067 "     FAXPAGES        - number of pages sent.\n"
00068 "     FAXBITRATE      - transmition rate.\n"
00069 "     FAXRESOLUTION   - resolution.\n"
00070 "\n"
00071 "Returns -1 in case of user hang up or any channel error.\n"
00072 "Returns 0 on success.\n";
00073 
00074 static char *app_rcvfax_name = "ReceiveFAX";
00075 static char *app_rcvfax_synopsis = "Receive a FAX";
00076 static char *app_rcvfax_desc = 
00077 "  ReceiveFAX(filename[|options]):\n"
00078 "Receives a fax from the channel into the given filename overwriting\n"
00079 "the file if it already exists. File created will have TIFF format.\n"
00080 "The option string may contain zero or more of the following characters:\n"
00081 "     'c' -- makes the application behave as a calling machine\n"
00082 "            The default behaviour is to behave as an answering machine.\n"
00083 "\n"
00084 "This application uses following variables:\n"
00085 "     LOCALSTATIONID to identify itself to the remote end.\n"
00086 "     LOCALHEADERINFO to generate a header line on each page.\n"
00087 "\n"
00088 "This application sets the following channel variables upon completion:\n"
00089 "     FAXSTATUS       - status of operation:\n"
00090 "                           SUCCESS | FAILED\n"
00091 "     FAXERROR        - Error when FAILED\n"
00092 "     FAXMODE         - Mode used:\n"
00093 "                           audio | T38\n"
00094 "     REMOTESTATIONID - CSID of the remote side.\n"
00095 "     FAXPAGES        - number of pages sent.\n"
00096 "     FAXBITRATE      - transmition rate.\n"
00097 "     FAXRESOLUTION   - resolution.\n"
00098 "\n"
00099 "Returns -1 in case of user hang up or any channel error.\n"
00100 "Returns 0 on success.\n";
00101 
00102 #define MAX_SAMPLES 240
00103 
00104 /* Watchdog. I have seen situations when remote fax disconnects (because of poor line
00105    quality) while SpanDSP continues staying in T30_STATE_IV_CTC state forever.
00106    To avoid this, we terminate when we see that T30 state does not change for 5 minutes.
00107    We also terminate application when more than 30 minutes passed regardless of
00108    state changes. This is just a precaution measure - no fax should take that long */
00109 
00110 #define WATCHDOG_TOTAL_TIMEOUT   30 * 60
00111 #define WATCHDOG_STATE_TIMEOUT   5 * 60
00112 
00113 typedef struct {
00114    struct ast_channel *chan;
00115    enum ast_t38_state t38state;  /* T38 state of the channel */
00116    int direction;       /* Fax direction: 0 - receiving, 1 - sending */
00117    int caller_mode;
00118    char *file_name;
00119    struct ast_control_t38_parameters t38parameters;
00120    volatile int finished;
00121 } fax_session;
00122 
00123 static void span_message(int level, const char *msg)
00124 {
00125    if (level == SPAN_LOG_ERROR) {
00126       ast_log(LOG_ERROR, "%s", msg);
00127    } else if (level == SPAN_LOG_WARNING) {
00128       ast_log(LOG_WARNING, "%s", msg);
00129    } else {
00130       ast_log(LOG_DEBUG, "%s", msg);
00131    }
00132 }
00133 
00134 static int t38_tx_packet_handler(t38_core_state_t *s, void *user_data, const uint8_t *buf, int len, int count)
00135 {
00136    struct ast_channel *chan = (struct ast_channel *) user_data;
00137 
00138    struct ast_frame outf = {
00139       .frametype = AST_FRAME_MODEM,
00140       .subclass = AST_MODEM_T38,
00141       .src = __FUNCTION__,
00142    };
00143 
00144    /* TODO: Asterisk does not provide means of resending the same packet multiple
00145      times so count is ignored at the moment */
00146 
00147    AST_FRAME_SET_BUFFER(&outf, buf, 0, len);
00148 
00149    if (ast_write(chan, &outf) < 0) {
00150       ast_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
00151       return -1;
00152    }
00153 
00154    return 0;
00155 }
00156 
00157 static void phase_e_handler(t30_state_t *f, void *user_data, int result)
00158 {
00159    const char *local_ident;
00160    const char *far_ident;
00161    char buf[20];
00162    fax_session *s = (fax_session *) user_data;
00163    t30_stats_t stat;
00164    int pages_transferred;
00165 
00166    ast_debug(1, "Fax phase E handler. result=%d\n", result);
00167 
00168    t30_get_transfer_statistics(f, &stat);
00169 
00170    s = (fax_session *) user_data;
00171 
00172    if (result != T30_ERR_OK) {
00173       s->finished = -1;
00174 
00175       /* FAXSTATUS is already set to FAILED */
00176       pbx_builtin_setvar_helper(s->chan, "FAXERROR", t30_completion_code_to_str(result));
00177 
00178       ast_log(LOG_WARNING, "Error transmitting fax. result=%d: %s.\n", result, t30_completion_code_to_str(result));
00179 
00180       return;
00181    }
00182    
00183    s->finished = 1; 
00184    
00185    local_ident = t30_get_tx_ident(f);
00186    far_ident = t30_get_rx_ident(f);
00187    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "SUCCESS"); 
00188    pbx_builtin_setvar_helper(s->chan, "FAXERROR", NULL); 
00189    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", far_ident);
00190 #if SPANDSP_RELEASE_DATE >= 20090220
00191    pages_transferred = (s->direction) ? stat.pages_tx : stat.pages_rx;
00192 #else
00193    pages_transferred = stat.pages_transferred;
00194 #endif
00195    snprintf(buf, sizeof(buf), "%d", pages_transferred);
00196    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", buf);
00197    snprintf(buf, sizeof(buf), "%d", stat.y_resolution);
00198    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", buf);
00199    snprintf(buf, sizeof(buf), "%d", stat.bit_rate);
00200    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", buf); 
00201    
00202    ast_debug(1, "Fax transmitted successfully.\n");
00203    ast_debug(1, "  Remote station ID: %s\n", far_ident);
00204    ast_debug(1, "  Pages transferred: %d\n", pages_transferred);
00205    ast_debug(1, "  Image resolution:  %d x %d\n", stat.x_resolution, stat.y_resolution);
00206    ast_debug(1, "  Transfer Rate:     %d\n", stat.bit_rate);
00207    
00208    manager_event(EVENT_FLAG_CALL,
00209             s->direction ? "FaxSent" : "FaxReceived", 
00210             "Channel: %s\r\n"
00211             "Exten: %s\r\n"
00212             "CallerID: %s\r\n"
00213             "RemoteStationID: %s\r\n"
00214             "LocalStationID: %s\r\n"
00215             "PagesTransferred: %d\r\n"
00216             "Resolution: %d\r\n"
00217             "TransferRate: %d\r\n"
00218             "FileName: %s\r\n",
00219             s->chan->name,
00220             s->chan->exten,
00221             S_OR(s->chan->cid.cid_num, ""),
00222             far_ident,
00223             local_ident,
00224             pages_transferred,
00225             stat.y_resolution,
00226             stat.bit_rate,
00227             s->file_name);
00228 }
00229 
00230 /* === Helper functions to configure fax === */
00231 
00232 /* Setup SPAN logging according to Asterisk debug level */
00233 static int set_logging(logging_state_t *state)
00234 {
00235    int level = SPAN_LOG_WARNING + option_debug;
00236 
00237    span_log_set_message_handler(state, span_message);
00238    span_log_set_level(state, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | level); 
00239 
00240    return 0;
00241 }
00242 
00243 static void set_local_info(t30_state_t *state, fax_session *s)
00244 {
00245    const char *x;
00246 
00247    x = pbx_builtin_getvar_helper(s->chan, "LOCALSTATIONID");
00248    if (!ast_strlen_zero(x))
00249       t30_set_tx_ident(state, x);
00250 
00251    x = pbx_builtin_getvar_helper(s->chan, "LOCALHEADERINFO");
00252    if (!ast_strlen_zero(x))
00253       t30_set_tx_page_header_info(state, x);
00254 }
00255 
00256 static void set_file(t30_state_t *state, fax_session *s)
00257 {
00258    if (s->direction)
00259       t30_set_tx_file(state, s->file_name, -1, -1);
00260    else
00261       t30_set_rx_file(state, s->file_name, -1);
00262 }
00263 
00264 static void set_ecm(t30_state_t *state, int ecm)
00265 {
00266    t30_set_ecm_capability(state, ecm);
00267    t30_set_supported_compressions(state, T30_SUPPORT_T4_1D_COMPRESSION | T30_SUPPORT_T4_2D_COMPRESSION | T30_SUPPORT_T6_COMPRESSION);
00268 }
00269 
00270 /* === Generator === */
00271 
00272 /* This function is only needed to return passed params so
00273    generator_activate will save it to channel's generatordata */
00274 static void *fax_generator_alloc(struct ast_channel *chan, void *params)
00275 {
00276    return params;
00277 }
00278 
00279 static int fax_generator_generate(struct ast_channel *chan, void *data, int len, int samples)
00280 {
00281    fax_state_t *fax = (fax_state_t*) data;
00282    uint8_t buffer[AST_FRIENDLY_OFFSET + MAX_SAMPLES * sizeof(uint16_t)];
00283    int16_t *buf = (int16_t *) (buffer + AST_FRIENDLY_OFFSET);
00284     
00285    struct ast_frame outf = {
00286       .frametype = AST_FRAME_VOICE,
00287       .subclass = AST_FORMAT_SLINEAR,
00288       .src = __FUNCTION__,
00289    };
00290 
00291    if (samples > MAX_SAMPLES) {
00292       ast_log(LOG_WARNING, "Only generating %d samples, where %d requested\n", MAX_SAMPLES, samples);
00293       samples = MAX_SAMPLES;
00294    }
00295    
00296    if ((len = fax_tx(fax, buf, samples)) > 0) {
00297       outf.samples = len;
00298       AST_FRAME_SET_BUFFER(&outf, buffer, AST_FRIENDLY_OFFSET, len * sizeof(int16_t));
00299 
00300       if (ast_write(chan, &outf) < 0) {
00301          ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
00302          return -1;
00303       }
00304    }
00305 
00306    return 0;
00307 }
00308 
00309 struct ast_generator generator = {
00310    alloc:      fax_generator_alloc,
00311    generate:   fax_generator_generate,
00312 };
00313 
00314 
00315 /* === Transmission === */
00316 
00317 static int transmit_audio(fax_session *s)
00318 {
00319    int res = -1;
00320    int original_read_fmt = AST_FORMAT_SLINEAR;
00321    int original_write_fmt = AST_FORMAT_SLINEAR;
00322    fax_state_t fax;
00323    t30_state_t *t30state;
00324    struct ast_frame *inf = NULL;
00325    int last_state = 0;
00326    struct timeval now, start, state_change;
00327    enum ast_t38_state t38_state;
00328    struct ast_control_t38_parameters t38_parameters = { .version = 0,
00329                           .max_ifp = 800,
00330                           .rate = AST_T38_RATE_9600,
00331                           .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
00332                           .fill_bit_removal = 1,
00333                           .transcoding_mmr = 1,
00334                           .transcoding_jbig = 1,
00335    };
00336 
00337    /* if in receive mode, try to use T.38 */
00338    if (!s->direction) {
00339       /* check if we are already in T.38 mode (unlikely), or if we can request
00340        * a switch... if so, request it now and wait for the result, rather
00341        * than starting an audio FAX session that will have to be cancelled
00342        */
00343       if ((t38_state = ast_channel_get_t38_state(s->chan)) == T38_STATE_NEGOTIATED) {
00344          return 1;
00345       } else if ((t38_state != T38_STATE_UNAVAILABLE) &&
00346             (t38_parameters.request_response = AST_T38_REQUEST_NEGOTIATE,
00347              (ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) == 0))) {
00348          /* wait up to five seconds for negotiation to complete */
00349          unsigned int timeout = 5000;
00350          int ms;
00351          
00352          ast_debug(1, "Negotiating T.38 for receive on %s\n", s->chan->name);
00353          while (timeout > 0) {
00354             ms = ast_waitfor(s->chan, 1000);
00355             if (ms < 0) {
00356                ast_log(LOG_WARNING, "something bad happened while channel '%s' was polling.\n", s->chan->name);
00357                return -1;
00358             }
00359             if (!ms) {
00360                /* nothing happened */
00361                if (timeout > 0) {
00362                   timeout -= 1000;
00363                   continue;
00364                } else {
00365                   ast_log(LOG_WARNING, "channel '%s' timed-out during the T.38 negotiation.\n", s->chan->name);
00366                   break;
00367                }
00368             }
00369             if (!(inf = ast_read(s->chan))) {
00370                return -1;
00371             }
00372             if ((inf->frametype == AST_FRAME_CONTROL) &&
00373                 (inf->subclass == AST_CONTROL_T38_PARAMETERS) &&
00374                 (inf->datalen == sizeof(t38_parameters))) {
00375                struct ast_control_t38_parameters *parameters = inf->data.ptr;
00376                
00377                switch (parameters->request_response) {
00378                case AST_T38_NEGOTIATED:
00379                   ast_debug(1, "Negotiated T.38 for receive on %s\n", s->chan->name);
00380                   res = 1;
00381                   break;
00382                case AST_T38_REFUSED:
00383                   ast_log(LOG_WARNING, "channel '%s' refused to negotiate T.38\n", s->chan->name);
00384                   break;
00385                default:
00386                   ast_log(LOG_ERROR, "channel '%s' failed to negotiate T.38\n", s->chan->name);
00387                   break;
00388                }
00389                ast_frfree(inf);
00390                if (res == 1) {
00391                   return 1;
00392                } else {
00393                   break;
00394                }
00395             }
00396             ast_frfree(inf);
00397          }
00398       }
00399    }
00400 
00401 #if SPANDSP_RELEASE_DATE >= 20080725
00402         /* for spandsp shaphots 0.0.6 and higher */
00403         t30state = &fax.t30;
00404 #else
00405         /* for spandsp release 0.0.5 */
00406         t30state = &fax.t30_state;
00407 #endif
00408 
00409    original_read_fmt = s->chan->readformat;
00410    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00411       res = ast_set_read_format(s->chan, AST_FORMAT_SLINEAR);
00412       if (res < 0) {
00413          ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up\n");
00414          goto done;
00415       }
00416    }
00417 
00418    original_write_fmt = s->chan->writeformat;
00419    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00420       res = ast_set_write_format(s->chan, AST_FORMAT_SLINEAR);
00421       if (res < 0) {
00422          ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up\n");
00423          goto done;
00424       }
00425    }
00426 
00427    /* Initialize T30 terminal */
00428    fax_init(&fax, s->caller_mode);
00429 
00430    /* Setup logging */
00431    set_logging(&fax.logging);
00432    set_logging(&t30state->logging);
00433 
00434    /* Configure terminal */
00435    set_local_info(t30state, s);
00436    set_file(t30state, s);
00437    set_ecm(t30state, TRUE);
00438 
00439    fax_set_transmit_on_idle(&fax, TRUE);
00440 
00441    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00442 
00443    start = state_change = ast_tvnow();
00444 
00445    ast_activate_generator(s->chan, &generator, &fax);
00446 
00447    while (!s->finished) {
00448       res = ast_waitfor(s->chan, 20);
00449       if (res < 0)
00450          break;
00451       else if (res > 0)
00452          res = 0;
00453 
00454       inf = ast_read(s->chan);
00455       if (inf == NULL) {
00456          ast_debug(1, "Channel hangup\n");
00457          res = -1;
00458          break;
00459       }
00460 
00461       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00462 
00463       /* Check the frame type. Format also must be checked because there is a chance
00464          that a frame in old format was already queued before we set chanel format
00465          to slinear so it will still be received by ast_read */
00466       if (inf->frametype == AST_FRAME_VOICE && inf->subclass == AST_FORMAT_SLINEAR) {
00467          if (fax_rx(&fax, inf->data.ptr, inf->samples) < 0) {
00468             /* I know fax_rx never returns errors. The check here is for good style only */
00469             ast_log(LOG_WARNING, "fax_rx returned error\n");
00470             res = -1;
00471             break;
00472          }
00473 
00474          /* Watchdog */
00475          if (last_state != t30state->state) {
00476             state_change = ast_tvnow();
00477             last_state = t30state->state;
00478          }
00479       } else if ((inf->frametype == AST_FRAME_CONTROL) &&
00480             (inf->subclass == AST_CONTROL_T38_PARAMETERS)) {
00481          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00482 
00483          if (parameters->request_response == AST_T38_NEGOTIATED) {
00484             /* T38 switchover completed */
00485             s->t38parameters = *parameters;
00486             ast_debug(1, "T38 negotiated, finishing audio loop\n");
00487             res = 1;
00488             break;
00489          } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
00490             t38_parameters.request_response = AST_T38_NEGOTIATED;
00491             ast_debug(1, "T38 request received, accepting\n");
00492             /* Complete T38 switchover */
00493             ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
00494             /* Do not break audio loop, wait until channel driver finally acks switchover
00495              * with AST_T38_NEGOTIATED
00496              */
00497          }
00498       }
00499 
00500       ast_frfree(inf);
00501       inf = NULL;
00502 
00503       /* Watchdog */
00504       now = ast_tvnow();
00505       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00506          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00507          res = -1;
00508          break;
00509       }
00510    }
00511 
00512    ast_debug(1, "Loop finished, res=%d\n", res);
00513 
00514    if (inf)
00515       ast_frfree(inf);
00516 
00517    ast_deactivate_generator(s->chan);
00518 
00519    /* If we are switching to T38, remove phase E handler. Otherwise it will be executed
00520       by t30_terminate, display diagnostics and set status variables although no transmittion
00521       has taken place yet. */
00522    if (res > 0) {
00523       t30_set_phase_e_handler(t30state, NULL, NULL);
00524    }
00525 
00526    t30_terminate(t30state);
00527    fax_release(&fax);
00528 
00529 done:
00530    if (original_write_fmt != AST_FORMAT_SLINEAR) {
00531       if (ast_set_write_format(s->chan, original_write_fmt) < 0)
00532          ast_log(LOG_WARNING, "Unable to restore write format on '%s'\n", s->chan->name);
00533    }
00534 
00535    if (original_read_fmt != AST_FORMAT_SLINEAR) {
00536       if (ast_set_read_format(s->chan, original_read_fmt) < 0)
00537          ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", s->chan->name);
00538    }
00539 
00540    return res;
00541 
00542 }
00543 
00544 static int transmit_t38(fax_session *s)
00545 {
00546    int res = 0;
00547    t38_terminal_state_t t38;
00548    struct ast_frame *inf = NULL;
00549    int last_state = 0;
00550    struct timeval now, start, state_change, last_frame;
00551    t30_state_t *t30state;
00552    t38_core_state_t *t38state;
00553 
00554 #if SPANDSP_RELEASE_DATE >= 20080725
00555    /* for spandsp shaphots 0.0.6 and higher */
00556    t30state = &t38.t30;
00557    t38state = &t38.t38_fe.t38;
00558 #else
00559    /* for spandsp releases 0.0.5 */
00560    t30state = &t38.t30_state;
00561    t38state = &t38.t38;
00562 #endif
00563 
00564    /* Initialize terminal */
00565    memset(&t38, 0, sizeof(t38));
00566    if (t38_terminal_init(&t38, s->caller_mode, t38_tx_packet_handler, s->chan) == NULL) {
00567       ast_log(LOG_WARNING, "Unable to start T.38 termination.\n");
00568       return -1;
00569    }
00570 
00571    t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
00572 
00573    if (s->t38parameters.fill_bit_removal) {
00574       t38_set_fill_bit_removal(t38state, TRUE);
00575    }
00576    if (s->t38parameters.transcoding_mmr) {
00577       t38_set_mmr_transcoding(t38state, TRUE);
00578    }
00579    if (s->t38parameters.transcoding_jbig) {
00580       t38_set_jbig_transcoding(t38state, TRUE);
00581    }
00582 
00583    /* Setup logging */
00584    set_logging(&t38.logging);
00585    set_logging(&t30state->logging);
00586    set_logging(&t38state->logging);
00587 
00588    /* Configure terminal */
00589    set_local_info(t30state, s);
00590    set_file(t30state, s);
00591    set_ecm(t30state, TRUE);
00592 
00593    t30_set_phase_e_handler(t30state, phase_e_handler, s);
00594 
00595    now = start = state_change = ast_tvnow();
00596 
00597    while (!s->finished) {
00598 
00599       res = ast_waitfor(s->chan, 20);
00600       if (res < 0)
00601          break;
00602       else if (res > 0)
00603          res = 0;
00604 
00605       last_frame = now;
00606       now = ast_tvnow();
00607       t38_terminal_send_timeout(&t38, ast_tvdiff_us(now, last_frame) / (1000000 / 8000));
00608 
00609       inf = ast_read(s->chan);
00610       if (inf == NULL) {
00611          ast_debug(1, "Channel hangup\n");
00612          res = -1;
00613          break;
00614       }
00615 
00616       ast_debug(10, "frame %d/%d, len=%d\n", inf->frametype, inf->subclass, inf->datalen);
00617 
00618       if (inf->frametype == AST_FRAME_MODEM && inf->subclass == AST_MODEM_T38) {
00619          t38_core_rx_ifp_packet(t38state, inf->data.ptr, inf->datalen, inf->seqno);
00620 
00621          /* Watchdog */
00622          if (last_state != t30state->state) {
00623             state_change = ast_tvnow();
00624             last_state = t30state->state;
00625          }
00626       } else if (inf->frametype == AST_FRAME_CONTROL && inf->subclass == AST_CONTROL_T38_PARAMETERS) {
00627          struct ast_control_t38_parameters *parameters = inf->data.ptr;
00628          if (parameters->request_response == AST_T38_TERMINATED) {
00629             ast_debug(1, "T38 down, finishing\n");
00630             break;
00631          }
00632       }
00633 
00634       ast_frfree(inf);
00635       inf = NULL;
00636 
00637       /* Watchdog */
00638       if (ast_tvdiff_sec(now, start) > WATCHDOG_TOTAL_TIMEOUT || ast_tvdiff_sec(now, state_change) > WATCHDOG_STATE_TIMEOUT) {
00639          ast_log(LOG_WARNING, "It looks like we hung. Aborting.\n");
00640          res = -1;
00641          break;
00642       }
00643    }
00644 
00645    ast_debug(1, "Loop finished, res=%d\n", res);
00646 
00647    if (inf)
00648       ast_frfree(inf);
00649 
00650    t30_terminate(t30state);
00651    t38_terminal_release(&t38);
00652 
00653    return res;
00654 }
00655 
00656 static int transmit(fax_session *s)
00657 {
00658    int res = 0;
00659 
00660    /* Clear all channel variables which to be set by the application.
00661       Pre-set status to error so in case of any problems we can just leave */
00662    pbx_builtin_setvar_helper(s->chan, "FAXSTATUS", "FAILED"); 
00663    pbx_builtin_setvar_helper(s->chan, "FAXERROR", "Channel problems"); 
00664 
00665    pbx_builtin_setvar_helper(s->chan, "FAXMODE", NULL);
00666    pbx_builtin_setvar_helper(s->chan, "REMOTESTATIONID", NULL);
00667    pbx_builtin_setvar_helper(s->chan, "FAXPAGES", NULL);
00668    pbx_builtin_setvar_helper(s->chan, "FAXRESOLUTION", NULL);
00669    pbx_builtin_setvar_helper(s->chan, "FAXBITRATE", NULL); 
00670 
00671    if (s->chan->_state != AST_STATE_UP) {
00672       /* Shouldn't need this, but checking to see if channel is already answered
00673        * Theoretically asterisk should already have answered before running the app */
00674       res = ast_answer(s->chan);
00675       if (res) {
00676          ast_log(LOG_WARNING, "Could not answer channel '%s'\n", s->chan->name);
00677          return res;
00678       }
00679    }
00680 
00681    s->t38state = ast_channel_get_t38_state(s->chan);
00682    if (s->t38state != T38_STATE_NEGOTIATED) {
00683       /* T38 is not negotiated on the channel yet. First start regular transmission. If it switches to T38, follow */   
00684       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "audio"); 
00685       res = transmit_audio(s);
00686       if (res > 0) {
00687          /* transmit_audio reports switchover to T38. Update t38state */
00688          s->t38state = ast_channel_get_t38_state(s->chan);
00689          if (s->t38state != T38_STATE_NEGOTIATED) {
00690             ast_log(LOG_ERROR, "Audio loop reports T38 switchover but t38state != T38_STATE_NEGOTIATED\n");
00691          }
00692       }
00693    }
00694 
00695    if (s->t38state == T38_STATE_NEGOTIATED) {
00696       pbx_builtin_setvar_helper(s->chan, "FAXMODE", "T38"); 
00697       res = transmit_t38(s);
00698    }
00699 
00700    if (res) {
00701       ast_log(LOG_WARNING, "Transmission error\n");
00702       res = -1;
00703    } else if (s->finished < 0) {
00704       ast_log(LOG_WARNING, "Transmission failed\n");
00705    } else if (s->finished > 0) {
00706       ast_debug(1, "Transmission finished Ok\n");
00707    }
00708 
00709    return res;
00710 }
00711 
00712 /* === Application functions === */
00713 
00714 static int sndfax_exec(struct ast_channel *chan, void *data)
00715 {
00716    int res = 0;
00717    char *parse;
00718    fax_session session = { 0, };
00719 
00720    AST_DECLARE_APP_ARGS(args,
00721       AST_APP_ARG(file_name);
00722       AST_APP_ARG(options);
00723    );
00724 
00725    if (chan == NULL) {
00726       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00727       return -1;
00728    }
00729 
00730    /* The next few lines of code parse out the filename and header from the input string */
00731    if (ast_strlen_zero(data)) {
00732       /* No data implies no filename or anything is present */
00733       ast_log(LOG_ERROR, "SendFAX requires an argument (filename)\n");
00734       return -1;
00735    }
00736 
00737    parse = ast_strdupa(data);
00738    AST_STANDARD_APP_ARGS(args, parse);
00739    
00740    session.caller_mode = TRUE;
00741 
00742    if (args.options) {
00743       if (strchr(args.options, 'a'))
00744          session.caller_mode = FALSE;
00745    }
00746 
00747    /* Done parsing */
00748    session.direction = 1;
00749    session.file_name = args.file_name;
00750    session.chan = chan;
00751    session.finished = 0;
00752 
00753    res = transmit(&session);
00754 
00755    return res;
00756 }
00757 
00758 static int rcvfax_exec(struct ast_channel *chan, void *data)
00759 {
00760    int res = 0;
00761    char *parse;
00762    fax_session session;
00763 
00764    AST_DECLARE_APP_ARGS(args,
00765       AST_APP_ARG(file_name);
00766       AST_APP_ARG(options);
00767    );
00768 
00769    if (chan == NULL) {
00770       ast_log(LOG_ERROR, "Fax channel is NULL. Giving up.\n");
00771       return -1;
00772    }
00773 
00774    /* The next few lines of code parse out the filename and header from the input string */
00775    if (ast_strlen_zero(data)) {
00776       /* No data implies no filename or anything is present */
00777       ast_log(LOG_ERROR, "ReceiveFAX requires an argument (filename)\n");
00778       return -1;
00779    }
00780 
00781    parse = ast_strdupa(data);
00782    AST_STANDARD_APP_ARGS(args, parse);
00783    
00784    session.caller_mode = FALSE;
00785 
00786    if (args.options) {
00787       if (strchr(args.options, 'c'))
00788          session.caller_mode = TRUE;
00789    }
00790 
00791    /* Done parsing */
00792    session.direction = 0;
00793    session.file_name = args.file_name;
00794    session.chan = chan;
00795    session.finished = 0;
00796 
00797    res = transmit(&session);
00798 
00799    return res;
00800 }
00801 
00802 static int unload_module(void)
00803 {
00804    int res;
00805 
00806    res = ast_unregister_application(app_sndfax_name); 
00807    res |= ast_unregister_application(app_rcvfax_name);   
00808 
00809    return res;
00810 }
00811 
00812 static int load_module(void)
00813 {
00814    int res ;
00815 
00816    res = ast_register_application(app_sndfax_name, sndfax_exec, app_sndfax_synopsis, app_sndfax_desc);
00817    res |= ast_register_application(app_rcvfax_name, rcvfax_exec, app_rcvfax_synopsis, app_rcvfax_desc);
00818 
00819    /* The default SPAN message handler prints to stderr. It is something we do not want */
00820    span_set_message_handler(NULL);
00821 
00822    return res;
00823 }
00824 
00825 
00826 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Simple FAX Application",
00827       .load = load_module,
00828       .unload = unload_module,
00829       );
00830 
00831 

Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1