Fix GCC 4.0 build (bug #3662)
[asterisk/asterisk.git] / channels / chan_modem_bestdata.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * BestData 56SX-92 Voice Modem Driver (Conexant)
5  * 
6  * Copyright (C) 1999, Mark Spencer and 2001 Jim Dixon
7  *
8  * Mark Spencer <markster@linux-support.net>
9  * Jim Dixon <jim@lambdatel.com>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  */
14
15 #include <stdio.h>
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <asterisk/lock.h>
22 #include <asterisk/vmodem.h>
23 #include <asterisk/module.h>
24 #include <asterisk/frame.h>
25 #include <asterisk/logger.h>
26 #include <asterisk/options.h>
27
28 #define STATE_COMMAND   0
29 #define STATE_VOICE     1
30 #define STATE_VOICEPLAY 2
31
32 #define VRA "40"                        /* Number of 100ms of non-ring after a ring cadence after which we consider the lien to be answered */
33 #define VRN "25"                        /* Number of 100ms of non-ring with no cadence after which we assume an answer */
34
35 #define RINGT   7000
36
37 static char *breakcmd = "\020!";
38
39 static char *desc = "BestData (Conexant V.90 Chipset) VoiceModem Driver";
40
41 static int usecnt;
42 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
43
44 static char *bestdata_idents[] = {
45         /* Identify BestData Modem */
46         "ACF3_V1.010-V90_P21_FSH",
47         NULL
48 };
49
50 static int bestdata_break(struct ast_modem_pvt *p);
51
52 static int bestdata_startrec(struct ast_modem_pvt *p)
53 {
54         if (p->ministate != STATE_COMMAND) bestdata_break(p);
55         if (ast_modem_send(p, "AT+VRX", 0) ||
56              ast_modem_expect(p, "CONNECT", 5)) {
57                 ast_log(LOG_WARNING, "Unable to start recording\n");
58                 return -1;
59         }
60         p->ministate = STATE_VOICE;
61         return 0;
62 }
63
64 static int bestdata_startplay(struct ast_modem_pvt *p)
65 {
66         if (p->ministate != STATE_COMMAND) bestdata_break(p);
67         if (ast_modem_send(p, "AT+VTX", 0) ||
68              ast_modem_expect(p, "CONNECT", 5)) {
69                 ast_log(LOG_WARNING, "Unable to start recording\n");
70                 return -1;
71         }
72         p->ministate = STATE_VOICEPLAY;
73         return 0;
74 }
75
76 static int bestdata_break(struct ast_modem_pvt *p)
77 {
78         if (ast_modem_send(p, breakcmd, 2)) {
79                 ast_log(LOG_WARNING, "Failed to break\n");
80                 return -1;
81         }
82         p->ministate = STATE_COMMAND;
83         usleep(10000);
84         /* Read any outstanding junk */
85         while(!ast_modem_read_response(p, 1));
86         if (ast_modem_send(p, "AT", 0)) {
87                 /* Modem might be stuck in some weird mode, try to get it out */
88                 ast_modem_send(p, "+++", 3);
89                 if (ast_modem_expect(p, "OK", 10)) {
90                         ast_log(LOG_WARNING, "Modem is not responding\n");
91                         return -1;
92                 }
93                 if (ast_modem_send(p, "AT", 0)) {
94                         ast_log(LOG_WARNING, "Modem is not responding\n");
95                         return -1;
96                 }
97         }
98         if (ast_modem_expect(p, "OK", 5)) {
99                 ast_log(LOG_WARNING, "Modem did not respond properly\n");
100                 return -1;
101         }
102         return 0;
103 }
104
105 static int bestdata_init(struct ast_modem_pvt *p)
106 {
107         if (option_debug)
108                 ast_log(LOG_DEBUG, "bestdata_init()\n");
109         if (bestdata_break(p))
110                 return -1;
111         /* Force into command mode */
112         p->ministate = STATE_COMMAND;
113         if (ast_modem_send(p, "AT+FCLASS=8", 0) ||
114              ast_modem_expect(p, "OK", 5)) {
115                 ast_log(LOG_WARNING, "Unable to set to voice mode\n");
116                 return -1;
117         }
118         if (ast_modem_send(p, "AT+VSM=1,8000,0,0", 0) ||
119              ast_modem_expect(p, "OK", 5)) {
120                 ast_log(LOG_WARNING, "Unable to set to 8000 Hz sampling\n");
121                 return -1;
122         }
123         if (ast_modem_send(p, "AT+VLS=0", 0) ||
124              ast_modem_expect(p, "OK", 5)) {
125                 ast_log(LOG_WARNING, "Unable to set to telco interface\n");
126                 return -1;
127         }
128         if (ast_modem_send(p, "AT+VRA=" VRA, 0) ||
129              ast_modem_expect(p, "OK", 5)) {
130                 ast_log(LOG_WARNING, "Unable to set to 'ringback goes away' timer\n");
131                 return -1;
132         }
133         if (ast_modem_send(p, "AT+VRN=" VRN, 0) ||
134              ast_modem_expect(p, "OK", 5)) {
135                 ast_log(LOG_WARNING, "Unable to set to 'ringback never came timer'\n");
136                 return -1;
137         }
138         if (ast_modem_send(p, "AT+VTD=63", 0) ||
139              ast_modem_expect(p, "OK", 5)) {
140                 ast_log(LOG_WARNING, "Unable to set to tone detection\n");
141                 return -1;
142         }
143         if (ast_modem_send(p, "AT+VCID=1", 0) ||
144              ast_modem_expect(p, "OK", 5)) {
145                 ast_log(LOG_WARNING, "Unable to enable Caller*ID\n");
146                 return -1;
147         }
148         return 0;
149 }
150
151 static struct ast_frame *bestdata_handle_escape(struct ast_modem_pvt *p, char esc)
152 {
153         char name[30]="",nmbr[30]="";
154         time_t  now;
155
156         /* Handle escaped characters -- but sometimes we call it directly as 
157            a quick way to cause known responses */
158         p->fr.frametype = AST_FRAME_NULL;
159         p->fr.subclass = 0;
160         p->fr.data = NULL;
161         p->fr.datalen = 0;
162         p->fr.samples = 0;
163         p->fr.offset = 0;
164         p->fr.mallocd = 0;
165         p->fr.delivery.tv_sec = 0;
166         p->fr.delivery.tv_usec = 0;
167         if (esc)
168                 ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
169         
170         switch(esc) {
171         case 'R': /* Pseudo ring */
172                 time(&now);
173                 if (now > (p->lastring + (RINGT / 1000)))
174                    { /* if stale, treat as new */
175                         p->gotclid = 0;
176                    }
177                 if (p->gotclid)
178                    {
179                         p->fr.frametype = AST_FRAME_CONTROL;
180                         p->fr.subclass = AST_CONTROL_RING;
181                    }
182                 p->ringt = RINGT;
183                 time(&p->lastring);
184                 return &p->fr;
185         case 'X': /* Caller-ID Spill */
186                 if (p->gotclid) return &p->fr;
187                 name[0] = nmbr[0] = 0;
188                 for(;;)
189                    {
190                         char res[1000]="";
191
192                         if (ast_modem_read_response(p, 5)) break;
193                         strncpy(res, p->response, sizeof(res)-1);
194                         ast_modem_trim(res);
195                         if (!strncmp(res,"\020.",2)) break;
196                         if (!strncmp(res,"NAME",4)) strncpy(name,res + 7, sizeof(name) - 1);
197                         if (!strncmp(res,"NMBR",4)) strncpy(nmbr,res + 7, sizeof(nmbr) - 1);
198                    }
199                 p->gotclid = 1;
200                 if ((!strcmp(name,"O")) || (!strcmp(name,"P"))) name[0] = 0;
201                 if ((!strcmp(nmbr,"O")) || (!strcmp(nmbr,"P"))) nmbr[0] = 0;
202                 if (name[0])
203                         strncpy(p->cid_name, name, sizeof(p->cid_name) - 1);
204                 if (nmbr[0])
205                         strncpy(p->cid_num, nmbr, sizeof(p->cid_num) - 1);
206                 if (p->owner) {
207                         p->owner->cid.cid_num = strdup(p->cid_num);
208                         p->owner->cid.cid_name = strdup(p->cid_name);
209                 }
210                 return &p->fr;
211         case '@': /* response from "OK" in command mode */
212                 if (p->owner)
213                         ast_setstate(p->owner, AST_STATE_UP);
214                 if (bestdata_startrec(p)) return NULL;
215                 p->fr.frametype = AST_FRAME_CONTROL;
216                 p->fr.subclass = AST_CONTROL_RING;
217                 return &p->fr;
218         case 'b': /* Busy signal */
219                 p->fr.frametype = AST_FRAME_CONTROL;
220                 p->fr.subclass = AST_CONTROL_BUSY;
221                 return &p->fr;
222         case 'o': /* Overrun */
223                 ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
224                 if (ast_modem_send(p, "\0x10E", 2)) 
225                         ast_log(LOG_WARNING, "Unable to flush buffers\n");
226                 return &p->fr;  
227         case '0':  /* All the DTMF characters */
228         case '1':
229         case '2':
230         case '3':
231         case '4':
232         case '5':
233         case '6':
234         case '7':
235         case '8':
236         case '9':
237         case '*':
238         case '#':
239         case 'A':
240         case 'B':
241         case 'C':
242         case 'D':
243                 p->dtmfrx = esc;  /* save this for when its done */
244                 return &p->fr;  
245         case '/': /* Start of DTMF tone shielding */
246                 p->dtmfrx = ' ';
247                 return &p->fr;  
248         case '~': /* DTMF transition to off */
249                 if (p->dtmfrx > ' ')
250                    {
251                         p->fr.frametype = AST_FRAME_DTMF;
252                         p->fr.subclass = p->dtmfrx;
253                    }
254                 p->dtmfrx = 0;
255                 return &p->fr;  
256         case 'u': /* Underrun */
257                 ast_log(LOG_WARNING, "Data underrun\n");
258                 /* Fall Through */
259         case CHAR_ETX: /* End Transmission */
260         case 'd': /* Dialtone */
261         case 'c': /* Calling Tone */
262         case 'e': /* European version */
263         case 'a': /* Answer Tone */
264         case 'f': /* Bell Answer Tone */
265         case 'T': /* Timing mark */
266         case 't': /* Handset off hook */
267         case 'h': /* Handset hungup */
268         case 0: /* Pseudo signal */
269                 /* Ignore */
270                 return &p->fr;  
271         default:
272                 ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
273         }
274         return &p->fr;
275 }
276
277 static struct ast_frame *bestdata_read(struct ast_modem_pvt *p)
278 {
279         char result[256];
280         short *b;
281         struct ast_frame *f=NULL;
282         int res;
283         int x;
284
285         if (p->ministate == STATE_COMMAND) {
286                 /* Read the first two bytes, first, in case it's a control message */
287                 fread(result, 1, 2, p->f);
288                 if (result[0] == CHAR_DLE) {
289                         return bestdata_handle_escape(p, result[1]);
290                 } else {
291                         if (p->ringt) /* if ring timeout specified */
292                            {
293                                 x = fileno(p->f);
294                                 res = ast_waitfor_n_fd(&x, 1, &p->ringt, NULL);
295                                 if (res < 0) {
296                                         return NULL;
297                                 }
298                            }
299                         if ((result[0] == '\n') || (result[0] == '\r'))
300                                 return bestdata_handle_escape(p, 0);
301                         /* Read the rest of the line */
302                         fgets(result + 2, sizeof(result) - 2, p->f);
303                         ast_modem_trim(result);
304                         if (!strcasecmp(result, "OK")) {
305                                 /* If we're in immediate mode, reply now */
306                                 if (p->mode == MODEM_MODE_IMMEDIATE)
307                                         return bestdata_handle_escape(p, '@');
308                         } else
309                         if (!strcasecmp(result, "BUSY")) {
310                                 /* Same as a busy signal */
311                                 return bestdata_handle_escape(p, 'b');
312                         } else
313                         if (!strcasecmp(result, "RING")) {
314                                 return bestdata_handle_escape(p, 'R');
315                         } else
316                         if (!strcasecmp(result, "NO DIALTONE")) {
317                                 /* There's no dialtone, so the line isn't working */
318                                 ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
319                                 return NULL;
320                         }
321                         ast_log(LOG_DEBUG, "Modem said '%s'\n", result);
322                         return bestdata_handle_escape(p, 0);
323                 }
324         } else {
325                   /* if playing, start recording instead */
326                 if (p->ministate == STATE_VOICEPLAY)
327                    {
328                         if (bestdata_startrec(p)) return NULL;
329                    }
330                 /* We have to be more efficient in voice mode */
331                 b = (short *)(p->obuf + p->obuflen);
332                 while (p->obuflen/2 < 240) {
333                         /* Read ahead the full amount */
334                         res = fread(result, 1, 240 - p->obuflen/2, p->f);
335                         if (res < 1) {
336                                 /* If there's nothing there, just continue on */
337                                 if (errno == EAGAIN)
338                                         return bestdata_handle_escape(p, 0);
339                                 ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno));
340                         }
341                         for (x=0;x<res;x++) {
342                                 /* Process all the bytes that we've read */
343                                 if (result[x] == CHAR_DLE) {
344                                         /* We assume there is no more than one signal frame among our
345                                            data.  */
346                                         if (f) ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n");
347                                           /* if not a DLE in the data */
348                                         if (result[++x] != CHAR_DLE)
349                                            {
350                                                 /* If bestdata_handle_escape says NULL, say it now, doesn't matter
351                                                    what else is there, the connection is dead. */
352                                                 f = bestdata_handle_escape(p, result[x]);
353                                                 if (p->dtmfrx) continue;
354                                                 return(f);
355                                            }
356                                 }
357                                 /* Generate a 16-bit signed linear value from our 
358                                    unsigned 8-bit value */
359                                 *(b++) = (((short)result[x]) - 127) * 0xff;
360                                 p->obuflen += 2;
361                         }
362                         if (f) break;
363                 }
364                 /* If we have a control frame, return it now */
365                 if (f) return f;
366                 /* If we get here, we have a complete voice frame */
367                 p->fr.frametype = AST_FRAME_VOICE;
368                 p->fr.subclass = AST_FORMAT_SLINEAR;
369                 p->fr.samples = 240;
370                 p->fr.data = p->obuf;
371                 p->fr.datalen = p->obuflen;
372                 p->fr.mallocd = 0;
373                 p->fr.delivery.tv_sec = 0;
374                 p->fr.delivery.tv_usec = 0;
375                 p->fr.offset = AST_FRIENDLY_OFFSET;
376                 p->fr.src = __FUNCTION__;
377                 if (option_debug)
378                         ast_log(LOG_DEBUG, "bestdata_read(voice frame)\n");
379                 p->obuflen = 0;
380                 return &p->fr;
381         }
382         return NULL;
383 }
384
385 static int bestdata_write(struct ast_modem_pvt *p, struct ast_frame *f)
386 {
387 unsigned char   c,buf[32768]; /* I hope we dont have frames larger then 16K */
388 int     i,j;
389 short   *sp;
390 unsigned long u;
391 #define DLE     16
392
393         if (p->owner && (p->owner->_state == AST_STATE_UP) && 
394                 (p->ministate != STATE_VOICEPLAY) && bestdata_startplay(p)) return -1;
395         sp = (short *) f->data;
396           /* stick DLE's in ahead of anything else */
397         for(i = 0,j = 0; i < f->datalen / 2; i++)
398            {
399                 *sp *= 3;
400                 u = *sp++ + 32768;
401                 c = u >> 8;
402                 if (c == DLE) buf[j++] = DLE;
403                 buf[j++] = c;
404            }
405         do i = fwrite(buf,1,j,p->f);
406         while ((i == -1) && (errno == EWOULDBLOCK));
407         if (i != j)
408            {
409                 ast_log(LOG_WARNING,"modem short write!!\n");
410                 return -1;
411            }
412         fflush(p->f);
413         if (option_debug)
414                 ast_log(LOG_DEBUG, "bestdata_write()\n");
415         return 0;
416 }
417
418 static char *bestdata_identify(struct ast_modem_pvt *p)
419 {
420         char identity[256];
421         char mfr[80];
422         char mdl[80];
423         char rev[80];
424         ast_modem_send(p, "AT+FMM", 0);
425         ast_modem_read_response(p, 5);
426         strncpy(mdl, p->response, sizeof(mdl)-1);
427         ast_modem_trim(mdl);
428         ast_modem_expect(p, "OK", 5);
429         ast_modem_send(p, "AT+FMI", 0);
430         ast_modem_read_response(p, 5);
431         strncpy(mfr, p->response, sizeof(mfr)-1);
432         ast_modem_trim(mfr);
433         ast_modem_expect(p, "OK", 5);
434         ast_modem_send(p, "AT+FMR", 0);
435         ast_modem_read_response(p, 5);
436         strncpy(rev, p->response, sizeof(rev)-1);
437         ast_modem_trim(rev);
438         ast_modem_expect(p, "OK", 5);
439         snprintf(identity, sizeof(identity), "%s Model %s Revision %s", mfr, mdl, rev);
440         return strdup(identity);
441 }
442
443 static void bestdata_incusecnt(void)
444 {
445         ast_mutex_lock(&usecnt_lock);
446         usecnt++;
447         ast_mutex_unlock(&usecnt_lock);
448         ast_update_use_count();
449 }
450
451 static void bestdata_decusecnt(void)
452 {
453         ast_mutex_lock(&usecnt_lock);
454         usecnt++;
455         ast_mutex_unlock(&usecnt_lock);
456         ast_update_use_count();
457 }
458
459 static int bestdata_answer(struct ast_modem_pvt *p)
460 {
461         p->ringt = 0;
462         p->lastring = 0;
463         if (ast_modem_send(p, "AT+VLS=1", 0) ||
464              ast_modem_expect(p, "OK", 10)) {
465                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
466                 return -1;
467         }
468         return 0;
469 }
470
471 static int bestdata_dialdigit(struct ast_modem_pvt *p, char digit)
472 {
473         char cmd[80];
474
475         if (p->ministate != STATE_COMMAND) bestdata_break(p);
476         snprintf(cmd, sizeof(cmd), "AT+VTS=%c", digit);
477         if (ast_modem_send(p, cmd, 0) ||
478              ast_modem_expect(p, "OK", 10)) {
479                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
480                 return -1;
481         }
482         return 0;
483 }
484
485 static int bestdata_dial(struct ast_modem_pvt *p, char *stuff)
486 {
487         char cmd[800] = "",a[20]="";
488         int i,j;
489
490         if (p->ministate != STATE_COMMAND)
491            {
492                 bestdata_break(p);
493                 strncpy(cmd, "AT+VTS=", sizeof(cmd) - 1);
494                 j = strlen(cmd);
495                 for(i = 0; stuff[i]; i++)
496                    {
497                         switch(stuff[i])
498                            {
499                             case '!' :
500                                 a[0] = stuff[i];
501                                 a[1] = 0;
502                                 break;
503                             case ',':
504                                 strncpy(a, "[,,100]", sizeof(a) - 1);
505                                 break;
506                             default:
507                                 snprintf(a, sizeof(a), "{%c,7}", stuff[i]);
508                            }
509                         if (stuff[i + 1]) strncat(a, ",", sizeof(a) - strlen(a) - 1);
510                         strncpy(cmd + j, a, sizeof(cmd) - j - 1);
511                         j += strlen(a);
512                    }
513            }
514         else
515            {
516                 snprintf(cmd, sizeof(cmd), "ATD%c %s", p->dialtype,stuff);
517            }
518         if (ast_modem_send(p, cmd, 0)) {
519                 ast_log(LOG_WARNING, "Unable to dial\n");
520                 return -1;
521         }
522         return 0;
523 }
524
525 static int bestdata_hangup(struct ast_modem_pvt *p)
526 {
527         if (bestdata_break(p))
528                 return -1;
529         /* Hangup by switching to data, then back to voice */
530         if (ast_modem_send(p, "ATH", 0) ||
531              ast_modem_expect(p, "OK", 8)) {
532                 ast_log(LOG_WARNING, "Unable to set to data mode\n");
533                 return -1;
534         }
535         if (ast_modem_send(p, "AT+FCLASS=8", 0) ||
536              ast_modem_expect(p, "OK", 5)) {
537                 ast_log(LOG_WARNING, "Unable to set to voice mode\n");
538                 return -1;
539         }
540         p->gotclid = 0;
541         p->ringt = 0;
542         p->lastring = 0;
543         p->dtmfrx = 0;
544         return 0;
545 }
546
547 static struct ast_modem_driver bestdata_driver =
548 {
549         "BestData",
550         bestdata_idents,
551         AST_FORMAT_SLINEAR,
552         0,              /* Not full duplex */
553         bestdata_incusecnt,     /* incusecnt */
554         bestdata_decusecnt,     /* decusecnt */
555         bestdata_identify,      /* identify */
556         bestdata_init,  /* init */
557         NULL,   /* setdev */
558         bestdata_read,
559         bestdata_write,
560         bestdata_dial,  /* dial */
561         bestdata_answer,        /* answer */
562         bestdata_hangup,        /* hangup */
563         bestdata_startrec,      /* start record */
564         NULL,   /* stop record */
565         bestdata_startplay,     /* start playback */
566         NULL,   /* stop playback */
567         NULL,   /* set silence supression */
568         bestdata_dialdigit,     /* dialdigit */
569 };
570
571
572
573 int usecount(void)
574 {
575         int res;
576         ast_mutex_lock(&usecnt_lock);
577         res = usecnt;
578         ast_mutex_unlock(&usecnt_lock);
579         return res;
580 }
581
582 int load_module(void)
583 {
584         return ast_register_modem_driver(&bestdata_driver);
585 }
586
587 int unload_module(void)
588 {
589         return ast_unregister_modem_driver(&bestdata_driver);
590 }
591
592 char *description()
593 {
594         return desc;
595 }
596
597 char *key()
598 {
599         return ASTERISK_GPL_KEY;
600 }