Version 0.3.0 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 <asterisk/lock.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 = AST_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)-1);
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 'R': /* Pseudo ring */
198                 p->fr.frametype = AST_FRAME_CONTROL;
199                 p->fr.subclass = AST_CONTROL_RING;
200                 return &p->fr;
201         case 'X': /* Pseudo connect */
202                 p->fr.frametype = AST_FRAME_CONTROL;
203                 p->fr.subclass = AST_CONTROL_RING;
204                 if (p->owner)
205                         ast_setstate(p->owner, AST_STATE_UP);
206                 if (aopen_startrec(p))
207                         return  NULL;
208                 return &p->fr;
209         case 'b': /* Busy signal */
210                 p->fr.frametype = AST_FRAME_CONTROL;
211                 p->fr.subclass = AST_CONTROL_BUSY;
212                 return &p->fr;
213         case 'o': /* Overrun */
214                 ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
215                 if (ast_modem_send(p, "\0x10E", 2)) 
216                         ast_log(LOG_WARNING, "Unable to flush buffers\n");
217                 return &p->fr;  
218         case 'u': /* Underrun */
219                 ast_log(LOG_WARNING, "Data underrun\n");
220                 /* Fall Through */
221         case CHAR_ETX: /* End Transmission */
222         case 'd': /* Dialtone */
223         case 'c': /* Calling Tone */
224         case 'e': /* European version */
225         case 'a': /* Answer Tone */
226         case 'f': /* Bell Answer Tone */
227         case 'T': /* Timing mark */
228         case 't': /* Handset off hook */
229         case 'h': /* Handset hungup */
230         case 0: /* Pseudo signal */
231                 /* Ignore */
232                 return &p->fr;  
233         default:
234                 ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
235         }
236         return &p->fr;
237 }
238
239 static struct ast_frame *aopen_read(struct ast_modem_pvt *p)
240 {
241         char result[256];
242         short *b;
243         struct ast_frame *f=NULL;
244         int res;
245         int x;
246         if (p->ministate == STATE_COMMAND) {
247                 /* Read the first two bytes, first, in case it's a control message */
248                 fread(result, 1, 2, p->f);
249                 if (result[0] == CHAR_DLE) {
250                         return aopen_handle_escape(p, result[1]);
251                         
252                 } else {
253                         if ((result[0] == '\n') || (result[0] == '\r'))
254                                 return aopen_handle_escape(p, 0);
255                         /* Read the rest of the line */
256                         fgets(result + 2, sizeof(result) - 2, p->f);
257                         ast_modem_trim(result);
258                         if (!strcasecmp(result, "VCON")) {
259                                 /* If we're in immediate mode, reply now */
260                                 if (p->mode == MODEM_MODE_IMMEDIATE)
261                                         return aopen_handle_escape(p, 'X');
262                         } else
263                         if (!strcasecmp(result, "BUSY")) {
264                                 /* Same as a busy signal */
265                                 return aopen_handle_escape(p, 'b');
266                         } else
267                         if (!strcasecmp(result, "RING")) {
268                                 return aopen_handle_escape(p, 'R');
269                         } else
270                         if (!strcasecmp(result, "NO DIALTONE")) {
271                                 /* There's no dialtone, so the line isn't working */
272                                 ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
273                                 return NULL;
274                         }
275                         ast_log(LOG_DEBUG, "Modem said '%s'\n", result);
276                         return aopen_handle_escape(p, 0);
277                 }
278         } else {
279                 /* We have to be more efficient in voice mode */
280                 b = (short *)(p->obuf + p->obuflen);
281                 while (p->obuflen/2 < 240) {
282                         /* Read ahead the full amount */
283                         res = fread(result, 1, 240 - p->obuflen/2, p->f);
284                         if (res < 1) {
285                                 /* If there's nothing there, just continue on */
286                                 if (errno == EAGAIN)
287                                         return aopen_handle_escape(p, 0);
288                                 ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno));
289                         }
290                         for (x=0;x<res;x++) {
291                                 /* Process all the bytes that we've read */
292                                 if (result[x] == CHAR_DLE) {
293                                         /* We assume there is no more than one signal frame among our
294                                            data.  */
295                                         if (f) 
296                                                 ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n");
297                                         f = aopen_handle_escape(p, result[x+1]);
298                                         /* If aopen_handle_escape says NULL, say it now, doesn't matter
299                                            what else is there, the connection is dead. */
300                                         if (!f)
301                                                 return NULL;
302                                 } else {
303                                 /* Generate a 16-bit signed linear value from our 
304                                    unsigned 8-bit value */
305                                         *(b++) = (((short)result[x]) - 127) * 0xff;
306                                         p->obuflen += 2;
307                                 }
308                         }
309                         if (f)
310                                 break;
311                 }
312                 /* If we have a control frame, return it now */
313                 if (f)
314                         return f;
315                 /* If we get here, we have a complete voice frame */
316                 p->fr.frametype = AST_FRAME_VOICE;
317                 p->fr.subclass = AST_FORMAT_SLINEAR;
318                 p->fr.timelen = 30;
319                 p->fr.data = p->obuf;
320                 p->fr.datalen = p->obuflen;
321                 p->fr.mallocd = 0;
322                 p->fr.offset = AST_FRIENDLY_OFFSET;
323                 p->fr.src = __FUNCTION__;
324                 if (option_debug)
325                         ast_log(LOG_DEBUG, "aopen_read(voice frame)\n");
326                 p->obuflen = 0;
327                 return &p->fr;
328         }
329         return NULL;
330 }
331
332 static int aopen_write(struct ast_modem_pvt *p, struct ast_frame *f)
333 {
334         if (option_debug)
335                 ast_log(LOG_DEBUG, "aopen_write()\n");
336         return 0;
337 }
338
339 static char *aopen_identify(struct ast_modem_pvt *p)
340 {
341         char identity[256];
342         char mfr[80];
343         char mdl[80];
344         char rev[80];
345         ast_modem_send(p, "AT#MDL?", 0);
346         ast_modem_read_response(p, 5);
347         strncpy(mdl, p->response, sizeof(mdl)-1);
348         ast_modem_trim(mdl);
349         ast_modem_expect(p, "OK", 5);
350         ast_modem_send(p, "AT#MFR?", 0);
351         ast_modem_read_response(p, 5);
352         strncpy(mfr, p->response, sizeof(mfr)-1);
353         ast_modem_trim(mfr);
354         ast_modem_expect(p, "OK", 5);
355         ast_modem_send(p, "AT#REV?", 0);
356         ast_modem_read_response(p, 5);
357         strncpy(rev, p->response, sizeof(rev)-1);
358         ast_modem_trim(rev);
359         ast_modem_expect(p, "OK", 5);
360         snprintf(identity, sizeof(identity), "%s Model %s Revision %s", mfr, mdl, rev);
361         return strdup(identity);
362 }
363
364 static void aopen_incusecnt()
365 {
366         ast_pthread_mutex_lock(&usecnt_lock);
367         usecnt++;
368         ast_pthread_mutex_unlock(&usecnt_lock);
369         ast_update_use_count();
370 }
371
372 static void aopen_decusecnt()
373 {
374         ast_pthread_mutex_lock(&usecnt_lock);
375         usecnt++;
376         ast_pthread_mutex_unlock(&usecnt_lock);
377         ast_update_use_count();
378 }
379
380 static int aopen_answer(struct ast_modem_pvt *p)
381 {
382         if (ast_modem_send(p, "ATA", 0) ||
383              ast_modem_expect(p, "VCON", 10)) {
384                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
385                 return -1;
386         }
387         return 0;
388 }
389
390 static int aopen_dialdigit(struct ast_modem_pvt *p, char digit)
391 {
392         char cmd[80];
393         snprintf(cmd, sizeof(cmd), "AT#VTS=%c", digit);
394         if (ast_modem_send(p, cmd, 0) ||
395              ast_modem_expect(p, "VCON", 10)) {
396                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
397                 return -1;
398         }
399         return 0;
400 }
401
402 static int aopen_dial(struct ast_modem_pvt *p, char *stuff)
403 {
404         char cmd[80];
405         snprintf(cmd, sizeof(cmd), "ATD%c %s", p->dialtype,stuff);
406         if (ast_modem_send(p, cmd, 0)) {
407                 ast_log(LOG_WARNING, "Unable to dial\n");
408                 return -1;
409         }
410         return 0;
411 }
412
413 static int aopen_hangup(struct ast_modem_pvt *p)
414 {
415         if (aopen_break(p))
416                 return -1;
417         /* Hangup by switching to data, then back to voice */
418         if (ast_modem_send(p, "ATH", 0) ||
419              ast_modem_expect(p, "OK", 8)) {
420                 ast_log(LOG_WARNING, "Unable to set to data mode\n");
421                 return -1;
422         }
423         if (ast_modem_send(p, "AT#CLS=8", 0) ||
424              ast_modem_expect(p, "OK", 5)) {
425                 ast_log(LOG_WARNING, "Unable to set to voice mode\n");
426                 return -1;
427         }
428         return 0;
429 }
430
431 static struct ast_modem_driver aopen_driver =
432 {
433         "AOpen",
434         aopen_idents,
435         AST_FORMAT_SLINEAR,
436         0,              /* Not full duplex */
437         aopen_incusecnt,        /* incusecnt */
438         aopen_decusecnt,        /* decusecnt */
439         aopen_identify, /* identify */
440         aopen_init,     /* init */
441         aopen_setdev,   /* setdev */
442         aopen_read,
443         aopen_write,
444         aopen_dial,     /* dial */
445         aopen_answer,   /* answer */
446         aopen_hangup,   /* hangup */
447         aopen_startrec, /* start record */
448         NULL,   /* stop record */
449         NULL,   /* start playback */
450         NULL,   /* stop playback */
451         NULL,   /* set silence supression */
452         aopen_dialdigit,        /* dialdigit */
453 };
454
455
456
457 int usecount(void)
458 {
459         int res;
460         ast_pthread_mutex_lock(&usecnt_lock);
461         res = usecnt;
462         ast_pthread_mutex_unlock(&usecnt_lock);
463         return res;
464 }
465
466 int load_module(void)
467 {
468         return ast_register_modem_driver(&aopen_driver);
469 }
470
471 int unload_module(void)
472 {
473         return ast_unregister_modem_driver(&aopen_driver);
474 }
475
476 char *description()
477 {
478         return desc;
479 }
480
481 char *key()
482 {
483         return ASTERISK_GPL_KEY;
484 }