aboutsummaryrefslogtreecommitdiffstats
path: root/channels/chan_vofr.c
diff options
context:
space:
mode:
authormarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>1999-12-04 21:35:07 +0000
committermarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>1999-12-04 21:35:07 +0000
commit1229f5d94d61c960deb1295dc75249d20003382f (patch)
tree5266c0f25c9d3f912bc8c77a6d6f3cdb7893f2b7 /channels/chan_vofr.c
parentd2d438a1226691ebd589d93105c414557b4b5cbc (diff)
Version 0.1.0 from FTP
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@84 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_vofr.c')
-rwxr-xr-xchannels/chan_vofr.c1138
1 files changed, 1138 insertions, 0 deletions
diff --git a/channels/chan_vofr.c b/channels/chan_vofr.c
new file mode 100755
index 000000000..ea17df951
--- /dev/null
+++ b/channels/chan_vofr.c
@@ -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;
+}
+