Version 0.1.0 from FTP
authorMark Spencer <markster@digium.com>
Sat, 4 Dec 1999 21:35:07 +0000 (21:35 +0000)
committerMark Spencer <markster@digium.com>
Sat, 4 Dec 1999 21:35:07 +0000 (21:35 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@84 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_dial.c [new file with mode: 0755]
channel.c [new file with mode: 0755]
channels/chan_vofr.c [new file with mode: 0755]

diff --git a/apps/app_dial.c b/apps/app_dial.c
new file mode 100755 (executable)
index 0000000..021f0d7
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Trivial application to dial a channel
+ * 
+ * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/options.h>
+#include <asterisk/module.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include <pthread.h>
+
+
+static char *tdesc = "Trivial Dialing Application";
+
+static char *app = "Dial";
+
+/* We define a customer "local user" structure because we
+   use it not only for keeping track of what is in use but
+   also for keeping track of who we're dialing. */
+
+struct localuser {
+       struct ast_channel *chan;
+       int stillgoing;
+       int allowredirect;
+       struct localuser *next;
+};
+
+LOCAL_USER_DECL;
+
+static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
+{
+       /* Hang up a tree of stuff */
+       struct localuser *oo;
+       while(outgoing) {
+               /* Hangup any existing lines we have open */
+               if (outgoing->chan != exception)
+                       ast_hangup(outgoing->chan);
+               oo = outgoing;
+               outgoing=outgoing->next;
+               free(oo);
+       }
+}
+
+static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir)
+{
+       fd_set rfds, efds;
+       struct localuser *o;
+       int found;
+       int numlines;
+       int numbusies = 0;
+       int orig = *to;
+       struct timeval tv;
+       struct ast_frame *f;
+       struct ast_channel *peer = NULL;
+       /* Watch all outgoing channels looking for an answer of some sort.  */
+       tv.tv_sec = *to / 1000;
+       tv.tv_usec = (*to % 1000) * 1000;
+       while((tv.tv_sec || tv.tv_usec) && !peer) {
+               FD_ZERO(&rfds);
+               FD_ZERO(&efds);
+               /* Always watch the input fd */
+               FD_SET(in->fd, &rfds);
+               FD_SET(in->fd, &efds);
+               o = outgoing;
+               found = -1;
+               numlines = 0;
+               while(o) {
+                       if (o->stillgoing) {
+                               /* Pay attention to this one */
+                               CHECK_BLOCKING(o->chan);
+                               FD_SET(o->chan->fd, &rfds);
+                               FD_SET(o->chan->fd, &efds);
+                               if (o->chan->fd > found)
+                                       found = o->chan->fd;
+                       }
+                       numlines++;
+                       o = o->next;
+               }
+               /* If nobody is left, just go ahead and stop */
+               if (found<0) {
+                       if (numlines == numbusies) {
+                               if (option_verbose > 2)
+                                       ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n");
+                               /* See if there is a special busy message */
+                               if (ast_exists_extension(in, in->context, in->exten, in->priority + 101)) 
+                                       in->priority+=100;
+                       } else {
+                               if (option_verbose > 2)
+                                       ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time\n");
+                       }
+                       break;
+               }
+               if (in->fd > found)
+                       found = in->fd;
+               if (*to > -1) 
+                       found = select(found + 1, &rfds, NULL, &efds, &tv);
+               else
+                       found = select(found + 1, &rfds, NULL, &efds, NULL);
+               if (found < 0) {
+                       ast_log(LOG_WARNING, "select failed, returned %d (%s)\n", errno, strerror(errno));
+                       *to = -1;
+                       o = outgoing;
+                       while(o) {
+                               if (o->stillgoing) {
+                                       o->chan->blocking = 0;
+                               }
+                               o = o->next;
+                       }
+                       return NULL;
+               }
+               o = outgoing;
+               while(o) {
+                       if (o->stillgoing) {
+                               o->chan->blocking = 0;
+                               if (FD_ISSET(o->chan->fd, &rfds) || FD_ISSET(o->chan->fd, &efds)) {
+                                       f = ast_read(o->chan);
+                                       if (f) {
+                                               if (f->frametype == AST_FRAME_CONTROL) {
+                                                       switch(f->subclass) {
+                                                   case AST_CONTROL_ANSWER:
+                                                               /* This is our guy if someone answered. */
+                                                               if (!peer) {
+                                                                       if (option_verbose > 2)
+                                                                               ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
+                                                                       peer = o->chan;
+                                                                       *allowredir = o->allowredirect;
+                                                               }
+                                                               break;
+                                                       case AST_CONTROL_BUSY:
+                                                               if (option_verbose > 2)
+                                                                       ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
+                                                               o->stillgoing = 0;
+                                                               numbusies++;
+                                                               break;
+                                                       case AST_CONTROL_RINGING:
+                                                               if (option_verbose > 2)
+                                                                       ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
+                                                               break;
+                                                       case AST_CONTROL_OFFHOOK:
+                                                               /* Ignore going off hook */
+                                                               break;
+                                                       default:
+                                                               ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
+                                                       }
+                                               }
+                                               ast_frfree(f);
+                                       } else {
+                                               o->stillgoing = 0;
+                                       }
+                                       
+                               }
+                       }
+                       o = o->next;
+               }
+               if (FD_ISSET(in->fd, &rfds) || FD_ISSET(in->fd, &efds)) {
+                       /* After unblocking the entirity of the list, check for the main channel */
+                       f = ast_read(in);
+#if 0
+                       if (f && (f->frametype != AST_FRAME_VOICE))
+                                       printf("Frame type: %d, %d\n", f->frametype, f->subclass);
+#endif
+                       if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass = AST_CONTROL_HANGUP))) {
+                               /* Got hung up */
+                               *to=-1;
+                               return NULL;
+                       }
+               }
+               
+       }
+       if (!(tv.tv_sec || tv.tv_usec) && (option_verbose > 2))
+               ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+       *to = 0;
+       return peer;
+}
+
+static int bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect)
+{
+       /* Copy voice back and forth between the two channels.  Give the peer
+          the ability to transfer calls with '#<extension' syntax. */
+       struct ast_channel *cs[3];
+       int to = -1, len;
+       struct ast_frame *f;
+       struct ast_channel *who;
+       char newext[256], *ptr;
+       int res;
+       /* Answer if need be */
+       if (chan->state != AST_STATE_UP)
+               if (ast_answer(chan))
+                       return -1;
+       cs[0] = chan;
+       cs[1] = peer;
+       for (/* ever */;;) {
+               who = ast_waitfor_n(cs, 2, &to);
+               if (!who) {
+                       ast_log(LOG_WARNING, "Nobody there??\n");
+                       continue;
+               }
+               f = ast_read(who);
+               if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) 
+                       return -1;
+               if ((f->frametype == AST_FRAME_VOICE) ||
+                   (f->frametype == AST_FRAME_DTMF)) {
+                       if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect) {
+                               if (f->subclass == '#') {
+                                       memset(newext, 0, sizeof(newext));
+                                       ptr = newext;
+                                       len = ast_pbx_longest_extension(chan->context) + 1;
+
+                                       /* Transfer */
+                                       if ((res=ast_streamfile(peer, "pbx-transfer")))
+                                               break;
+                                       if ((res=ast_waitstream(peer, AST_DIGIT_ANY)) < 0)
+                                               break;
+                                       ast_stopstream(peer);
+                                       if (res > 0) {
+                                               /* If they've typed a digit already, handle it */
+                                               newext[0] = res;
+                                               ptr++;
+                                               len --;
+                                       }
+                                       res = ast_readstring(peer, ptr, len, 3000, 2000, "#");
+                                       if (res)
+                                               break;
+                                       if (ast_exists_extension(chan, chan->context, newext, 1)) {
+                                               /* Set the channel's new extension, since it exists */
+                                               strncpy(chan->exten, newext, sizeof(chan->exten));
+                                               chan->priority = 0;
+                                               ast_frfree(f);
+                                               res=0;
+                                               break;
+                                       }
+                                       res = ast_streamfile(peer, "pbx-invalid");
+                                       if (res)
+                                               break;
+                                       res = ast_waitstream(peer, AST_DIGIT_ANY);
+                                       ast_stopstream(peer);
+                                       res = 0;
+                               }
+                       } else {
+#if 0
+                               ast_log(LOG_DEBUG, "Read from %s\n", who->name);
+#endif
+                               if (who == chan) 
+                                       ast_write(peer, f);
+                               else 
+                                       ast_write(chan, f);
+                       }
+                       ast_frfree(f);
+                       
+               } else
+                       ast_frfree(f);
+               /* Swap who gets priority */
+               cs[2] = cs[0];
+               cs[0] = cs[1];
+               cs[1] = cs[2];
+       }
+       return res;
+}
+
+static int dial_exec(struct ast_channel *chan, void *data)
+{
+       int res=-1;
+       struct localuser *u;
+       char *info, *peers, *timeout, *tech, *number, *rest, *cur;
+       struct localuser *outgoing=NULL, *tmp;
+       struct ast_channel *peer;
+       int to;
+       int allowredir=0;
+       
+       if (!data) {
+               ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout)\n");
+               return -1;
+       }
+       
+       LOCAL_USER_ADD(u);
+       
+       /* Parse our arguments */
+       info = strdup((char *)data);
+       peers = strtok(info, "|");
+       if (!peers) {
+               ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
+               goto out;
+       }
+       timeout = strtok(NULL, "|");
+       rest = peers;
+       do {
+               cur = strtok(rest, "&");
+               /* Remember where to start next time */
+               rest = strtok(NULL, "\128");
+               /* Get a technology/number pair */
+               tech = strtok(cur, "/");
+               number = strtok(NULL, "&");
+               if (!number) {
+                       ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
+                       goto out;
+               }
+               tmp = malloc(sizeof(struct localuser));
+               if (!tmp) {
+                       ast_log(LOG_WARNING, "Out of memory\n");
+                       goto out;
+               }
+               tmp->allowredirect = 1;
+               /* If we're dialing by extension, look at the extension to know what to dial */
+               if (!strcasecmp(number, "BYEXTENSION")) {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Dialing by extension %s\n", chan->exten);
+                       number = chan->exten;
+                       /* By default, if we're dialing by extension, don't permit redirecting */
+                       tmp->allowredirect = 0;
+               }
+               /* Request the peer */
+               tmp->chan = ast_request(tech, chan->format, number);
+               if (!tmp->chan) {
+                       /* If we can't, just go on to the next call */
+                       ast_log(LOG_WARNING, "Unable to create channel of type '%s'\n", tech);
+                       free(tmp);
+                       continue;
+               }
+               /* Place the call, but don't wait on the answer */
+               res = ast_call(tmp->chan, number, 0);
+               if (res) {
+                       /* Again, keep going even if there's an error */
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
+                       else if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", number);
+                       ast_hangup(tmp->chan);
+                       free(tmp);
+                       continue;
+               } else
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", number);
+               /* Put them in the list of outgoing thingies...  We're ready now. 
+                  XXX If we're forcibly removed, these outgoing calls won't get
+                  hung up XXX */
+               tmp->stillgoing = -1;
+               tmp->next = outgoing;
+               outgoing = tmp;
+       } while(rest);
+       if (timeout)
+               to = atoi(timeout) * 1000;
+       else
+               to = -1;
+       peer = wait_for_answer(chan, outgoing, &to, &allowredir);
+       if (!peer) {
+               if (to) 
+                       /* Musta gotten hung up */
+                       res = -1;
+                else 
+                       /* Nobody answered, next please? */
+                       res=0;
+               
+               goto out;
+       }
+       if (peer) {
+               /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
+                  we will always return with -1 so that it is hung up properly after the 
+                  conversation.  */
+               hanguptree(outgoing, peer);
+               outgoing = NULL;
+               res = bridge_call(chan, peer, allowredir);
+               ast_hangup(peer);
+       }       
+out:
+       hanguptree(outgoing, NULL);
+       free(info);
+       LOCAL_USER_REMOVE(u);
+       return res;
+}
+
+int unload_module(void)
+{
+       STANDARD_HANGUP_LOCALUSERS;
+       return ast_unregister_application(app);
+}
+
+int load_module(void)
+{
+       return ast_register_application(app, dial_exec);
+}
+
+char *description(void)
+{
+       return tdesc;
+}
+
+int usecount(void)
+{
+       int res;
+       STANDARD_USECOUNT(res);
+       return res;
+}
diff --git a/channel.c b/channel.c
new file mode 100755 (executable)
index 0000000..b4359d2
--- /dev/null
+++ b/channel.c
@@ -0,0 +1,402 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Channel Management
+ * 
+ * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <asterisk/sched.h>
+#include <asterisk/options.h>
+#include <asterisk/channel.h>
+#include <asterisk/channel_pvt.h>
+#include <asterisk/logger.h>
+#include <asterisk/file.h>
+
+struct chanlist {
+       char type[80];
+       char description[80];
+       int capabilities;
+       struct ast_channel * (*requester)(char *type, int format, void *data);
+       struct chanlist *next;
+} *backends = NULL;
+
+/* Protect the channel list (highly unlikely that two things would change
+   it at the same time, but still! */
+   
+static pthread_mutex_t chlock = PTHREAD_MUTEX_INITIALIZER;
+
+int ast_channel_register(char *type, char *description, int capabilities,
+               struct ast_channel *(*requester)(char *type, int format, void *data))
+{
+       struct chanlist *chan, *last=NULL;
+       if (pthread_mutex_lock(&chlock)) {
+               ast_log(LOG_WARNING, "Unable to lock channel list\n");
+               return -1;
+       }
+       chan = backends;
+       while(chan) {
+               if (!strcasecmp(type, chan->type)) {
+                       ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", type);
+                       pthread_mutex_unlock(&chlock);
+                       return -1;
+               }
+               last = chan;
+               chan = chan->next;
+       }
+       chan = malloc(sizeof(struct chanlist));
+       if (!chan) {
+               ast_log(LOG_WARNING, "Out of memory\n");
+               pthread_mutex_unlock(&chlock);
+               return -1;
+       }
+       strncpy(chan->type, type, sizeof(chan->type));
+       strncpy(chan->description, description, sizeof(chan->description));
+       chan->capabilities = capabilities;
+       chan->requester = requester;
+       chan->next = NULL;
+       if (last)
+               last->next = chan;
+       else
+               backends = chan;
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->type, chan->description);
+       else if (option_verbose > 1)
+               ast_verbose( VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->type, chan->description);
+       pthread_mutex_unlock(&chlock);
+       return 0;
+}
+
+struct ast_channel *ast_channel_alloc(void)
+{
+       struct ast_channel *tmp;
+       struct ast_channel_pvt *pvt;
+       tmp = malloc(sizeof(struct ast_channel));
+       memset(tmp, 0, sizeof(struct ast_channel));
+       if (tmp) {
+               pvt = malloc(sizeof(struct ast_channel_pvt));
+               if (pvt) {
+                       memset(pvt, 0, sizeof(struct ast_channel_pvt));
+                       tmp->sched = sched_context_create();
+                       if (tmp->sched) {
+                               tmp->fd = -1;
+                               strncpy(tmp->name, "**Unknown**", sizeof(tmp->name));
+                               tmp->pvt = pvt;
+                               tmp->state = AST_STATE_DOWN;
+                               tmp->stack = -1;
+                               tmp->streamid = -1;
+                               strncpy(tmp->context, "default", sizeof(tmp->context));
+                               strncpy(tmp->exten, "s", sizeof(tmp->exten));
+                               tmp->priority=1;
+                       } else {
+                               ast_log(LOG_WARNING, "Unable to create schedule context\n");
+                               free(tmp);
+                               tmp = NULL;
+                       }
+               } else {
+                       ast_log(LOG_WARNING, "Out of memory\n");
+                       free(tmp);
+                       tmp = NULL;
+               }
+       } else 
+               ast_log(LOG_WARNING, "Out of memory\n");
+       return tmp;
+}
+
+int ast_softhangup(struct ast_channel *chan)
+{
+       int res = 0;
+       if (chan->stream)
+               ast_stopstream(chan);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
+       if (chan->trans)
+               ast_log(LOG_WARNING, "Soft hangup called on '%s' while a translator is in place!  Expect a failure.\n", chan->name);
+       if (chan->pvt->hangup)
+               res = chan->pvt->hangup(chan);
+       if (chan->pvt->pvt)
+               ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
+       if (chan->pbx) 
+               ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);        
+       /* Interrupt any select call or such */
+       if (chan->blocking)
+               pthread_kill(chan->blocker, SIGURG);
+       return res;
+}
+
+int ast_hangup(struct ast_channel *chan)
+{
+       int res = 0;
+       if (chan->stream)
+               ast_stopstream(chan);
+       if (chan->sched)
+               sched_context_destroy(chan->sched);
+       if (chan->blocking)
+               ast_log(LOG_WARNING, "Hard hangup called, while fd is blocking!  Expect a failure\n");
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
+       if (chan->pvt->hangup)
+               res = chan->pvt->hangup(chan);
+       if (chan->pvt->pvt)
+               ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
+       if (chan->trans)
+               ast_log(LOG_WARNING, "Hard hangup called on '%s' while a translator is in place!  Expect a failure.\n", chan->name);
+       if (chan->pbx) 
+               ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
+       if (chan->dnid)
+               free(chan->dnid);
+       if (chan->callerid)
+               free(chan->callerid);   
+       free(chan->pvt);
+       free(chan);
+       return res;
+}
+
+void ast_channel_unregister(char *type)
+{
+       struct chanlist *chan, *last=NULL;
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", type);
+       if (pthread_mutex_lock(&chlock)) {
+               ast_log(LOG_WARNING, "Unable to lock channel list\n");
+               return;
+       }
+       chan = backends;
+       while(chan) {
+               if (!strcasecmp(chan->type, type)) {
+                       if (last)
+                               last->next = chan->next;
+                       else
+                               backends = backends->next;
+                       free(chan);
+                       pthread_mutex_unlock(&chlock);
+                       return;
+               }
+               last = chan;
+               chan = chan->next;
+       }
+       pthread_mutex_unlock(&chlock);
+}
+
+int ast_answer(struct ast_channel *chan)
+{
+       /* Answer the line, if possible */
+       if (chan->state == AST_STATE_RING) {
+               if (chan->pvt->answer)
+                       return chan->pvt->answer(chan);
+       }
+       return 0;
+}
+
+int ast_waitfor_n_fd(int *fds, int n, int *ms)
+{
+       /* Wait for x amount of time on a file descriptor to have input.  */
+       struct timeval tv;
+       fd_set rfds, efds;
+       int res;
+       int x, max=-1;
+       int winner = -1;
+       
+       tv.tv_sec = *ms / 1000;
+       tv.tv_usec = (*ms % 1000) * 1000;
+       FD_ZERO(&rfds);
+       FD_ZERO(&efds);
+       for (x=0;x<n;x++) {
+               FD_SET(fds[x], &rfds);
+               FD_SET(fds[x], &efds);
+               if (fds[x] > max)
+                       max = fds[x];
+       }
+       if (*ms >= 0) 
+               res = select(max + 1, &rfds, NULL, &efds, &tv);
+       else
+               res = select(max + 1, &rfds, NULL, &efds, NULL);
+       for (x=0;x<n;x++) {
+               if ((FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0))
+                       winner = fds[x];
+       }
+       *ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+       if (res < 0)
+               *ms = -10;
+       return winner;
+}
+
+struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
+{
+       /* Wait for x amount of time on a file descriptor to have input.  */
+       struct timeval tv;
+       fd_set rfds, efds;
+       int res;
+       int x, max=-1;
+       struct ast_channel *winner = NULL;
+       
+       tv.tv_sec = *ms / 1000;
+       tv.tv_usec = (*ms % 1000) * 1000;
+       FD_ZERO(&rfds);
+       FD_ZERO(&efds);
+       for (x=0;x<n;x++) {
+               FD_SET(c[x]->fd, &rfds);
+               FD_SET(c[x]->fd, &efds);
+               CHECK_BLOCKING(c[x]);
+               if (c[x]->fd > max)
+                       max = c[x]->fd;
+       }
+       if (*ms >= 0) 
+               res = select(max + 1, &rfds, NULL, &efds, &tv);
+       else
+               res = select(max + 1, &rfds, NULL, &efds, NULL);
+       for (x=0;x<n;x++) {
+               c[x]->blocking = 0;
+               if ((FD_ISSET(c[x]->fd, &rfds) || FD_ISSET(c[x]->fd, &efds)) && !winner)
+                       winner = c[x];
+       }
+       *ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+       if (res < 0)
+               *ms = -10;
+       return winner;
+}
+
+int ast_waitfor(struct ast_channel *c, int ms)
+{
+       if (ast_waitfor_n(&c, 1, &ms)) {
+               if (ms < 0)
+                       return -ms;
+               return ms;
+       }
+       /* Error if ms < 0 */
+       if (ms < 0) 
+               return -1;
+       return 0;
+}
+
+char ast_waitfordigit(struct ast_channel *c, int ms)
+{
+       struct ast_frame *f;
+       char result = 0;
+       /* Wait for a digit, no more than ms milliseconds total. */
+       while(ms && !result) {
+               ms = ast_waitfor(c, ms);
+               if (ms < 0) /* Error */
+                       result = -1; 
+               else if (ms > 0) {
+                       /* Read something */
+                       f = ast_read(c);
+                       if (f) {
+                               if (f->frametype == AST_FRAME_DTMF) 
+                                       result = f->subclass;
+                               ast_frfree(f);
+                       } else
+                               result = -1;
+               }
+       }
+       return result;
+}
+
+struct ast_frame *ast_read(struct ast_channel *chan)
+{
+       struct ast_frame *f = NULL;
+       chan->blocker = pthread_self();
+       if (chan->pvt->read)
+               f = chan->pvt->read(chan);
+       else
+               ast_log(LOG_WARNING, "No read routine on channel %s\n", chan);
+       return f;
+}
+
+int ast_write(struct ast_channel *chan, struct ast_frame *fr)
+{
+       int res = -1;
+       CHECK_BLOCKING(chan);
+       switch(fr->frametype) {
+       case AST_FRAME_CONTROL:
+               /* XXX Interpret control frames XXX */
+               ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
+               break;
+       case AST_FRAME_DTMF:
+               
+               if (chan->pvt->send_digit)
+                       res = chan->pvt->send_digit(chan, fr->subclass);
+               break;
+       default:
+               if (chan->pvt->write)
+                       res = chan->pvt->write(chan, fr);
+       }
+       chan->blocking = 0;
+       return res;
+}
+
+struct ast_channel *ast_request(char *type, int format, void *data)
+{
+       struct chanlist *chan;
+       struct ast_channel *c = NULL;
+       if (pthread_mutex_lock(&chlock)) {
+               ast_log(LOG_WARNING, "Unable to lock channel list\n");
+               return NULL;
+       }
+       chan = backends;
+       while(chan) {
+               if (!strcasecmp(type, chan->type)) {
+                       if (chan->requester)
+                               c = chan->requester(type, format, data);
+                       pthread_mutex_unlock(&chlock);
+                       break;
+               }
+               chan = chan->next;
+       }
+       if (!chan)
+               ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+       return c;
+}
+
+int ast_call(struct ast_channel *chan, char *addr, int timeout) 
+{
+       /* Place an outgoing call, but don't wait any longer than timeout ms before returning. 
+          If the remote end does not answer within the timeout, then do NOT hang up, but 
+          return anyway.  */
+       int res = -1;
+       if (chan->pvt->call)
+               res = chan->pvt->call(chan, addr, timeout);
+       return res;
+}
+
+int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
+{
+       int pos=0;
+       int to = ftimeout;
+       char d;
+       if (!len)
+               return -1;
+       do {
+               if (c->streamid > -1) {
+                       d = ast_waitstream(c, AST_DIGIT_ANY);
+                       ast_stopstream(c);
+                       if (!d)
+                               d = ast_waitfordigit(c, to);
+               } else {
+                       d = ast_waitfordigit(c, to);
+               }
+               if (d < 0)
+                       return -1;
+               if (!strchr(enders, d))
+                       s[pos++] = d;
+               if ((d == 0) || strchr(enders, d) || (pos >= len - 1)) {
+                       s[pos]='\0';
+                       return 0;
+               }
+               to = timeout;
+       } while(1);
+       /* Never reached */
+       return 0;
+}
diff --git a/channels/chan_vofr.c b/channels/chan_vofr.c
new file mode 100755 (executable)
index 0000000..ea17df9
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Voice over Frame Relay, Adtran Style
+ * 
+ * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <asterisk/channel.h>
+#include <asterisk/channel_pvt.h>
+#include <asterisk/config.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/pbx.h>
+#include <asterisk/options.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include "adtranvofr.h"
+
+#define G723_MAX_BUF 2048
+
+#define FR_API_MESS 16
+
+static char *desc = "Adtran Voice over Frame Relay";
+static char *type = "AdtranVoFR";
+static char *tdesc = "Voice over Frame Relay/Adtran style";
+static char *config = "adtranvofr.conf";
+
+static char context[AST_MAX_EXTENSION] = "default";
+
+static int usecnt =0;
+static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Protect the interface list (of vofr_pvt's) */
+static pthread_mutex_t iflock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Protect the monitoring thread, so only one process can kill or start it, and not
+   when it's doing something critical. */
+static pthread_mutex_t monlock = PTHREAD_MUTEX_INITIALIZER;
+
+/* This is the thread for the monitor which checks for input on the channels
+   which are not currently in use.  */
+static pthread_t monitor_thread = -1;
+
+static int restart_monitor();
+
+/* The private structures of the Adtran VoFR channels are linked for
+   selecting outgoing channels */
+   
+static struct vofr_pvt {
+       int s;                                                  /* Raw socket for this DLCI */
+       struct sockaddr_pkt sa;                 /* Sockaddr needed for sending, also has iface name */
+       struct ast_channel *owner;              /* Channel we belong to, possibly NULL */
+       int outgoing;                                   /* Does this channel support outgoing calls? */
+       struct vofr_pvt *next;                  /* Next channel in list */
+       struct vofr_hdr *hdr;                           /* VOFR header version of buf */
+       struct vofr_hdr *ohdr;
+       u_int8_t dlcih;                                 /* High two bits of DLCI */
+       u_int8_t dlcil;                                 /* Bottom two bits of DLCI */
+       u_int8_t cid;                                   /* Call ID */
+       char buf[G723_MAX_BUF];                                 /* Static buffer for reading frames */
+       char obuf[G723_MAX_BUF];                                /* Output buffer */
+       char context[AST_MAX_EXTENSION];
+} *iflist = NULL;
+
+#ifdef VOFRDUMPER
+
+/* Some useful debugging routines */
+
+static char *set(int val)
+{
+       return (val ? "Set  " : "Unset");
+}
+
+static char *controlstr(int control)
+{
+       switch(control) {
+       case VOFR_CONTROL_ADTRAN:
+               return "Adtran Proprietary";
+       case VOFR_CONTROL_VOICE:
+               return "Voice";
+       case VOFR_CONTROL_RFC1490:
+               return "RFC 1490";
+       }
+       return "Unknown";
+}
+
+static char *dtypestr(int control)
+{
+       switch(control) {
+       case VOFR_TYPE_SIGNAL:
+               return "Signal Frame";
+       case VOFR_TYPE_VOICE:
+               return "Voice Frame";
+       case VOFR_TYPE_ANSWER:
+               return "Answer Tone";
+       case VOFR_TYPE_FAX:     
+               return "FAX";
+       case VOFR_TYPE_DTMF:
+               return "DTMF Digit";
+       }
+       return "Unknown";
+}
+
+static char *vflagsstr(int flags)
+{
+       static char buf[80];
+       buf[0] = '\0';
+       if (!flags)
+               return "(None)";
+       if (flags & VOFR_ROUTE_LOCAL)
+               strcat(buf, "Local ");
+       if (flags & VOFR_ROUTE_VOICE)
+               strcat(buf, "Voice ");
+       if (flags & VOFR_ROUTE_DTE)
+               strcat(buf, "DTE ");
+       else if (flags & VOFR_ROUTE_DTE1)
+               strcat(buf, "DTE1 ");
+       else if (flags & VOFR_ROUTE_DTE2)       
+               strcat(buf, "DTE2 ");
+       return buf;
+}
+
+static char *remidstr(int remid)
+{
+       switch(remid) {
+       case VOFR_CARD_TYPE_UNSPEC:
+               return "Unspecified";
+       case VOFR_CARD_TYPE_FXS:
+               return "FXS";
+       case VOFR_CARD_TYPE_FXO:
+               return "FXO";
+       case VOFR_CARD_TYPE_ENM:        
+               return "E&M";
+       case VOFR_CARD_TYPE_VCOM:       
+               return "Atlas/VCOM";
+       }
+       return "Unknown";
+}
+
+static char *modulationstr(int modulation)
+{
+       switch(modulation) {
+       case VOFR_MODULATION_SINGLE:
+               return "Single Frequency";
+       case VOFR_MODULATION_V21:
+               return "V.21";
+       case VOFR_MODULATION_V27ter_2:
+               return "V.27 (2400bps)";
+       case VOFR_MODULATION_V27ter_4:
+               return "V.27 (4800bps)";
+       case VOFR_MODULATION_V29_7:
+               return "V.29 (7200bps)";
+       case VOFR_MODULATION_V29_9:
+               return "V.29 (9600bps)";
+       case VOFR_MODULATION_V33_12:
+               return "V.33 (12000bps)";
+       case VOFR_MODULATION_V33_14:
+               return "V.33 (14400BPS)";
+       }
+       return "Unknown";
+}
+
+static char *signalstr(int signal)
+{
+       switch(signal) {
+       case VOFR_SIGNAL_ON_HOOK:
+               return "On Hook";
+       case VOFR_SIGNAL_OFF_HOOK:
+               return "Off Hook";
+       case VOFR_SIGNAL_RING:
+               return "Ring";
+       case VOFR_SIGNAL_SWITCHED_DIAL:
+               return "Switched Dial";
+       case VOFR_SIGNAL_BUSY:
+               return "Busy";
+       case VOFR_SIGNAL_TRUNK_BUSY:
+               return "Trunk Busy";
+       }
+       return "Unknown";
+}
+
+static char *vofr_digitstr(int val)
+{
+       static char num[5];
+       if (val < 10) {
+               snprintf(num, sizeof(num), "%d", val);
+               return num;
+       }
+       switch(val) {
+       case 10:
+               return "*";
+       case 11:
+               return "#";
+       }
+       return "Unknown";
+}
+
+
+static void vofr_dump_packet(struct vofr_hdr *vh, int len)
+{
+       printf("VoFR Packet Dump\n");
+       printf("================\n");
+       printf("EI: %s ", set(vh->control & VOFR_MASK_EI));
+       printf("LI: %s\n", set(vh->control & VOFR_MASK_LI));
+       printf("Control: %s (0x%02x)\n", 
+               controlstr(vh->control & VOFR_MASK_CONTROL), vh->control & VOFR_MASK_CONTROL);
+       printf("Data Type: %s (0x%02x)\n", dtypestr(vh->dtype), vh->dtype);
+       if (vh->dtype == VOFR_TYPE_SIGNAL) {
+               printf(" \\--Signal: %s (0x%02x)\n", signalstr(vh->data[0]), vh->data[0]);
+       }
+       if (vh->dtype == VOFR_TYPE_DTMF) {
+               printf(" \\--Digit: %s (0x%02x)\n", vofr_digitstr(vh->data[0]), vh->data[0]);
+       }
+       printf("Connect Tag: 0x%02x\n", vh->ctag);
+       printf("Voice Rt Flags: %s\n", vflagsstr(vh->vflags));
+       printf("DLCI X-Ref: %d\n", (vh->dlcih << 8) | (vh->dlcil));
+       printf("Channel ID: %d\n", vh->cid);
+       printf("Remote ID: %s (0x%02x)\n", remidstr(vh->remid), vh->remid);
+       printf("Modulation: %s (0x%02x)\n", modulationstr(vh->mod), vh->mod);
+       printf("\n");
+       fflush(stdout);
+}
+
+#endif
+
+static int vofr_xmit(struct vofr_pvt *p, char *data, int len)
+{
+       int res;
+    res=sendto(p->s, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_pkt));
+       if (res != len) {
+               ast_log(LOG_WARNING, "vofr_xmit returned %d\n", res);
+       }
+       return res;
+}
+
+static int vofr_digit(struct ast_channel *ast, char digit)
+{
+       /*
+        * T H I S   I S   T O T A L L Y   U N D O C U M E N T E D
+        *     A N D   D O E S   N O T   S O U N D   R I G H T
+        *   XXX Figure out how to really send a decent digit XXX
+        */
+       struct vofr_pvt *p;
+       struct vofr_hdr *vh;
+       p = ast->pvt->pvt;
+       vh = p->ohdr;
+       vh->control = VOFR_CONTROL_VOICE;
+       vh->dtype = VOFR_TYPE_DTMF;
+       vh->vflags = VOFR_ROUTE_NONE;
+       vh->dlcih = p->dlcih;
+       vh->dlcil = p->dlcil;
+       vh->cid = p->cid;
+       vh->remid = VOFR_CARD_TYPE_ASTERISK;
+       vh->mod = VOFR_MODULATION_SINGLE;
+       if ((digit >= '0') && (digit <= '9'))
+                vh->data[0] = digit - '0';
+        else if (digit == '*')
+                vh->data[0] = 10;
+        else if (digit == '#')
+                vh->data[0] = 11;
+        else {
+                ast_log(LOG_WARNING, "%s: tried to dial a non digit '%c'\n", ast->name, digit);
+                return -1;
+        }
+        vh->data[1] = 0x14;
+        vh->data[2] = 0x1f;
+        vh->data[3] = 0x70;
+               /* We sorta start the digit */
+        vofr_xmit(p, p->obuf, VOFR_HDR_SIZE + 4 + FR_API_MESS);
+        usleep(30000);
+               /* And terminate with an empty voice frame */
+        vh->control = VOFR_CONTROL_VOICE;
+        vh->dtype = VOFR_TYPE_VOICE;
+        vh->vflags = VOFR_ROUTE_NONE;
+        vh->dlcih = p->dlcih;
+        vh->dlcil = p->dlcil;
+        vh->cid = p->cid;
+        vh->remid = VOFR_CARD_TYPE_ASTERISK;
+        vh->mod = VOFR_MODULATION_SINGLE;
+        vofr_xmit(p, p->obuf, VOFR_HDR_SIZE + FR_API_MESS);
+       return 0;
+}
+
+static int vofr_xmit_signal(struct vofr_pvt *p, int signal, int pad)
+{
+        /* Prepare and transmit outgoing buffer with given signal and
+           pad the end with *pad* bytes of data presumed to already
+           be in the buffer (like DTMF tones, etc) */
+        struct vofr_hdr *vh = p->ohdr;
+       int res;
+    vh->control = VOFR_CONTROL_VOICE;
+    vh->dtype = VOFR_TYPE_SIGNAL;
+    vh->vflags = VOFR_ROUTE_NONE;
+    vh->dlcih = p->dlcih;
+    vh->dlcil = p->dlcil;
+    vh->cid = p->cid;
+    vh->remid = VOFR_CARD_TYPE_ASTERISK;
+    vh->mod = VOFR_MODULATION_SINGLE;
+    vh->data[0] = signal;
+       if (FR_API_MESS)
+               memset(p->obuf, 0, FR_API_MESS);
+       res = vofr_xmit(p, p->obuf,  VOFR_HDR_SIZE + pad + 1 + FR_API_MESS);
+       return res;
+
+}
+
+static int vofr_call(struct ast_channel *ast, char *dest, int timeout)
+{
+       int res;
+       int otimeout;
+       struct ast_frame *f;
+       struct vofr_pvt *p;
+       p = ast->pvt->pvt;
+       if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
+               ast_log(LOG_WARNING, "vofr_call called on %s, neither down nor reserved\n", ast->name);
+               return -1;
+       }
+       /* Take the system off hook */
+       vofr_xmit_signal(p, VOFR_SIGNAL_OFFHOOK, 0);
+       /* Wait for an acknowledgement */
+       otimeout = 1000;
+       while(otimeout) {
+               otimeout = ast_waitfor(ast, 1000);
+               if (otimeout < 1) {
+                       ast_log(LOG_WARNING, "Unable to take line off hook\n");
+                       /* Musta gotten hung up, or no ack on off hook */
+                       return -1;      
+               }
+               f = ast_read(ast);
+               if (!f)
+                       return -1;
+               if ((f->frametype == AST_FRAME_CONTROL) &&
+                   (f->subclass == AST_CONTROL_OFFHOOK)) 
+                       /* Off hook */
+                               break;
+       }
+       if (!otimeout) {
+               ast_log(LOG_WARNING, "Unable to take line off hook\n");
+               return -1;
+       }
+       /* Send the digits */
+       while(*dest) {
+               ast->state = AST_STATE_DIALING;
+               vofr_digit(ast, *dest);
+               /* Wait .1 seconds before dialing next digit */
+               usleep(100000);
+               dest++;
+       }
+       if (timeout) {
+               /* Wait for the ack that it's ringing */
+               otimeout = 1000;
+               while(otimeout) {
+                       otimeout = ast_waitfor(ast, 1000);
+                       if (otimeout < 1) {
+                               ast_log(LOG_WARNING, "No acknowledgement for ringing\n");
+                               /* Musta gotten hung up, or no ack on off hook */
+                               return -1;      
+                       }
+                       f = ast_read(ast);
+                       if (!f) 
+                               return -1;
+                       
+                       if (f->frametype == AST_FRAME_CONTROL) {
+                           if (f->subclass == AST_CONTROL_RINGING) {
+                                       ast->state = AST_STATE_RINGING;
+                                       /* We're ringing -- good enough */
+                                       break;
+                               }
+                               if (f->subclass == AST_CONTROL_BUSY)
+                               /* It's busy */
+                                       return -1;
+                       }
+                       ast_frfree(f);
+               }               
+       }
+       otimeout = timeout;
+       while(timeout) {
+               /* Wait for an answer, up to timeout... */
+               res = ast_waitfor(ast, timeout);
+               if (res < 0)
+                       /* Musta gotten hung up */
+                       return -1;
+               else
+                       timeout = res;
+               if (res) {
+                       /* Ooh, read what's there. */
+                       f = ast_read(ast);
+                       if (!f)
+                               return -1;
+                       if ((f->frametype == AST_FRAME_CONTROL) && 
+                           (f->subclass == AST_CONTROL_ANSWER)) 
+                               /* Got an answer -- return the # of ms it took */
+                               return otimeout - res;
+                               
+               }
+       }
+       return 0;
+}
+
+static int send_hangup(struct vofr_pvt *p)
+{
+       /* Just send the hangup sequence */
+       return vofr_xmit_signal(p, 0x80, 0);
+}
+
+static int vofr_hangup(struct ast_channel *ast)
+{
+       int res;
+       if (option_debug)
+               ast_log(LOG_DEBUG, "vofr_hangup(%s)\n", ast->name);
+       if (!ast->pvt->pvt) {
+               ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+               return 0;
+       }
+       res = send_hangup(ast->pvt->pvt);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+               return -1;
+       }
+       ast->state = AST_STATE_DOWN;
+       ((struct vofr_pvt *)(ast->pvt->pvt))->owner = NULL;
+       pthread_mutex_lock(&usecnt_lock);
+       usecnt--;
+       if (usecnt < 0) 
+               ast_log(LOG_WARNING, "Usecnt < 0???\n");
+       pthread_mutex_unlock(&usecnt_lock);
+       ast_update_use_count();
+       if (option_verbose > 2) 
+               ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name);
+       ast->pvt->pvt = NULL;
+       ast->state = AST_STATE_DOWN;
+       restart_monitor();
+       return 0;
+}
+
+static int vofr_answer(struct ast_channel *ast)
+{
+       int res;
+       int cnt = 1000;
+       char buf[2048];
+       struct vofr_hdr *vh;
+       ast->rings = 0;
+       if (option_debug)
+               ast_log(LOG_DEBUG, "vofr_answer(%s)\n", ast->name);
+       res = vofr_xmit_signal(ast->pvt->pvt, VOFR_SIGNAL_OFFHOOK, 0);
+       if (res < 0)
+               ast_log(LOG_WARNING, "Unable to anaswer line %s\n", ast->name);
+       ast->state = AST_STATE_UP;
+       while(cnt > 0) {
+               cnt = ast_waitfor(ast, cnt);
+               if (cnt > 0) {
+                       res = read(ast->fd, buf, sizeof(buf));
+                       res -= FR_API_MESS;
+                       if (res < 0)
+                               ast_log(LOG_WARNING, "Warning:  read failed (%s) on %s\n", strerror(errno), ast->name);
+                       else {
+                               /* We're looking for an answer */
+                               vh = (struct vofr_hdr *)(buf + FR_API_MESS);
+                               switch(vh->dtype) {
+                               case VOFR_TYPE_SIGNAL:
+                                       switch(vh->data[0]) {
+                                       case VOFR_SIGNAL_UNKNOWN:
+                                               switch(vh->data[1]) {
+                                               case 0x1:
+                                                       if (option_debug) 
+                                                               ast_log(LOG_DEBUG, "Answered '%s'\n", ast->name);
+                                                       else if (option_verbose > 2) 
+                                                               ast_verbose( VERBOSE_PREFIX_3 "Answered '%s'\n", ast->name);
+                                                       ast->state = AST_STATE_UP;
+                                                       return 0;
+                                                       break;
+                                               default:
+                                                       ast_log(LOG_WARNING, "Unexpected 'unknown' frame type %d\n", vh->data[1]);
+                                               }
+                                               break;
+                                       case VOFR_SIGNAL_ON_HOOK:
+                                               /* Ignore onhooks.  */
+                                               break;
+                                       default:
+                                               ast_log(LOG_WARNING, "Unexpected signal type %d\n", vh->data[0]);
+                                       }
+                                       break;
+                               default:
+                                       ast_log(LOG_WARNING, "Unexpected data type %d\n", vh->dtype);
+                               }
+                       }
+               }
+       }
+       ast_log(LOG_WARNING, "Did not get acknowledged answer\n");
+       return -1;
+}
+
+static char vofr_2digit(char c)
+{
+       if (c == 11)
+               return '#';
+       else if (c == 10)
+               return '*';
+       else if ((c < 10) && (c >= 0))
+               return '0' + c;
+       else
+               return '?';
+}
+
+static struct ast_frame  *vofr_read(struct ast_channel *ast)
+{
+       int res;
+       char tone;
+       int timeout,x;
+       struct vofr_pvt *p = ast->pvt->pvt;
+       short *swapping;
+       struct ast_frame *fr = (struct ast_frame *)(p->buf);
+       struct vofr_hdr *vh = (struct vofr_hdr *)(p->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET - sizeof(struct vofr_hdr));
+       /* Read into the right place in the buffer, in case we send this
+          as a voice frame. */
+       CHECK_BLOCKING(ast);
+       res = read(p->s, ((char *)vh)  - FR_API_MESS, 
+                               G723_MAX_BUF - AST_FRIENDLY_OFFSET - sizeof(struct ast_frame) + sizeof(struct vofr_hdr) + FR_API_MESS);
+       ast->blocking = 0;
+       res -= FR_API_MESS;             
+       if (res < sizeof(struct vofr_hdr *)) {
+               ast_log(LOG_WARNING, "Nonsense frame on %s\n", ast->name);
+               return NULL;
+       }
+
+       /* Some nice norms */
+       fr->datalen = 0;
+       fr->timelen = 0;
+       fr->data =  NULL;
+       fr->src = type;
+       fr->offset = 0;
+       fr->mallocd=0;
+       
+       /* Now, what we do depends on what we read */
+       switch(vh->dtype) {
+       case VOFR_TYPE_SIGNAL:
+               switch(vh->data[0]) {
+               case VOFR_SIGNAL_ON_HOOK:
+                       /* Hang up this line */
+                       if (ast->state == AST_STATE_UP)
+                               return NULL;
+                       else {
+                               fr->frametype = AST_FRAME_NULL;
+                               fr->subclass = 0;
+                               break;
+                       }
+               case VOFR_SIGNAL_UNKNOWN:
+                       switch(vh->data[1]) {
+                       case 0x1:
+                               /* This is a little tricky, because it depends
+                                  on the context of what state we're in */
+                               switch(ast->state) {
+                               case AST_STATE_RINGING:
+                                       fr->frametype = AST_FRAME_CONTROL;
+                                       fr->subclass = AST_CONTROL_ANSWER;
+                                       ast->state = AST_STATE_UP;
+                                       break;
+                               case AST_STATE_DOWN:
+                               case AST_STATE_UP:
+                                       fr->frametype = AST_FRAME_NULL;
+                                       fr->subclass = 0;
+                                       break;
+                               }
+                               break;
+                       case 0x2:
+                               /* Remote acknowledged off hook */
+                               fr->frametype = AST_FRAME_CONTROL;
+                               fr->subclass = AST_CONTROL_OFFHOOK;
+                               ast->state = AST_STATE_OFFHOOK;
+                               break;
+                       case 0x3:
+                               /* Busy signal */
+                               fr->frametype = AST_FRAME_CONTROL;
+                               fr->subclass = AST_CONTROL_BUSY;
+                               ast->state = AST_STATE_BUSY;
+                               break;
+                       case 0x5:
+                               /* Ringing -- acknowledged */
+                               fr->frametype = AST_FRAME_CONTROL;
+                               fr->subclass = AST_CONTROL_RINGING;
+                               ast->state = AST_STATE_RINGING;
+                               break;
+                       case 0x6:
+                               /* Hang up detected.  Return NULL */
+                               return NULL;
+                       default:
+                               ast_log(LOG_WARNING, "Don't know what to do with 'unknown' signal '%d'\n", vh->data[1]);
+                               fr->frametype = AST_FRAME_NULL;
+                               fr->subclass = 0;
+                       }
+                       return fr;
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know what to do with signal '%d'\n", vh->data[0]);
+               }
+               break;
+       case VOFR_TYPE_DTMF:
+               /* If it's a DTMF tone, then we want to wait until we don't get any more dtmf tones or
+                  the DTMF tone changes.  
+                      XXX Note: We will drop at least one frame here though XXX */
+               
+               tone = vofr_2digit(vh->data[0]);
+               timeout = 50;
+               do {
+                       if ((timeout = ast_waitfor(ast, timeout)) < 1)
+                               break;
+                       CHECK_BLOCKING(ast);
+                       res = read(p->s, ((char *)vh)  - FR_API_MESS, 
+                                       G723_MAX_BUF - AST_FRIENDLY_OFFSET - sizeof(struct ast_frame) + sizeof(struct vofr_hdr) + FR_API_MESS);
+                       ast->blocking = 0;
+                       res -= FR_API_MESS;             
+                       if (res < sizeof(struct vofr_hdr *)) {
+                               ast_log(LOG_WARNING, "Nonsense frame on %s\n", ast->name);
+                               return NULL;
+                       }
+                       if (vh->dtype == VOFR_TYPE_DTMF) {
+                               /* Reset the timeout */
+                               timeout = 50;
+                               if ((tone != vofr_2digit(vh->data[0])) )
+                                       /* Or not...  Something else now.. Just send our first frame */
+                                       break;
+                       }
+                       
+               } while (timeout);
+               fr->frametype = AST_FRAME_DTMF;
+               fr->subclass = tone;
+               fr->datalen = 0;
+               fr->data = NULL;
+               fr->offset = 0;
+               return fr;
+       case VOFR_TYPE_VOICE:
+               /* XXX Bug in the Adtran: Sometimes we don't know when calls are picked up, so if we
+                      get voice frames, go ahead and consider it answered even though it probably has
+                          not been answered XXX */
+               if ((ast->state == AST_STATE_RINGING) || (ast->state == AST_STATE_DIALING))  {
+                       ast_log(LOG_DEBUG, "Adtran bug! (state = %d)\n", ast->state);
+                       fr->frametype = AST_FRAME_CONTROL;
+                       fr->subclass = AST_CONTROL_ANSWER;
+                       ast->state = AST_STATE_UP;
+                       return fr;
+               } else if (ast->state !=  AST_STATE_UP) {
+                       ast_log(LOG_WARNING, "Voice in weird state %d\n", ast->state);
+               }
+               fr->frametype = AST_FRAME_VOICE;
+               fr->subclass = AST_FORMAT_G723_1;
+               fr->datalen = res - sizeof(struct vofr_hdr);
+               fr->data = ((char *)vh) + sizeof(struct vofr_hdr);
+               fr->src = type;
+               /* XXX Byte swapping is a bug XXX */
+               swapping = fr->data;
+               for (x=0;x<fr->datalen/2;x++)
+                       swapping[x] = ntohs(swapping[x]);
+               fr->offset = AST_FRIENDLY_OFFSET;
+               /* Thirty ms of sound per frame */
+               fr->timelen = 30;
+               return fr;
+       default:
+               ast_log(LOG_WARNING, "Don't know what to do with data type %d frames\n", vh->dtype);
+       }
+       /* If we don't know what it is, send a NULL frame */
+       fr->frametype = AST_FRAME_NULL;
+       fr->subclass = 0;
+       return fr;
+}
+
+static int vofr_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+       struct vofr_hdr *vh;
+       struct vofr_pvt *p = ast->pvt->pvt;
+       short *swapping;
+    int x;
+       char *start;
+       int res;
+       /* Write a frame of (presumably voice) data */
+       if (frame->frametype != AST_FRAME_VOICE) {
+               ast_log(LOG_WARNING, "Don't know what to do with  frame type '%d'\n", frame->frametype);
+               return -1;
+       }
+       if (frame->subclass != AST_FORMAT_G723_1) {
+               ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
+               return -1;
+       }
+       /* If we get here, we have a voice frame of G.723.1 data.  First check to be
+          sure we have enough headroom for the vofr header.  If there isn't enough
+          headroom, we're lazy and just error out rather than copying it into the
+          output buffer, because applications should always leave AST_FRIENDLY_OFFSET
+          bytes just for this reason. */
+       if (frame->offset < sizeof(struct vofr_hdr) + FR_API_MESS) {
+               ast_log(LOG_WARNING, "Frame source '%s' didn't provide a friendly enough offset\n", (frame->src ? frame->src : "**Unknown**"));
+               return -1;
+       }
+       /* XXX Byte swapping is a bug XXX */
+       swapping = frame->data;
+       for (x=0;x<frame->datalen/2;x++)
+               swapping[x] = ntohs(swapping[x]);
+       vh = (struct vofr_hdr *)(frame->data - sizeof(struct vofr_hdr));
+       /* Some versions of the API have some header mess that needs to be
+          zero'd out and acounted for..  */
+       start = ((void *)vh) - FR_API_MESS;
+       if (start)
+               memset(start, 0, FR_API_MESS);
+       /* Now we fill in the vofr header */
+       vh->control = VOFR_CONTROL_VOICE;
+       vh->dtype = VOFR_TYPE_VOICE;
+       vh->vflags = VOFR_ROUTE_NONE;
+       vh->dlcih = p->dlcih;
+       vh->dlcil = p->dlcil;
+       vh->cid = p->cid;
+       vh->remid = VOFR_CARD_TYPE_ASTERISK;
+       vh->mod = VOFR_MODULATION_SINGLE;
+       res = vofr_xmit(p, start, 
+                               VOFR_HDR_SIZE + frame->datalen + FR_API_MESS);
+       res -= FR_API_MESS;
+       /* XXX Byte swapping is a bug, but get it back to the right format XXX */
+       swapping = frame->data;
+       for (x=0;x<frame->datalen/2;x++)
+               swapping[x] = htons(swapping[x]);
+       if (res != VOFR_HDR_SIZE + frame->datalen) {
+               ast_log(LOG_WARNING, "Unable to write frame correctly\n");
+               return -1;
+       }
+       return 0;
+}
+
+static struct ast_channel *vofr_new(struct vofr_pvt *i, int state)
+{
+       struct ast_channel *tmp;
+       tmp = ast_channel_alloc();
+       if (tmp) {
+               snprintf(tmp->name, sizeof(tmp->name), "AdtranVoFR/%s", i->sa.spkt_device);
+               tmp->type = type;
+               tmp->fd = i->s;
+               /* Adtran VoFR supports only G723.1 format data.  G711 (ulaw) would be nice too */
+               tmp->format = AST_FORMAT_G723_1;
+               tmp->state = state;
+               if (state == AST_STATE_RING)
+                       tmp->rings = 1;
+               tmp->pvt->pvt = i;
+               tmp->pvt->send_digit = vofr_digit;
+               tmp->pvt->call = vofr_call;
+               tmp->pvt->hangup = vofr_hangup;
+               tmp->pvt->answer = vofr_answer;
+               tmp->pvt->read = vofr_read;
+               tmp->pvt->write = vofr_write;
+               i->owner = tmp;
+               pthread_mutex_lock(&usecnt_lock);
+               usecnt++;
+               pthread_mutex_unlock(&usecnt_lock);
+               ast_update_use_count();
+               strncpy(tmp->context, i->context, sizeof(tmp->context));
+               if (state != AST_STATE_DOWN) {
+                       if (ast_pbx_start(tmp)) {
+                               ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+                               ast_hangup(tmp);
+                       }
+               }
+       } else
+               ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+       return tmp;
+}
+
+static int vofr_mini_packet(struct vofr_pvt *i, struct vofr_hdr *pkt, int len)
+{
+       /* Here, we're looking for rings or off hooks -- signals that
+          something is about to happen and we need to start the 
+          PBX thread */
+       switch(pkt->dtype) {
+       case VOFR_TYPE_SIGNAL:
+               switch(pkt->data[0]) {
+               case VOFR_SIGNAL_RING:
+                       /* If we get a RING, we definitely want to start a new thread */
+                       vofr_new(i, AST_STATE_RING);
+                       break;
+               case VOFR_SIGNAL_ON_HOOK:
+                       break;
+               case VOFR_SIGNAL_UNKNOWN:
+                       switch(pkt->data[1]) {
+                       case 0x1:
+                               /* ignore */
+                               break;
+                       case 0x6:
+                               /* A remote hangup request */
+                               if (option_debug)
+                                       ast_log(LOG_DEBUG, "Sending hangup reply\n");
+                               send_hangup(i);
+                               break;
+                       default:
+                               ast_log(LOG_WARNING, "Unexected 'unknown' signal '%d'\n", pkt->data[1]);
+                       }
+                       break;
+               default:
+                       ast_log(LOG_DEBUG, "Unknown signal type '%d'\n", pkt->data[0]);
+               }                       
+               break;
+       case VOFR_TYPE_VOICE:
+               break;
+       default:
+               ast_log(LOG_DEBUG, "Unknown packet type '%d'\n", pkt->dtype);
+       }
+       return 0;
+}
+
+static void *do_monitor(void *data)
+{
+       fd_set rfds;
+       int n, res;
+       struct vofr_pvt *i;
+       /* This thread monitors all the frame relay interfaces which are not yet in use
+          (and thus do not have a separate thread) indefinitely */
+       /* From here on out, we die whenever asked */
+       if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
+               ast_log(LOG_WARNING, "Unable to set cancel type to asynchronous\n");
+               return NULL;
+       }
+       for(;;) {
+               /* Don't let anybody kill us right away.  Nobody should lock the interface list
+                  and wait for the monitor list, but the other way around is okay. */
+               if (pthread_mutex_lock(&monlock)) {
+                       ast_log(LOG_ERROR, "Unable to grab monitor lock\n");
+                       return NULL;
+               }
+               /* Lock the interface list */
+               if (pthread_mutex_lock(&iflock)) {
+                       ast_log(LOG_ERROR, "Unable to grab interface lock\n");
+                       pthread_mutex_unlock(&monlock);
+                       return NULL;
+               }
+               /* Build the stuff we're going to select on, that is the socket of every
+                  vofr_pvt that does not have an associated owner channel */
+               n = -1;
+               FD_ZERO(&rfds);
+               i = iflist;
+               while(i) {
+                       if (FD_ISSET(i->s, &rfds)) 
+                               ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->s, i->sa.spkt_device);
+                       if (!i->owner) {
+                               /* This needs to be watched, as it lacks an owner */
+                               FD_SET(i->s, &rfds);
+                               if (i->s > n)
+                                       n = i->s;
+                       }
+                       i = i->next;
+               }
+               /* Okay, now that we know what to do, release the interface lock */
+               pthread_mutex_unlock(&iflock);
+               
+               /* And from now on, we're okay to be killed, so release the monitor lock as well */
+               pthread_mutex_unlock(&monlock);
+               /* Wait indefinitely for something to happen */
+               res = select(n + 1, &rfds, NULL, NULL, NULL);
+               /* Okay, select has finished.  Let's see what happened.  */
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "select return %d: %s\n", res, strerror(errno));
+                       continue;
+               }
+               /* Alright, lock the interface list again, and let's look and see what has
+                  happened */
+               if (pthread_mutex_lock(&iflock)) {
+                       ast_log(LOG_WARNING, "Unable to lock the interface list\n");
+                       continue;
+               }
+               i = iflist;
+               while(i) {
+                       if (FD_ISSET(i->s, &rfds)) {
+                               if (i->owner) {
+                                       ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d, %s)...\n", i->s, i->sa.spkt_device);
+                                       continue;
+                               }
+                               res = read(i->s, i->buf, sizeof(i->buf));
+                               res -= FR_API_MESS;
+#ifdef VOFRDUMPER
+                               vofr_dump_packet(i->hdr, res);
+#endif
+                               vofr_mini_packet(i, i->hdr, res);
+                       }
+                       i=i->next;
+               }
+               pthread_mutex_unlock(&iflock);
+       }
+       /* Never reached */
+       return NULL;
+       
+}
+
+static int restart_monitor()
+{
+       /* If we're supposed to be stopped -- stay stopped */
+       if (monitor_thread == -2)
+               return 0;
+       if (pthread_mutex_lock(&monlock)) {
+               ast_log(LOG_WARNING, "Unable to lock monitor\n");
+               return -1;
+       }
+       if (monitor_thread == pthread_self()) {
+               pthread_mutex_unlock(&monlock);
+               ast_log(LOG_WARNING, "Cannot kill myself\n");
+               return -1;
+       }
+       if (monitor_thread != -1) {
+               pthread_cancel(monitor_thread);
+#if 0
+               pthread_join(monitor_thread, NULL);
+#endif
+       }
+       /* Start a new monitor */
+       if (pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
+               pthread_mutex_unlock(&monlock);
+               ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
+               return -1;
+       }
+       pthread_mutex_unlock(&monlock);
+       return 0;
+}
+
+struct vofr_pvt *mkif(char *type, char *iface)
+{
+       /* Make a vofr_pvt structure for this interface */
+       struct vofr_pvt *tmp;
+       int sndbuf = 4096;
+       
+       tmp = malloc(sizeof(struct vofr_pvt));
+       if (tmp) {
+
+               /* Allocate a packet socket */
+               tmp->s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));
+               if (tmp->s < 0) {
+                       ast_log(LOG_ERROR, "Unable to create socket: %s\n", strerror(errno));
+                       free(tmp);
+                       return NULL;
+               }
+
+               /* Prepare sockaddr for binding */
+               memset(&tmp->sa, 0, sizeof(tmp->sa));
+               strncpy(tmp->sa.spkt_device, iface, sizeof(tmp->sa.spkt_device));
+               tmp->sa.spkt_protocol = htons(0x16);
+               tmp->sa.spkt_family = AF_PACKET;
+               
+               /* Bind socket to specific interface */
+               if (bind(tmp->s, (struct sockaddr *)&tmp->sa, sizeof(struct sockaddr))) {
+                       ast_log(LOG_ERROR, "Unable to bind to '%s': %s\n", tmp->sa.spkt_device, 
+                                                                               strerror(errno));
+                       free(tmp);
+                       return NULL;
+               }
+               
+               /* Set magic send buffer size */
+               if (setsockopt(tmp->s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
+                       ast_log(LOG_ERROR, "Unable to set send buffer size to %d: %s\n", sndbuf, strerror(errno));
+                       free(tmp);
+                       return NULL;
+               }
+               tmp->owner =  NULL;
+               tmp->hdr = (struct vofr_hdr *)(tmp->buf + FR_API_MESS);
+               tmp->ohdr = (struct vofr_hdr *)(tmp->obuf + FR_API_MESS);
+               tmp->dlcil = 0;
+               tmp->dlcih = 0;
+               tmp->cid = 1;
+               strncpy(tmp->context, context, sizeof(tmp->context));
+               /* User terminations are game for outgoing connections */
+               if (!strcasecmp(type, "user")) 
+                       tmp->outgoing = 1;
+               else
+                       tmp->outgoing = 0;
+               tmp->next = NULL;
+               /* Hang it up to be sure it's good */
+               send_hangup(tmp);
+               
+       }
+       return tmp;
+}
+
+static struct ast_channel *vofr_request(char *type, int format, void *data)
+{
+       int oldformat;
+       struct vofr_pvt *p;
+       struct ast_channel *tmp = NULL;
+       /* We can only support G.723.1 formatted frames, but we should never
+          be asked to support anything else anyway, since we've published
+          our capabilities when we registered. */
+       oldformat = format;
+       format &= AST_FORMAT_G723_1;
+       if (!format) {
+               ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", format);
+               return NULL;
+       }
+       /* Search for an unowned channel */
+       if (pthread_mutex_lock(&iflock)) {
+               ast_log(LOG_ERROR, "Unable to lock interface list???\n");
+               return NULL;
+       }
+       p = iflist;
+       while(p) {
+               if (!p->owner) {
+                       tmp = vofr_new(p, AST_STATE_DOWN);
+                       break;
+               }
+               p = p->next;
+       }
+       pthread_mutex_unlock(&iflock);
+       restart_monitor();
+       return tmp;
+}
+
+int load_module()
+{
+       struct ast_config *cfg;
+       struct ast_variable *v;
+       struct vofr_pvt *tmp;
+       cfg = ast_load(config);
+
+       /* We *must* have a config file otherwise stop immediately */
+       if (!cfg) {
+               ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+               return -1;
+       }
+       if (pthread_mutex_lock(&iflock)) {
+               /* It's a little silly to lock it, but we mind as well just to be sure */
+               ast_log(LOG_ERROR, "Unable to lock interface list???\n");
+               return -1;
+       }
+       v = ast_variable_browse(cfg, "interfaces");
+       while(v) {
+               /* Create the interface list */
+               if (!strcasecmp(v->name, "user") ||
+                       !strcasecmp(v->name, "network")) {
+                               tmp = mkif(v->name, v->value);
+                               if (tmp) {
+                                       tmp->next = iflist;
+                                       iflist = tmp;
+                               } else {
+                                       ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
+                                       ast_destroy(cfg);
+                                       pthread_mutex_unlock(&iflock);
+                                       unload_module();
+                                       return -1;
+                               }
+               } else if (!strcasecmp(v->name, "context")) {
+                       strncpy(context, v->value, sizeof(context));
+               }
+               v = v->next;
+       }
+       pthread_mutex_unlock(&iflock);
+       /* Make sure we can register our AdtranVoFR channel type */
+       if (ast_channel_register(type, tdesc, AST_FORMAT_G723_1, vofr_request)) {
+               ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+               ast_destroy(cfg);
+               unload_module();
+               return -1;
+       }
+       ast_destroy(cfg);
+       /* And start the monitor for the first time */
+       restart_monitor();
+       return 0;
+}
+
+
+
+int unload_module()
+{
+       struct vofr_pvt *p, *pl;
+       /* First, take us out of the channel loop */
+       ast_channel_unregister(type);
+       if (!pthread_mutex_lock(&iflock)) {
+               /* Hangup all interfaces if they have an owner */
+               p = iflist;
+               while(p) {
+                       if (p->owner)
+                               ast_softhangup(p->owner);
+                       p = p->next;
+               }
+               iflist = NULL;
+               pthread_mutex_unlock(&iflock);
+       } else {
+               ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+               return -1;
+       }
+       if (!pthread_mutex_lock(&monlock)) {
+               if (monitor_thread > -1) {
+                       pthread_cancel(monitor_thread);
+                       pthread_join(monitor_thread, NULL);
+               }
+               monitor_thread = -2;
+               pthread_mutex_unlock(&monlock);
+       } else {
+               ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+               return -1;
+       }
+
+       if (!pthread_mutex_lock(&iflock)) {
+               /* Destroy all the interfaces and free their memory */
+               p = iflist;
+               while(p) {
+                       /* Close the socket, assuming it's real */
+                       if (p->s > -1)
+                               close(p->s);
+                       pl = p;
+                       p = p->next;
+                       /* Free associated memory */
+                       free(pl);
+               }
+               iflist = NULL;
+               pthread_mutex_unlock(&iflock);
+       } else {
+               ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+               return -1;
+       }
+               
+       return 0;
+}
+
+int usecount()
+{
+       int res;
+       pthread_mutex_lock(&usecnt_lock);
+       res = usecnt;
+       pthread_mutex_unlock(&usecnt_lock);
+       return res;
+}
+
+char *description()
+{
+       return desc;
+}
+