Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:51 2009

## Asterisk developer's documentation

# chan_modem_bestdata.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999, Mark Spencer and 2001 Jim Dixon
    00005  *
    00006  * Mark Spencer <markster@linux-support.net>
    00007  * Jim Dixon <jim@lambdatel.com>
    00008  *
    00009  * See http://www.asterisk.org for more information about
    00010  * the Asterisk project. Please do not directly contact
    00011  * any of the maintainers of this project for assistance;
    00012  * the project provides a web site, mailing lists and IRC
    00013  * channels for your use.
    00014  *
    00015  * This program is free software, distributed under the terms of
    00016  * the GNU General Public License Version 2. See the LICENSE file
    00017  * at the top of the source tree.
    00018  */
    00019
    00020 /*
    00021  *
    00022  * BestData 56SX-92 Voice Modem Driver (Conexant)
    00023  *
    00024  * \ingroup channel_drivers
    00025  */
    00026
    00027 #include <stdio.h>
    00028
    00029 #include <string.h>
    00030 #include <stdlib.h>
    00031 #include <errno.h>
    00032 #include <unistd.h>
    00033
    00034 #include "asterisk.h"
    00035
    00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
    00037
    00038 #include "asterisk/lock.h"
    00039 #include "asterisk/vmodem.h"
    00040 #include "asterisk/module.h"
    00041 #include "asterisk/frame.h"
    00042 #include "asterisk/logger.h"
    00043 #include "asterisk/options.h"
    00044
    00045 #define STATE_COMMAND   0
    00046 #define STATE_VOICE  1
    00047 #define  STATE_VOICEPLAY   2
    00048
    00049 #define VRA "40"        /* Number of 100ms of non-ring after a ring cadence after which we consider the lien to be answered */
    00050 #define VRN "25"        /* Number of 100ms of non-ring with no cadence after which we assume an answer */
    00051
    00052 #define  RINGT 7000
    00053
    00054 static char *breakcmd = "\020!";
    00055
    00056 static char *desc = "BestData (Conexant V.90 Chipset) VoiceModem Driver";
    00057
    00058 static int usecnt;
    00059 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
    00060
    00061 static char *bestdata_idents[] = {
    00062    /* Identify BestData Modem */
    00063    "ACF3_V1.010-V90_P21_FSH",
    00064    NULL
    00065 };
    00066
    00067 static int bestdata_break(struct ast_modem_pvt *p);
    00068
    00069 static int bestdata_startrec(struct ast_modem_pvt *p)
    00070 {
    00071    if (p->ministate != STATE_COMMAND) bestdata_break(p);
    00072    if (ast_modem_send(p, "AT+VRX", 0) ||
    00073         ast_modem_expect(p, "CONNECT", 5)) {
    00074       ast_log(LOG_WARNING, "Unable to start recording\n");
    00075       return -1;
    00076    }
    00077    p->ministate = STATE_VOICE;
    00078    return 0;
    00079 }
    00080
    00081 static int bestdata_startplay(struct ast_modem_pvt *p)
    00082 {
    00083    if (p->ministate != STATE_COMMAND) bestdata_break(p);
    00084    if (ast_modem_send(p, "AT+VTX", 0) ||
    00085         ast_modem_expect(p, "CONNECT", 5)) {
    00086       ast_log(LOG_WARNING, "Unable to start recording\n");
    00087       return -1;
    00088    }
    00089    p->ministate = STATE_VOICEPLAY;
    00090    return 0;
    00091 }
    00092
    00093 static int bestdata_break(struct ast_modem_pvt *p)
    00094 {
    00095    if (ast_modem_send(p, breakcmd, 2)) {
    00096       ast_log(LOG_WARNING, "Failed to break\n");
    00097       return -1;
    00098    }
    00099    p->ministate = STATE_COMMAND;
    00100    usleep(10000);
    00101    /* Read any outstanding junk */
    00102    while(!ast_modem_read_response(p, 1));
    00103    if (ast_modem_send(p, "AT", 0)) {
    00104       /* Modem might be stuck in some weird mode, try to get it out */
    00105       ast_modem_send(p, "+++", 3);
    00106       if (ast_modem_expect(p, "OK", 10)) {
    00107          ast_log(LOG_WARNING, "Modem is not responding\n");
    00108          return -1;
    00109       }
    00110       if (ast_modem_send(p, "AT", 0)) {
    00111          ast_log(LOG_WARNING, "Modem is not responding\n");
    00112          return -1;
    00113       }
    00114    }
    00115    if (ast_modem_expect(p, "OK", 5)) {
    00116       ast_log(LOG_WARNING, "Modem did not respond properly\n");
    00117       return -1;
    00118    }
    00119    return 0;
    00120 }
    00121
    00122 static int bestdata_init(struct ast_modem_pvt *p)
    00123 {
    00124    if (option_debug)
    00125       ast_log(LOG_DEBUG, "bestdata_init()\n");
    00126    if (bestdata_break(p))
    00127       return -1;
    00128    /* Force into command mode */
    00129    p->ministate = STATE_COMMAND;
    00130    if (ast_modem_send(p, "AT+FCLASS=8", 0) ||
    00131         ast_modem_expect(p, "OK", 5)) {
    00132       ast_log(LOG_WARNING, "Unable to set to voice mode\n");
    00133       return -1;
    00134    }
    00135    if (ast_modem_send(p, "AT+VSM=1,8000,0,0", 0) ||
    00136         ast_modem_expect(p, "OK", 5)) {
    00137       ast_log(LOG_WARNING, "Unable to set to 8000 Hz sampling\n");
    00138       return -1;
    00139    }
    00140    if (ast_modem_send(p, "AT+VLS=0", 0) ||
    00141         ast_modem_expect(p, "OK", 5)) {
    00142       ast_log(LOG_WARNING, "Unable to set to telco interface\n");
    00143       return -1;
    00144    }
    00145    if (ast_modem_send(p, "AT+VRA=" VRA, 0) ||
    00146         ast_modem_expect(p, "OK", 5)) {
    00147       ast_log(LOG_WARNING, "Unable to set to 'ringback goes away' timer\n");
    00148       return -1;
    00149    }
    00150    if (ast_modem_send(p, "AT+VRN=" VRN, 0) ||
    00151         ast_modem_expect(p, "OK", 5)) {
    00152       ast_log(LOG_WARNING, "Unable to set to 'ringback never came timer'\n");
    00153       return -1;
    00154    }
    00155    if (ast_modem_send(p, "AT+VTD=63", 0) ||
    00156         ast_modem_expect(p, "OK", 5)) {
    00157       ast_log(LOG_WARNING, "Unable to set to tone detection\n");
    00158       return -1;
    00159    }
    00160    if (ast_modem_send(p, "AT+VCID=1", 0) ||
    00161         ast_modem_expect(p, "OK", 5)) {
    00162       ast_log(LOG_WARNING, "Unable to enable Caller*ID\n");
    00163       return -1;
    00164    }
    00165    return 0;
    00166 }
    00167
    00168 static struct ast_frame *bestdata_handle_escape(struct ast_modem_pvt *p, char esc)
    00169 {
    00170    char name[30]="",nmbr[30]="";
    00171    time_t   now;
    00172
    00173    /* Handle escaped characters -- but sometimes we call it directly as
    00174       a quick way to cause known responses */
    00175    p->fr.frametype = AST_FRAME_NULL;
    00176    p->fr.subclass = 0;
    00177    p->fr.data = NULL;
    00178    p->fr.datalen = 0;
    00179    p->fr.samples = 0;
    00180    p->fr.offset = 0;
    00181    p->fr.mallocd = 0;
    00182    p->fr.delivery.tv_sec = 0;
    00183    p->fr.delivery.tv_usec = 0;
    00184    if (esc)
    00185       ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
    00186
    00187    switch(esc) {
    00188    case 'R': /* Pseudo ring */
    00189       time(&now);
    00190       if (now > (p->lastring + (RINGT / 1000)))
    00191          { /* if stale, treat as new */
    00192          p->gotclid = 0;
    00193          }
    00194       if (p->gotclid)
    00195          {
    00196          p->fr.frametype = AST_FRAME_CONTROL;
    00197          p->fr.subclass = AST_CONTROL_RING;
    00198          }
    00199       p->ringt = RINGT;
    00200       time(&p->lastring);
    00201       return &p->fr;
    00202    case 'X': /* Caller-ID Spill */
    00203       if (p->gotclid) return &p->fr;
    00204       name[0] = nmbr[0] = 0;
    00205       for(;;)
    00206          {
    00207          char res[1000]="";
    00208
    00209          if (ast_modem_read_response(p, 5)) break;
    00210          strncpy(res, p->response, sizeof(res)-1);
    00211          ast_modem_trim(res);
    00212          if (!strncmp(res,"\020.",2)) break;
    00213          if (!strncmp(res,"NAME",4)) strncpy(name,res + 7, sizeof(name) - 1);
    00214          if (!strncmp(res,"NMBR",4)) strncpy(nmbr,res + 7, sizeof(nmbr) - 1);
    00215          }
    00216       p->gotclid = 1;
    00217       if ((!strcmp(name,"O")) || (!strcmp(name,"P"))) name[0] = 0;
    00218       if ((!strcmp(nmbr,"O")) || (!strcmp(nmbr,"P"))) nmbr[0] = 0;
    00219       if (name[0])
    00220          strncpy(p->cid_name, name, sizeof(p->cid_name) - 1);
    00221       if (nmbr[0])
    00222          strncpy(p->cid_num, nmbr, sizeof(p->cid_num) - 1);
    00223       if (p->owner) {
    00224          p->owner->cid.cid_num = strdup(p->cid_num);
    00225          p->owner->cid.cid_name = strdup(p->cid_name);
    00226       }
    00227       return &p->fr;
    00228    case '@': /* response from "OK" in command mode */
    00229       if (p->owner)
    00230          ast_setstate(p->owner, AST_STATE_UP);
    00231       if (bestdata_startrec(p)) return NULL;
    00232       p->fr.frametype = AST_FRAME_CONTROL;
    00233       p->fr.subclass = AST_CONTROL_RING;
    00234       return &p->fr;
    00235    case 'b': /* Busy signal */
    00236       p->fr.frametype = AST_FRAME_CONTROL;
    00237       p->fr.subclass = AST_CONTROL_BUSY;
    00238       return &p->fr;
    00239    case 'o': /* Overrun */
    00240       ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
    00241       if (ast_modem_send(p, "\0x10E", 2))
    00242          ast_log(LOG_WARNING, "Unable to flush buffers\n");
    00243       return &p->fr;
    00244    case '0':  /* All the DTMF characters */
    00245    case '1':
    00246    case '2':
    00247    case '3':
    00248    case '4':
    00249    case '5':
    00250    case '6':
    00251    case '7':
    00252    case '8':
    00253    case '9':
    00254    case '*':
    00255    case '#':
    00256    case 'A':
    00257    case 'B':
    00258    case 'C':
    00259    case 'D':
    00260       p->dtmfrx = esc;  /* save this for when its done */
    00261       return &p->fr;
    00262    case '/': /* Start of DTMF tone shielding */
    00263       p->dtmfrx = ' ';
    00264       return &p->fr;
    00265    case '~': /* DTMF transition to off */
    00266       if (p->dtmfrx > ' ')
    00267          {
    00268          p->fr.frametype = AST_FRAME_DTMF;
    00269          p->fr.subclass = p->dtmfrx;
    00270          }
    00271       p->dtmfrx = 0;
    00272       return &p->fr;
    00273    case 'u': /* Underrun */
    00274       ast_log(LOG_WARNING, "Data underrun\n");
    00275       /* Fall Through */
    00276    case CHAR_ETX: /* End Transmission */
    00277    case 'd': /* Dialtone */
    00278    case 'c': /* Calling Tone */
    00279    case 'e': /* European version */
    00280    case 'a': /* Answer Tone */
    00281    case 'f': /* Bell Answer Tone */
    00282    case 'T': /* Timing mark */
    00283    case 't': /* Handset off hook */
    00284    case 'h': /* Handset hungup */
    00285    case 0: /* Pseudo signal */
    00286       /* Ignore */
    00287       return &p->fr;
    00288    default:
    00289       ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
    00290    }
    00291    return &p->fr;
    00292 }
    00293
    00294 static struct ast_frame *bestdata_read(struct ast_modem_pvt *p)
    00295 {
    00296    char result[256];
    00297    short *b;
    00298    struct ast_frame *f=NULL;
    00299    int res;
    00300    int x;
    00301
    00302    if (p->ministate == STATE_COMMAND) {
    00303       /* Read the first two bytes, first, in case it's a control message */
    00304       fread(result, 1, 2, p->f);
    00305       if (result[0] == CHAR_DLE) {
    00306          return bestdata_handle_escape(p, result[1]);
    00307       } else {
    00308          if (p->ringt) /* if ring timeout specified */
    00309             {
    00310             x = fileno(p->f);
    00311             res = ast_waitfor_n_fd(&x, 1, &p->ringt, NULL);
    00312             if (res < 0) {
    00313                return NULL;
    00314             }
    00315             }
    00316          if ((result[0] == '\n') || (result[0] == '\r'))
    00317             return bestdata_handle_escape(p, 0);
    00318          /* Read the rest of the line */
    00319          fgets(result + 2, sizeof(result) - 2, p->f);
    00320          ast_modem_trim(result);
    00321          if (!strcasecmp(result, "OK")) {
    00322             /* If we're in immediate mode, reply now */
    00323             if (p->mode == MODEM_MODE_IMMEDIATE)
    00324                return bestdata_handle_escape(p, '@');
    00325          } else
    00326          if (!strcasecmp(result, "BUSY")) {
    00327             /* Same as a busy signal */
    00328             return bestdata_handle_escape(p, 'b');
    00329          } else
    00330          if (!strcasecmp(result, "RING")) {
    00331             return bestdata_handle_escape(p, 'R');
    00332          } else
    00333          if (!strcasecmp(result, "NO DIALTONE")) {
    00334             /* There's no dialtone, so the line isn't working */
    00335             ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
    00336             return NULL;
    00337          }
    00338          ast_log(LOG_DEBUG, "Modem said '%s'\n", result);
    00339          return bestdata_handle_escape(p, 0);
    00340       }
    00341    } else {
    00342         /* if playing, start recording instead */
    00343       if (p->ministate == STATE_VOICEPLAY)
    00344          {
    00345          if (bestdata_startrec(p)) return NULL;
    00346          }
    00347       /* We have to be more efficient in voice mode */
    00348       b = (short *)(p->obuf + p->obuflen);
    00349       while (p->obuflen/2 < 240) {
    00350          /* Read ahead the full amount */
    00351          res = fread(result, 1, 240 - p->obuflen/2, p->f);
    00352          if (res < 1) {
    00353             /* If there's nothing there, just continue on */
    00354             if (errno == EAGAIN)
    00355                return bestdata_handle_escape(p, 0);
    00356             ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno));
    00357          }
    00358          for (x=0;x<res;x++) {
    00359             /* Process all the bytes that we've read */
    00360             if (result[x] == CHAR_DLE) {
    00361                /* We assume there is no more than one signal frame among our
    00362                   data.  */
    00363                if (f) ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n");
    00364                  /* if not a DLE in the data */
    00365                if (result[++x] != CHAR_DLE)
    00366                   {
    00367                   /* If bestdata_handle_escape says NULL, say it now, doesn't matter
    00368                      what else is there, the connection is dead. */
    00369                   f = bestdata_handle_escape(p, result[x]);
    00370                   if (p->dtmfrx) continue;
    00371                   return(f);
    00372                   }
    00373             }
    00374             /* Generate a 16-bit signed linear value from our
    00375                unsigned 8-bit value */
    00376             *(b++) = (((short)result[x]) - 127) * 0xff;
    00377             p->obuflen += 2;
    00378          }
    00379          if (f) break;
    00380       }
    00381       /* If we have a control frame, return it now */
    00382       if (f) return f;
    00383       /* If we get here, we have a complete voice frame */
    00384       p->fr.frametype = AST_FRAME_VOICE;
    00385       p->fr.subclass = AST_FORMAT_SLINEAR;
    00386       p->fr.samples = 240;
    00387       p->fr.data = p->obuf;
    00388       p->fr.datalen = p->obuflen;
    00389       p->fr.mallocd = 0;
    00390       p->fr.delivery.tv_sec = 0;
    00391       p->fr.delivery.tv_usec = 0;
    00392       p->fr.offset = AST_FRIENDLY_OFFSET;
    00393       p->fr.src = __FUNCTION__;
    00394       if (option_debug)
    00395          ast_log(LOG_DEBUG, "bestdata_read(voice frame)\n");
    00396       p->obuflen = 0;
    00397       return &p->fr;
    00398    }
    00399    return NULL;
    00400 }
    00401
    00402 static int bestdata_write(struct ast_modem_pvt *p, struct ast_frame *f)
    00403 {
    00404 unsigned char  c,buf[32768]; /* I hope we dont have frames larger then 16K */
    00405 int   i,j;
    00406 short *sp;
    00407 unsigned long u;
    00408 #define  DLE   16
    00409
    00410    if (p->owner && (p->owner->_state == AST_STATE_UP) &&
    00411       (p->ministate != STATE_VOICEPLAY) && bestdata_startplay(p)) return -1;
    00412    sp = (short *) f->data;
    00413      /* stick DLE's in ahead of anything else */
    00414    for(i = 0,j = 0; i < f->datalen / 2; i++)
    00415       {
    00416       *sp *= 3;
    00417       u = *sp++ + 32768;
    00418       c = u >> 8;
    00419       if (c == DLE) buf[j++] = DLE;
    00420       buf[j++] = c;
    00421       }
    00422    do i = fwrite(buf,1,j,p->f);
    00423    while ((i == -1) && (errno == EWOULDBLOCK));
    00424    if (i != j)
    00425       {
    00426       ast_log(LOG_WARNING,"modem short write!!\n");
    00427       return -1;
    00428       }
    00429    fflush(p->f);
    00430    if (option_debug)
    00431       ast_log(LOG_DEBUG, "bestdata_write()\n");
    00432    return 0;
    00433 }
    00434
    00435 static char *bestdata_identify(struct ast_modem_pvt *p)
    00436 {
    00437    char identity[256];
    00438    char mfr[80];
    00439    char mdl[80];
    00440    char rev[80];
    00441    ast_modem_send(p, "AT+FMM", 0);
    00442    ast_modem_read_response(p, 5);
    00443    strncpy(mdl, p->response, sizeof(mdl)-1);
    00444    ast_modem_trim(mdl);
    00445    ast_modem_expect(p, "OK", 5);
    00446    ast_modem_send(p, "AT+FMI", 0);
    00447    ast_modem_read_response(p, 5);
    00448    strncpy(mfr, p->response, sizeof(mfr)-1);
    00449    ast_modem_trim(mfr);
    00450    ast_modem_expect(p, "OK", 5);
    00451    ast_modem_send(p, "AT+FMR", 0);
    00452    ast_modem_read_response(p, 5);
    00453    strncpy(rev, p->response, sizeof(rev)-1);
    00454    ast_modem_trim(rev);
    00455    ast_modem_expect(p, "OK", 5);
    00456    snprintf(identity, sizeof(identity), "%s Model %s Revision %s", mfr, mdl, rev);
    00457    return strdup(identity);
    00458 }
    00459
    00460 static void bestdata_incusecnt(void)
    00461 {
    00462    ast_mutex_lock(&usecnt_lock);
    00463    usecnt++;
    00464    ast_mutex_unlock(&usecnt_lock);
    00465    ast_update_use_count();
    00466 }
    00467
    00468 static void bestdata_decusecnt(void)
    00469 {
    00470    ast_mutex_lock(&usecnt_lock);
    00471    usecnt++;
    00472    ast_mutex_unlock(&usecnt_lock);
    00473    ast_update_use_count();
    00474 }
    00475
    00476 static int bestdata_answer(struct ast_modem_pvt *p)
    00477 {
    00478    p->ringt = 0;
    00479    p->lastring = 0;
    00480    if (ast_modem_send(p, "AT+VLS=1", 0) ||
    00481         ast_modem_expect(p, "OK", 10)) {
    00482       ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
    00483       return -1;
    00484    }
    00485    return 0;
    00486 }
    00487
    00488 static int bestdata_dialdigit(struct ast_modem_pvt *p, char digit)
    00489 {
    00490    char cmd[80];
    00491
    00492    if (p->ministate != STATE_COMMAND) bestdata_break(p);
    00493    snprintf(cmd, sizeof(cmd), "AT+VTS=%c", digit);
    00494    if (ast_modem_send(p, cmd, 0) ||
    00495         ast_modem_expect(p, "OK", 10)) {
    00496       ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
    00497       return -1;
    00498    }
    00499    return 0;
    00500 }
    00501
    00502 static int bestdata_dial(struct ast_modem_pvt *p, char *stuff)
    00503 {
    00504    char cmd[800] = "",a[20]="";
    00505    int i,j;
    00506
    00507    if (p->ministate != STATE_COMMAND)
    00508       {
    00509       bestdata_break(p);
    00510       strncpy(cmd, "AT+VTS=", sizeof(cmd) - 1);
    00511       j = strlen(cmd);
    00512       for(i = 0; stuff[i]; i++)
    00513          {
    00514          switch(stuff[i])
    00515             {
    00516              case '!' :
    00517             a[0] = stuff[i];
    00518             a[1] = 0;
    00519             break;
    00520              case ',':
    00521             strncpy(a, "[,,100]", sizeof(a) - 1);
    00522             break;
    00523              default:
    00524             snprintf(a, sizeof(a), "{%c,7}", stuff[i]);
    00525             }
    00526          if (stuff[i + 1]) strncat(a, ",", sizeof(a) - strlen(a) - 1);
    00527          strncpy(cmd + j, a, sizeof(cmd) - j - 1);
    00528          j += strlen(a);
    00529          }
    00530       }
    00531    else
    00532       {
    00533       snprintf(cmd, sizeof(cmd), "ATD%c %s", p->dialtype,stuff);
    00534       }
    00535    if (ast_modem_send(p, cmd, 0)) {
    00536       ast_log(LOG_WARNING, "Unable to dial\n");
    00537       return -1;
    00538    }
    00539    return 0;
    00540 }
    00541
    00542 static int bestdata_hangup(struct ast_modem_pvt *p)
    00543 {
    00544    if (bestdata_break(p))
    00545       return -1;
    00546    /* Hangup by switching to data, then back to voice */
    00547    if (ast_modem_send(p, "ATH", 0) ||
    00548         ast_modem_expect(p, "OK", 8)) {
    00549       ast_log(LOG_WARNING, "Unable to set to data mode\n");
    00550       return -1;
    00551    }
    00552    if (ast_modem_send(p, "AT+FCLASS=8", 0) ||
    00553         ast_modem_expect(p, "OK", 5)) {
    00554       ast_log(LOG_WARNING, "Unable to set to voice mode\n");
    00555       return -1;
    00556    }
    00557    p->gotclid = 0;
    00558    p->ringt = 0;
    00559    p->lastring = 0;
    00560    p->dtmfrx = 0;
    00561    return 0;
    00562 }
    00563
    00564 static struct ast_modem_driver bestdata_driver =
    00565 {
    00566    "BestData",
    00567    bestdata_idents,
    00568    AST_FORMAT_SLINEAR,
    00569    0,    /* Not full duplex */
    00570    bestdata_incusecnt,  /* incusecnt */
    00571    bestdata_decusecnt,  /* decusecnt */
    00572    bestdata_identify,   /* identify */
    00573    bestdata_init, /* init */
    00574    NULL, /* setdev */
    00575    bestdata_read,
    00576    bestdata_write,
    00577    bestdata_dial, /* dial */
    00578    bestdata_answer,  /* answer */
    00579    bestdata_hangup,  /* hangup */
    00580    bestdata_startrec,   /* start record */
    00581    NULL, /* stop record */
    00582    bestdata_startplay,  /* start playback */
    00583    NULL, /* stop playback */
    00584    NULL, /* set silence supression */
    00585    bestdata_dialdigit,  /* dialdigit */
    00586 };
    00587
    00588
    00589
    00590 int usecount(void)
    00591 {
    00592    return usecnt;
    00593 }
    00594
    00595 int load_module(void)
    00596 {
    00597    return ast_register_modem_driver(&bestdata_driver);
    00598 }
    00599
    00600 int unload_module(void)
    00601 {
    00602    return ast_unregister_modem_driver(&bestdata_driver);
    00603 }
    00604
    00605 char *description()
    00606 {
    00607    return desc;
    00608 }
    00609
    00610 char *key()
    00611 {
    00612    return ASTERISK_GPL_KEY;
    00613 }