Version 0.1.3 from FTP
[asterisk/asterisk.git] / channels / chan_modem_aopen.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * A/Open ITU-56/2 Voice Modem Driver (Rockwell, IS-101, and others)
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15
16 #include <string.h>
17 #include <stdlib.h>
18 #include <errno.h>
19 #include <pthread.h>
20 #include <asterisk/vmodem.h>
21 #include <asterisk/module.h>
22 #include <asterisk/frame.h>
23 #include <asterisk/logger.h>
24 #include <asterisk/options.h>
25
26 #define STATE_COMMAND   0
27 #define STATE_VOICE     1
28
29 #define VRA "40"                        /* Number of 100ms of non-ring after a ring cadence after which we consider the lien to be answered */
30 #define VRN "100"                       /* Number of 100ms of non-ring with no cadence after which we assume an answer */
31
32 static char *breakcmd = "\0x10\0x03";
33
34 static char *desc = "A/Open (Rockwell Chipset) ITU-2 VoiceModem Driver";
35
36 int usecnt;
37 pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
38
39 static char *aopen_idents[] = {
40         /* Identify A/Open Modem */
41         "V2.210-V90_2M_DLP",
42         NULL
43 };
44
45 static int aopen_setdev(struct ast_modem_pvt *p, int dev)
46 {
47         char cmd[80];
48         if (ast_modem_send(p, "AT#VLS?", 0)) {
49                 ast_log(LOG_WARNING, "Unable to select current mode %d\n", dev);
50                 return -1;
51         }
52         if (ast_modem_read_response(p, 5)) {
53                 ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
54                 return -1;
55         }
56         ast_modem_trim(p->response);
57         strncpy(cmd, p->response, sizeof(cmd));
58         if (ast_modem_expect(p, "OK", 5)) {
59                 ast_log(LOG_WARNING, "Modem did not respond properly\n");
60                 return -1;
61         }
62         if (dev == atoi(cmd)) {
63                 /* We're already in the right mode, don't bother changing for fear of
64                    hanging up */
65                 return 0;
66         }
67         snprintf(cmd, sizeof(cmd), "AT#VLS=%d", dev);
68         if (ast_modem_send(p, cmd, 0))  {
69                 ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
70                 return -1;
71         }
72         if (ast_modem_read_response(p, 5)) {
73                 ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
74                 return -1;
75         }
76         ast_modem_trim(p->response);
77         if (strcasecmp(p->response, "VCON") && strcasecmp(p->response, "OK")) {
78                 ast_log(LOG_WARNING, "Unexpected reply: %s\n", p->response);
79                 return -1;
80         }
81         return 0;
82 }
83
84 static int aopen_startrec(struct ast_modem_pvt *p)
85 {
86         if (ast_modem_send(p, "AT#VRX", 0) ||
87              ast_modem_expect(p, "CONNECT", 5)) {
88                 ast_log(LOG_WARNING, "Unable to start recording\n");
89                 return -1;
90         }
91         p->ministate = STATE_VOICE;
92         return 0;
93 }
94
95 static int aopen_break(struct ast_modem_pvt *p)
96 {
97         if (ast_modem_send(p, "\r\n", 2)) {
98                 ast_log(LOG_WARNING, "Failed to send enter?\n");
99                 return -1;
100         }
101         if (ast_modem_send(p, breakcmd, 2)) {
102                 ast_log(LOG_WARNING, "Failed to break\n");
103                 return -1;
104         }
105         if (ast_modem_send(p, "\r\n", 2)) {
106                 ast_log(LOG_WARNING, "Failed to send enter?\n");
107                 return -1;
108         }
109         /* Read any outstanding junk */
110         while(!ast_modem_read_response(p, 1));
111         if (ast_modem_send(p, "AT", 0)) {
112                 /* Modem might be stuck in some weird mode, try to get it out */
113                 ast_modem_send(p, "+++", 3);
114                 if (ast_modem_expect(p, "OK", 10)) {
115                         ast_log(LOG_WARNING, "Modem is not responding\n");
116                         return -1;
117                 }
118                 if (ast_modem_send(p, "AT", 0)) {
119                         ast_log(LOG_WARNING, "Modem is not responding\n");
120                         return -1;
121                 }
122         }
123         if (ast_modem_expect(p, "OK", 5)) {
124                 ast_log(LOG_WARNING, "Modem did not respond properly\n");
125                 return -1;
126         }
127         return 0;
128 }
129
130 static int aopen_init(struct ast_modem_pvt *p)
131 {
132         if (option_debug)
133                 ast_log(LOG_DEBUG, "aopen_init()\n");
134         if (aopen_break(p))
135                 return -1;
136         /* Force into command mode */
137         p->ministate = STATE_COMMAND;
138         if (ast_modem_send(p, "AT#BDR=0", 0) ||
139              ast_modem_expect(p, "OK", 5)) {
140                 ast_log(LOG_WARNING, "Unable to set to auto-baud\n");
141                 return -1;
142         }
143         if (ast_modem_send(p, "AT#CLS=8", 0) ||
144              ast_modem_expect(p, "OK", 5)) {
145                 ast_log(LOG_WARNING, "Unable to set to voice mode\n");
146                 return -1;
147         }
148         if (ast_modem_send(p, "AT#VBS=8", 0) ||
149              ast_modem_expect(p, "OK", 5)) {
150                 ast_log(LOG_WARNING, "Unable to set to 8-bit mode\n");
151                 return -1;
152         }
153         if (ast_modem_send(p, "AT#VSR=8000", 0) ||
154              ast_modem_expect(p, "OK", 5)) {
155                 ast_log(LOG_WARNING, "Unable to set to 8000 Hz sampling\n");
156                 return -1;
157         }
158         if (ast_modem_send(p, "AT#VLS=0", 0) ||
159              ast_modem_expect(p, "OK", 5)) {
160                 ast_log(LOG_WARNING, "Unable to set to telco interface\n");
161                 return -1;
162         }
163         if (ast_modem_send(p, "AT#VRA=" VRA, 0) ||
164              ast_modem_expect(p, "OK", 5)) {
165                 ast_log(LOG_WARNING, "Unable to set to 'ringback goes away' timer\n");
166                 return -1;
167         }
168         if (ast_modem_send(p, "AT#VRN=" VRN, 0) ||
169              ast_modem_expect(p, "OK", 5)) {
170                 ast_log(LOG_WARNING, "Unable to set to 'ringback never came timer'\n");
171                 return -1;
172         }
173         if (ast_modem_send(p, "AT#VTD=3F,3F,3F", 0) ||
174              ast_modem_expect(p, "OK", 5)) {
175                 ast_log(LOG_WARNING, "Unable to set to tone detection\n");
176                 return -1;
177         }
178         
179         return 0;
180 }
181
182 static struct ast_frame *aopen_handle_escape(struct ast_modem_pvt *p, char esc)
183 {
184         /* Handle escaped characters -- but sometimes we call it directly as 
185            a quick way to cause known responses */
186         p->fr.frametype = AST_FRAME_NULL;
187         p->fr.subclass = 0;
188         p->fr.data = NULL;
189         p->fr.datalen = 0;
190         p->fr.timelen = 0;
191         p->fr.offset = 0;
192         p->fr.mallocd = 0;
193         if (esc)
194                 ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
195         
196         switch(esc) {
197         case 'X': /* Pseudo connect */
198                 p->fr.frametype = AST_FRAME_CONTROL;
199                 p->fr.subclass = AST_CONTROL_ANSWER;
200                 if (p->owner)
201                         p->owner->state = AST_STATE_UP;
202                 if (aopen_startrec(p))
203                         return  NULL;
204                 return &p->fr;
205         case 'b': /* Busy signal */
206                 p->fr.frametype = AST_FRAME_CONTROL;
207                 p->fr.subclass = AST_CONTROL_BUSY;
208                 return &p->fr;
209         case 'o': /* Overrun */
210                 ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
211                 if (ast_modem_send(p, "\0x10E", 2)) 
212                         ast_log(LOG_WARNING, "Unable to flush buffers\n");
213                 return &p->fr;  
214         case 'u': /* Underrun */
215                 ast_log(LOG_WARNING, "Data underrun\n");
216                 /* Fall Through */
217         case CHAR_ETX: /* End Transmission */
218         case 'd': /* Dialtone */
219         case 'c': /* Calling Tone */
220         case 'e': /* European version */
221         case 'a': /* Answer Tone */
222         case 'f': /* Bell Answer Tone */
223         case 'T': /* Timing mark */
224         case 't': /* Handset off hook */
225         case 'h': /* Handset hungup */
226         case 0: /* Pseudo signal */
227                 /* Ignore */
228                 return &p->fr;  
229         default:
230                 ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
231         }
232         return &p->fr;
233 }
234
235 static struct ast_frame *aopen_read(struct ast_modem_pvt *p)
236 {
237         char result[256];
238         short *b;
239         struct ast_frame *f=NULL;
240         int res;
241         int x;
242         if (p->ministate == STATE_COMMAND) {
243                 /* Read the first two bytes, first, in case it's a control message */
244                 fread(result, 1, 2, p->f);
245                 if (result[0] == CHAR_DLE) {
246                         return aopen_handle_escape(p, result[1]);
247                         
248                 } else {
249                         if ((result[0] == '\n') || (result[0] == '\r'))
250                                 return aopen_handle_escape(p, 0);
251                         /* Read the rest of the line */
252                         fgets(result + 2, sizeof(result) - 2, p->f);
253                         ast_modem_trim(result);
254                         if (!strcasecmp(result, "VCON")) {
255                                 /* If we're in immediate mode, reply now */
256                                 if (p->mode == MODEM_MODE_IMMEDIATE)
257                                         return aopen_handle_escape(p, 'X');
258                         }
259                         if (!strcasecmp(result, "BUSY")) {
260                                 /* Same as a busy signal */
261                                 return aopen_handle_escape(p, 'b');
262                         }
263                         if (!strcasecmp(result, "NO DIALTONE")) {
264                                 /* There's no dialtone, so the line isn't working */
265                                 ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
266                                 return NULL;
267                         }
268                         ast_log(LOG_DEBUG, "Modem said '%s'\n", result);
269                         return aopen_handle_escape(p, 0);
270                 }
271         } else {
272                 /* We have to be more efficient in voice mode */
273                 b = (short *)(p->obuf + p->obuflen);
274                 while (p->obuflen/2 < 240) {
275                         /* Read ahead the full amount */
276                         res = fread(result, 1, 240 - p->obuflen/2, p->f);
277                         if (res < 1) {
278                                 /* If there's nothing there, just continue on */
279                                 if (errno == EAGAIN)
280                                         return aopen_handle_escape(p, 0);
281                                 ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno));
282                         }
283                         for (x=0;x<res;x++) {
284                                 /* Process all the bytes that we've read */
285                                 if (result[x] == CHAR_DLE) {
286                                         /* We assume there is no more than one signal frame among our
287                                            data.  */
288                                         if (f) 
289                                                 ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n");
290                                         f = aopen_handle_escape(p, result[x+1]);
291                                         /* If aopen_handle_escape says NULL, say it now, doesn't matter
292                                            what else is there, the connection is dead. */
293                                         if (!f)
294                                                 return NULL;
295                                 } else {
296                                 /* Generate a 16-bit signed linear value from our 
297                                    unsigned 8-bit value */
298                                         *(b++) = (((short)result[x]) - 127) * 0xff;
299                                         p->obuflen += 2;
300                                 }
301                         }
302                         if (f)
303                                 break;
304                 }
305                 /* If we have a control frame, return it now */
306                 if (f)
307                         return f;
308                 /* If we get here, we have a complete voice frame */
309                 p->fr.frametype = AST_FRAME_VOICE;
310                 p->fr.subclass = AST_FORMAT_SLINEAR;
311                 p->fr.timelen = 30;
312                 p->fr.data = p->obuf;
313                 p->fr.datalen = p->obuflen;
314                 p->fr.mallocd = 0;
315                 p->fr.offset = AST_FRIENDLY_OFFSET;
316                 p->fr.src = __FUNCTION__;
317                 if (option_debug)
318                         ast_log(LOG_DEBUG, "aopen_read(voice frame)\n");
319                 p->obuflen = 0;
320                 return &p->fr;
321         }
322         return NULL;
323 }
324
325 static int aopen_write(struct ast_modem_pvt *p, struct ast_frame *f)
326 {
327         if (option_debug)
328                 ast_log(LOG_DEBUG, "aopen_write()\n");
329         return 0;
330 }
331
332 static char *aopen_identify(struct ast_modem_pvt *p)
333 {
334         char identity[256];
335         char mfr[80];
336         char mdl[80];
337         char rev[80];
338         ast_modem_send(p, "AT#MDL?", 0);
339         ast_modem_read_response(p, 5);
340         strncpy(mdl, p->response, sizeof(mdl));
341         ast_modem_trim(mdl);
342         ast_modem_expect(p, "OK", 5);
343         ast_modem_send(p, "AT#MFR?", 0);
344         ast_modem_read_response(p, 5);
345         strncpy(mfr, p->response, sizeof(mfr));
346         ast_modem_trim(mfr);
347         ast_modem_expect(p, "OK", 5);
348         ast_modem_send(p, "AT#REV?", 0);
349         ast_modem_read_response(p, 5);
350         strncpy(rev, p->response, sizeof(rev));
351         ast_modem_trim(rev);
352         ast_modem_expect(p, "OK", 5);
353         snprintf(identity, sizeof(identity), "%s Model %s Revision %s", mfr, mdl, rev);
354         return strdup(identity);
355 }
356
357 static void aopen_incusecnt()
358 {
359         pthread_mutex_lock(&usecnt_lock);
360         usecnt++;
361         pthread_mutex_unlock(&usecnt_lock);
362         ast_update_use_count();
363 }
364
365 static void aopen_decusecnt()
366 {
367         pthread_mutex_lock(&usecnt_lock);
368         usecnt++;
369         pthread_mutex_unlock(&usecnt_lock);
370         ast_update_use_count();
371 }
372
373 static int aopen_answer(struct ast_modem_pvt *p)
374 {
375         if (ast_modem_send(p, "ATA", 0) ||
376              ast_modem_expect(p, "VCON", 10)) {
377                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
378                 return -1;
379         }
380         return 0;
381 }
382
383 static int aopen_dialdigit(struct ast_modem_pvt *p, char digit)
384 {
385         char cmd[80];
386         snprintf(cmd, sizeof(cmd), "AT#VTS=%c", digit);
387         if (ast_modem_send(p, cmd, 0) ||
388              ast_modem_expect(p, "VCON", 10)) {
389                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
390                 return -1;
391         }
392         return 0;
393 }
394
395 static int aopen_dial(struct ast_modem_pvt *p, char *stuff)
396 {
397         char cmd[80];
398         snprintf(cmd, sizeof(cmd), "ATD%c %s", p->dialtype,stuff);
399         if (ast_modem_send(p, cmd, 0)) {
400                 ast_log(LOG_WARNING, "Unable to dial\n");
401                 return -1;
402         }
403         return 0;
404 }
405
406 static int aopen_hangup(struct ast_modem_pvt *p)
407 {
408         if (aopen_break(p))
409                 return -1;
410         /* Hangup by switching to data, then back to voice */
411         if (ast_modem_send(p, "ATH", 0) ||
412              ast_modem_expect(p, "OK", 8)) {
413                 ast_log(LOG_WARNING, "Unable to set to data mode\n");
414                 return -1;
415         }
416         if (ast_modem_send(p, "AT#CLS=8", 0) ||
417              ast_modem_expect(p, "OK", 5)) {
418                 ast_log(LOG_WARNING, "Unable to set to voice mode\n");
419                 return -1;
420         }
421         return 0;
422 }
423
424 static struct ast_modem_driver aopen_driver =
425 {
426         "AOpen",
427         aopen_idents,
428         AST_FORMAT_SLINEAR,
429         0,              /* Not full duplex */
430         aopen_incusecnt,        /* incusecnt */
431         aopen_decusecnt,        /* decusecnt */
432         aopen_identify, /* identify */
433         aopen_init,     /* init */
434         aopen_setdev,   /* setdev */
435         aopen_read,
436         aopen_write,
437         aopen_dial,     /* dial */
438         aopen_answer,   /* answer */
439         aopen_hangup,   /* hangup */
440         aopen_startrec, /* start record */
441         NULL,   /* stop record */
442         NULL,   /* start playback */
443         NULL,   /* stop playback */
444         NULL,   /* set silence supression */
445         aopen_dialdigit,        /* dialdigit */
446 };
447
448
449
450 int usecount(void)
451 {
452         int res;
453         pthread_mutex_lock(&usecnt_lock);
454         res = usecnt;
455         pthread_mutex_unlock(&usecnt_lock);
456         return res;
457 }
458
459 int load_module(void)
460 {
461         return ast_register_modem_driver(&aopen_driver);
462 }
463
464 int unload_module(void)
465 {
466         return ast_unregister_modem_driver(&aopen_driver);
467 }
468
469 char *description()
470 {
471         return desc;
472 }
473