mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3295 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			1272 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1272 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * Asterisk -- A telephony toolkit for Linux.
 | |
|  *
 | |
|  * Implementation of Voice over Frame Relay, Adtran Style
 | |
|  * 
 | |
|  * Copyright (C) 1999, Mark Spencer
 | |
|  *
 | |
|  * 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 <string.h>
 | |
| #include <asterisk/lock.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 <fcntl.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <linux/if_packet.h>
 | |
| #include <linux/if_ether.h>
 | |
| #ifndef OLD_SANGOMA_API
 | |
| #include <linux/if_wanpipe.h>
 | |
| #include <linux/wanpipe.h>
 | |
| #endif
 | |
| #include <sys/signal.h>
 | |
| #include "adtranvofr.h"
 | |
| 
 | |
| /* #define VOFRDUMPER */
 | |
| 
 | |
| #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 char language[MAX_LANGUAGE] = "";
 | |
| 
 | |
| static int usecnt =0;
 | |
| AST_MUTEX_DEFINE_STATIC(usecnt_lock);
 | |
| 
 | |
| /* Protect the interface list (of vofr_pvt's) */
 | |
| AST_MUTEX_DEFINE_STATIC(iflock);
 | |
| 
 | |
| /* Protect the monitoring thread, so only one process can kill or start it, and not
 | |
|    when it's doing something critical. */
 | |
| AST_MUTEX_DEFINE_STATIC(monlock);
 | |
| 
 | |
| /* 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 = AST_PTHREADT_NULL;
 | |
| 
 | |
| static int restart_monitor(void);
 | |
| 
 | |
| /* 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 */
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 	struct sockaddr_pkt sa;			/* Sockaddr needed for sending, also has iface name */
 | |
| #else
 | |
| 	struct wan_sockaddr_ll sa;		/* Wanpipe sockaddr */
 | |
| #endif
 | |
| 	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];
 | |
| 	char language[MAX_LANGUAGE];
 | |
| 	int ringgothangup;				/* Have we received exactly one hangup after a ring */
 | |
| } *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 struct ast_frame  *vofr_read(struct ast_channel *ast);
 | |
| 
 | |
| static int vofr_xmit(struct vofr_pvt *p, char *data, int len)
 | |
| {
 | |
| 	int res;
 | |
| #ifdef OLD_SANGOMA_API
 | |
|     res=sendto(p->s, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_pkt));
 | |
| #else
 | |
|     res=sendto(p->s, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct wan_sockaddr_ll));
 | |
| #endif
 | |
| 	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 '%s' off hook\n", ast->name);
 | |
| 			/* Musta gotten hung up, or no ack on off hook */
 | |
| 			return -1;	
 | |
| 		}
 | |
| 		f = vofr_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 = vofr_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 = vofr_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;
 | |
| 	((struct vofr_pvt *)(ast->pvt->pvt))->ringgothangup = 0;
 | |
| 	ast_mutex_lock(&usecnt_lock);
 | |
| 	usecnt--;
 | |
| 	if (usecnt < 0) 
 | |
| 		ast_log(LOG_WARNING, "Usecnt < 0???\n");
 | |
| 	ast_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->fds[0], buf, sizeof(buf));
 | |
| #ifdef VOFRDUMPER
 | |
| 				vofr_dump_packet((void *)(buf +FR_API_MESS), res - FR_API_MESS);
 | |
| #endif
 | |
| 			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);
 | |
| retry:
 | |
| 	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);
 | |
