From 1229f5d94d61c960deb1295dc75249d20003382f Mon Sep 17 00:00:00 2001 From: markster Date: Sat, 4 Dec 1999 21:35:07 +0000 Subject: Version 0.1.0 from FTP git-svn-id: http://svn.digium.com/svn/asterisk/trunk@84 f38db490-d61c-443f-a65b-d21fe96a405b --- channels/chan_vofr.c | 1138 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1138 insertions(+) create mode 100755 channels/chan_vofr.c (limited to 'channels') 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 + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;xdatalen/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;xdatalen/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;xdatalen/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; +} + -- cgit v1.2.3