From c62ffabb2fbd2e8f0f73c5f0bc519bd090f95268 Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Tue, 19 Jun 2001 16:17:52 +0000 Subject: Version 0.1.9 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@335 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_modem.c | 93 +++++-- channels/chan_modem_bestdata.c | 596 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 668 insertions(+), 21 deletions(-) create mode 100755 channels/chan_modem_bestdata.c (limited to 'channels') diff --git a/channels/chan_modem.c b/channels/chan_modem.c index 9c4f7d234..ea5757e2f 100755 --- a/channels/chan_modem.c +++ b/channels/chan_modem.c @@ -53,7 +53,7 @@ static char context[AST_MAX_EXTENSION]= "default"; static char language[MAX_LANGUAGE] = ""; /* Initialization String */ -static char initstr[AST_MAX_INIT_STR] = "ATE1Q0"; +static char initstr[AST_MAX_INIT_STR] = "ATE0Q0"; /* Default MSN */ static char msn[AST_MAX_EXTENSION]=""; @@ -161,9 +161,10 @@ int ast_unregister_modem_driver(struct ast_modem_driver *mc) static int modem_call(struct ast_channel *ast, char *idest, int timeout) { + static int modem_hangup(struct ast_channel *ast); struct ast_modem_pvt *p; int ms = timeout; - char rdest[80], *where; + char rdest[80], *where, dstr[100]; strncpy(rdest, idest, sizeof(rdest)); strtok(rdest, ":"); where = strtok(NULL, ":"); @@ -172,9 +173,17 @@ static int modem_call(struct ast_channel *ast, char *idest, int timeout) return -1; } p = ast->pvt->pvt; - if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) { - ast_log(LOG_WARNING, "modem_call called on %s, neither down nor reserved\n", ast->name); - return -1; + strcpy(dstr,where + p->stripmsd); + /* if not a transfer or just sending tones, must be in correct state */ + if (strcasecmp(rdest, "transfer") && strcasecmp(rdest,"sendtones")) { + if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) { + ast_log(LOG_WARNING, "modem_call called on %s, neither down nor reserved\n", ast->name); + return -1; + } + } + if (!strcasecmp(rdest,"transfer")) /* if a transfer, put in transfer stuff */ + { + sprintf(dstr,"!,%s",where + p->stripmsd); } if (!strcasecmp(where, "handset")) { if (p->mc->setdev) @@ -187,7 +196,7 @@ static int modem_call(struct ast_channel *ast, char *idest, int timeout) if (p->mc->setdev(p, MODEM_DEV_TELCO_SPK)) return -1; if (p->mc->dial) - p->mc->dial(p, where + p->stripmsd); + p->mc->dial(p, dstr); ast->state = AST_STATE_DIALING; while((ast->state != AST_STATE_UP) && (ms > 0)) { ms = ast_waitfor(ast, ms); @@ -205,14 +214,20 @@ static int modem_call(struct ast_channel *ast, char *idest, int timeout) int ast_modem_send(struct ast_modem_pvt *p, char *cmd, int len) { - int res; + int i; + usleep(5000); if (!len) { - fprintf(p->f, "%s\r\n", cmd); - res = ast_modem_expect(p, cmd, ECHO_TIMEOUT); - if (res) { - ast_log(LOG_WARNING, "Unexpected reply %s\n", p->response); - return -1; - } + for(i = 0; cmd[i];) + { + if (fwrite(cmd + i,1,1,p->f) != 1) + { + if (errno == EWOULDBLOCK) continue; + return -1; + } + i++; + } + tcdrain(fileno(p->f)); + fprintf(p->f,"\r\n"); return 0; } else { if (fwrite(cmd, 1, len, p->f) < len) @@ -223,18 +238,50 @@ int ast_modem_send(struct ast_modem_pvt *p, char *cmd, int len) int ast_modem_read_response(struct ast_modem_pvt *p, int timeout) { - int res = -1; + int res = -1,c,i; timeout *= 1000; - strncpy(p->response, "(No Response)", sizeof(p->response)); + p->response[0] = 0; + c = i = 0; do { res = ast_waitfor_n_fd(&p->fd, 1, &timeout, NULL); if (res < 0) { + strncpy(p->response, "(No Response)", sizeof(p->response)); return -1; } - /* Read a response */ - fgets(p->response, sizeof(p->response), p->f); - return 0; + /* get no more then buffer length */ + while(i < sizeof(p->response) - 1) + { + c = fgetc(p->f); /* get a char */ + if (c < 1) /* if error */ + { + /* if nothing in buffer, go back into timeout stuff */ + if (errno == EWOULDBLOCK) break; + /* return as error */ + strncpy(p->response, "(No Response)", sizeof(p->response)); + return -1; + } + /* save char */ + p->response[i++] = c; + p->response[i] = 0; + /* if end of input */ + if (c == '\n') break; + } + if (c >= 0) /* if input terminated normally */ + { + /* ignore just CR/LF */ + if (!strcmp(p->response,"\r\n")) + { + /* reset input buffer stuff */ + i = 0; + p->response[0] = 0; + } + else /* otherwise return with info in buffer */ + { + return 0; + } + } } while(timeout > 0); + strncpy(p->response, "(No Response)", sizeof(p->response)); return -1; } @@ -250,7 +297,7 @@ int ast_modem_expect(struct ast_modem_pvt *p, char *result, int timeout) } /* Read a response */ fgets(p->response, sizeof(p->response), p->f); -#if 0 +#if 0 fprintf(stderr, "Modem said: %s", p->response); #endif if (!strncasecmp(p->response, result, strlen(result))) @@ -273,11 +320,12 @@ void ast_modem_trim(char *s) static int modem_setup(struct ast_modem_pvt *p, int baudrate) { + /* Make sure there's a modem there and that it's in a reasonable mode. Set the baud rate, etc. */ char identity[256]; char *ident = NULL; - char etx[2] = { 0x10, 0x03 }; + char etx[2] = { 0x10, '!' }; if (option_debug) ast_log(LOG_DEBUG, "Setting up modem %s\n", p->dev); if (ast_modem_send(p, etx, 2)) { @@ -288,6 +336,7 @@ static int modem_setup(struct ast_modem_pvt *p, int baudrate) ast_log(LOG_WARNING, "Failed to send enter?\n"); return -1; } + usleep(10000); /* Read any outstanding stuff */ while(!ast_modem_read_response(p, 0)); if (ast_modem_send(p, "ATZ", 0)) { @@ -394,6 +443,7 @@ static int modem_answer(struct ast_channel *ast) return res; } +#if 0 static char modem_2digit(char c) { if (c == 12) @@ -405,7 +455,7 @@ static char modem_2digit(char c) else return '?'; } - +#endif static struct ast_frame *modem_read(struct ast_channel *ast) { struct ast_modem_pvt *p = ast->pvt->pvt; @@ -469,6 +519,7 @@ static void modem_mini_packet(struct ast_modem_pvt *i) { struct ast_frame *fr; fr = i->mc->read(i); + if (!fr) return; if (fr->frametype == AST_FRAME_CONTROL) { if (fr->subclass == AST_CONTROL_RING) { ast_modem_new(i, AST_STATE_RING); diff --git a/channels/chan_modem_bestdata.c b/channels/chan_modem_bestdata.c new file mode 100755 index 000000000..59fbca11c --- /dev/null +++ b/channels/chan_modem_bestdata.c @@ -0,0 +1,596 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * BestData 56SX-92 Voice Modem Driver (Conexant) + * + * Copyright (C) 1999, Mark Spencer and 2001 Jim Dixon + * + * Mark Spencer + * Jim Dixon + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STATE_COMMAND 0 +#define STATE_VOICE 1 +#define STATE_VOICEPLAY 2 + +#define VRA "40" /* Number of 100ms of non-ring after a ring cadence after which we consider the lien to be answered */ +#define VRN "25" /* Number of 100ms of non-ring with no cadence after which we assume an answer */ + +#define RINGT 7000 + +static char *breakcmd = "\020!"; + +static char *desc = "BestData (Conexant V.90 Chipset) VoiceModem Driver"; + +int usecnt; +pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER; + +static char *bestdata_idents[] = { + /* Identify BestData Modem */ + "ACF3_V1.010-V90_P21_FSH", + NULL +}; + +static int bestdata_startrec(struct ast_modem_pvt *p) +{ +static int bestdata_break(struct ast_modem_pvt *p); + + if (p->ministate != STATE_COMMAND) bestdata_break(p); + if (ast_modem_send(p, "AT+VRX", 0) || + ast_modem_expect(p, "CONNECT", 5)) { + ast_log(LOG_WARNING, "Unable to start recording\n"); + return -1; + } + p->ministate = STATE_VOICE; + return 0; +} + +static int bestdata_startplay(struct ast_modem_pvt *p) +{ +static int bestdata_break(struct ast_modem_pvt *p); + + if (p->ministate != STATE_COMMAND) bestdata_break(p); + if (ast_modem_send(p, "AT+VTX", 0) || + ast_modem_expect(p, "CONNECT", 5)) { + ast_log(LOG_WARNING, "Unable to start recording\n"); + return -1; + } + p->ministate = STATE_VOICEPLAY; + return 0; +} + +static int bestdata_break(struct ast_modem_pvt *p) +{ + if (ast_modem_send(p, breakcmd, 2)) { + ast_log(LOG_WARNING, "Failed to break\n"); + return -1; + } + p->ministate = STATE_COMMAND; + usleep(10000); + /* Read any outstanding junk */ + while(!ast_modem_read_response(p, 1)); + if (ast_modem_send(p, "AT", 0)) { + /* Modem might be stuck in some weird mode, try to get it out */ + ast_modem_send(p, "+++", 3); + if (ast_modem_expect(p, "OK", 10)) { + ast_log(LOG_WARNING, "Modem is not responding\n"); + return -1; + } + if (ast_modem_send(p, "AT", 0)) { + ast_log(LOG_WARNING, "Modem is not responding\n"); + return -1; + } + } + if (ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Modem did not respond properly\n"); + return -1; + } + return 0; +} + +static int bestdata_init(struct ast_modem_pvt *p) +{ + if (option_debug) + ast_log(LOG_DEBUG, "bestdata_init()\n"); + if (bestdata_break(p)) + return -1; + /* Force into command mode */ + p->ministate = STATE_COMMAND; + if (ast_modem_send(p, "AT+FCLASS=8", 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to voice mode\n"); + return -1; + } + if (ast_modem_send(p, "AT+VSM=1,8000,0,0", 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to 8000 Hz sampling\n"); + return -1; + } + if (ast_modem_send(p, "AT+VLS=0", 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to telco interface\n"); + return -1; + } + if (ast_modem_send(p, "AT+VRA=" VRA, 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to 'ringback goes away' timer\n"); + return -1; + } + if (ast_modem_send(p, "AT+VRN=" VRN, 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to 'ringback never came timer'\n"); + return -1; + } + if (ast_modem_send(p, "AT+VTD=63", 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to tone detection\n"); + return -1; + } + if (ast_modem_send(p, "AT+VCID=1", 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to enable Caller*ID\n"); + return -1; + } + return 0; +} + +static struct ast_frame *bestdata_handle_escape(struct ast_modem_pvt *p, char esc) +{ + char name[30],nmbr[30]; + time_t now; + + /* Handle escaped characters -- but sometimes we call it directly as + a quick way to cause known responses */ + p->fr.frametype = AST_FRAME_NULL; + p->fr.subclass = 0; + p->fr.data = NULL; + p->fr.datalen = 0; + p->fr.timelen = 0; + p->fr.offset = 0; + p->fr.mallocd = 0; + if (esc) + ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc); + + switch(esc) { + case 'R': /* Pseudo ring */ + time(&now); + if (now > (p->lastring + (RINGT / 1000))) + { /* if stale, treat as new */ + p->gotclid = 0; + } + if (p->gotclid) + { + p->fr.frametype = AST_FRAME_CONTROL; + p->fr.subclass = AST_CONTROL_RING; + } + p->ringt = RINGT; + time(&p->lastring); + return &p->fr; + case 'X': /* Caller-ID Spill */ + if (p->gotclid) return &p->fr; + name[0] = nmbr[0] = 0; + for(;;) + { + char res[1000]; + + if (ast_modem_read_response(p, 5)) break; + strncpy(res, p->response, sizeof(res)); + ast_modem_trim(res); + if (!strncmp(res,"\020.",2)) break; + if (!strncmp(res,"NAME",4)) strcpy(name,res + 7); + if (!strncmp(res,"NMBR",4)) strcpy(nmbr,res + 7); + } + p->gotclid = 1; + if ((!strcmp(name,"O")) || (!strcmp(name,"P"))) name[0] = 0; + if ((!strcmp(nmbr,"O")) || (!strcmp(nmbr,"P"))) nmbr[0] = 0; + if ((name[0]) && (nmbr[0])) snprintf(p->cid,sizeof(p->cid), + "\"%s\" <%s>",name,nmbr); + else if (name[0]) snprintf(p->cid,sizeof(p->cid), + "\"%s\"",name); + else if (nmbr[0]) snprintf(p->cid,sizeof(p->cid), + "%s",nmbr); + if (p->owner) p->owner->callerid = strdup(p->cid); + return &p->fr; + case '@': /* response from "OK" in command mode */ + if (p->owner) + p->owner->state = AST_STATE_UP; + if (bestdata_startrec(p)) return NULL; + p->fr.frametype = AST_FRAME_CONTROL; + p->fr.subclass = AST_CONTROL_RING; + return &p->fr; + case 'b': /* Busy signal */ + p->fr.frametype = AST_FRAME_CONTROL; + p->fr.subclass = AST_CONTROL_BUSY; + return &p->fr; + case 'o': /* Overrun */ + ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n"); + if (ast_modem_send(p, "\0x10E", 2)) + ast_log(LOG_WARNING, "Unable to flush buffers\n"); + return &p->fr; + case '0': /* All the DTMF characters */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '*': + case '#': + case 'A': + case 'B': + case 'C': + case 'D': + p->dtmfrx = esc; /* save this for when its done */ + return &p->fr; + case '/': /* Start of DTMF tone shielding */ + p->dtmfrx = ' '; + return &p->fr; + case '~': /* DTMF transition to off */ + if (p->dtmfrx > ' ') + { + p->fr.frametype = AST_FRAME_DTMF; + p->fr.subclass = p->dtmfrx; + } + p->dtmfrx = 0; + return &p->fr; + case 'u': /* Underrun */ + ast_log(LOG_WARNING, "Data underrun\n"); + /* Fall Through */ + case CHAR_ETX: /* End Transmission */ + case 'd': /* Dialtone */ + case 'c': /* Calling Tone */ + case 'e': /* European version */ + case 'a': /* Answer Tone */ + case 'f': /* Bell Answer Tone */ + case 'T': /* Timing mark */ + case 't': /* Handset off hook */ + case 'h': /* Handset hungup */ + case 0: /* Pseudo signal */ + /* Ignore */ + return &p->fr; + default: + ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc); + } + return &p->fr; +} + +static struct ast_frame *bestdata_read(struct ast_modem_pvt *p) +{ + char result[256]; + short *b; + struct ast_frame *f=NULL; + int res; + int x; + + if (p->ministate == STATE_COMMAND) { + /* Read the first two bytes, first, in case it's a control message */ + fread(result, 1, 2, p->f); + if (result[0] == CHAR_DLE) { + return bestdata_handle_escape(p, result[1]); + } else { + if (p->ringt) /* if ring timeout specified */ + { + x = fileno(p->f); + res = ast_waitfor_n_fd(&x, 1, &p->ringt, NULL); + if (res < 0) { + return NULL; + } + } + if ((result[0] == '\n') || (result[0] == '\r')) + return bestdata_handle_escape(p, 0); + /* Read the rest of the line */ + fgets(result + 2, sizeof(result) - 2, p->f); + ast_modem_trim(result); + if (!strcasecmp(result, "OK")) { + /* If we're in immediate mode, reply now */ + if (p->mode == MODEM_MODE_IMMEDIATE) + return bestdata_handle_escape(p, '@'); + } else + if (!strcasecmp(result, "BUSY")) { + /* Same as a busy signal */ + return bestdata_handle_escape(p, 'b'); + } else + if (!strcasecmp(result, "RING")) { + return bestdata_handle_escape(p, 'R'); + } else + if (!strcasecmp(result, "NO DIALTONE")) { + /* There's no dialtone, so the line isn't working */ + ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev); + return NULL; + } + ast_log(LOG_DEBUG, "Modem said '%s'\n", result); + return bestdata_handle_escape(p, 0); + } + } else { + /* if playing, start recording instead */ + if (p->ministate == STATE_VOICEPLAY) + { + if (bestdata_startrec(p)) return NULL; + } + /* We have to be more efficient in voice mode */ + b = (short *)(p->obuf + p->obuflen); + while (p->obuflen/2 < 240) { + /* Read ahead the full amount */ + res = fread(result, 1, 240 - p->obuflen/2, p->f); + if (res < 1) { + /* If there's nothing there, just continue on */ + if (errno == EAGAIN) + return bestdata_handle_escape(p, 0); + ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno)); + } + for (x=0;xdtmfrx) continue; + return(f); + } + } + /* Generate a 16-bit signed linear value from our + unsigned 8-bit value */ + *(b++) = (((short)result[x]) - 127) * 0xff; + p->obuflen += 2; + } + if (f) break; + } + /* If we have a control frame, return it now */ + if (f) return f; + /* If we get here, we have a complete voice frame */ + p->fr.frametype = AST_FRAME_VOICE; + p->fr.subclass = AST_FORMAT_SLINEAR; + p->fr.timelen = 30; + p->fr.data = p->obuf; + p->fr.datalen = p->obuflen; + p->fr.mallocd = 0; + p->fr.offset = AST_FRIENDLY_OFFSET; + p->fr.src = __FUNCTION__; + if (option_debug) + ast_log(LOG_DEBUG, "bestdata_read(voice frame)\n"); + p->obuflen = 0; + return &p->fr; + } + return NULL; +} + +static int bestdata_write(struct ast_modem_pvt *p, struct ast_frame *f) +{ +unsigned char c,buf[32768]; /* I hope we dont have frames larger then 16K */ +int i,j; +short *sp; +unsigned long u; +#define DLE 16 + + if (p->owner && (p->owner->state == AST_STATE_UP) && + (p->ministate != STATE_VOICEPLAY) && bestdata_startplay(p)) return -1; + sp = (short *) f->data; + /* stick DLE's in ahead of anything else */ + for(i = 0,j = 0; i < f->datalen / 2; i++) + { + *sp *= 3; + u = *sp++ + 32768; + c = u >> 8; + if (c == DLE) buf[j++] = DLE; + buf[j++] = c; + } + do i = fwrite(buf,1,j,p->f); + while ((i == -1) && (errno == EWOULDBLOCK)); + if (i != j) + { + ast_log(LOG_WARNING,"modem short write!!\n"); + return -1; + } + fflush(p->f); + if (option_debug) + ast_log(LOG_DEBUG, "bestdata_write()\n"); + return 0; +} + +static char *bestdata_identify(struct ast_modem_pvt *p) +{ + char identity[256]; + char mfr[80]; + char mdl[80]; + char rev[80]; + ast_modem_send(p, "AT+FMM", 0); + ast_modem_read_response(p, 5); + strncpy(mdl, p->response, sizeof(mdl)); + ast_modem_trim(mdl); + ast_modem_expect(p, "OK", 5); + ast_modem_send(p, "AT+FMI", 0); + ast_modem_read_response(p, 5); + strncpy(mfr, p->response, sizeof(mfr)); + ast_modem_trim(mfr); + ast_modem_expect(p, "OK", 5); + ast_modem_send(p, "AT+FMR", 0); + ast_modem_read_response(p, 5); + strncpy(rev, p->response, sizeof(rev)); + ast_modem_trim(rev); + ast_modem_expect(p, "OK", 5); + snprintf(identity, sizeof(identity), "%s Model %s Revision %s", mfr, mdl, rev); + return strdup(identity); +} + +static void bestdata_incusecnt() +{ + ast_pthread_mutex_lock(&usecnt_lock); + usecnt++; + ast_pthread_mutex_unlock(&usecnt_lock); + ast_update_use_count(); +} + +static void bestdata_decusecnt() +{ + ast_pthread_mutex_lock(&usecnt_lock); + usecnt++; + ast_pthread_mutex_unlock(&usecnt_lock); + ast_update_use_count(); +} + +static int bestdata_answer(struct ast_modem_pvt *p) +{ + p->ringt = 0; + p->lastring = 0; + if (ast_modem_send(p, "AT+VLS=1", 0) || + ast_modem_expect(p, "OK", 10)) { + ast_log(LOG_WARNING, "Unable to answer: %s", p->response); + return -1; + } + return 0; +} + +static int bestdata_dialdigit(struct ast_modem_pvt *p, char digit) +{ + char cmd[80]; + + if (p->ministate != STATE_COMMAND) bestdata_break(p); + snprintf(cmd, sizeof(cmd), "AT+VTS=%c", digit); + if (ast_modem_send(p, cmd, 0) || + ast_modem_expect(p, "OK", 10)) { + ast_log(LOG_WARNING, "Unable to answer: %s", p->response); + return -1; + } + return 0; +} + +static int bestdata_dial(struct ast_modem_pvt *p, char *stuff) +{ + char cmd[800],a[20]; + int i,j; + + if (p->ministate != STATE_COMMAND) + { + bestdata_break(p); + strcpy(cmd,"AT+VTS="); + j = strlen(cmd); + for(i = 0; stuff[i]; i++) + { + switch(stuff[i]) + { + case '!' : + a[0] = stuff[i]; + a[1] = 0; + break; + case ',': + strcpy(a,"[,,100]"); + break; + default: + sprintf(a,"{%c,7}",stuff[i]); + } + if (stuff[i + 1]) strcat(a,","); + strcpy(cmd + j,a); + j += strlen(a); + } + } + else + { + snprintf(cmd, sizeof(cmd), "ATD%c %s", p->dialtype,stuff); + } + if (ast_modem_send(p, cmd, 0)) { + ast_log(LOG_WARNING, "Unable to dial\n"); + return -1; + } + return 0; +} + +static int bestdata_hangup(struct ast_modem_pvt *p) +{ + if (bestdata_break(p)) + return -1; + /* Hangup by switching to data, then back to voice */ + if (ast_modem_send(p, "ATH", 0) || + ast_modem_expect(p, "OK", 8)) { + ast_log(LOG_WARNING, "Unable to set to data mode\n"); + return -1; + } + if (ast_modem_send(p, "AT+FCLASS=8", 0) || + ast_modem_expect(p, "OK", 5)) { + ast_log(LOG_WARNING, "Unable to set to voice mode\n"); + return -1; + } + p->gotclid = 0; + p->ringt = 0; + p->lastring = 0; + p->dtmfrx = 0; + return 0; +} + +static struct ast_modem_driver bestdata_driver = +{ + "BestData", + bestdata_idents, + AST_FORMAT_SLINEAR, + 0, /* Not full duplex */ + bestdata_incusecnt, /* incusecnt */ + bestdata_decusecnt, /* decusecnt */ + bestdata_identify, /* identify */ + bestdata_init, /* init */ + NULL, /* setdev */ + bestdata_read, + bestdata_write, + bestdata_dial, /* dial */ + bestdata_answer, /* answer */ + bestdata_hangup, /* hangup */ + bestdata_startrec, /* start record */ + NULL, /* stop record */ + bestdata_startplay, /* start playback */ + NULL, /* stop playback */ + NULL, /* set silence supression */ + bestdata_dialdigit, /* dialdigit */ +}; + + + +int usecount(void) +{ + int res; + ast_pthread_mutex_lock(&usecnt_lock); + res = usecnt; + ast_pthread_mutex_unlock(&usecnt_lock); + return res; +} + +int load_module(void) +{ + return ast_register_modem_driver(&bestdata_driver); +} + +int unload_module(void) +{ + return ast_unregister_modem_driver(&bestdata_driver); +} + +char *description() +{ + return desc; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} -- cgit v1.2.3