Fix i4l breakage (bug #5729)
[asterisk/asterisk.git] / channels / chan_modem_i4l.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief ISDN4Linux TTY Driver
22  * 
23  * \ingroup channel_drivers
24  */
25
26 #include <stdio.h>
27
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/lock.h"
39 #include "asterisk/vmodem.h"
40 #include "asterisk/module.h"
41 #include "asterisk/frame.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/options.h"
44 #include "asterisk/dsp.h"
45 #include "asterisk/callerid.h"
46 #include "asterisk/ulaw.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/utils.h"
49
50 #define STATE_COMMAND   0
51 #define STATE_VOICE     1
52
53 static char *breakcmd = "\0x10\0x14\0x10\0x3";
54
55 static char *desc = "ISDN4Linux Emulated Modem Driver";
56
57 static int usecnt;
58 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
59
60 static char *i4l_idents[] = {
61         /* Identify ISDN4Linux Driver */
62         "Linux ISDN",
63         NULL
64 };
65
66 static int i4l_setdev(struct ast_modem_pvt *p, int dev)
67 {
68         char cmd[80];
69         if ((dev != MODEM_DEV_TELCO) && (dev != MODEM_DEV_TELCO_SPK)) {
70                 ast_log(LOG_WARNING, "ISDN4Linux only supports telco device, not %d.\n", dev);
71                 return -1;
72         } else  /* Convert DEV to our understanding of it */
73                 dev = 2;
74         if (ast_modem_send(p, "AT+VLS?", 0)) {
75                 ast_log(LOG_WARNING, "Unable to select current mode %d\n", dev);
76                 return -1;
77         }
78         if (ast_modem_read_response(p, 5)) {
79                 ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
80                 return -1;
81         }
82         ast_modem_trim(p->response);
83         strncpy(cmd, p->response, sizeof(cmd)-1);
84         if (ast_modem_expect(p, "OK", 5)) {
85                 ast_log(LOG_WARNING, "Modem did not respond properly\n");
86                 return -1;
87         }
88         if (dev == atoi(cmd)) {
89                 /* We're already in the right mode, don't bother changing for fear of
90                    hanging up */
91                 return 0;
92         }
93         snprintf(cmd, sizeof(cmd), "AT+VLS=%d", dev);
94         if (ast_modem_send(p, cmd, 0))  {
95                 ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
96                 return -1;
97         }
98         if (ast_modem_read_response(p, 5)) {
99                 ast_log(LOG_WARNING, "Unable to select device %d\n", dev);
100                 return -1;
101         }
102         ast_modem_trim(p->response);
103         if (strcasecmp(p->response, "VCON") && strcasecmp(p->response, "OK")) {
104                 ast_log(LOG_WARNING, "Unexpected reply: %s\n", p->response);
105                 return -1;
106         }
107         return 0;
108 }
109
110 static int i4l_startrec(struct ast_modem_pvt *p)
111 {
112         if (ast_modem_send(p, "AT+VRX+VTX", 0) ||
113              ast_modem_expect(p, "CONNECT", 5)) {
114                 ast_log(LOG_WARNING, "Unable to start recording\n");
115                 return -1;
116         }
117         p->ministate = STATE_VOICE;
118         
119         /*  let ast dsp detect dtmf */
120         if (p->dtmfmode & MODEM_DTMF_AST) {
121                 if (p->dsp) {
122                         ast_log(LOG_DEBUG, "Already have a dsp on %s?\n", p->dev);
123                 } else {
124                         p->dsp = ast_dsp_new();
125                         if (p->dsp) {
126                                 ast_log(LOG_DEBUG, "Detecting DTMF inband with sw DSP on %s\n",p->dev);
127                                 ast_dsp_set_features(p->dsp, DSP_FEATURE_DTMF_DETECT|DSP_FEATURE_FAX_DETECT);
128                                 ast_dsp_digitmode(p->dsp, DSP_DIGITMODE_DTMF | 0);
129                         }
130                 }
131         }
132
133         return 0;
134 }
135
136 static int i4l_break(struct ast_modem_pvt *p)
137 {
138         if (ast_modem_send(p, breakcmd, 2)) {
139                 ast_log(LOG_WARNING, "Failed to break\n");
140                 return -1;
141         }
142         if (ast_modem_send(p, "\r\n", 2)) {
143                 ast_log(LOG_WARNING, "Failed to send enter?\n");
144                 return -1;
145         }
146 #if 0
147         /* Read any outstanding junk */
148         while(!ast_modem_read_response(p, 1));
149 #endif
150         if (ast_modem_send(p, "AT", 0)) {
151                 /* Modem might be stuck in some weird mode, try to get it out */
152                 ast_modem_send(p, "+++", 3);
153                 if (ast_modem_expect(p, "OK", 10)) {
154                         ast_log(LOG_WARNING, "Modem is not responding\n");
155                         return -1;
156                 }
157                 if (ast_modem_send(p, "AT", 0)) {
158                         ast_log(LOG_WARNING, "Modem is not responding\n");
159                         return -1;
160                 }
161         }
162         if (ast_modem_expect(p, "OK", 5)) {
163                 ast_log(LOG_WARNING, "Modem did not respond properly\n");
164                 return -1;
165         }
166         return 0;
167 }
168
169 static int i4l_init(struct ast_modem_pvt *p)
170 {
171         char cmd[256];
172         if (option_debug)
173                 ast_log(LOG_DEBUG, "i4l_init()\n");
174         if (i4l_break(p))
175                 return -1;
176         /* Force into command mode */
177         p->ministate = STATE_COMMAND;
178         if (ast_modem_send(p, "AT+FCLASS=8", 0) ||
179              ast_modem_expect(p, "OK", 5)) {
180                 ast_log(LOG_WARNING, "Unable to set to voice mode\n");
181                 return -1;
182         }
183         if (!ast_strlen_zero(p->msn)) {
184                 snprintf(cmd, sizeof(cmd), "AT&E%s", p->msn);
185                 if (ast_modem_send(p, cmd, 0) ||
186                     ast_modem_expect(p, "OK", 5)) {
187                         ast_log(LOG_WARNING, "Unable to set MSN to %s\n", p->msn);
188                         return -1;
189                 }
190         }
191         if (!ast_strlen_zero(p->incomingmsn)) {
192                 char *q;
193                 snprintf(cmd, sizeof(cmd), "AT&L%s", p->incomingmsn);
194                 /* translate , into ; since that is the seperator I4L uses, but can't be directly */
195                 /* put in the config file because it will interpret the rest of the line as comment. */
196                 q = cmd+4;
197                 while (*q) {
198                         if (*q == ',') *q = ';';
199                         ++q;
200                 }
201                 if (ast_modem_send(p, cmd, 0) ||
202                     ast_modem_expect(p, "OK", 5)) {
203                         ast_log(LOG_WARNING, "Unable to set Listen to %s\n", p->msn);
204                         return -1;
205                 }
206         }
207         if (ast_modem_send(p, "AT&D2", 0) ||
208              ast_modem_expect(p, "OK", 5)) {
209                 ast_log(LOG_WARNING, "Unable to set to DTR disconnect mode\n");
210                 return -1;
211         }
212         if (ast_modem_send(p, "ATS18=1", 0) ||
213              ast_modem_expect(p, "OK", 5)) {
214                 ast_log(LOG_WARNING, "Unable to set to audio only mode\n");
215                 return -1;
216         }
217         if (ast_modem_send(p, "ATS13.6=1", 0) ||
218              ast_modem_expect(p, "OK", 5)) {
219                 ast_log(LOG_WARNING, "Unable to set to RUNG indication\n");
220                 return -1;
221         }
222         if (ast_modem_send(p, "ATS14=4", 0) ||
223              ast_modem_expect(p, "OK", 5)) {
224                 ast_log(LOG_WARNING, "Unable to set to transparent mode\n");
225                 return -1;
226         }
227         if (ast_modem_send(p, "ATS23=9", 0) ||
228              ast_modem_expect(p, "OK", 5)) {
229                 ast_log(LOG_WARNING, "Unable to set to transparent/ringing mode\n");
230                 return -1;
231         }
232
233         if (ast_modem_send(p, "AT+VSM=6", 0) ||
234              ast_modem_expect(p, "OK", 5)) {
235                 ast_log(LOG_WARNING, "Unable to set to muLAW mode\n");
236                 return -1;
237         }
238         if (ast_modem_send(p, "AT+VLS=2", 0) ||
239              ast_modem_expect(p, "OK", 5)) {
240                 ast_log(LOG_WARNING, "Unable to set to phone line interface\n");
241                 return -1;
242         }
243         p->escape = 0;
244         return 0;
245 }
246
247 static struct ast_frame *i4l_handle_escape(struct ast_modem_pvt *p, char esc)
248 {
249         /* Handle escaped characters -- but sometimes we call it directly as 
250            a quick way to cause known responses */
251         p->fr.frametype = AST_FRAME_NULL;
252         p->fr.subclass = 0;
253         p->fr.data = NULL;
254         p->fr.datalen = 0;
255         p->fr.samples = 0;
256         p->fr.offset = 0;
257         p->fr.mallocd = 0;
258         p->fr.delivery.tv_sec = 0;
259         p->fr.delivery.tv_usec = 0;
260         if (esc && option_debug)
261                 ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
262         
263         switch(esc) {
264         case 'R': /* Pseudo ring */
265                 p->fr.frametype = AST_FRAME_CONTROL;
266                 p->fr.subclass = AST_CONTROL_RING;
267                 return &p->fr;
268         case 'I': /* Pseudo ringing */
269                 p->fr.frametype = AST_FRAME_CONTROL;
270                 p->fr.subclass =  AST_CONTROL_RINGING;
271                 return &p->fr;
272         case 'X': /* Pseudo connect */
273                 p->fr.frametype = AST_FRAME_CONTROL;
274                 p->fr.subclass = AST_CONTROL_ANSWER;
275                 if (p->owner)
276                         ast_setstate(p->owner, AST_STATE_UP);
277                 if (i4l_startrec(p))
278                         return  NULL;
279                 return &p->fr;
280         case 'b': /* Busy signal */
281                 p->fr.frametype = AST_FRAME_CONTROL;
282                 p->fr.subclass = AST_CONTROL_BUSY;
283                 return &p->fr;
284         case 'o': /* Overrun */
285                 ast_log(LOG_WARNING, "Overflow on modem, flushing buffers\n");
286                 if (ast_modem_send(p, "\0x10E", 2)) 
287                         ast_log(LOG_WARNING, "Unable to flush buffers\n");
288                 return &p->fr;  
289         case CHAR_ETX: /* End Transmission */
290                 return NULL;
291         case 'u': /* Underrun */
292                 ast_log(LOG_WARNING, "Data underrun\n");
293                 /* Fall Through */
294         case 'd': /* Dialtone */
295         case 'c': /* Calling Tone */
296         case 'e': /* European version */
297         case 'a': /* Answer Tone */
298         case 'f': /* Bell Answer Tone */
299         case 'T': /* Timing mark */
300         case 't': /* Handset off hook */
301         case 'h': /* Handset hungup */
302                 /* Ignore */
303                 if (option_debug)
304                         ast_log(LOG_DEBUG, "Ignoring Escaped character '%c' (%d)\n", esc, esc);
305                 return &p->fr;
306         case '0':
307         case '1':
308         case '2':
309         case '3':
310         case '4':
311         case '5':
312         case '6':
313         case '7':
314         case '8':
315         case '9':
316         case '*':
317         case '#':
318                 ast_log(LOG_DEBUG, "Detected outband DTMF digit: '%c' (%d)\n", esc, esc);
319                 p->fr.frametype=AST_FRAME_DTMF;
320                 p->fr.subclass=esc;
321                 return &p->fr;
322         case 0: /* Pseudo signal */
323                 return &p->fr;
324         default:
325                 ast_log(LOG_DEBUG, "Unknown Escaped character '%c' (%d)\n", esc, esc);
326         }
327         return &p->fr;
328 }
329
330 static struct ast_frame *i4l_read(struct ast_modem_pvt *p)
331 {
332         unsigned char result[256];
333         short *b;
334         struct ast_frame *f=NULL;
335         int res;
336         int x;
337         if (p->ministate == STATE_COMMAND) {
338                 /* Read the first two bytes, first, in case it's a control message */
339                 res = read(p->fd, result, 2);
340                 if (res < 2) {
341                         /* short read, means there was a hangup? */
342                         /* (or is this also possible without hangup?) */
343                         /* Anyway, reading from unitialized buffers is a bad idea anytime. */
344                         if (errno == EAGAIN)
345                                 return i4l_handle_escape(p, 0);
346                         return NULL;
347                 }
348                 if (result[0] == CHAR_DLE) {
349                         return i4l_handle_escape(p, result[1]);
350                         
351                 } else {
352                         if ((result[0] == '\n') || (result[0] == '\r'))
353                                 return i4l_handle_escape(p, 0);
354                         /* Read the rest of the line */
355                         fgets(result + 2, sizeof(result) - 2, p->f);
356                         ast_modem_trim(result);
357                         if (!strcasecmp(result, "VCON")) {
358                                 /* If we're in immediate mode, reply now */
359 /*                              if (p->mode == MODEM_MODE_IMMEDIATE) */
360                                         return i4l_handle_escape(p, 'X');
361                         } else
362                         if (!strcasecmp(result, "BUSY")) {
363                                 /* Same as a busy signal */
364                                 return i4l_handle_escape(p, 'b');
365                         } else
366                         if (!strncasecmp(result, "CALLER NUMBER: ", 15 )) {
367                                 strncpy(p->cid_num, result + 15, sizeof(p->cid_num)-1);
368                                 return i4l_handle_escape(p, 0);
369                         } else
370                         if (!strcasecmp(result, "RINGING")) {
371                                 if (option_verbose > 2)
372                                         ast_verbose(VERBOSE_PREFIX_3 "%s is ringing...\n", p->dev);
373                                 return i4l_handle_escape(p, 'I');
374                         } else
375                         if (!strncasecmp(result, "RUNG", 4)) {
376                                 /* PM2002: the line was hung up before we picked it up, bye bye */
377                                 if (option_verbose > 2) 
378                                         ast_verbose(VERBOSE_PREFIX_3 "%s was hung up on before we answered\n", p->dev);
379                                 return NULL;
380                         } else
381                         if (!strncasecmp(result, "RING", 4)) {
382                                 if (result[4]=='/') 
383                                         strncpy(p->dnid, result + 5, sizeof(p->dnid)-1);
384                                 return i4l_handle_escape(p, 'R');
385                         } else
386                         if (!strcasecmp(result, "NO CARRIER")) {
387                                 if (option_verbose > 2) 
388                                         ast_verbose(VERBOSE_PREFIX_3 "%s hung up on\n", p->dev);
389                                 return NULL;
390                         } else
391                         if (!strcasecmp(result, "NO DIALTONE")) {
392                                 /* There's no dialtone, so the line isn't working */
393                                 ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
394                                 return NULL;
395                         }
396                         if (option_debug)
397                                 ast_log(LOG_DEBUG, "Modem said '%s'\n", result);
398                         return i4l_handle_escape(p, 0);
399                 }
400         } else {
401                 /* We have to be more efficient in voice mode */
402                 b = (short *)(p->obuf + p->obuflen);
403                 while (p->obuflen/2 < 240) {
404                         /* Read ahead the full amount */
405                         res = read(p->fd, result, 240 - p->obuflen/2);
406                         if (res < 1) {
407                                 /* If there's nothing there, just continue on */
408                                 if (errno == EAGAIN)
409                                         return i4l_handle_escape(p, 0);
410                                 ast_log(LOG_WARNING, "Read failed: %s\n", strerror(errno));
411                                 return NULL;
412                         }
413                         
414                         for (x=0;x<res;x++) {
415                                 /* Process all the bytes that we've read */
416                                 switch(result[x]) {
417                                 case CHAR_DLE:
418 #if 0
419                                         ast_log(LOG_DEBUG, "Ooh, an escape at %d...\n", x);
420 #endif
421                                         if (!p->escape) {
422                                                 /* Note that next value is
423                                                    an escape, and continue. */
424                                                 p->escape++;
425                                                 break;
426                                         } else {
427                                                 /* Send as is -- fallthrough */
428                                                 p->escape = 0;
429                                         }
430                                 default:
431                                         if (p->escape) {
432                                                 ast_log(LOG_DEBUG, "Value of escape is %c (%d)...\n", result[x] < 32 ? '^' : result[x], result[x]);
433                                                 p->escape = 0;
434                                                 if (f) 
435                                                         ast_log(LOG_WARNING, "Warning: Dropped a signal frame\n");
436                                                 f = i4l_handle_escape(p, result[x]);
437                                                 /* If i4l_handle_escape says NULL, say it now, doesn't matter
438                                                 what else is there, the connection is dead. */
439                                                 if (!f)
440                                                         return NULL;
441                                         } else {
442                                                 *(b++) = AST_MULAW((int)result[x]);
443                                                 p->obuflen += 2;
444                                         }
445                                 }
446                         }
447                         if (f)
448                                 break;
449                 }
450                 if (f) {
451                         if( ! (!(p->dtmfmode & MODEM_DTMF_I4L) && f->frametype == AST_FRAME_DTMF))
452                         return f;
453                 }
454
455                 /* If we get here, we have a complete voice frame */
456                 p->fr.frametype = AST_FRAME_VOICE;
457                 p->fr.subclass = AST_FORMAT_SLINEAR;
458                 p->fr.samples = 240;
459                 p->fr.data = p->obuf;
460                 p->fr.datalen = p->obuflen;
461                 p->fr.mallocd = 0;
462                 p->fr.delivery.tv_sec = 0;
463                 p->fr.delivery.tv_usec = 0;
464                 p->fr.offset = AST_FRIENDLY_OFFSET;
465                 p->fr.src = __FUNCTION__;
466                 p->obuflen = 0;
467
468                 /* process with dsp */
469                 if (p->dsp) {
470                         f = ast_dsp_process(p->owner, p->dsp, &p->fr);
471                         if (f && (f->frametype == AST_FRAME_DTMF)) {
472                                 ast_log(LOG_DEBUG, "Detected inband DTMF digit: %c on %s\n", f->subclass, p->dev);
473                                 if (f->subclass == 'f') {
474                                         /* Fax tone -- Handle and return NULL */
475                                         struct ast_channel *ast = p->owner;
476                                         if (!p->faxhandled) {
477                                                 p->faxhandled++;
478                                                 if (strcmp(ast->exten, "fax")) {
479                                                         const char *target_context = ast_strlen_zero(ast->macrocontext) ? ast->context : ast->macrocontext;
480                                                         
481                                                         if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) {
482                                                                 if (option_verbose > 2)
483                                                                         ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", ast->name);
484                                                                 /* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
485                                                                 pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
486                                                                 if (ast_async_goto(ast, target_context, "fax", 1))
487                                                                         ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
488                                                         } else
489                                                                 ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
490                                                 } else
491                                                         ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
492                                         } else
493                                                 ast_log(LOG_DEBUG, "Fax already handled\n");
494                                         p->fr.frametype = AST_FRAME_NULL;
495                                         p->fr.subclass = 0;
496                                         f = &p->fr;
497                                 }
498                                 return f;
499                         }
500                 }
501                 
502                 return &p->fr;
503         }
504         return NULL;
505 }
506
507 static int i4l_write(struct ast_modem_pvt *p, struct ast_frame *f)
508 {
509 #define MAX_WRITE_SIZE 2048
510         unsigned char result[MAX_WRITE_SIZE << 1];
511         unsigned char b;
512         int bpos=0, x;
513         int res;
514         if (f->datalen > MAX_WRITE_SIZE) {
515                 ast_log(LOG_WARNING, "Discarding too big frame of size %d\n", f->datalen);
516                 return -1;
517         }
518         if (f->frametype != AST_FRAME_VOICE) {
519                 ast_log(LOG_WARNING, "Don't know how to handle %d type frames\n", f->frametype);
520                 return -1;
521         }
522         if (f->subclass != AST_FORMAT_SLINEAR) {
523                 ast_log(LOG_WARNING, "Don't know how to handle anything but signed linear frames\n");
524                 return -1;
525         }
526         for (x=0;x<f->datalen/2;x++) {
527                 b = AST_LIN2MU(((short *)f->data)[x]);
528                 result[bpos++] = b;
529                 if (b == CHAR_DLE)
530                         result[bpos++]=b;
531         }
532 #if 0
533         res = fwrite(result, bpos, 1, p->f);
534         res *= bpos;
535 #else
536         res = write(p->fd, result, bpos);
537 #endif
538         if (res < 1) {
539                 if (errno != EAGAIN) {
540                         ast_log(LOG_WARNING, "Failed to write buffer\n");
541                         return -1;
542                 }
543         }
544 #if 0
545         printf("Result of write is %d\n", res);
546 #endif
547         return 0;
548 }
549
550 static char *i4l_identify(struct ast_modem_pvt *p)
551 {
552         return strdup("Linux ISDN");
553 }
554
555 static void i4l_incusecnt(void)
556 {
557         ast_mutex_lock(&usecnt_lock);
558         usecnt++;
559         ast_mutex_unlock(&usecnt_lock);
560         ast_update_use_count();
561 }
562
563 static void i4l_decusecnt(void)
564 {
565         ast_mutex_lock(&usecnt_lock);
566         usecnt++;
567         ast_mutex_unlock(&usecnt_lock);
568         ast_update_use_count();
569 }
570
571 static int i4l_answer(struct ast_modem_pvt *p)
572 {
573         if (ast_modem_send(p, "ATA\r", 4) ||
574              ast_modem_expect(p, "VCON", 10)) {
575                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
576                 return -1;
577         }
578 #if 1
579         if (ast_modem_send(p, "AT+VDD=0,8", 0) ||
580              ast_modem_expect(p, "OK", 5)) {
581                 ast_log(LOG_WARNING, "Unable to set to phone line interface\n");
582                 return -1;
583         }
584 #endif
585         if (ast_modem_send(p, "AT+VTX+VRX", 0) ||
586              ast_modem_expect(p, "CONNECT", 10)) {
587                 ast_log(LOG_WARNING, "Unable to answer: %s", p->response);
588                 return -1;
589         }
590         p->ministate = STATE_VOICE;
591
592         /*  let ast dsp detect dtmf */
593         if (p->dtmfmode & MODEM_DTMF_AST) {
594                 if (p->dsp) {
595                         ast_log(LOG_DEBUG, "Already have a dsp on %s?\n", p->dev);
596                 } else {
597                         p->dsp = ast_dsp_new();
598                         if (p->dsp) {
599                                 ast_log(LOG_DEBUG, "Detecting DTMF inband with sw DSP on %s\n",p->dev);
600                                 ast_dsp_set_features(p->dsp, DSP_FEATURE_DTMF_DETECT|DSP_FEATURE_FAX_DETECT);
601                                 ast_dsp_digitmode(p->dsp, DSP_DIGITMODE_DTMF | 0);
602                         }
603                 }
604         }
605
606         return 0;
607 }
608
609 static int i4l_dialdigit(struct ast_modem_pvt *p, char digit)
610 {
611         char c[2];
612         if (p->ministate == STATE_VOICE) {
613                 if (p->dtmfmodegen & MODEM_DTMF_I4L) {
614                 c[0] = CHAR_DLE;
615                 c[1] = digit;
616                 write(p->fd, c, 2);
617                         ast_log(LOG_DEBUG, "Send ISDN out-of-band DTMF %c\n",digit);
618                 }
619                 if(p->dtmfmodegen & MODEM_DTMF_AST) {
620                         ast_log(LOG_DEBUG, "Generating inband DTMF\n");
621                         return -1;
622                 }
623         } else
624                 ast_log(LOG_DEBUG, "Asked to send digit but call not up on %s\n", p->dev);
625         return 0;
626 }
627
628 static int i4l_dial(struct ast_modem_pvt *p, char *stuff)
629 {
630         char cmd[80];
631         char tmpmsn[255];
632         struct ast_channel *c = p->owner;
633
634         /* Find callerid number first, to set the correct A number */
635         if (c && c->cid.cid_num && !(c->cid.cid_pres & 0x20)) {
636             snprintf(tmpmsn, sizeof(tmpmsn), ",%s,", c->cid.cid_num);
637             if(!ast_strlen_zero(p->outgoingmsn) && strstr(p->outgoingmsn,tmpmsn) != NULL) {
638               /* Tell ISDN4Linux to use this as A number */
639               snprintf(cmd, sizeof(cmd), "AT&E%s\n", c->cid.cid_num);
640               if (ast_modem_send(p, cmd, strlen(cmd))) {
641                 ast_log(LOG_WARNING, "Unable to set A number to %s\n", c->cid.cid_num);
642               }
643
644             } else {
645               ast_log(LOG_WARNING, "Outgoing MSN %s not allowed (see outgoingmsn=%s in modem.conf)\n",c->cid.cid_num,p->outgoingmsn);
646             }
647         }
648
649         snprintf(cmd, sizeof(cmd), "ATD%c %s\n", p->dialtype,stuff);
650         if (ast_modem_send(p, cmd, strlen(cmd))) {
651                 ast_log(LOG_WARNING, "Unable to dial\n");
652                 return -1;
653         }
654         return 0;
655 }
656
657 static int i4l_hangup(struct ast_modem_pvt *p)
658 {
659         char dummy[50];
660         int dtr = TIOCM_DTR;
661
662         /* free the memory used by the DSP */
663         if (p->dsp) {
664                 ast_dsp_free(p->dsp);
665                 p->dsp = NULL;
666         }
667
668         /* down DTR to hangup modem */
669         ioctl(p->fd, TIOCMBIC, &dtr);
670         /* Read anything outstanding */
671         while(read(p->fd, dummy, sizeof(dummy)) > 0);
672
673         /* rise DTR to re-enable line */
674         ioctl(p->fd, TIOCMBIS, &dtr);
675         
676         /* Read anything outstanding */
677         while(read(p->fd, dummy, sizeof(dummy)) > 0);
678
679         /* basically we're done, just to be sure */
680         write(p->fd, "\n\n", 2);
681         read(p->fd, dummy, sizeof(dummy));
682         if (ast_modem_send(p, "ATH", 0)) {
683                 ast_log(LOG_WARNING, "Unable to hang up\n");
684                 return -1;
685         }
686         if (ast_modem_expect(p, "OK", 5)) {
687                 ast_log(LOG_WARNING, "Final 'OK' not received\n");
688                 return -1;
689         }
690
691         return 0;
692 }
693
694 static struct ast_modem_driver i4l_driver =
695 {
696         "i4l",
697         i4l_idents,
698         AST_FORMAT_SLINEAR,
699         0,              /* Not full duplex */
700         i4l_incusecnt,  /* incusecnt */
701         i4l_decusecnt,  /* decusecnt */
702         i4l_identify,   /* identify */
703         i4l_init,       /* init */
704         i4l_setdev,     /* setdev */
705         i4l_read,
706         i4l_write,
707         i4l_dial,       /* dial */
708         i4l_answer,     /* answer */
709         i4l_hangup,     /* hangup */
710         i4l_startrec,   /* start record */
711         NULL,   /* stop record */
712         NULL,   /* start playback */
713         NULL,   /* stop playback */
714         NULL,   /* set silence supression */
715         i4l_dialdigit,  /* dialdigit */
716 };
717
718
719
720 int usecount(void)
721 {
722         return usecnt;
723 }
724
725 int load_module(void)
726 {
727         return ast_register_modem_driver(&i4l_driver);
728 }
729
730 int unload_module(void)
731 {
732         return ast_unregister_modem_driver(&i4l_driver);
733 }
734
735 char *description()
736 {
737         return desc;
738 }
739
740 char *key()
741 {
742         return ASTERISK_GPL_KEY;
743 }