| 	if (res < 0) {
 | |
| 		/*  XXX HUGE BUG IN SANGOMA'S STACK: IT IGNORES O_NONBLOCK XXX */
 | |
| 		if (errno == EAGAIN) {
 | |
| 			fd_set fds;
 | |
| 			FD_ZERO(&fds);
 | |
| 			FD_SET(p->s, &fds);
 | |
| 			ast_select(p->s + 1, &fds, NULL, NULL, NULL);
 | |
| 			goto retry;
 | |
| 		}
 | |
| 		ast->blocking = 0;
 | |
| 		ast_log(LOG_WARNING, "Read error on %s: %s (%d)\n", ast->name, strerror(errno));
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	ast->blocking = 0;
 | |
| 		
 | |
| #ifdef VOFRDUMPER
 | |
| 	vofr_dump_packet((void *)(vh), res);
 | |
| #endif
 | |
| 	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;
 | |
| 	fr->delivery.tv_sec = 0;
 | |
| 	fr->delivery.tv_usec = 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) || (p->ringgothangup)) {
 | |
| 				return NULL;
 | |
| 			} else {
 | |
| 				fr->frametype = AST_FRAME_NULL;
 | |
| 				fr->subclass = 0;
 | |
| 				p->ringgothangup=1;
 | |
| 			}
 | |
| 			break;
 | |
| 		case VOFR_SIGNAL_RING:
 | |
| 			ast->rings++;
 | |
| 			p->ringgothangup = 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, "%s: Voice in weird state %d\n", ast->name, 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 int vofr_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 | |
| {
 | |
| 	struct vofr_pvt *p = newchan->pvt->pvt;
 | |
| 	if (p->owner != oldchan) {
 | |
| 		ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	p->owner = newchan;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct ast_channel *vofr_new(struct vofr_pvt *i, int state)
 | |
| {
 | |
| 	struct ast_channel *tmp;
 | |
| 	tmp = ast_channel_alloc(0);
 | |
| 	if (tmp) {
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 		snprintf(tmp->name, sizeof(tmp->name), "AdtranVoFR/%s", i->sa.spkt_device);
 | |
| #else
 | |
| 		snprintf(tmp->name, sizeof(tmp->name), "AdtranVoFR/%s", i->sa.sll_device);
 | |
| #endif
 | |
| 		tmp->type = type;
 | |
| 		tmp->fds[0] = i->s;
 | |
| 		/* Adtran VoFR supports only G723.1 format data.  G711 (ulaw) would be nice too */
 | |
| 		tmp->nativeformats = AST_FORMAT_G723_1;
 | |
| 		tmp->state = state;
 | |
| 		if (state == AST_STATE_RING)
 | |
| 			tmp->rings = 1;
 | |
| 		tmp->writeformat = AST_FORMAT_G723_1;
 | |
| 		tmp->readformat = AST_FORMAT_G723_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;
 | |
| 		tmp->pvt->fixup = vofr_fixup;
 | |
| 		if (strlen(i->language))
 | |
| 			strncpy(tmp->language, i->language, sizeof(tmp->language)-1);
 | |
| 		i->owner = tmp;
 | |
| 		ast_mutex_lock(&usecnt_lock);
 | |
| 		usecnt++;
 | |
| 		ast_mutex_unlock(&usecnt_lock);
 | |
| 		ast_update_use_count();
 | |
| 		strncpy(tmp->context, i->context, sizeof(tmp->context)-1);
 | |
| 		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);
 | |
| 				tmp = NULL;
 | |
| 			}
 | |
| 		}
 | |
| 	} 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 */
 | |
| 			if (!i->owner) {
 | |
| 				i->ringgothangup = 0;
 | |
| 				vofr_new(i, AST_STATE_RING);
 | |
| 			} else
 | |
| 				ast_log(LOG_WARNING, "Got a ring, but there's an owner?\n");
 | |
| 			break;
 | |
| 		case VOFR_SIGNAL_OFF_HOOK:
 | |
| 			/* Network termination, go off hook */
 | |
| #if 0
 | |
| 			ast_log(LOG_DEBUG, "Off hook\n");
 | |
| #endif
 | |
| 			vofr_xmit_signal(i, 0x10, 2);
 | |
| 			if (!i->owner)
 | |
| 				vofr_new(i, AST_STATE_UP);
 | |
| 			else
 | |
| 				ast_log(LOG_WARNING, "Got an offhook, but there's an owner?\n");
 | |
| 			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 0
 | |
