Version 0.1.2 from FTP
[asterisk/asterisk.git] / channels / chan_modem.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 #include <pthread.h>
16 #include <string.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/config.h>
20 #include <asterisk/logger.h>
21 #include <asterisk/module.h>
22 #include <asterisk/pbx.h>
23 #include <asterisk/options.h>
24 #include <asterisk/vmodem.h>
25 #include <sys/socket.h>
26 #include <sys/time.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <arpa/inet.h>
31 #include <fcntl.h>
32 #include <sys/ioctl.h>
33 #include <sys/termios.h>
34 #include <sys/signal.h>
35 #include <ctype.h>
36
37 /* Up to 10 seconds for an echo to arrive */
38 #define ECHO_TIMEOUT 10
39
40 static char *desc = "Generic Voice Modem Driver";
41 static char *tdesc = "Generic Voice Modem Channel Driver";
42 static char *type = "Modem";
43 static char *config = "modem.conf";
44 static char dialtype = 'T';
45 static int gmode = MODEM_MODE_IMMEDIATE;
46
47 /* Default modem type */
48 static char mtype[80] = "autodetect";
49 /* Default context for incoming calls */
50 static char context[AST_MAX_EXTENSION]= "default";
51
52 /* Initialization String */
53 static char initstr[AST_MAX_INIT_STR] = "ATE1Q0";
54
55 static int usecnt =0;
56
57 static int baudrate = 115200;
58
59 static int stripmsd = 0;
60
61 static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
62
63 /* Protect the interface list (of ast_modem_pvt's) */
64 static pthread_mutex_t iflock = PTHREAD_MUTEX_INITIALIZER;
65
66 /* Protect the monitoring thread, so only one process can kill or start it, and not
67    when it's doing something critical. */
68 static pthread_mutex_t monlock = PTHREAD_MUTEX_INITIALIZER;
69
70 /* This is the thread for the monitor which checks for input on the channels
71    which are not currently in use.  */
72 static pthread_t monitor_thread = -1;
73
74 static int restart_monitor(void);
75
76 /* The private structures of the Phone Jack channels are linked for
77    selecting outgoing channels */
78    
79 static struct ast_modem_pvt  *iflist = NULL;
80
81 static int modem_digit(struct ast_channel *ast, char digit)
82 {
83         struct ast_modem_pvt *p;
84         p = ast->pvt->pvt;
85         if (p->mc->dialdigit)
86                 return p->mc->dialdigit(p, digit);
87         else ast_log(LOG_DEBUG, "Channel %s lacks digit dialing\n");
88         return 0;
89 }
90
91 static struct ast_modem_driver *drivers = NULL;
92
93 static struct ast_frame *modem_read(struct ast_channel *);
94
95 static struct ast_modem_driver *find_capability(char *ident)
96 {
97         struct ast_modem_driver *mc;
98         int x;
99         mc = drivers;
100         while(mc) {
101                 for (x=0;mc->idents[x];x++) {
102                         if (!strcmp(ident, mc->idents[x])) 
103                                 break;
104                 }
105                 if (mc->idents[x])
106                         break;
107                 mc = mc->next;
108         }
109         if (mc) {
110                 if (mc->incusecnt)
111                         mc->incusecnt();
112         }
113         return mc;
114 }
115
116 static struct ast_modem_driver *find_driver(char *drv)
117 {
118         struct ast_modem_driver *mc;
119         mc = drivers;
120         while(mc) {
121                 if (!strcasecmp(mc->name, drv)) 
122                         break;
123                 mc = mc->next;
124         }
125         if (mc) {
126                 if (mc->incusecnt)
127                         mc->incusecnt();
128         }
129         return mc;
130 }
131
132 int ast_register_modem_driver(struct ast_modem_driver *mc)
133 {
134         mc->next = drivers;
135         drivers = mc;
136         return 0;
137 }
138
139 int ast_unregister_modem_driver(struct ast_modem_driver *mc)
140 {
141         struct ast_modem_driver *last = NULL, *cur;
142         cur = drivers;
143         while(cur) {
144                 if (cur == mc) {
145                         if (last)
146                                 last->next = mc->next;
147                         else
148                                 drivers = mc->next;
149                         return 0;
150                 }
151                 cur = cur->next;
152         }
153         return -1;
154 }
155
156 static int modem_call(struct ast_channel *ast, char *idest, int timeout)
157 {
158         struct ast_modem_pvt *p;
159         int ms = timeout;
160         char rdest[80], *where;
161         strncpy(rdest, idest, sizeof(rdest));
162         strtok(rdest, ":");
163         where = strtok(NULL, ":");
164         if (!where) {
165                 ast_log(LOG_WARNING, "Destination %s requres a real destination (device:destination)\n", idest);
166                 return -1;
167         }
168         p = ast->pvt->pvt;
169         if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
170                 ast_log(LOG_WARNING, "modem_call called on %s, neither down nor reserved\n", ast->name);
171                 return -1;
172         }
173         if (!strcasecmp(where, "handset")) {
174                 if (p->mc->setdev)
175                         if (p->mc->setdev(p, MODEM_DEV_HANDSET))
176                                 return -1;
177                 /* Should be immediately up */
178                 ast->state = AST_STATE_UP;
179         } else {
180                 if (p->mc->setdev)
181                         if (p->mc->setdev(p, MODEM_DEV_TELCO_SPK))
182                                 return -1;
183                 if (p->mc->dial)
184                         p->mc->dial(p, where + p->stripmsd);
185                 ast->state = AST_STATE_DIALING;
186                 while((ast->state != AST_STATE_UP) && (ms > 0)) {
187                         ms = ast_waitfor(ast, ms);
188                         /* Just read packets and watch what happens */
189                         if (ms > 0) {
190                                 if (!modem_read(ast))
191                                         return -1;
192                         }
193                 }
194                 if (ms < 0)     
195                         return -1;
196         }
197         return 0;
198 }
199
200 int ast_modem_send(struct ast_modem_pvt *p, char *cmd, int len)
201 {
202         int res;
203         if (!len) {
204                 fprintf(p->f, "%s\r\n", cmd);
205                 res = ast_modem_expect(p, cmd, ECHO_TIMEOUT);
206                 if (res) {
207                         ast_log(LOG_WARNING, "Unexpected reply %s\n", p->response);
208                         return -1;
209                 }
210                 return 0;
211         } else {
212                 if (fwrite(cmd, 1, len, p->f) < len)
213                         return -1;
214                 return 0;
215         }
216 }
217
218 int ast_modem_read_response(struct ast_modem_pvt *p, int timeout)
219 {
220         int res = -1;
221         timeout *= 1000;
222         strncpy(p->response, "(No Response)", sizeof(p->response));
223         do {
224                 res = ast_waitfor_n_fd(&p->fd, 1, &timeout);
225                 if (res < 0) {
226                         return -1;
227                 }
228                 /* Read a response */
229                 fgets(p->response, sizeof(p->response), p->f);
230                 return 0;
231         } while(timeout > 0);
232         return -1;
233 }
234
235 int ast_modem_expect(struct ast_modem_pvt *p, char *result, int timeout)
236 {
237         int res = -1;
238         timeout *= 1000;
239         strncpy(p->response, "(No Response)", sizeof(p->response));
240         do {
241                 res = ast_waitfor_n_fd(&p->fd, 1, &timeout);
242                 if (res < 0) {
243                         return -1;
244                 }
245                 /* Read a response */
246                 fgets(p->response, sizeof(p->response), p->f);
247 #if 0
248                 fprintf(stderr, "Modem said: %s", p->response);
249 #endif
250                 if (!strncasecmp(p->response, result, strlen(result))) 
251                         return 0;
252         } while(timeout > 0);
253         return -1;
254 }
255
256 void ast_modem_trim(char *s)
257 {
258         int x;
259         x = strlen(s) - 1;
260         while(x >= 0) {
261                 if ((s[x] != '\r') && (s[x] != '\n') && (s[x] != ' '))
262                         break;
263                 s[x] = '\0';
264                 x--;
265         }
266 }
267
268 static int modem_setup(struct ast_modem_pvt *p, int baudrate)
269 {
270         /* Make sure there's a modem there and that it's in a reasonable 
271            mode.  Set the baud rate, etc.  */
272         char identity[256];
273         char *ident = NULL;
274         if (option_debug)
275                 ast_log(LOG_DEBUG, "Setting up modem %s\n", p->dev);
276         if (ast_modem_send(p, "\r\n", 2)) {
277                 ast_log(LOG_WARNING, "Failed to send enter?\n");
278                 return -1;
279         }
280         /* Read any outstanding stuff */
281         while(!ast_modem_read_response(p, 0));
282         if (ast_modem_send(p, "ATZ", 0)) {
283                 ast_log(LOG_WARNING, "Modem not responding on %s\n", p->dev);
284                 return -1;
285         }
286         if (ast_modem_expect(p, "OK", ECHO_TIMEOUT)) {
287                 ast_log(LOG_WARNING, "Modem reset failed: %s\n", p->response);
288                 return -1;
289         }
290         if (ast_modem_send(p, p->initstr, 0)) {
291                 ast_log(LOG_WARNING, "Modem not responding on %s\n", p->dev);
292                 return -1;
293         }
294         if (ast_modem_expect(p, "OK", ECHO_TIMEOUT)) {
295                 ast_log(LOG_WARNING, "Modem initialization failed: %s\n", p->response);
296                 return -1;
297         }
298         if (ast_modem_send(p, "ATI3", 0)) {
299                 ast_log(LOG_WARNING, "Modem not responding on %s\n", p->dev);
300                 return -1;
301         }
302         if (ast_modem_read_response(p, ECHO_TIMEOUT)) {
303                 ast_log(LOG_WARNING, "Modem did not provide identification\n");
304                 return -1;
305         }
306         strncpy(identity, p->response, sizeof(identity));
307         ast_modem_trim(identity);
308         if (ast_modem_expect(p, "OK", ECHO_TIMEOUT)) {
309                 ast_log(LOG_WARNING, "Modem did not provide identification\n");
310                 return -1;
311         }
312         if (!strcasecmp(mtype, "autodetect")) {
313                 p->mc = find_capability(identity);
314                 if (!p->mc) {
315                         ast_log(LOG_WARNING, "Unable to autodetect modem.  You'll need to specify a driver in modem.conf.  Please report modem identification (%s) and which driver works to markster@linux-support.net.\n", identity); 
316                         return -1;
317                 }
318         } else {
319                 p->mc = find_driver(mtype);
320                 if (!p->mc) {
321                         ast_log(LOG_WARNING, "No driver for modem type '%s'\n", mtype);
322                         return -1;
323                 }
324         }
325         if (p->mc->init) {
326                 if (p->mc->init(p)) {
327                         ast_log(LOG_WARNING, "Modem Initialization Failed on '%s', driver %s.\n", p->dev, p->mc->name);
328                         p->mc->decusecnt();
329                         return -1;
330                 }
331         }                       
332         if (option_verbose > 2) {
333                 ast_verbose(VERBOSE_PREFIX_3 "Configured modem %s with driver %s (%s)\n", p->dev, p->mc->name, p->mc->identify ? (ident = p->mc->identify(p)) : "No identification");
334         }
335         if (ident)
336                 free(ident);
337         return 0;
338 }
339
340 static int modem_hangup(struct ast_channel *ast)
341 {
342         struct ast_modem_pvt *p;
343         ast_log(LOG_DEBUG, "modem_hangup(%s)\n", ast->name);
344         p = ast->pvt->pvt;
345         /* Hang up */
346         if (p->mc->hangup)
347                 p->mc->hangup(p);
348         /* Re-initialize */
349         if (p->mc->init)
350                 p->mc->init(p);
351         ast->state = AST_STATE_DOWN;
352         memset(p->cid, 0, sizeof(p->cid));
353         ((struct ast_modem_pvt *)(ast->pvt->pvt))->owner = NULL;
354         pthread_mutex_lock(&usecnt_lock);
355         usecnt--;
356         if (usecnt < 0) 
357                 ast_log(LOG_WARNING, "Usecnt < 0???\n");
358         pthread_mutex_unlock(&usecnt_lock);
359         ast_update_use_count();
360         if (option_verbose > 2) 
361                 ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name);
362         ast->pvt->pvt = NULL;
363         ast->state = AST_STATE_DOWN;
364         restart_monitor();
365         return 0;
366 }
367
368 static int modem_answer(struct ast_channel *ast)
369 {
370         struct ast_modem_pvt *p;
371         int res=0;
372         ast_log(LOG_DEBUG, "modem_hangup(%s)\n", ast->name);
373         p = ast->pvt->pvt;
374         if (p->mc->answer) {
375                 res = p->mc->answer(p);
376         }
377         if (!res) {
378                 ast->rings = 0;
379                 ast->state = AST_STATE_UP;
380         }
381         return res;
382 }
383
384 static char modem_2digit(char c)
385 {
386         if (c == 12)
387                 return '#';
388         else if (c == 11)
389                 return '*';
390         else if ((c < 10) && (c >= 0))
391                 return '0' + c - 1;
392         else
393                 return '?';
394 }
395
396 static struct ast_frame *modem_read(struct ast_channel *ast)
397 {
398         struct ast_modem_pvt *p = ast->pvt->pvt;
399         struct ast_frame *fr=NULL;
400         if (p->mc->read)
401                 fr = p->mc->read(p);
402         return fr;
403 }
404
405 static int modem_write(struct ast_channel *ast, struct ast_frame *frame)
406 {
407         int res=0;
408         struct ast_modem_pvt *p = ast->pvt->pvt;
409         if (p->mc->write)
410                 res = p->mc->write(p, frame);
411         return 0;
412 }
413
414 struct ast_channel *ast_modem_new(struct ast_modem_pvt *i, int state)
415 {
416         struct ast_channel *tmp;
417         tmp = ast_channel_alloc();
418         if (tmp) {
419                 snprintf(tmp->name, sizeof(tmp->name), "Modem[%s]/%s", i->mc->name, i->dev + 5);
420                 tmp->type = type;
421                 tmp->fd = i->fd;
422                 tmp->format = i->mc->formats;
423                 tmp->state = state;
424                 if (state == AST_STATE_RING)
425                         tmp->rings = 1;
426                 tmp->pvt->pvt = i;
427                 tmp->pvt->send_digit = modem_digit;
428                 tmp->pvt->call = modem_call;
429                 tmp->pvt->hangup = modem_hangup;
430                 tmp->pvt->answer = modem_answer;
431                 tmp->pvt->read = modem_read;
432                 tmp->pvt->write = modem_write;
433                 strncpy(tmp->context, i->context, sizeof(tmp->context));
434                 if (strlen(i->cid))
435                         tmp->callerid = strdup(i->cid);
436                 i->owner = tmp;
437                 pthread_mutex_lock(&usecnt_lock);
438                 usecnt++;
439                 pthread_mutex_unlock(&usecnt_lock);
440                 ast_update_use_count();
441                 if (state != AST_STATE_DOWN) {
442                         if (ast_pbx_start(tmp)) {
443                                 ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
444                                 ast_hangup(tmp);
445                                 tmp = NULL;
446                         }
447                 }
448         } else
449                 ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
450         return tmp;
451 }
452
453 static void modem_mini_packet(struct ast_modem_pvt *i)
454 {
455         struct ast_frame *fr;
456         fr = i->mc->read(i);
457         if (fr->frametype == AST_FRAME_CONTROL) {
458                 if (fr->subclass == AST_CONTROL_RING) {
459                         ast_modem_new(i, AST_STATE_RING);
460                         restart_monitor();
461                 }
462         }
463 }
464
465 static void *do_monitor(void *data)
466 {
467         fd_set rfds, efds;
468         int n, res;
469         struct ast_modem_pvt *i;
470         /* This thread monitors all the frame relay interfaces which are not yet in use
471            (and thus do not have a separate thread) indefinitely */
472         /* From here on out, we die whenever asked */
473 #if 0
474         if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
475                 ast_log(LOG_WARNING, "Unable to set cancel type to asynchronous\n");
476                 return NULL;
477         }
478 #endif
479         for(;;) {
480                 /* Don't let anybody kill us right away.  Nobody should lock the interface list
481                    and wait for the monitor list, but the other way around is okay. */
482                 if (pthread_mutex_lock(&monlock)) {
483                         ast_log(LOG_ERROR, "Unable to grab monitor lock\n");
484                         return NULL;
485                 }
486                 /* Lock the interface list */
487                 if (pthread_mutex_lock(&iflock)) {
488                         ast_log(LOG_ERROR, "Unable to grab interface lock\n");
489                         pthread_mutex_unlock(&monlock);
490                         return NULL;
491                 }
492                 /* Build the stuff we're going to select on, that is the socket of every
493                    ast_modem_pvt that does not have an associated owner channel */
494                 n = -1;
495                 FD_ZERO(&rfds);
496                 FD_ZERO(&efds);
497                 i = iflist;
498                 while(i) {
499                         if (FD_ISSET(i->fd, &rfds)) 
500                                 ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->fd, i->dev);
501                         if (!i->owner) {
502                                 /* This needs to be watched, as it lacks an owner */
503                                 FD_SET(i->fd, &rfds);
504                                 FD_SET(i->fd, &efds);
505                                 if (i->fd > n)
506                                         n = i->fd;
507                         }
508                         
509                         i = i->next;
510                 }
511                 /* Okay, now that we know what to do, release the interface lock */
512                 pthread_mutex_unlock(&iflock);
513                 
514                 /* And from now on, we're okay to be killed, so release the monitor lock as well */
515                 pthread_mutex_unlock(&monlock);
516 #if 0
517                 ast_log(LOG_DEBUG, "In monitor, n=%d, pid=%d\n", n, getpid());
518 #endif
519                 /* Wait indefinitely for something to happen */
520                 pthread_testcancel();
521                 res = select(n + 1, &rfds, NULL, &efds, NULL);
522                 pthread_testcancel();
523                 /* Okay, select has finished.  Let's see what happened.  */
524                 if (res < 1) {
525                         ast_log(LOG_WARNING, "select return %d: %s\n", res, strerror(errno));
526                         continue;
527                 }
528                 /* Alright, lock the interface list again, and let's look and see what has
529                    happened */
530                 if (pthread_mutex_lock(&iflock)) {
531                         ast_log(LOG_WARNING, "Unable to lock the interface list\n");
532                         continue;
533                 }
534                 i = iflist;
535                 while(i) {
536                         if (FD_ISSET(i->fd, &rfds) || FD_ISSET(i->fd, &efds)) {
537                                 if (i->owner) {
538                                         ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d, %s)...\n", i->fd, i->dev);
539                                         i = i->next;
540                                         continue;
541                                 }
542                                 modem_mini_packet(i);
543                         }
544                         i=i->next;
545                 }
546                 pthread_mutex_unlock(&iflock);
547         }
548         /* Never reached */
549         return NULL;
550         
551 }
552
553 static int restart_monitor()
554 {
555         /* If we're supposed to be stopped -- stay stopped */
556         if (monitor_thread == -2)
557                 return 0;
558         if (pthread_mutex_lock(&monlock)) {
559                 ast_log(LOG_WARNING, "Unable to lock monitor\n");
560                 return -1;
561         }
562         if (monitor_thread == pthread_self()) {
563                 pthread_mutex_unlock(&monlock);
564                 ast_log(LOG_WARNING, "Cannot kill myself\n");
565                 return -1;
566         }
567         if (monitor_thread != -1) {
568                 pthread_cancel(monitor_thread);
569                 /* Nudge it a little, as it's probably stuck in select */
570                 pthread_kill(monitor_thread, SIGURG);
571                 pthread_join(monitor_thread, NULL);
572         }
573         /* Start a new monitor */
574         if (pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
575                 pthread_mutex_unlock(&monlock);
576                 ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
577                 return -1;
578         }
579         pthread_mutex_unlock(&monlock);
580         return 0;
581 }
582
583 static struct ast_modem_pvt *mkif(char *iface)
584 {
585         /* Make a ast_modem_pvt structure for this interface */
586         struct ast_modem_pvt *tmp;
587 #if 0
588         int flags;      
589 #endif
590         
591         tmp = malloc(sizeof(struct ast_modem_pvt));
592         if (tmp) {
593                 tmp->fd = open(iface, O_RDWR | O_NONBLOCK);
594                 if (tmp->fd < 0) {
595                         ast_log(LOG_WARNING, "Unable to open '%s'\n", iface);
596                         free(tmp);
597                         return NULL;
598                 }
599                 tmp->f = fdopen(tmp->fd, "w+");
600                 /* Disable buffering */
601                 setvbuf(tmp->f, NULL, _IONBF,0);
602                 if (tmp->f < 0) {
603                         ast_log(LOG_WARNING, "Unable to fdopen '%s'\n", iface);
604                         free(tmp);
605                         return NULL;
606                 }
607 #if 0
608                 flags = fcntl(tmp->fd, F_GETFL);
609                 fcntl(tmp->fd, F_SETFL, flags | O_NONBLOCK);
610 #endif
611                 tmp->owner = NULL;
612                 tmp->ministate = 0;
613                 tmp->stripmsd = stripmsd;
614                 tmp->dialtype = dialtype;
615                 tmp->mode = gmode;
616                 memset(tmp->cid, 0, sizeof(tmp->cid));
617                 strncpy(tmp->dev, iface, sizeof(tmp->dev));
618                 strncpy(tmp->context, context, sizeof(tmp->context));
619                 strncpy(tmp->initstr, initstr, sizeof(tmp->initstr));
620                 tmp->next = NULL;
621                 tmp->obuflen = 0;
622                 
623                 if (modem_setup(tmp, baudrate) < 0) {
624                         ast_log(LOG_WARNING, "Unable to configure modem '%s'\n", iface);
625                         free(tmp);
626                         return NULL;
627                 }
628         }
629         return tmp;
630 }
631
632 static struct ast_channel *modem_request(char *type, int format, void *data)
633 {
634         int oldformat;
635         struct ast_modem_pvt *p;
636         struct ast_channel *tmp = NULL;
637         char dev[80];
638         strncpy(dev, (char *)data, sizeof(dev));
639         strtok(dev, ":");
640         oldformat = format;
641         /* Search for an unowned channel */
642         if (pthread_mutex_lock(&iflock)) {
643                 ast_log(LOG_ERROR, "Unable to lock interface list???\n");
644                 return NULL;
645         }
646         p = iflist;
647         while(p) {
648                 if (!strcmp(dev, p->dev + 5)) {
649                         if (p->mc->formats & format) {
650                                 if (!p->owner) {
651                                         tmp = ast_modem_new(p, AST_STATE_DOWN);
652                                         restart_monitor();
653                                         break;
654                                 } else
655                                         ast_log(LOG_WARNING, "Device '%s' is busy\n", p->dev);
656                         } else 
657                                 ast_log(LOG_WARNING, "Asked for a format %d line on %s\n", format, p->dev);
658                         break;
659                 }
660                 p = p->next;
661         }
662         if (!p) 
663                 ast_log(LOG_WARNING, "Requested device '%s' does not exist\n", p->dev);
664         
665         pthread_mutex_unlock(&iflock);
666         return tmp;
667 }
668
669 int load_module()
670 {
671         struct ast_config *cfg;
672         struct ast_variable *v;
673         struct ast_modem_pvt *tmp;
674         char driver[80];
675         cfg = ast_load(config);
676
677         /* We *must* have a config file otherwise stop immediately */
678         if (!cfg) {
679                 ast_log(LOG_ERROR, "Unable to load config %s\n", config);
680                 return -1;
681         }
682         if (pthread_mutex_lock(&iflock)) {
683                 /* It's a little silly to lock it, but we mind as well just to be sure */
684                 ast_log(LOG_ERROR, "Unable to lock interface list???\n");
685                 return -1;
686         }
687         v = ast_variable_browse(cfg, "interfaces");
688         while(v) {
689                 /* Create the interface list */
690                 if (!strcasecmp(v->name, "device")) {
691                                 tmp = mkif(v->value);
692                                 if (tmp) {
693                                         tmp->next = iflist;
694                                         iflist = tmp;
695                                         
696                                 } else {
697                                         ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
698                                         ast_destroy(cfg);
699                                         pthread_mutex_unlock(&iflock);
700                                         unload_module();
701                                         pthread_mutex_unlock(&iflock);
702                                         return -1;
703                                 }
704                 } else if (!strcasecmp(v->name, "driver")) {
705                         snprintf(driver, sizeof(driver), "chan_modem_%s.so", v->value);
706                         if (option_verbose > 1) 
707                                 ast_verbose(VERBOSE_PREFIX_2 "Loading modem driver %s", driver);
708                                 
709                         if (ast_load_resource(driver)) {
710                                 ast_log(LOG_ERROR, "Failed to load driver %s\n", driver);
711                                 ast_destroy(cfg);
712                                 pthread_mutex_unlock(&iflock);
713                                 unload_module();
714                                 return -1;
715                         }
716                 } else if (!strcasecmp(v->name, "mode")) {
717                         if (!strncasecmp(v->value, "ri", 2)) 
718                                 gmode = MODEM_MODE_WAIT_RING;
719                         else if (!strncasecmp(v->value, "im", 2))
720                                 gmode = MODEM_MODE_IMMEDIATE;
721                         else if (!strncasecmp(v->value, "an", 2))
722                                 gmode = MODEM_MODE_WAIT_ANSWER;
723                         else
724                                 ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value);
725                 } else if (!strcasecmp(v->name, "stripmsd")) {
726                         stripmsd = atoi(v->value);
727                 } else if (!strcasecmp(v->name, "type")) {
728                         strncpy(mtype, v->value, sizeof(mtype));
729                 } else if (!strcasecmp(v->name, "initstr")) {
730                         strncpy(initstr, v->value, sizeof(initstr));
731                 } else if (!strcasecmp(v->name, "dialtype")) {
732                         dialtype = toupper(v->value[0]);
733                 } else if (!strcasecmp(v->name, "context")) {
734                         strncpy(context, v->value, sizeof(context));
735                 }
736                 v = v->next;
737         }
738         pthread_mutex_unlock(&iflock);
739         if (ast_channel_register(type, tdesc, /* XXX Don't know our types -- maybe we should register more than one XXX */ AST_FORMAT_SLINEAR, modem_request)) {
740                 ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
741                 ast_destroy(cfg);
742                 unload_module();
743                 return -1;
744         }
745         ast_destroy(cfg);
746         /* And start the monitor for the first time */
747         restart_monitor();
748         return 0;
749 }
750
751 int unload_module()
752 {
753         struct ast_modem_pvt *p, *pl;
754         /* First, take us out of the channel loop */
755         ast_channel_unregister(type);
756         if (!pthread_mutex_lock(&iflock)) {
757                 /* Hangup all interfaces if they have an owner */
758                 p = iflist;
759                 while(p) {
760                         if (p->owner)
761                                 ast_softhangup(p->owner);
762                         p = p->next;
763                 }
764                 iflist = NULL;
765                 pthread_mutex_unlock(&iflock);
766         } else {
767                 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
768                 return -1;
769         }
770         if (!pthread_mutex_lock(&monlock)) {
771                 if (monitor_thread > -1) {
772                         pthread_cancel(monitor_thread);
773                         pthread_join(monitor_thread, NULL);
774                 }
775                 monitor_thread = -2;
776                 pthread_mutex_unlock(&monlock);
777         } else {
778                 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
779                 return -1;
780         }
781
782         if (!pthread_mutex_lock(&iflock)) {
783                 /* Destroy all the interfaces and free their memory */
784                 p = iflist;
785                 while(p) {
786                         /* Close the socket, assuming it's real */
787                         if (p->fd > -1)
788                                 close(p->fd);
789                         pl = p;
790                         p = p->next;
791                         /* Free associated memory */
792                         free(pl);
793                 }
794                 iflist = NULL;
795                 pthread_mutex_unlock(&iflock);
796         } else {
797                 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
798                 return -1;
799         }
800                 
801         return 0;
802 }
803
804 int usecount(void)
805 {
806         int res;
807         pthread_mutex_lock(&usecnt_lock);
808         res = usecnt;
809         pthread_mutex_unlock(&usecnt_lock);
810         return res;
811 }
812
813 char *description()
814 {
815         return desc;
816 }
817