Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:51 2009

## Asterisk developer's documentation

# chan_modem_aopen.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@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 /*
    00020  *
    00021  * A/Open ITU-56/2 Voice Modem Driver (Rockwell, IS-101, and others)
    00022  *
    00023  * \ingroup channel_drivers
    00024  */
    00025
    00026 #include <stdio.h>
    00027
    00028 #include <string.h>
    00029 #include <stdlib.h>
    00030 #include <errno.h>
    00031
    00032 #include "asterisk.h"
    00033
    00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
    00035
    00036 #include "asterisk/lock.h"
    00037 #include "asterisk/vmodem.h"
    00038 #include "asterisk/module.h"
    00039 #include "asterisk/frame.h"
    00040 #include "asterisk/logger.h"
    00041 #include "asterisk/options.h"
    00042
    00043 #define STATE_COMMAND   0
    00044 #define STATE_VOICE  1
    00045
    00046 #define VRA "40"        /* Number of 100ms of non-ring after a ring cadence after which we consider the lien to be answered */
    00047 #define VRN "100"       /* Number of 100ms of non-ring with no cadence after which we assume an answer */
    00048
    00049 static char *breakcmd = "\0x10\0x03";
    00050
    00051 static char *desc = "A/Open (Rockwell Chipset) ITU-2 VoiceModem Driver";
    00052
    00053 static int usecnt;
    00054 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
    00055
    00056 static char *aopen_idents[] = {
    00057    /* Identify A/Open Modem */
    00058    "V2.210-V90_2M_DLP",
    00059    NULL
    00060 };
    00061
    00062 static int aopen_setdev(struct ast_modem_pvt *p, int dev)
    00063 {
    00064    char cmd[80];
    00065    if (ast_modem_send(p, "AT#VLS?", 0)) {
    00066       ast_log(LOG_WARNING, "Unable to select current mode %d\n", dev);
    00067       return -1;
    00068    }
    00069    if (ast_modem_read_response(p, 5)) {
    00070       ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
    00071       return -1;
    00072    }
    00073    ast_modem_trim(p->response);
    00074    strncpy(cmd, p->response, sizeof(cmd)-1);
    00075    if (ast_modem_expect(p, "OK", 5)) {
    00076       ast_log(LOG_WARNING, "Modem did not respond properly\n");
    00077       return -1;
    00078    }
    00079    if (dev == atoi(cmd)) {
    00080       /* We're already in the right mode, don't bother changing for fear of
    00081          hanging up */
    00082       return 0;
    00083    }
    00084    snprintf(cmd, sizeof(cmd), "AT#VLS=%d", dev);
    00085    if (ast_modem_send(p, cmd, 0))  {
    00086       ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
    00087       return -1;
    00088    }
    00089    if (ast_modem_read_response(p, 5)) {
    00090       ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
    00091       return -1;
    00092    }
    00093    ast_modem_trim(p->response);
    00094    if (strcasecmp(p->response, "VCON") && strcasecmp(p->response, "OK")) {
    00095       ast_log(LOG_WARNING, "Unexpected reply: %s\n", p->response);
    00096       return -1;
    00097    }
    00098    return 0;
    00099 }
    00100
    00101 static int aopen_startrec(struct ast_modem_pvt *p)
    00102 {
    00103    if (ast_modem_send(p, "AT#VRX", 0) ||
    00104         ast_modem_expect(p, "CONNECT", 5)) {
    00105       ast_log(LOG_WARNING, "Unable to start recording\n");
    00106       return -1;
    00107    }
    00108    p->ministate = STATE_VOICE;
    00109    return 0;
    00110 }
    00111
    00112 static int aopen_break(struct ast_modem_pvt *p)
    00113 {
    00114    if (ast_modem_send(p, "\r\n", 2)) {
    00115       ast_log(LOG_WARNING, "Failed to send enter?\n");
    00116       return -1;
    00117    }
    00118    if (ast_modem_send(p, breakcmd, 2)) {
    00119       ast_log(LOG_WARNING, "Failed to break\n");
    00120       return -1;
    00121    }
    00122    if (ast_modem_send(p, "\r\n", 2)) {
    00123       ast_log(LOG_WARNING, "Failed to send enter?\n");
    00124       return -1;
    00125    }
    00126    /* Read any outstanding junk */
    00127    while(!ast_modem_read_response(p, 1));
    00128    if (ast_modem_send(p, "AT", 0)) {
    00129       /* Modem might be stuck in some weird mode, try to get it out */
    00130       ast_modem_send(p, "+++", 3);
    00131       if (ast_modem_expect(p, "OK", 10)) {
    00132          ast_log(LOG_WARNING, "Modem is not responding\n");
    00133          return -1;
    00134       }
    00135       if (ast_modem_send(p, "AT", 0)) {
    00136          ast_log(LOG_WARNING, "Modem is not responding\n");
    00137          return -1;
    00138       }
    00139    }
    00140    if (ast_modem_expect(p, "OK", 5)) {
    00141       ast_log(LOG_WARNING, "Modem did not respond properly\n");
    00142       return -1;
    00143    }
    00144    return 0;
    00145 }
    00146
    00147 static int aopen_init(struct ast_modem_pvt *p)
    00148 {
    00149    if (option_debug)
    00150       ast_log(LOG_DEBUG, "aopen_init()\n");
    00151    if (aopen_break(p))
    00152       return -1;
    00153    /* Force into command mode */
    00154    p->ministate = STATE_COMMAND;
    00155    if (ast_modem_send(p, "AT#BDR=0", 0) ||
    00156         ast_modem_expect(p, "OK", 5)) {
    00157       ast_log(LOG_WARNING, "Unable to set to auto-baud\n");
    00158       return -1;
    00159    }
    00160    if (ast_modem_send(p, "AT#CLS=8", 0) ||
    00161         ast_modem_expect(p, "OK", 5)) {
    00162       ast_log(LOG_WARNING, "Unable to set to voice mode\n");
    00163       return -1;
    00164    }
    00165    if (ast_modem_send(p, "AT#VBS=8", 0) ||
    00166         ast_modem_expect(p, "OK", 5)) {
    00167       ast_log(LOG_WARNING, "Unable to set to 8-bit mode\n");
    00168       return -1;
    00169    }
    00170    if (ast_modem_send(p, "AT#VSR=8000", 0) ||
    00171         ast_modem_expect(p, "OK", 5)) {
    00172       ast_log(LOG_WARNING, "Unable to set to 8000 Hz sampling\n");
    00173       return -1;
    00174    }
    00175    if (ast_modem_send(p, "AT#VLS=0", 0) ||
    00176         ast_modem_expect(p, "OK", 5)) {
    00177       ast_log(LOG_WARNING, "Unable to set to telco interface\n");
    00178       return -1;
    00179    }
    00180    if (ast_modem_send(p, "AT#VRA=" VRA, 0) ||
    00181         ast_modem_expect(p, "OK", 5)) {
    00182       ast_log(LOG_WARNING, "Unable to set to 'ringback goes away' timer\n");
    00183       return -1;
    00184    }
    00185    if (ast_modem_send(p, "AT#VRN=" VRN, 0) ||
    00186         ast_modem_expect(p, "OK", 5)) {
    00187       ast_log(LOG_WARNING, "Unable to set to 'ringback never came timer'\n");
    00188       return -1;
    00189    }
    00190    if (ast_modem_send(p, "AT#VTD=3F,3F,3F", 0) ||
    00191         ast_modem_expect(p, "OK", 5)) {
    00192       ast_log(LOG_WARNING, "Unable to set to tone detection\n");
    00193       return -1;
    00194    }
    00195
    00196    return 0;
    00197 }
    00198
    00199 static struct ast_frame *aopen_handle_escape(struct ast_modem_pvt *p, char esc)
    00200 {
    00201    /* Handle escaped characters -- but sometimes we call it directly as
    00202       a quick way to cause known responses */
    00203    p->fr.frametype = AST_FRAME_NULL;
    00204    p->fr.subclass = 0;
    00205    p->fr.data = NULL;
    00206    p->fr.datalen = 0;
    00207    p->fr.samples = 0;
    00208    p->fr.offset = 0;
    00209    p->fr.mallocd = 0;
    00210         p->fr.delivery.tv_sec = 0;
    00211         p->fr.delivery.tv_usec = 0;
    00212    if (esc)
    00213       ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
    00214
    00215    switch(esc) {
    00216    case 'R': /* Pseudo ring */
    00217       p->fr.frametype = AST_FRAME_CONTROL;
    00218       p->fr.subclass = AST_CONTROL_RING;
    00219       return &p->fr;
    00220    case 'X': /* Pseudo connect */
    00221       p->fr.frametype = AST_FRAME_CONTROL;
    00222       p->fr.subclass = AST_CONTROL_RING;
    00223       if (p->owner)
    00224          ast_setstate(p->owner, AST_STATE_UP);
    00225       if (aopen_startrec(p))
    00226          return   NULL;
    00227       return &p->fr;
    00228    case 'b': /* Busy signal */
    00229       p->fr.frametype = AST_FRAME_CONTROL;
    00230       p->fr.subclass = AST_CONTROL_BUSY;
    00231       return &p->fr;
    00232    case 'o': /* Overrun */
    00233       ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
    00234       if (ast_modem_send(p, "\0x10E", 2))
    00235          ast_log(LOG_WARNING, "Unable to flush buffers\n");
    00236       return &p->fr;
    00237    case 'u': /* Underrun */
    00238       ast_log(LOG_WARNING, "Data underrun\n");
    00239       /* Fall Through */
    00240    case CHAR_ETX: /* End Transmission */
    00241    case 'd': /* Dialtone */
    00242    case 'c': /* Calling Tone */
    00243    case 'e': /* European version */
    00244    case 'a': /* Answer Tone */
    00245    case 'f': /* Bell Answer Tone */
    00246    case 'T': /* Timing mark */
    00247    case 't': /* Handset off hook */
    00248    case 'h': /* Handset hungup */
    00249    case 0: /* Pseudo signal */
    00250       /* Ignore */
    00251       return &p->fr;
    00252    default:
    00253       ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
    00254    }
    00255    return &p->fr;
    00256 }
    00257
    00258 static struct ast_frame *aopen_read(struct ast_modem_pvt *p)
    00259 {
    00260    char result[256];
    00261    short *b;
    00262    struct ast_frame *f=NULL;
    00263    int res;
    00264    int x;
    00265    if (p->ministate == STATE_COMMAND) {
    00266       /* Read the first two bytes, first, in case it's a control message */
    00267       fread(result, 1, 2, p->f);
    00268       if (result[0] == CHAR_DLE) {
    00269          return aopen_handle_escape(p, result[1]);
    00270
    00271       } else {
    00272          if ((result[0] == '\n') || (result[0] == '\r'))
    00273             return aopen_handle_escape(p, 0);
    00274          /* Read the rest of the line */
    00275          fgets(result + 2, sizeof(result) - 2, p->f);
    00276          ast_modem_trim(result);
    00277          if (!strcasecmp(result, "VCON")) {
    00278             /* If we're in immediate mode, reply now */
    00279             if (p->mode == MODEM_MODE_IMMEDIATE)
    00280                return aopen_handle_escape(p, 'X');
    00281          } else
    00282          if (!strcasecmp(result, "BUSY")) {
    00283             /* Same as a busy signal */
    00284             return aopen_handle_escape(p, 'b');
    00285          } else
    00286          if (!strcasecmp(result, "RING")) {
    00287             return aopen_handle_escape(p, 'R');
    00288          } else
    00289          if (!strcasecmp(result, "NO DIALTONE")) {
    00290             /* There's no dialtone, so the line isn't working */
    00291             ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
    00292             return NULL;
    00293          }
    00294          ast_log(LOG_DEBUG, "Modem said '%s'\n", result);
    00295          return aopen_handle_escape(p, 0);
    00296       }
    00297    } else {
    00298       /* We have to be more efficient in voice mode */
    00299       b = (short *)(p->obuf + p->obuflen);
    00300       while (p->obuflen/2 < 240) {
    00301          /* Read ahead the full amount */
    00302          res = fread(result, 1, 240 - p->obuflen/2, p->f);
    00303          if (res < 1) {
    00304             /* If there's nothing there, just continue on */
    00305             if (errno == EAGAIN)
    00306                return aopen_handle_escape(p, 0);
    00307             ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno));
    00308          }
    00309          for (x=0;x<res;x++) {
    00310             /* Process all the bytes that we've read */
    00311             if (result[x] == CHAR_DLE) {
    00312                /* We assume there is no more than one signal frame among our
    00313                   data.  */
    00314                if (f)
    00315                   ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n");
    00316                f = aopen_handle_escape(p, result[x+1]);
    00317                /* If aopen_handle_escape says NULL, say it now, doesn't matter
    00318                   what else is there, the connection is dead. */
    00319                if (!f)
    00320                   return NULL;
    00321             } else {
    00322             /* Generate a 16-bit signed linear value from our
    00323                unsigned 8-bit value */
    00324                *(b++) = (((short)result[x]) - 127) * 0xff;
    00325                p->obuflen += 2;
    00326             }
    00327          }
    00328          if (f)
    00329             break;
    00330       }
    00331       /* If we have a control frame, return it now */
    00332       if (f)
    00333          return f;
    00334       /* If we get here, we have a complete voice frame */
    00335       p->fr.frametype = AST_FRAME_VOICE;
    00336       p->fr.subclass = AST_FORMAT_SLINEAR;
    00337       p->fr.samples = 240;
    00338       p->fr.data = p->obuf;
    00339       p->fr.datalen = p->obuflen;
    00340       p->fr.mallocd = 0;
    00341       p->fr.delivery.tv_sec = 0;
    00342       p->fr.delivery.tv_usec = 0;
    00343       p->fr.offset = AST_FRIENDLY_OFFSET;
    00344       p->fr.src = __FUNCTION__;
    00345       if (option_debug)
    00346          ast_log(LOG_DEBUG, "aopen_read(voice frame)\n");
    00347       p->obuflen = 0;
    00348       return &p->fr;
    00349    }
    00350    return NULL;
    00351 }
    00352
    00353 static int aopen_write(struct ast_modem_pvt *p, struct ast_frame *f)
    00354 {
    00355    if (option_debug)
    00356       ast_log(LOG_DEBUG, "aopen_write()\n");
    00357    return 0;
    00358 }
    00359
    00360 static char *aopen_identify(struct ast_modem_pvt *p)
    00361 {
    00362    char identity[256];
    00363    char mfr[80];
    00364    char mdl[80];
    00365    char rev[80];
    00366    ast_modem_send(p, "AT#MDL?", 0);
    00367    ast_modem_read_response(p, 5);
    00368    strncpy(mdl, p->response, sizeof(mdl)-1);
    00369    ast_modem_trim(mdl);
    00370    ast_modem_expect(p, "OK", 5);
    00371    ast_modem_send(p, "AT#MFR?", 0);
    00372    ast_modem_read_response(p, 5);
    00373    strncpy(mfr, p->response, sizeof(mfr)-1);
    00374    ast_modem_trim(mfr);
    00375    ast_modem_expect(p, "OK", 5);
    00376    ast_modem_send(p, "AT#REV?", 0);
    00377    ast_modem_read_response(p, 5);
    00378    strncpy(rev, p->response, sizeof(rev)-1);
    00379    ast_modem_trim(rev);
    00380    ast_modem_expect(p, "OK", 5);
    00381    snprintf(identity, sizeof(identity), "%s Model %s Revision %s", mfr, mdl, rev);
    00382    return strdup(identity);
    00383 }
    00384
    00385 static void aopen_incusecnt(void)
    00386 {
    00387    ast_mutex_lock(&usecnt_lock);
    00388    usecnt++;
    00389    ast_mutex_unlock(&usecnt_lock);
    00390    ast_update_use_count();
    00391 }
    00392
    00393 static void aopen_decusecnt(void)
    00394 {
    00395    ast_mutex_lock(&usecnt_lock);
    00396    usecnt++;
    00397    ast_mutex_unlock(&usecnt_lock);
    00398    ast_update_use_count();
    00399 }
    00400
    00401 static int aopen_answer(struct ast_modem_pvt *p)
    00402 {
    00403    if (ast_modem_send(p, "ATA", 0) ||
    00404         ast_modem_expect(p, "VCON", 10)) {
    00405       ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
    00406       return -1;
    00407    }
    00408    return 0;
    00409 }
    00410
    00411 static int aopen_dialdigit(struct ast_modem_pvt *p, char digit)
    00412 {
    00413    char cmd[80];
    00414    snprintf(cmd, sizeof(cmd), "AT#VTS=%c", digit);
    00415    if (ast_modem_send(p, cmd, 0) ||
    00416         ast_modem_expect(p, "VCON", 10)) {
    00417       ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
    00418       return -1;
    00419    }
    00420    return 0;
    00421 }
    00422
    00423 static int aopen_dial(struct ast_modem_pvt *p, char *stuff)
    00424 {
    00425    char cmd[80];
    00426    snprintf(cmd, sizeof(cmd), "ATD%c %s", p->dialtype,stuff);
    00427    if (ast_modem_send(p, cmd, 0)) {
    00428       ast_log(LOG_WARNING, "Unable to dial\n");
    00429       return -1;
    00430    }
    00431    return 0;
    00432 }
    00433
    00434 static int aopen_hangup(struct ast_modem_pvt *p)
    00435 {
    00436    if (aopen_break(p))
    00437       return -1;
    00438    /* Hangup by switching to data, then back to voice */
    00439    if (ast_modem_send(p, "ATH", 0) ||
    00440         ast_modem_expect(p, "OK", 8)) {
    00441       ast_log(LOG_WARNING, "Unable to set to data mode\n");
    00442       return -1;
    00443    }
    00444    if (ast_modem_send(p, "AT#CLS=8", 0) ||
    00445         ast_modem_expect(p, "OK", 5)) {
    00446       ast_log(LOG_WARNING, "Unable to set to voice mode\n");
    00447       return -1;
    00448    }
    00449    return 0;
    00450 }
    00451
    00452 static struct ast_modem_driver aopen_driver =
    00453 {
    00454    "AOpen",
    00455    aopen_idents,
    00456    AST_FORMAT_SLINEAR,
    00457    0,    /* Not full duplex */
    00458    aopen_incusecnt,  /* incusecnt */
    00459    aopen_decusecnt,  /* decusecnt */
    00460    aopen_identify,   /* identify */
    00461    aopen_init, /* init */
    00462    aopen_setdev,  /* setdev */
    00463    aopen_read,
    00464    aopen_write,
    00465    aopen_dial, /* dial */
    00466    aopen_answer,  /* answer */
    00467    aopen_hangup,  /* hangup */
    00468    aopen_startrec,   /* start record */
    00469    NULL, /* stop record */
    00470    NULL, /* start playback */
    00471    NULL, /* stop playback */
    00472    NULL, /* set silence supression */
    00473    aopen_dialdigit,  /* dialdigit */
    00474 };
    00475
    00476
    00477
    00478 int usecount(void)
    00479 {
    00480    return usecnt;
    00481 }
    00482
    00483 int load_module(void)
    00484 {
    00485    return ast_register_modem_driver(&aopen_driver);
    00486 }
    00487
    00488 int unload_module(void)
    00489 {
    00490    return ast_unregister_modem_driver(&aopen_driver);
    00491 }
    00492
    00493 char *description()
    00494 {
    00495    return desc;
    00496 }
    00497
    00498 char *key()
    00499 {
    00500    return ASTERISK_GPL_KEY;
    00501 }