| 	if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) {
 | |
| 		ast_log(LOG_WARNING, "Unable to set cancel type to asynchronous\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| #endif
 | |
| 	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 (ast_mutex_lock(&monlock)) {
 | |
| 			ast_log(LOG_ERROR, "Unable to grab monitor lock\n");
 | |
| 			return NULL;
 | |
| 		}
 | |
| 		/* Lock the interface list */
 | |
| 		if (ast_mutex_lock(&iflock)) {
 | |
| 			ast_log(LOG_ERROR, "Unable to grab interface lock\n");
 | |
| 			ast_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)) 
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 				ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->s, i->sa.spkt_device);
 | |
| #else
 | |
| 				ast_log(LOG_WARNING, "Descriptor %d appears twice (%s)?\n", i->s, i->sa.sll_device);
 | |
| #endif
 | |
| 			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 */
 | |
| 		ast_mutex_unlock(&iflock);
 | |
| 		
 | |
| 		/* And from now on, we're okay to be killed, so release the monitor lock as well */
 | |
| 		ast_mutex_unlock(&monlock);
 | |
| 		pthread_testcancel();
 | |
| 		/* Wait indefinitely for something to happen */
 | |
| 		res = ast_select(n + 1, &rfds, NULL, NULL, NULL);
 | |
| 		pthread_testcancel();
 | |
| 		/* Okay, select has finished.  Let's see what happened.  */
 | |
| 		if (res < 0) {
 | |
| 			if ((errno != EAGAIN) && (errno != EINTR))
 | |
| 				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 (ast_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) {
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 					ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d, %s)...\n", i->s, i->sa.spkt_device);
 | |
| #else
 | |
| 					ast_log(LOG_WARNING, "Whoa....  I'm owned but found (%d, %s)...\n", i->s, i->sa.sll_device);
 | |
| #endif
 | |
| 					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;
 | |
| 		}
 | |
| 		ast_mutex_unlock(&iflock);
 | |
| 	}
 | |
| 	/* Never reached */
 | |
| 	return NULL;
 | |
| 	
 | |
| }
 | |
| 
 | |
| static int restart_monitor(void)
 | |
| {
 | |
| 	/* If we're supposed to be stopped -- stay stopped */
 | |
| 	if (monitor_thread == AST_PTHREADT_STOP)
 | |
| 		return 0;
 | |
| 	if (ast_mutex_lock(&monlock)) {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (monitor_thread == pthread_self()) {
 | |
| 		ast_mutex_unlock(&monlock);
 | |
| 		ast_log(LOG_WARNING, "Cannot kill myself\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (monitor_thread != AST_PTHREADT_NULL) {
 | |
| 		/* Wake up the thread */
 | |
| 		pthread_kill(monitor_thread, SIGURG);
 | |
| 	} else {
 | |
| 		/* Start a new monitor */
 | |
| 		if (pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
 | |
| 			ast_mutex_unlock(&monlock);
 | |
| 			ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 	ast_mutex_unlock(&monlock);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static 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 */
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 		tmp->s = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL));
 | |
| #else
 | |
| 		/* Why the HELL does Sangoma change their API every damn time
 | |
| 		   they make a new driver release?!?!?!  Leave it the hell
 | |
| 		   alone this time.  */
 | |
| 		tmp->s = socket(AF_WANPIPE, SOCK_RAW, 0);
 | |
| #endif		
 | |
| 
 | |
| 		if (tmp->s < 0) {
 | |
| 			ast_log(LOG_ERROR, "Unable to create socket: %s\n", strerror(errno));
 | |
| 			free(tmp);
 | |
| 			return NULL;
 | |
| 		}
 | |
| 
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 		/* Prepare sockaddr for binding */
 | |
| 		memset(&tmp->sa, 0, sizeof(tmp->sa));
 | |
| 		strncpy(tmp->sa.spkt_device, iface, sizeof(tmp->sa.spkt_device)-1);
 | |
| 		tmp->sa.spkt_protocol = htons(0x16);
 | |
| 		tmp->sa.spkt_family = AF_PACKET;
 | |
| 		if (bind(tmp->s, (struct sockaddr *)&tmp->sa, sizeof(struct sockaddr))) {
 | |
| #else
 | |
| 		/* Prepare sockaddr for binding */
 | |
| 		memset(&tmp->sa, 0, sizeof(tmp->sa));
 | |
| 		tmp->sa.sll_family = AF_WANPIPE;
 | |
| 		tmp->sa.sll_protocol = htons(ETH_P_IP);
 | |
| 		strncpy(tmp->sa.sll_device, iface, sizeof(tmp->sa.sll_device)-1);
 | |
| 		strncpy(tmp->sa.sll_card, "wanpipe1", sizeof(tmp->sa.sll_card)-1);
 | |
| 		tmp->sa.sll_ifindex = 0;
 | |
| 		if (bind(tmp->s, (struct sockaddr *)&tmp->sa, sizeof(struct wan_sockaddr_ll))) {
 | |
| #endif		
 | |
| 		/* Bind socket to specific interface */
 | |
| #ifdef OLD_SANGOMA_API
 | |
| 			ast_log(LOG_ERROR, "Unable to bind to '%s': %s\n", tmp->sa.spkt_device, 
 | |
| #else
 | |
| 			ast_log(LOG_ERROR, "Unable to bind to '%s': %s\n", tmp->sa.sll_device, 
 | |
| #endif
 | |
| 										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;
 | |
| 		tmp->ringgothangup = 0;
 | |
| 		strncpy(tmp->language, language, sizeof(tmp->language)-1);
 | |
| 		strncpy(tmp->context, context, sizeof(tmp->context)-1);
 | |
| 		/* 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 (ast_mutex_lock(&iflock)) {
 | |
| 		ast_log(LOG_ERROR, "Unable to lock interface list???\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	p = iflist;
 | |
| 	while(p) {
 | |
| 		if (!p->owner && p->outgoing) {
 | |
| 			tmp = vofr_new(p, AST_STATE_DOWN);
 | |
| 			break;
 | |
| 		}
 | |
| 		p = p->next;
 | |
| 	}
 | |
| 	ast_mutex_unlock(&iflock);
 | |
| 	restart_monitor();
 | |
| 	return tmp;
 | |
| }
 | |
| 
 | |
| static int __unload_module(void)
 | |
| {
 | |
| 	struct vofr_pvt *p, *pl;
 | |
| 	/* First, take us out of the channel loop */
 | |
| 	ast_channel_unregister(type);
 | |
| 	if (!ast_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;
 | |
| 		ast_mutex_unlock(&iflock);
 | |
| 	} else {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (!ast_mutex_lock(&monlock)) {
 | |
| 		if (monitor_thread) {
 | |
| 			pthread_cancel(monitor_thread);
 | |
| 			pthread_kill(monitor_thread, SIGURG);
 | |
| 			pthread_join(monitor_thread, NULL);
 | |
| 		}
 | |
| 		monitor_thread = AST_PTHREADT_STOP;
 | |
| 		ast_mutex_unlock(&monlock);
 | |
| 	} else {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (!ast_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;
 | |
| 		ast_mutex_unlock(&iflock);
 | |
| 	} else {
 | |
| 		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 		
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int unload_module()
 | |
| {
 | |
| 	return __unload_module();
 | |
| }
 | |
| 
 | |
| 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 (ast_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);
 | |
| 					ast_mutex_unlock(&iflock);
 | |
| 					__unload_module();
 | |
| 					return -1;
 | |
| 				}
 | |
| 		} else if (!strcasecmp(v->name, "context")) {
 | |
| 			strncpy(context, v->value, sizeof(context)-1);
 | |
| 		} else if (!strcasecmp(v->name, "language")) {
 | |
| 			strncpy(language, v->value, sizeof(language)-1);
 | |
| 		}
 | |
| 		v = v->next;
 | |
| 	}
 | |
| 	ast_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 usecount()
 | |
| {
 | |
| 	int res;
 | |
| 	ast_mutex_lock(&usecnt_lock);
 | |
| 	res = usecnt;
 | |
| 	ast_mutex_unlock(&usecnt_lock);
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| char *key()
 | |
| {
 | |
| 	return ASTERISK_GPL_KEY;
 | |
| }
 | |
| 
 | |
| char *description()
 | |
| {
 | |
| 	return desc;
 | |
| }
 | |
| 
 |