aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xCHANGES15
-rwxr-xr-xMakefile64
-rwxr-xr-xREADME56
-rwxr-xr-xchannels/chan_iax.c2238
-rwxr-xr-xchannels/chan_modem.c16
-rwxr-xr-xchannels/chan_modem_aopen.c13
-rwxr-xr-xchannels/chan_oss.c791
-rwxr-xr-xchannels/chan_phone.c (renamed from channels/chan_ixj.c)378
-rwxr-xr-xchannels/ixjuser.h4
-rwxr-xr-xcodecs/Makefile12
-rwxr-xr-xcodecs/codec_lpc10.c348
-rwxr-xr-xcodecs/lpc10/lpc10.h4
-rwxr-xr-xcodecs/lpc10_slin_ex.h13
-rwxr-xr-xcodecs/slin_lpc10_ex.h21
-rwxr-xr-xconfig.c2
-rwxr-xr-xconfigs/iax.conf.sample72
-rwxr-xr-xconfigs/ixj.conf.sample19
-rwxr-xr-xconfigs/oss.conf.sample23
-rwxr-xr-xconfigs/phone.conf.sample35
-rwxr-xr-xframe.c102
-rwxr-xr-xpbx.c110
-rwxr-xr-xsounds/demo-moreinfo.gsmbin0 -> 4158 bytes
-rwxr-xr-xtranslate.c266
23 files changed, 4325 insertions, 277 deletions
diff --git a/CHANGES b/CHANGES
index 7464c2d67..573664ce1 100755
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,18 @@
+* Asterisk 0.1.2
+ -- Updated README file with a "Getting Started" section
+ -- Added sample sounds and configuration files.
+ -- Added LPC10 very low bandwidth (low quality) compression
+ -- Enhanced translation selection mechanism.
+ -- Enhanced IAX jitter buffer, improved reliability
+ -- Support echo cancelation on PhoneJack
+ -- Updated PhoneJack driver to std. Telephony interface
+ -- Added app_echo for evaluating VoIP latency
+ -- Added app_system to execute arbitrary programs
+ -- Updated sample configuration files
+ -- Added OSS channel driver (full duplex only)
+ -- Added IAX implementation
+ -- Fixed some deadlocks.
+ -- A whole bunch of bug fixes
* Asterisk 0.1.1
-- Revised translator, fixed some general race conditions throughout *
-- Made dialer somewhat more aware of incompatible voice channels
diff --git a/Makefile b/Makefile
index 1ac473afc..1b157c9b7 100755
--- a/Makefile
+++ b/Makefile
@@ -16,16 +16,31 @@
MODULES_DIR=/usr/lib/asterisk/modules
+# Pentium Pro Optimize
+#PROC=i686
+# Pentium Optimize
+PROC=i586
+
DEBUG=-g #-pg
INCLUDE=-Iinclude -I../include
CFLAGS=-pipe -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -O6 $(DEBUG) $(INCLUDE) -D_REENTRANT
-CFLAGS+=$(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
+CFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
SUBDIRS=channels pbx apps codecs formats
-LIBS=-ldl -lpthread -lreadline # -lefence
-OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o translate.o file.o say.o pbx.o cli.o asterisk.o
+LIBS=-ldl -lpthread -lreadline #-lefence
+OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o translate.o file.o say.o pbx.o cli.o md5.o asterisk.o
CC=gcc
INSTALL=install
+_all: all
+ @echo " +--------- Asterisk Build Complete ---------+"
+ @echo " + Asterisk has successfully been built, but +"
+ @echo " + cannot be run before being installed by +"
+ @echo " + running: +"
+ @echo " + +"
+ @echo " + make install +"
+ @echo " + +"
+ @echo " +-------------------------------------------+"
+
all: asterisk subdirs
asterisk: $(OBJS)
@@ -38,8 +53,51 @@ clean:
for x in $(SUBDIRS); do $(MAKE) -C $$x clean || exit 1 ; done
rm -f *.o *.so asterisk
+datafiles: all
+ mkdir -p /var/lib/asterisk/sounds/digits
+ for x in sounds/digits/*; do \
+ install $$x /var/lib/asterisk/sounds/digits ; \
+ done
+ for x in sounds/vm-* sounds/transfer* ; do \
+ install $$x /var/lib/asterisk/sounds ; \
+ done
+
install: all
mkdir -p $(MODULES_DIR)
for x in $(SUBDIRS); do $(MAKE) -C $$x install || exit 1 ; done
install -d /usr/include/asterisk
install include/asterisk/*.h /usr/include/asterisk
+ rm -f /var/lib/asterisk/sounds/vm
+ mkdir -p /var/spool/asterisk/vm
+ rm -f /usr/lib/asterisk/modules/chan_ixj.so
+ ( cd /var/lib/asterisk/sounds ; ln -s ../../../spool/asterisk/vm . )
+ @echo " +---- Asterisk Installation Complete -------+"
+ @echo " + Asterisk has successfully been installed. +"
+ @echo " + If you would like to install the sample +"
+ @echo " + configuration files (overwriting any +"
+ @echo " + existing config files), run: +"
+ @echo " + +"
+ @echo " + make samples +"
+ @echo " + +"
+ @echo " +-------------------------------------------+"
+
+samples: all datafiles
+ mkdir -p /etc/asterisk
+ for x in configs/*.sample; do \
+ if [ -f /etc/asterisk/`basename $$x .sample` ]; then \
+ mv -f /etc/asterisk/`basename $$x .sample` /etc/asterisk/`basename $$x .sample`.old ; \
+ fi ; \
+ install $$x /etc/asterisk/`basename $$x .sample` ;\
+ done
+ for x in sounds/demo-*; do \
+ install $$x /var/lib/asterisk/sounds; \
+ done
+ mkdir -p /var/lib/asterisk/sounds/vm/1234
+ :> /var/lib/asterisk/sounds/vm/1234/unavail.gsm
+ for x in vm-theperson digits/1 digits/2 digits/3 digits/4 vm-isunavail; do \
+ cat /var/lib/asterisk/sounds/$$x.gsm >> /var/lib/asterisk/sounds/vm/1234/unavail.gsm ; \
+ done
+ :> /var/lib/asterisk/sounds/vm/1234/busy.gsm
+ for x in vm-theperson digits/1 digits/2 digits/3 digits/4 vm-isonphone; do \
+ cat /var/lib/asterisk/sounds/$$x.gsm >> /var/lib/asterisk/sounds/vm/1234/busy.gsm ; \
+ done
diff --git a/README b/README
index 18b850e34..eb3f335a0 100755
--- a/README
+++ b/README
@@ -22,6 +22,60 @@ as well.
If you want to use format_wav module, then you need a very recent
version of libaudiofile (at least version 0.2.0, or you can apply the
-following patch to version 0.1.9):
+included patch. RPMS for the patched libaudiofile are available at:
+ftp://ftp.asteriskpbx.com/pub/asterisk/support
+* GETTING STARTED
+
+First, be sure you've installed the required libaudiofile upgrade if
+you want to use the non-GSM WAV format. Next, be sure you've got
+supported hardware. To use Asterisk right now, you will need one of
+the following:
+
+ * Adtran Atlas 800 Plus
+ * QuickNet Internet PhoneJack
+ * Full Duplex Sound Card supported by Linux
+
+Assuming you have one of these (most likely the third) you're ready to
+proceed:
+
+1) Run "make"
+2) Run "make install"
+
+If this is your first time working with Asterisk, you may wish to install
+the sample PBX, with demonstration extensions, etc. If so, run:
+
+ "make samples"
+
+Doing so will overwrite any existing config files you have.
+
+Finally, you can launch Asterisk with:
+
+ ./asterisk -vvvc
+
+If you get an error about unresolved symbols, install the updated
+libaudiofile (available at ftp://ftp.asteriskpbx.com/pub/asterisk/support
+
+You'll see a bunch of verbose messages fly by your screen as Asterisk
+initializes (that's the "very very verbose" mode). When it's ready, if
+you specified the "c" then you'll get a command line console, that looks
+like this:
+
+*CLI>
+
+You can type "help" at any time to get help with the system. For help
+with a specific command, type "help <command>". To start the PBX using
+your sound card, you can type "dial" to dial the PBX. Then you can use
+"answer", "hangup", and "dial" to simulate the actions of a telephone.
+Remember that if you don't have a full duplex sound card (And asterisk
+will tell you somewhere in its verbose messages if you do/don't) than it
+won't work right (not yet).
+
+Feel free to look over the configuration files in /etc/asterisk, where
+you'll find a lot of information about what you can do with Asterisk.
+
+Finally, you may wish to visit the web site and join the mailing list if
+you're interested in getting more information.
+
+Mark
diff --git a/channels/chan_iax.c b/channels/chan_iax.c
new file mode 100755
index 000000000..f3e7d337e
--- /dev/null
+++ b/channels/chan_iax.c
@@ -0,0 +1,2238 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Implementation of Inter-Asterisk eXchange
+ *
+ * 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 <asterisk/frame.h>
+#include <asterisk/channel.h>
+#include <asterisk/channel_pvt.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/pbx.h>
+#include <asterisk/sched.h>
+#include <asterisk/io.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
+#include <asterisk/cli.h>
+#include <asterisk/translate.h>
+#include <asterisk/md5.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "iax.h"
+
+#define DEFAULT_RETRY_TIME 1000
+#define MEMORY_SIZE 100
+#define DEFAULT_DROP 3
+
+/* If you want to use the simulator, then define IAX_SIMULATOR. */
+
+/*
+#define IAX_SIMULATOR
+*/
+static char *desc = "Inter Asterisk eXchange";
+static char *tdesc = "Inter Asterisk eXchange Drver";
+static char *type = "IAX";
+static char *config = "iax.conf";
+
+static char context[80] = "default";
+
+static int max_retries = 4;
+static int ping_time = 20;
+static int lagrq_time = 10;
+static int nextcallno = 0;
+static int maxjitterbuffer=4000;
+
+static int netsocket = -1;
+
+static int usecnt;
+static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t iaxs_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Ethernet, etc */
+#define IAX_CAPABILITY_FULLBANDWIDTH 0x7FFFFFFF
+/* T1, maybe ISDN */
+#define IAX_CAPABILITY_MEDBANDWIDTH (IAX_CAPABILITY_FULLBANDWIDTH & \
+ ~AST_FORMAT_SLINEAR & \
+ ~AST_FORMAT_ULAW & \
+ ~AST_FORMAT_ALAW)
+/* A modem */
+#define IAX_CAPABILITY_LOWBANDWIDTH (IAX_CAPABILITY_MEDBANDWIDTH & \
+ ~AST_FORMAT_MP3 & \
+ ~AST_FORMAT_ADPCM)
+
+#define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \
+ ~AST_FORMAT_G723_1)
+
+static struct io_context *io;
+static struct sched_context *sched;
+
+static int iax_capability = IAX_CAPABILITY_FULLBANDWIDTH;
+
+static int iax_dropcount = DEFAULT_DROP;
+
+static int use_jitterbuffer = 1;
+
+static pthread_t netthreadid;
+
+#define IAX_STATE_STARTED (1 << 0)
+#define IAX_STATE_AUTHENTICATED (1 << 1)
+
+#define IAX_SENSE_DENY 0
+#define IAX_SENSE_ALLOW 1
+
+struct iax_ha {
+ /* Host access rule */
+ struct in_addr netaddr;
+ struct in_addr netmask;
+ int sense;
+ struct iax_ha *next;
+};
+
+struct iax_context {
+ char context[AST_MAX_EXTENSION];
+ struct iax_context *next;
+};
+
+struct iax_user {
+ char name[80];
+ char secret[80];
+ char methods[80];
+ struct iax_ha *ha;
+ struct iax_context *contexts;
+ struct iax_user *next;
+};
+
+struct iax_peer {
+ char name[80];
+ char username[80];
+ char secret[80];
+ struct sockaddr_in addr;
+ struct in_addr mask;
+ struct iax_peer *next;
+};
+
+/* Don't retry more frequently than every 10 ms, or less frequently than every 5 seconds */
+#define MIN_RETRY_TIME 10
+#define MAX_RETRY_TIME 10000
+#define MAX_JITTER_BUFFER 50
+
+/* If we have more than this much excess real jitter buffer, srhink it. */
+static int max_jitter_buffer = MAX_JITTER_BUFFER;
+
+struct chan_iax_pvt {
+ /* Pipes for communication. pipe[1] belongs to the
+ network thread (write), and pipe[0] belongs to the individual
+ channel (read) */
+ int pipe[2];
+ /* Last received voice format */
+ int voiceformat;
+ /* Last sent voice format */
+ int svoiceformat;
+ /* Last received timestamp */
+ unsigned int last;
+ /* Last sent timestamp - never send the same timestamp twice in a single call */
+ unsigned int lastsent;
+ /* Ping time */
+ unsigned int pingtime;
+ /* Peer Address */
+ struct sockaddr_in addr;
+ /* Our call number */
+ int callno;
+ /* Peer callno */
+ int peercallno;
+ /* Peer supported formats */
+ int peerformats;
+ /* timeval that we base our transmission on */
+ struct timeval offset;
+ /* timeval that we base our delivery on */
+ struct timeval rxcore;
+ /* Historical delivery time */
+ int history[MEMORY_SIZE];
+ /* Current base jitterbuffer */
+ int jitterbuffer;
+ /* Current jitter measure */
+ int jitter;
+ /* LAG */
+ int lag;
+ /* Error, as discovered by the manager */
+ int error;
+ /* Onwer if we have one */
+ struct ast_channel *owner;
+ /* What's our state? */
+ int state;
+ /* Next outgoing sequence number */
+ unsigned short oseqno;
+ /* Next incoming sequence number */
+ unsigned short iseqno;
+ /* Peer name */
+ char peer[80];
+ /* Default Context */
+ char context[80];
+ /* Caller ID if available */
+ char callerid[80];
+ /* DNID */
+ char dnid[80];
+ /* Requested Extension */
+ char exten[AST_MAX_EXTENSION];
+ /* Expected Username */
+ char username[80];
+ /* Expected Secret */
+ char secret[80];
+ /* permitted authentication methods */
+ char methods[80];
+ /* MD5 challenge */
+ char challenge[10];
+};
+
+struct ast_iax_frame {
+ /* Actual, isolated frame */
+ struct ast_frame *f;
+ /* /Our/ call number */
+ short callno;
+ /* Start of raw frame (outgoing only) */
+ void *data;
+ /* Length of frame (outgoing only) */
+ int datalen;
+ /* How many retries so far? */
+ int retries;
+ /* Outgoing relative timestamp (ms) */
+ unsigned int ts;
+ /* How long to wait before retrying */
+ int retrytime;
+ /* Are we received out of order? */
+ int outoforder;
+ /* Have we been sent at all yet? */
+ int sentyet;
+ /* Packet sequence number */
+ int seqno;
+ /* Easy linking */
+ struct ast_iax_frame *next;
+ struct ast_iax_frame *prev;
+};
+
+static struct ast_iax_queue {
+ struct ast_iax_frame *head;
+ struct ast_iax_frame *tail;
+ int count;
+ pthread_mutex_t lock;
+} iaxq;
+
+static struct ast_user_list {
+ struct iax_user *users;
+ pthread_mutex_t lock;
+} userl;
+
+static struct ast_peer_list {
+ struct iax_peer *peers;
+ pthread_mutex_t lock;
+} peerl;
+
+/* XXX We probably should use a mutex when working with this XXX */
+static struct chan_iax_pvt *iaxs[AST_IAX_MAX_CALLS];
+
+static int send_command(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int);
+
+static unsigned int calc_timestamp(struct chan_iax_pvt *p, unsigned int ts);
+
+static int send_ping(void *data)
+{
+ int callno = (long)data;
+ if (iaxs[callno]) {
+ send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_PING, 0, NULL, 0, -1);
+ return 1;
+ } else
+ return 0;
+}
+
+static int send_lagrq(void *data)
+{
+ int callno = (long)data;
+ if (iaxs[callno]) {
+ send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_LAGRQ, 0, NULL, 0, -1);
+ return 1;
+ } else
+ return 0;
+}
+
+static unsigned char compress_subclass(int subclass)
+{
+ int x;
+ int power=-1;
+ /* If it's 128 or smaller, just return it */
+ if (subclass < AST_FLAG_SC_LOG)
+ return subclass;
+ /* Otherwise find its power */
+ for (x = 0; x < AST_MAX_SHIFT; x++) {
+ if (subclass & (1 << x)) {
+ if (power > -1) {
+ ast_log(LOG_WARNING, "Can't compress subclass %d\n", subclass);
+ return 0;
+ } else
+ power = x;
+ }
+ }
+ return power | AST_FLAG_SC_LOG;
+}
+
+static int uncompress_subclass(unsigned char csub)
+{
+ /* If the SC_LOG flag is set, return 2^csub otherwise csub */
+ if (csub & AST_FLAG_SC_LOG)
+ return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT);
+ else
+ return csub;
+}
+
+static struct chan_iax_pvt *new_iax(void)
+{
+ struct chan_iax_pvt *tmp;
+ tmp = malloc(sizeof(struct chan_iax_pvt));
+ if (tmp) {
+ memset(tmp, 0, sizeof(struct chan_iax_pvt));
+ /* On my linux system, pipe's are more than 2x as fast as socketpairs */
+ if (pipe(tmp->pipe)) {
+ ast_log(LOG_WARNING, "Unable to create pipe: %s\n", strerror(errno));
+ free(tmp);
+ return NULL;
+ }
+ tmp->callno = -1;
+ tmp->peercallno = -1;
+ /* strncpy(tmp->context, context, sizeof(tmp->context)); */
+ strncpy(tmp->exten, "s", sizeof(tmp->exten));
+ }
+ return tmp;
+}
+
+static int get_timelen(struct ast_frame *f)
+{
+ int timelen=0;
+ switch(f->subclass) {
+ case AST_FORMAT_G723_1:
+ timelen = 30;
+ break;
+ case AST_FORMAT_GSM:
+ timelen = 20;
+ break;
+ case AST_FORMAT_SLINEAR:
+ timelen = f->datalen / 8;
+ break;
+ case AST_FORMAT_LPC10:
+ timelen = 22;
+ timelen += ((char *)(f->data))[7] & 0x1;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to calculate timelen on %d packets\n", f->subclass);
+ }
+ return timelen;
+}
+
+#if 0
+static struct ast_iax_frame *iaxfrdup(struct ast_iax_frame *fr)
+{
+ /* Malloc() a copy of a frame */
+ struct ast_iax_frame *new = malloc(sizeof(struct ast_iax_frame));
+ if (new)
+ memcpy(new, fr, sizeof(struct ast_iax_frame));
+ return new;
+}
+#endif
+
+static struct ast_iax_frame *iaxfrdup2(struct ast_iax_frame *fr, int ch)
+{
+ /* Malloc() a copy of a frame */
+ struct ast_iax_frame *new = malloc(sizeof(struct ast_iax_frame));
+ if (new) {
+ memcpy(new, fr, sizeof(struct ast_iax_frame));
+ new->f = ast_frdup(fr->f);
+ /* Copy full header */
+ if (ch) {
+ memcpy(new->f->data - sizeof(struct ast_iax_full_hdr),
+ fr->f->data - sizeof(struct ast_iax_full_hdr),
+ sizeof(struct ast_iax_full_hdr));
+ /* Grab new data pointer */
+ new->data = new->f->data - (fr->f->data - fr->data);
+ } else {
+ new->data = NULL;
+ new->datalen = 0;
+ }
+ }
+ return new;
+}
+
+#define NEW_PREVENT 0
+#define NEW_ALLOW 1
+#define NEW_FORCE 2
+
+static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int new)
+{
+ int res = -1;
+ int x;
+ int start;
+ if (new <= NEW_ALLOW) {
+ /* Look for an existing connection first */
+ for (x=0;x<AST_IAX_MAX_CALLS;x++) {
+ if (iaxs[x]) {
+ /* Look for an exact match */
+ if ((sin->sin_port == iaxs[x]->addr.sin_port) &&
+ (sin->sin_addr.s_addr == iaxs[x]->addr.sin_addr.s_addr) &&
+ ((callno == iaxs[x]->peercallno) || /* Our expected source call number is the same */
+ ((dcallno == x) && (iaxs[x]->peercallno = -1))
+ /* We have no expected source number, and the destination is right */
+ )) {
+ res = x;
+ break;
+ }
+ }
+ }
+ }
+ if ((res < 0) && (new >= NEW_ALLOW)) {
+ /* Create a new one */
+ start = nextcallno;
+ for (x = nextcallno + 1; iaxs[x] && (x != start); x = (x + 1) % AST_IAX_MAX_CALLS)
+ if (x == start) {
+ ast_log(LOG_WARNING, "Unable to accept more calls\n");
+ return -1;
+ }
+ iaxs[x] = new_iax();
+ if (iaxs[x]) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
+ iaxs[x]->addr.sin_port = sin->sin_port;
+ iaxs[x]->addr.sin_family = sin->sin_family;
+ iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr;
+ iaxs[x]->peercallno = callno;
+ iaxs[x]->callno = x;
+ iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
+ ast_sched_add(sched, ping_time * 1000, send_ping, (void *)x);
+ ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)x);
+ } else
+ ast_log(LOG_DEBUG, "Out of memory\n");
+ res = x;
+ nextcallno = x;
+ }
+ return res;
+}
+
+static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno);
+
+static int do_deliver(void *data)
+{
+ /* Just deliver the packet by writing it to half of the pipe. */
+ struct ast_iax_frame *fr = data;
+ unsigned int ts;
+ if (iaxs[fr->callno]) {
+ if (fr->f->frametype == AST_FRAME_IAX) {
+ /* We have to treat some of these packets specially because
+ they're LAG measurement packets */
+ if (fr->f->subclass == AST_IAX_COMMAND_LAGRQ) {
+ /* If we got a queued request, build a reply and send it */
+ fr->f->subclass = AST_IAX_COMMAND_LAGRP;
+ iax_send(iaxs[fr->callno], fr->f, fr->ts, -1);
+ } else if (fr->f->subclass == AST_IAX_COMMAND_LAGRP) {
+ /* This is a reply we've been given, actually measure the difference */
+ ts = calc_timestamp(iaxs[fr->callno], 0);
+ iaxs[fr->callno]->lag = ts - fr->ts;
+ }
+ } else
+ ast_fr_fdwrite(iaxs[fr->callno]->pipe[1], fr->f);
+ }
+ /* Free the packet */
+ ast_frfree(fr->f);
+ /* And our iax frame */
+ free(fr);
+ /* And don't run again */
+ return 0;
+}
+
+static int handle_error()
+{
+ /* XXX Ideally we should figure out why an error occured and then abort those
+ rather than continuing to try. Unfortunately, the published interface does
+ not seem to work XXX */
+#if 0
+ struct sockaddr_in *sin;
+ int res;
+ struct msghdr m;
+ struct sock_extended_err e;
+ m.msg_name = NULL;
+ m.msg_namelen = 0;
+ m.msg_iov = NULL;
+ m.msg_control = &e;
+ m.msg_controllen = sizeof(e);
+ m.msg_flags = 0;
+ res = recvmsg(netsocket, &m, MSG_ERRQUEUE);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno));
+ else {
+ if (m.msg_controllen) {
+ sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e);
+ if (sin)
+ ast_log(LOG_WARNING, "Receive error from %s\n", inet_ntoa(sin->sin_addr));
+ else
+ ast_log(LOG_WARNING, "No address detected??\n");
+ } else {
+ ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno));
+ }
+ }
+#endif
+ return 0;
+}
+
+#ifdef IAX_SIMULATOR
+static int __send_packet(struct ast_iax_frame *f)
+#else
+static int send_packet(struct ast_iax_frame *f)
+#endif
+{
+ int res;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Sending %d on %d/%d to %s:%d\n", f->ts, f->callno, iaxs[f->callno]->peercallno, inet_ntoa(iaxs[f->callno]->addr.sin_addr), ntohs(iaxs[f->callno]->addr.sin_port));
+ /* Don't send if there was an error, but return error instead */
+ if (f->callno < 0) {
+ ast_log(LOG_WARNING, "Call number = %d\n", f->callno);
+ return -1;
+ }
+ if (iaxs[f->callno]->error)
+ return -1;
+ res = sendto(netsocket, f->data, f->datalen, 0, &iaxs[f->callno]->addr,
+ sizeof(iaxs[f->callno]->addr));
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Received error: %s\n", strerror(errno));
+ handle_error();
+ } else
+ res = 0;
+ return res;
+}
+
+#ifdef IAX_SIMULATOR
+
+/* Average amount of delay in the connection */
+static int average_delay = 0;
+/* Permitted deviation either side of the average delay */
+static int delay_deviation = 0;
+/* Percent chance that a packet arrives O.K. */
+static int reliability = 100;
+
+static int iax_sim_calc_delay()
+{
+ int ms;
+ ms = average_delay - delay_deviation;
+ ms += ((float)(delay_deviation * 2)) * rand() / (RAND_MAX + 1.0);
+ if (ms < 0)
+ ms = 0;
+ if ((float)rand()/(RAND_MAX + 1.0) < ((float)reliability)/100)
+ return ms;
+ else
+ return -1;
+}
+
+static int d_send_packet(void *v)
+{
+ struct ast_iax_frame *f = (struct ast_iax_frame *)v;
+ if (iaxs[f->callno])
+ __send_packet(f);
+ ast_frfree(f->f);
+ free(f);
+ return 0;
+}
+
+static int send_packet(struct ast_iax_frame *f)
+{
+ struct ast_iax_frame *fn;
+ int ms;
+ ms = iax_sim_calc_delay();
+ if (ms == 0)
+ return __send_packet(f);
+ else if (ms > 0) {
+ /* Make a completely independent frame, in case the other
+ is destroyed -- still doesn't make things like hangups
+ arrive if the main channel is destroyed, but close enough */
+ fn = iaxfrdup2(f, 1);
+ ast_sched_add(sched, ms, d_send_packet, fn);
+ } /* else we drop the packet */
+ return 0;
+}
+
+static int iax_sim_set(int fd, int argc, char *argv[])
+{
+ if (argc != 4)
+ return RESULT_SHOWUSAGE;
+ if (!strcasecmp(argv[2], "delay"))
+ average_delay = atoi(argv[3]);
+ else if (!strcasecmp(argv[2], "deviation"))
+ delay_deviation = atoi(argv[3]);
+ else if (!strcasecmp(argv[2], "reliability"))
+ reliability = atoi(argv[3]);
+ else
+ return RESULT_SHOWUSAGE;
+ if (reliability > 100)
+ reliability = 100;
+ if (reliability < 0)
+ reliability = 0;
+ if (delay_deviation > average_delay)
+ delay_deviation = average_delay;
+ return RESULT_SUCCESS;
+}
+
+static char delay_usage[] =
+"Usage: sim set delay <value>\n"
+" Configure the IAX network simulator to generate average\n"
+" delays equal to the specified value (in milliseconds).\n";
+
+static char deviation_usage[] =
+"Usage: sim set deviation <value>\n"
+" Configures the IAX network simulator's deviation value.\n"
+" The delays generated by the simulator will always be within\n"
+" this value of milliseconds (postive or negative) of the \n"
+" average delay.\n";
+
+static char reliability_usage[] =
+"Usage: sim set reliability <value>\n"
+" Configure the probability that a packet will be delivered.\n"
+" The value specified is a percentage from 0 to 100\n";
+
+static int iax_sim_show(int fd, int argc, char *argv[])
+{
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, "Average Delay: %d ms\n", average_delay);
+ ast_cli(fd, "Delay Deviation: %d ms\n", delay_deviation);
+ ast_cli(fd, "Reliability: %d %\n", reliability);
+ return RESULT_SUCCESS;
+}
+
+static char sim_show_usage[] =
+"Usage: sim show\n"
+" Displays average delay, deviation, and reliability\n"
+" used by the network simulator.\n";
+
+static struct ast_cli_entry delay_cli =
+{ { "sim", "set", "delay", NULL }, iax_sim_set, "Sets simulated average delay", delay_usage };
+static struct ast_cli_entry deviation_cli =
+{ { "sim", "set", "deviation", NULL }, iax_sim_set, "Sets simulated delay deviation", deviation_usage };
+static struct ast_cli_entry reliability_cli =
+{ { "sim", "set", "reliability", NULL }, iax_sim_set, "Sets simulated reliability", reliability_usage };
+static struct ast_cli_entry sim_show_cli =
+{ { "sim", "show", NULL }, iax_sim_show, "Displays simulation parameters", sim_show_usage };
+
+#endif
+
+static int attempt_transmit(void *data)
+{
+ /* Attempt to transmit the frame to the remote peer */
+ char zero = 0;
+ struct ast_iax_frame *f = data;
+ int res = 0;
+ /* Make sure this call is still active */
+ if (iaxs[f->callno]) {
+ if ((f->retries == -1) /* Already ACK'd */ ||
+ (f->retries >= max_retries) /* Too many attempts */) {
+ /* Record an error if we've transmitted too many times */
+ if (f->retries >= max_retries) {
+ ast_log(LOG_WARNING, "Max retries exceeded to host %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", inet_ntoa(iaxs[f->callno]->addr.sin_addr), f->f->frametype, f->f->subclass, f->ts, f->seqno);
+ iaxs[f->callno]->error = ETIMEDOUT;
+ /* Send a bogus frame to wake up the waiting process */
+ write(iaxs[f->callno]->pipe[1], &zero, 1);
+ }
+ /* Don't attempt delivery, just remove it from the queue */
+ pthread_mutex_lock(&iaxq.lock);
+ if (f->prev)
+ f->prev->next = f->next;
+ else
+ iaxq.head = f->next;
+ if (f->next)
+ f->next->prev = f->prev;
+ else
+ iaxq.tail = f->prev;
+ iaxq.count--;
+ pthread_mutex_unlock(&iaxq.lock);
+ } else {
+ /* Attempt transmission */
+ send_packet(f);
+ f->retries++;
+ /* Try again later after 2 times as long */
+ f->retrytime *= 2;
+ if (f->retrytime > MAX_RETRY_TIME)
+ f->retrytime = MAX_RETRY_TIME;
+ ast_sched_add(sched, f->retrytime, attempt_transmit, f);
+ res=0;
+ }
+ }
+ /* Do not try again */
+ return res;
+}
+
+static int iax_set_jitter(int fd, int argc, char *argv[])
+{
+ if ((argc != 4) && (argc != 5))
+ return RESULT_SHOWUSAGE;
+ if (argc == 4) {
+ max_jitter_buffer = atoi(argv[3]);
+ if (max_jitter_buffer < 0)
+ max_jitter_buffer = 0;
+ } else {
+ if (argc == 5) {
+ pthread_mutex_lock(&iaxs_lock);
+ if ((atoi(argv[3]) >= 0) && (atoi(argv[3]) < AST_IAX_MAX_CALLS)) {
+ if (iaxs[atoi(argv[3])]) {
+ iaxs[atoi(argv[3])]->jitterbuffer = atoi(argv[4]);
+ if (iaxs[atoi(argv[3])]->jitterbuffer < 0)
+ iaxs[atoi(argv[3])]->jitterbuffer = 0;
+ } else
+ ast_cli(fd, "No such call '%d'\n", atoi(argv[3]));
+ } else
+ ast_cli(fd, "%d is not a valid call number\n", atoi(argv[3]));
+ pthread_mutex_unlock(&iaxs_lock);
+ }
+ }
+ return RESULT_SUCCESS;
+}
+
+static char jitter_usage[] =
+"Usage: iax set jitter [callid] <value>\n"
+" If used with a callid, it sets the jitter buffer to the given static\n"
+"value (until its next calculation). If used without a callid, the value is used\n"
+"to establish the maximum excess jitter buffer that is permitted before the jitter\n"
+"buffer size is reduced.";
+
+static struct ast_cli_entry cli_set_jitter =
+{ { "iax", "set", "jitter", NULL }, iax_set_jitter, "Sets IAX jitter buffer", jitter_usage };
+
+static unsigned int calc_rxstamp(struct chan_iax_pvt *p);
+
+static int schedule_delivery(struct ast_iax_frame *fr)
+{
+ /* XXX FIXME: I should delay delivery with a sliding jitter buffer XXX */
+ int ms,x;
+ int drops[MEMORY_SIZE];
+ int min, max=0, maxone=0,y,z, match;
+ /* ms is a measure of the "lateness" of the packet relative to the first
+ packet we received, which always has a lateness of 1. */
+ ms = calc_rxstamp(iaxs[fr->callno]) - fr->ts;
+
+ /* Rotate our history queue of "lateness". Don't worry about those initial
+ zeros because the first entry will always be zero */
+ for (x=0;x<MEMORY_SIZE - 1;x++)
+ iaxs[fr->callno]->history[x] = iaxs[fr->callno]->history[x+1];
+ /* Add a history entry for this one */
+ iaxs[fr->callno]->history[x] = ms;
+
+ /* Initialize the minimum to reasonable values. It's too much
+ work to do the same for the maximum, repeatedly */
+ min=iaxs[fr->callno]->history[0];
+ for (z=0;z < iax_dropcount + 1;z++) {
+ /* Start very pessimistic ;-) */
+ max=-999999999;
+ for (x=0;x<MEMORY_SIZE;x++) {
+ if (max < iaxs[fr->callno]->history[x]) {
+ /* We have a candidate new maximum value. Make
+ sure it's not in our drop list */
+ match = 0;
+ for (y=0;!match && (y<z);y++)
+ match |= (drops[y] == x);
+ if (!match) {
+ /* It's not in our list, use it as the new maximum */
+ max = iaxs[fr->callno]->history[x];
+ maxone = x;
+ }
+
+ }
+ if (!z) {
+ /* On our first pass, find the minimum too */
+ if (min > iaxs[fr->callno]->history[x])
+ min = iaxs[fr->callno]->history[x];
+ }
+ }
+#if 1
+ drops[z] = maxone;
+#endif
+ }
+ /* Just for reference, keep the "jitter" value, the difference between the
+ earliest and the latest. */
+ iaxs[fr->callno]->jitter = max - min;
+
+ /* If our jitter buffer is too big (by a significant margin), then we slowly
+ shrink it by about 1 ms each time to avoid letting the change be perceived */
+ if (max < iaxs[fr->callno]->jitterbuffer - max_jitter_buffer)
+ iaxs[fr->callno]->jitterbuffer -= 2;
+
+#if 1
+ /* Constrain our maximum jitter buffer appropriately */
+ if (max > min + maxjitterbuffer) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Constraining buffer from %d to %d + %d\n", max, min , maxjitterbuffer);
+ max = min + maxjitterbuffer;
+ }
+#endif
+
+ /* If our jitter buffer is smaller than our maximum delay, grow the jitter
+ buffer immediately to accomodate it (and a little more). */
+ if (max > iaxs[fr->callno]->jitterbuffer)
+ iaxs[fr->callno]->jitterbuffer = max
+ /* + ((float)iaxs[fr->callno]->jitter) * 0.1 */;
+
+
+ if (option_debug)
+ ast_log(LOG_DEBUG, "min = %d, max = %d, jb = %d, lateness = %d\n", min, max, iaxs[fr->callno]->jitterbuffer, ms);
+
+ /* Subtract the lateness from our jitter buffer to know how long to wait
+ before sending our packet. */
+ ms = iaxs[fr->callno]->jitterbuffer - ms;
+
+ if (!use_jitterbuffer)
+ ms = 0;
+
+ if (ms < 1) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Calculated ms is %d\n", ms);
+ /* Don't deliver it more than 4 ms late */
+ if ((ms > -4) || (fr->f->frametype != AST_FRAME_VOICE)) {
+ do_deliver(fr);
+ }
+ else {
+ /* Free the packet */
+ ast_frfree(fr->f);
+ /* And our iax frame */
+ free(fr);
+ }
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Scheduling delivery in %d ms\n", ms);
+ ast_sched_add(sched, ms, do_deliver, fr);
+ }
+ return 0;
+}
+
+static int iax_transmit(struct ast_iax_frame *fr)
+{
+ /* Lock the queue and place this packet at the end */
+ fr->next = NULL;
+ fr->prev = NULL;
+ /* By setting this to 0, the network thread will send it for us, and
+ queue retransmission if necessary */
+ fr->sentyet = 0;
+ pthread_mutex_lock(&iaxq.lock);
+ if (!iaxq.head) {
+ /* Empty queue */
+ iaxq.head = fr;
+ iaxq.tail = fr;
+ } else {
+ /* Double link */
+ iaxq.tail->next = fr;
+ fr->prev = iaxq.tail;
+ iaxq.tail = fr;
+ }
+ iaxq.count++;
+ pthread_mutex_unlock(&iaxq.lock);
+ /* Wake up the network thread */
+ pthread_kill(netthreadid, SIGURG);
+ return 0;
+}
+
+
+
+static int iax_digit(struct ast_channel *c, char digit)
+{
+ return send_command(c->pvt->pvt, AST_FRAME_DTMF, digit, 0, NULL, 0, -1);
+}
+
+static int create_addr(struct sockaddr_in *sin, char *peer)
+{
+ struct hostent *hp;
+ struct iax_peer *p;
+ sin->sin_family = AF_INET;
+ pthread_mutex_lock(&peerl.lock);
+ p = peerl.peers;
+ while(p) {
+ if (!strcasecmp(p->name, peer)) {
+ sin->sin_addr = p->addr.sin_addr;
+ sin->sin_port = p->addr.sin_port;
+ break;
+ }
+ p = p->next;
+ }
+ pthread_mutex_unlock(&peerl.lock);
+ if (!p) {
+ hp = gethostbyname(peer);
+ if (hp) {
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+ sin->sin_port = htons(AST_DEFAULT_IAX_PORTNO);
+ return 0;
+ } else
+ return -1;
+ } else
+ return 0;
+}
+static int iax_call(struct ast_channel *c, char *dest, int timeout)
+{
+ struct sockaddr_in sin;
+ char host[256];
+ char *rdest;
+ char *rcontext;
+ char *username;
+ char *hname;
+ char requeststr[256] = "";
+ char myrdest [5] = "s";
+ if ((c->state != AST_STATE_DOWN) && (c->state != AST_STATE_RESERVED)) {
+ ast_log(LOG_WARNING, "Line is already in use (%s)?\n", c->name);
+ return -1;
+ }
+ strncpy(host, dest, sizeof(host));
+ strtok(host, ":");
+ /* If no destination extension specified, use 's' */
+ rdest = strtok(NULL, ":");
+ if (!rdest)
+ rdest = myrdest;
+ strtok(rdest, "@");
+ rcontext = strtok(NULL, "@");
+ strtok(host, "@");
+ username = strtok(NULL, "@");
+ if (username) {
+ /* Really the second argument is the host, not the username */
+ hname = username;
+ username = host;
+ } else {
+ hname = host;
+ }
+ if (create_addr(&sin, hname)) {
+ ast_log(LOG_WARNING, "No address associated with '%s'\n", hname);
+ return -1;
+ }
+ /* Now we build our request string */
+#define MYSNPRINTF snprintf(requeststr + strlen(requeststr), sizeof(requeststr) - strlen(requeststr),
+ MYSNPRINTF "exten=%s;", rdest);
+ if (c->callerid)
+ MYSNPRINTF "callerid=%s;", c->callerid);
+ if (c->dnid)
+ MYSNPRINTF "dnid=%s;", c->dnid);
+ if (rcontext)
+ MYSNPRINTF "context=%s;", rcontext);
+ if (username)
+ MYSNPRINTF "username=%s;", username);
+ MYSNPRINTF "formats=%d;", c->format);
+ MYSNPRINTF "version=%d;", AST_IAX_PROTO_VERSION);
+ /* Trim the trailing ";" */
+ if (strlen(requeststr))
+ requeststr[strlen(requeststr) - 1] = '\0';
+ /* Transmit the string in a "NEW" request */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr);
+ send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_IAX,
+ AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1);
+ c->state = AST_STATE_RINGING;
+ return 0;
+}
+
+static void iax_destroy(int callno)
+{
+ char zero=0;
+ struct chan_iax_pvt *pvt = iaxs[callno];
+ if (pvt) {
+ if (pvt->owner) {
+ /* If there's an owner, prod it to give up */
+ write(pvt->pipe[1], &zero, 1);
+ return;
+ }
+ iaxs[callno] = NULL;
+ close(pvt->pipe[0]);
+ close(pvt->pipe[1]);
+ free(pvt);
+ }
+}
+
+static int iax_hangup(struct ast_channel *c) {
+ struct chan_iax_pvt *pvt = c->pvt->pvt;
+ /* Send the hangup unless we have had a transmission error */
+ if (!pvt->error) {
+ send_command(pvt, AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, NULL, 0, -1);
+ /* Wait for the network thread to transmit our command -- of course, if
+ it doesn't, that's okay too -- the other end will find out
+ soon enough, but it's a nicity if it can know now. */
+ sleep(1);
+ }
+ pthread_mutex_lock(&iaxs_lock);
+ c->pvt->pvt = NULL;
+ 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", c->name);
+ iax_destroy(pvt->callno);
+ pthread_mutex_unlock(&iaxs_lock);
+ return 0;
+}
+
+static struct ast_frame *iax_read(struct ast_channel *c)
+{
+ struct chan_iax_pvt *pvt = c->pvt->pvt;
+ struct ast_frame *f;
+ if (pvt->error) {
+ ast_log(LOG_DEBUG, "Connection closed, error: %s\n", strerror(pvt->error));
+ return NULL;
+ }
+ f = ast_fr_fdread(pvt->pipe[0]);
+ if (f) {
+ if ((f->frametype == AST_FRAME_CONTROL) &&
+ (f->subclass == AST_CONTROL_ANSWER))
+ c->state = AST_STATE_UP;
+ }
+ return f;
+}
+
+static int iax_answer(struct ast_channel *c)
+{
+ struct chan_iax_pvt *pvt = c->pvt->pvt;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Answering\n");
+ return send_command(pvt, AST_FRAME_CONTROL, AST_CONTROL_ANSWER, 0, NULL, 0, -1);
+}
+
+static int iax_write(struct ast_channel *c, struct ast_frame *f);
+
+static struct ast_channel *ast_iax_new(struct chan_iax_pvt *i, int state)
+{
+ struct ast_channel *tmp;
+ tmp = ast_channel_alloc();
+ if (tmp) {
+ snprintf(tmp->name, sizeof(tmp->name), "IAX[%s:%d]/%d", inet_ntoa(i->addr.sin_addr), ntohs(i->addr.sin_port), i->callno);
+ tmp->type = type;
+ tmp->fd = i->pipe[0];
+ /* We can support any format by default, until we get restricted */
+ tmp->format = iax_capability;
+ tmp->pvt->pvt = i;
+ tmp->pvt->send_digit = iax_digit;
+ tmp->pvt->call = iax_call;
+ tmp->pvt->hangup = iax_hangup;
+ tmp->pvt->answer = iax_answer;
+ tmp->pvt->read = iax_read;
+ tmp->pvt->write = iax_write;
+ if (strlen(i->callerid))
+ tmp->callerid = strdup(i->callerid);
+ if (strlen(i->dnid))
+ tmp->dnid = strdup(i->dnid);
+ strncpy(tmp->context, i->context, sizeof(tmp->context));
+ strncpy(tmp->exten, i->exten, sizeof(tmp->exten));
+ i->owner = tmp;
+ tmp->state = state;
+ pthread_mutex_lock(&usecnt_lock);
+ usecnt++;
+ pthread_mutex_unlock(&usecnt_lock);
+ ast_update_use_count();
+ 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;
+ }
+ }
+ }
+ return tmp;
+}
+
+static unsigned int calc_timestamp(struct chan_iax_pvt *p, unsigned int ts)
+{
+ struct timeval tv;
+ unsigned int ms;
+ if (!p->offset.tv_sec && !p->offset.tv_usec)
+ gettimeofday(&p->offset, NULL);
+ /* If the timestamp is specified, just send it as is */
+ if (ts)
+ return ts;
+ gettimeofday(&tv, NULL);
+ ms = (tv.tv_sec - p->offset.tv_sec) * 1000 + (tv.tv_usec - p->offset.tv_usec) / 1000;
+ /* We never send the same timestamp twice, so fudge a little if we must */
+ if (ms <= p->lastsent)
+ ms = p->lastsent + 1;
+ p->lastsent = ms;
+ return ms;
+}
+
+static unsigned int calc_rxstamp(struct chan_iax_pvt *p)
+{
+ /* Returns where in "receive time" we are */
+ struct timeval tv;
+ unsigned int ms;
+ if (!p->rxcore.tv_sec && !p->rxcore.tv_usec)
+ gettimeofday(&p->rxcore, NULL);
+ gettimeofday(&tv, NULL);
+ ms = (tv.tv_sec - p->offset.tv_sec) * 1000 + (tv.tv_usec - p->offset.tv_usec) / 1000;
+ return ms;
+}
+static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno)
+{
+ /* Queue a packet for delivery on a given private structure. Use "ts" for
+ timestamp, or calculate if ts is 0 */
+ struct ast_iax_full_hdr *fh;
+ struct ast_iax_mini_hdr *mh;
+ struct ast_iax_frame *fr;
+ int res;
+ unsigned int lastsent;
+ /* Allocate an ast_iax_frame */
+ fr = malloc(sizeof(struct ast_iax_frame));
+ if (!fr) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return -1;
+ }
+ if (!pvt) {
+ ast_log(LOG_WARNING, "No private structure for packet (%d)?\n", fr->callno);
+ free(fr);
+ return -1;
+ }
+ /* Isolate our frame for transmission */
+ fr->f = ast_frdup(f);
+ if (!fr->f) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ free(fr);
+ return -1;
+ }
+ if (fr->f->offset < sizeof(struct ast_iax_full_hdr)) {
+ ast_log(LOG_WARNING, "Packet from '%s' not friendly\n", fr->f->src);
+ free(fr);
+ return -1;
+ }
+ lastsent = pvt->lastsent;
+ fr->ts = calc_timestamp(pvt, ts);
+ if (!fr->ts) {
+ ast_log(LOG_WARNING, "timestamp is 0?\n");
+ return -1;
+ }
+ fr->callno = pvt->callno;
+ if (((fr->ts & 0xFFFF0000L) != (lastsent & 0xFFFF0000L))
+ /* High two bits of timestamp differ */ ||
+ (fr->f->frametype != AST_FRAME_VOICE)
+ /* or not a voice frame */ ||
+ (fr->f->subclass != pvt->svoiceformat)
+ /* or new voice format */ ) {
+ /* We need a full frame */
+ if (seqno > -1)
+ fr->seqno = seqno;
+ else
+ fr->seqno = pvt->oseqno++;
+ fh = (struct ast_iax_full_hdr *)(fr->f->data - sizeof(struct ast_iax_full_hdr));
+ fh->callno = htons(fr->callno | AST_FLAG_FULL);
+ fh->ts = htonl(fr->ts);
+ fh->seqno = htons(fr->seqno);
+ fh->type = fr->f->frametype & 0xFF;
+ fh->csub = compress_subclass(fr->f->subclass);
+#if 0
+ fh->subclasshigh = (fr->f->subclass & 0xFF0000) >> 16;
+ fh->subclasslow = htons(fr->f->subclass & 0xFFFF);
+#endif
+ fh->dcallno = htons(pvt->peercallno);
+ fr->datalen = fr->f->datalen + sizeof(struct ast_iax_full_hdr);
+ fr->data = fh;
+ fr->retries = 0;
+ /* Retry after 2x the ping time has passed */
+ fr->retrytime = pvt->pingtime * 2;
+ if (fr->retrytime < MIN_RETRY_TIME)
+ fr->retrytime = MIN_RETRY_TIME;
+ if (fr->retrytime > MAX_RETRY_TIME)
+ fr->retrytime = MAX_RETRY_TIME;
+ /* Acks' don't get retried */
+ if ((f->frametype == AST_FRAME_IAX) && (f->subclass == AST_IAX_COMMAND_ACK))
+ fr->retries = -1;
+ if (f->frametype == AST_FRAME_VOICE) {
+ pvt->svoiceformat = f->subclass;
+ }
+ res = iax_transmit(fr);
+ } else {
+ /* Mini-frames have no sequence number */
+ fr->seqno = -1;
+ /* Mini frame will do */
+ mh = (struct ast_iax_mini_hdr *)(fr->f->data - sizeof(struct ast_iax_mini_hdr));
+ mh->callno = htons(fr->callno);
+ mh->ts = htons(fr->ts & 0xFFFF);
+ fr->datalen = fr->f->datalen + sizeof(struct ast_iax_mini_hdr);
+ fr->data = mh;
+ fr->retries = -1;
+ res = iax_transmit(fr);
+ }
+ return res;
+}
+
+
+
+static int iax_show_users(int fd, int argc, char *argv[])
+{
+#define FORMAT "%-15.15s %-15.15s %-15.15s %-15.15s %-5.5s\n"
+ struct iax_user *user;
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ pthread_mutex_lock(&userl.lock);
+ ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C");
+ for(user=userl.users;user;user=user->next) {
+ ast_cli(fd, FORMAT, user->name, user->secret, user->methods,
+ user->contexts ? user->contexts->context : context,
+ user->ha ? "Yes" : "No");
+ }
+ pthread_mutex_unlock(&userl.lock);
+ return RESULT_SUCCESS;
+#undef FORMAT
+}
+
+static int iax_show_peers(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-15.15s %-15.15s %-15.15s %-15.15s %s\n"
+#define FORMAT "%-15.15s %-15.15s %-15.15s %-15.15s %d\n"
+ struct iax_peer *peer;
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ pthread_mutex_lock(&peerl.lock);
+ ast_cli(fd, FORMAT2, "Name", "Username", "Host", "Mask", "Port");
+ for (peer = peerl.peers;peer;peer = peer->next) {
+ char nm[20];
+ strncpy(nm, inet_ntoa(peer->mask), sizeof(nm));
+ ast_cli(fd, FORMAT, peer->name,
+ peer->username ? peer->username : "(Any)",
+ peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Any)",
+ nm,
+ ntohs(peer->addr.sin_port));
+ }
+ pthread_mutex_unlock(&peerl.lock);
+ return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int iax_show_channels(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-15.15s %-10.10s %-11.11s %-11.11s %-7.7s %-6.6s %s\n"
+#define FORMAT "%-15.15s %-10.10s %5.5d/%5.5d %5.5d/%5.5d %-5.5dms %-4.4dms %d\n"
+ int x;
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ pthread_mutex_lock(&iaxs_lock);
+ ast_cli(fd, FORMAT2, "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "Format");
+ for (x=0;x<AST_IAX_MAX_CALLS;x++)
+ if (iaxs[x])
+ ast_cli(fd, FORMAT, inet_ntoa(iaxs[x]->addr.sin_addr),
+ strlen(iaxs[x]->username) ? iaxs[x]->username : "(None)",
+ iaxs[x]->callno, iaxs[x]->peercallno,
+ iaxs[x]->oseqno, iaxs[x]->iseqno,
+ iaxs[x]->lag,
+ iaxs[x]->jitter,
+ iaxs[x]->voiceformat);
+ pthread_mutex_unlock(&iaxs_lock);
+ return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char show_users_usage[] =
+"Usage: iax show users\n"
+" Lists all users known to the IAX (Inter-Asterisk eXchange) subsystem.\n";
+
+static char show_channels_usage[] =
+"Usage: iax show channels\n"
+" Lists all currently active IAX channels.\n";
+
+static char show_peers_usage[] =
+"Usage: iax show peers\n"
+" Lists all known IAX peers.\n";
+
+static struct ast_cli_entry cli_show_users =
+ { { "iax", "show", "users", NULL }, iax_show_users, "Show defined IAX users", show_users_usage };
+static struct ast_cli_entry cli_show_channels =
+ { { "iax", "show", "channels", NULL }, iax_show_channels, "Show active IAX channels", show_channels_usage };
+static struct ast_cli_entry cli_show_peers =
+ { { "iax", "show", "peers", NULL }, iax_show_peers, "Show defined IAX peers", show_peers_usage };
+
+static int iax_write(struct ast_channel *c, struct ast_frame *f)
+{
+ struct chan_iax_pvt *i = c->pvt->pvt;
+ /* If there's an outstanding error, return failure now */
+ if (i->error) {
+ ast_log(LOG_DEBUG, "Write error: %s\n", strerror(errno));
+ return -1;
+ }
+ /* Don't waste bandwidth sending null frames */
+ if (f->frametype == AST_FRAME_NULL)
+ return 0;
+ /* Simple, just queue for transmission */
+ return iax_send(i, f, 0, -1);
+}
+
+static int send_command(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno)
+{
+ struct ast_frame f;
+ f.frametype = type;
+ f.subclass = command;
+ f.datalen = datalen;
+ f.timelen = 0;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __FUNCTION__;
+ f.data = data;
+ return iax_send(i, &f, ts, seqno);
+}
+
+static int apply_context(struct iax_context *con, char *context)
+{
+ while(con) {
+ if (!strcmp(con->context, context))
+ return -1;
+ con = con->next;
+ }
+ return 0;
+}
+
+static int apply_ha(struct iax_ha *ha, struct sockaddr_in *sin)
+{
+ /* Start optimistic */
+ int res = IAX_SENSE_ALLOW;
+ while(ha) {
+ /* For each rule, if this address and the netmask = the net address
+ apply the current rule */
+ if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == (ha->netaddr.s_addr))
+ res = ha->sense;
+ ha = ha->next;
+ }
+ return res;
+}
+
+static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int requestl)
+{
+ /* Start pessimistic */
+ int res = -1;
+ int version = 1;
+ char *var, *value;
+ struct iax_user *user;
+ char request[256];
+ strncpy(request, orequest, sizeof(request));
+ if (!iaxs[callno])
+ return res;
+ var = strtok(request, ";");
+ while(var) {
+ value = strchr(var, '=');
+ if (value) {
+ *value='\0';
+ value++;
+ if (!strcmp(var, "exten"))
+ strncpy(iaxs[callno]->exten, value, sizeof(iaxs[callno]->exten));
+ else if (!strcmp(var, "callerid"))
+ strncpy(iaxs[callno]->callerid, value, sizeof(iaxs[callno]->callerid));
+ else if (!strcmp(var, "dnid"))
+ strncpy(iaxs[callno]->dnid, value, sizeof(iaxs[callno]->dnid));
+ else if (!strcmp(var, "context"))
+ strncpy(iaxs[callno]->context, value, sizeof(iaxs[callno]->context));
+ else if (!strcmp(var, "username"))
+ strncpy(iaxs[callno]->username, value, sizeof(iaxs[callno]->username));
+ else if (!strcmp(var, "formats"))
+ iaxs[callno]->peerformats = atoi(value);
+ else if (!strcmp(var, "version"))
+ version = atoi(value);
+ else
+ ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+ }
+ var = strtok(NULL, ";");
+ }
+ if (version > AST_IAX_PROTO_VERSION) {
+ ast_log(LOG_WARNING, "Peer '%s' has too new a protocol version (%d) for me\n",
+ inet_ntoa(sin->sin_addr), version);
+ return res;
+ }
+ pthread_mutex_lock(&userl.lock);
+ /* Search the userlist for a compatible entry, and fill in the rest */
+ user = userl.users;
+ while(user) {
+ if ((!strlen(iaxs[callno]->username) || /* No username specified */
+ !strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
+ && (apply_ha(user->ha, sin) == IAX_SENSE_ALLOW) /* Access is permitted from this IP */
+ && (!strlen(iaxs[callno]->context) || /* No context specified */
+ apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
+ /* We found our match (use the first) */
+
+ /* Store the requested username if not specified */
+ if (!strlen(iaxs[callno]->username))
+ strncpy(iaxs[callno]->username, user->name, sizeof(iaxs[callno]->username));
+ /* And use the default context */
+ if (!strlen(iaxs[callno]->context)) {
+ if (user->contexts)
+ strncpy(iaxs[callno]->context, user->contexts->context, sizeof(iaxs[callno]->context));
+ else
+ strncpy(iaxs[callno]->context, context, sizeof(iaxs[callno]->context));
+ }
+ /* Copy the secret */
+ strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret));
+ /* And the permitted authentication methods */
+ strncpy(iaxs[callno]->methods, user->methods, sizeof(iaxs[callno]->methods));
+ res = 0;
+ break;
+ }
+ user = user->next;
+ }
+ pthread_mutex_unlock(&userl.lock);
+ return res;
+}
+
+static int raw_hangup(struct sockaddr_in *sin, short src, short dst)
+{
+ struct ast_iax_full_hdr fh;
+ fh.callno = htons(src | AST_FLAG_FULL);
+ fh.dcallno = htons(dst);
+ fh.ts = 0;
+ fh.seqno = 0;
+ fh.type = AST_FRAME_IAX;
+ fh.csub = compress_subclass(AST_IAX_COMMAND_INVAL);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Raw Hangup\n");
+ return sendto(netsocket, &fh, sizeof(fh), 0, sin, sizeof(*sin));
+}
+
+static int authenticate_request(struct chan_iax_pvt *p)
+{
+ char requeststr[256] = "";
+ MYSNPRINTF "methods=%s;", p->methods);
+ if (strstr(p->methods, "md5")) {
+ /* Build the challenge */
+ srand(time(NULL));
+ snprintf(p->challenge, sizeof(p->challenge), "%d", rand());
+ MYSNPRINTF "challenge=%s;", p->challenge);
+ }
+ MYSNPRINTF "username=%s;", p->username);
+ if (strlen(requeststr))
+ requeststr[strlen(requeststr) - 1] = '\0';
+ return send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_AUTHREQ, 0, requeststr, strlen(requeststr) + 1, -1);
+}
+
+static int authenticate_verify(struct chan_iax_pvt *p, char *orequest)
+{
+ char requeststr[256] = "";
+ char *var, *value, request[256];
+ char md5secret[256] = "";
+ char secret[256] = "";
+ int res = -1;
+ int x;
+
+ if (!(p->state & IAX_STATE_AUTHENTICATED))
+ return res;
+ strncpy(request, orequest, sizeof(request));
+ var = strtok(request, ";");
+ while(var) {
+ value = strchr(var, '=');
+ if (value) {
+ *value='\0';
+ value++;
+ if (!strcmp(var, "secret"))
+ strncpy(secret, value, sizeof(secret));
+ else if (!strcmp(var, "md5secret"))
+ strncpy(md5secret, value, sizeof(md5secret));
+ else
+ ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+ }
+ var = strtok(NULL, ";");
+ }
+ if (strstr(p->methods, "md5")) {
+ struct MD5Context md5;
+ unsigned char digest[16];
+ MD5Init(&md5);
+ MD5Update(&md5, p->challenge, strlen(p->challenge));
+ MD5Update(&md5, p->secret, strlen(p->secret));
+ MD5Final(digest, &md5);
+ /* If they support md5, authenticate with it. */
+ for (x=0;x<16;x++)
+ MYSNPRINTF "%2.2x", digest[x]);
+ if (!strcasecmp(requeststr, md5secret))
+ res = 0;
+ } else if (strstr(p->methods, "plaintext")) {
+ if (!strcmp(secret, p->secret))
+ res = 0;
+ }
+ return res;
+}
+
+static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, char *orequest)
+{
+ struct iax_peer *peer;
+ /* Start pessimistic */
+ int res = -1;
+ char request[256];
+ char methods[80] = "";
+ char requeststr[256] = "";
+ char *var, *value;
+ int x;
+ strncpy(request, orequest, sizeof(request));
+ var = strtok(request, ";");
+ while(var) {
+ value = strchr(var, '=');
+ if (value) {
+ *value='\0';
+ value++;
+ if (!strcmp(var, "username"))
+ strncpy(p->username, value, sizeof(p->username));
+ else if (!strcmp(var, "challenge"))
+ strncpy(p->challenge, value, sizeof(p->challenge));
+ else if (!strcmp(var, "methods"))
+ strncpy(methods, value, sizeof(methods));
+ else
+ ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value);
+ }
+ var = strtok(NULL, ";");
+ }
+ pthread_mutex_lock(&peerl.lock);
+ peer = peerl.peers;
+ while(peer) {
+ if ((!strlen(p->peer) || !strcmp(p->peer, peer->name))
+ /* No peer specified at our end, or this is the peer */
+ && (!strlen(peer->username) || (!strcmp(peer->username, p->username)))
+ /* No username specified in peer rule, or this is the right username */
+ && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr)))
+ /* No specified host, or this is our host */
+ ) {
+ /* We have a match, authenticate it. */
+ res = 0;
+ if (strstr(methods, "md5")) {
+ struct MD5Context md5;
+ unsigned char digest[16];
+ MD5Init(&md5);
+ MD5Update(&md5, p->challenge, strlen(p->challenge));
+ MD5Update(&md5, peer->secret, strlen(peer->secret));
+ MD5Final(digest, &md5);
+ /* If they support md5, authenticate with it. */
+ MYSNPRINTF "md5secret=");
+ for (x=0;x<16;x++)
+ MYSNPRINTF "%2.2x", digest[x]);
+ MYSNPRINTF ";");
+ } else if (strstr(methods, "plaintext")) {
+ MYSNPRINTF "secret=%s;", peer->secret);
+ } else
+ res = -1;
+ if (strlen(requeststr))
+ requeststr[strlen(requeststr)-1] = '\0';
+ if (!res)
+ res = send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_AUTHREP, 0, requeststr, strlen(requeststr) + 1, -1);
+ break;
+ }
+ peer = peer->next;
+ }
+ pthread_mutex_unlock(&peerl.lock);
+ return res;
+}
+
+static int socket_read(int *id, int fd, short events, void *cbdata)
+{
+ struct sockaddr_in sin;
+ int res;
+ int new = NEW_PREVENT;
+ char buf[4096];
+ char src[80];
+ int len = sizeof(sin);
+ int dcallno = -1;
+ struct ast_iax_full_hdr *fh = (struct ast_iax_full_hdr *)buf;
+ struct ast_iax_mini_hdr *mh = (struct ast_iax_mini_hdr *)buf;
+ struct ast_iax_frame fr, *cur;
+ struct ast_frame f;
+ struct ast_channel *c;
+ res = recvfrom(netsocket, buf, sizeof(buf), 0, &sin, &len);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+ handle_error();
+ return 1;
+ }
+ if (res < sizeof(struct ast_iax_mini_hdr)) {
+ ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_mini_hdr));
+ return 1;
+ }
+ if (ntohs(mh->callno) & AST_FLAG_FULL) {
+ /* Get the destination call number */
+ dcallno = ntohs(fh->dcallno);
+ /* Retrieve the type and subclass */
+ f.frametype = fh->type;
+ f.subclass = uncompress_subclass(fh->csub);
+#if 0
+ f.subclass = fh->subclasshigh << 16;
+ f.subclass += ntohs(fh->subclasslow);
+#endif
+ if ((f.frametype == AST_FRAME_IAX) && (f.subclass == AST_IAX_COMMAND_NEW))
+ new = NEW_ALLOW;
+ }
+ pthread_mutex_lock(&iaxs_lock);
+ fr.callno = find_callno(ntohs(mh->callno) & ~AST_FLAG_FULL, dcallno, &sin, new);
+ if ((fr.callno < 0) || !iaxs[fr.callno]) {
+ /* A call arrived for a non-existant destination. Unless it's an "inval"
+ frame, reply with an inval */
+ if (ntohs(mh->callno) & AST_FLAG_FULL) {
+ /* We can only raw hangup control frames */
+ if ((f.subclass != AST_IAX_COMMAND_INVAL) || (f.frametype != AST_FRAME_IAX))
+ raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno));
+ }
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ iaxs[fr.callno]->peercallno = ntohs(mh->callno) & ~AST_FLAG_FULL;
+ if (ntohs(mh->callno) & AST_FLAG_FULL) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", ntohs(fh->seqno), f.frametype, f.subclass);
+ /* Check if it's out of order (and not an ACK or INVAL) */
+ fr.seqno = ntohs(fh->seqno);
+ if (iaxs[fr.callno]->iseqno != fr.seqno) {
+ if (
+ ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL)) ||
+ (f.frametype != AST_FRAME_IAX)) {
+ /* If it's not an ACK packet, it's out of order. */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n",
+ iaxs[fr.callno]->iseqno, fr.seqno, f.frametype, f.subclass);
+ if (iaxs[fr.callno]->iseqno > fr.seqno) {
+ /* If we've already seen it, ack it XXX There's a border condition here XXX */
+ if ((f.frametype != AST_FRAME_IAX) ||
+ ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Acking anyway\n");
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
+ }
+ }
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ } else {
+ /* Increment unless it's an ACK */
+ if ((f.subclass != AST_IAX_COMMAND_ACK) ||
+ (f.frametype != AST_FRAME_IAX))
+ iaxs[fr.callno]->iseqno++;
+ }
+ /* A full frame */
+ if (res < sizeof(struct ast_iax_full_hdr)) {
+ ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_full_hdr));
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ f.datalen = res - sizeof(struct ast_iax_full_hdr);
+ if (f.datalen)
+ f.data = buf + sizeof(struct ast_iax_full_hdr);
+ else
+ f.data = NULL;
+ fr.ts = ntohl(fh->ts);
+ /* Unless this is an ACK or INVAL frame, ack it */
+ if ((f.frametype != AST_FRAME_IAX) ||
+ ((f.subclass != AST_IAX_COMMAND_ACK) && (f.subclass != AST_IAX_COMMAND_INVAL)))
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.seqno);
+ if (f.frametype == AST_FRAME_VOICE)
+ iaxs[fr.callno]->voiceformat = f.subclass;
+ if (f.frametype == AST_FRAME_IAX) {
+ /* Handle the IAX pseudo frame itself */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass);
+ switch(f.subclass) {
+ case AST_IAX_COMMAND_ACK:
+ /* Ack the packet with the given timestamp */
+ pthread_mutex_lock(&iaxq.lock);
+ for (cur = iaxq.head; cur ; cur = cur->next) {
+ /* If it's our call, and our timestamp, mark -1 retries */
+ if ((fr.callno == cur->callno) && (fr.seqno == cur->seqno))
+ cur->retries = -1;
+ }
+ pthread_mutex_unlock(&iaxq.lock);
+ break;
+ case AST_IAX_COMMAND_NEW:
+ ((char *)f.data)[f.datalen] = '\0';
+ if (check_access(fr.callno, &sin, f.data, f.datalen)) {
+ /* They're not allowed on */
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
+ ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s'\n", inet_ntoa(sin.sin_addr), f.data);
+ /* XXX Not guaranteed to work, but probably does XXX */
+ pthread_mutex_lock(&iaxq.lock);
+ send_packet(iaxq.tail);
+ pthread_mutex_unlock(&iaxq.lock);
+ iax_destroy(fr.callno);
+ break;
+ }
+ if (!strlen(iaxs[fr.callno]->secret)) {
+ /* No authentication required, let them in */
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
+ iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+ if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
+ iax_destroy(fr.callno);
+ else
+ c->format = iaxs[fr.callno]->peerformats;
+ break;
+ }
+ authenticate_request(iaxs[fr.callno]);
+ iaxs[fr.callno]->state |= IAX_STATE_AUTHENTICATED;
+ break;
+ case AST_IAX_COMMAND_HANGUP:
+#if 0
+ iaxs[fr.callno]->error = ENOTCONN;
+#endif
+ iax_destroy(fr.callno);
+ break;
+ case AST_IAX_COMMAND_REJECT:
+ ((char *)f.data)[f.datalen] = '\0';
+ ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), f.data);
+ iaxs[fr.callno]->error = EPERM;
+ iax_destroy(fr.callno);
+ break;
+ case AST_IAX_COMMAND_ACCEPT:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call accepted by %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
+ iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+ break;
+ case AST_IAX_COMMAND_PING:
+ /* Send back a pong packet with the original timestamp */
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
+ break;
+ case AST_IAX_COMMAND_PONG:
+ iaxs[fr.callno]->pingtime = calc_timestamp(iaxs[fr.callno], 0) - fr.ts;
+ break;
+ case AST_IAX_COMMAND_LAGRQ:
+ case AST_IAX_COMMAND_LAGRP:
+ /* A little strange -- We have to actually go through the motions of
+ delivering the packet. In the very last step, it will be properly
+ handled by do_deliver */
+ snprintf(src, sizeof(src), "LAGRQ-IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
+ f.src = src;
+ f.mallocd = 0;
+ f.offset = 0;
+ fr.f = &f;
+ f.timelen = 0;
+ schedule_delivery(iaxfrdup2(&fr, 0));
+ break;
+ case AST_IAX_COMMAND_AUTHREQ:
+ ((char *)f.data)[f.datalen] = '\0';
+ if (authenticate_reply(iaxs[fr.callno], &iaxs[fr.callno]->addr, (char *)f.data)) {
+ ast_log(LOG_WARNING,
+ "I don't know how to authenticate %s to %s\n",
+ f.data, inet_ntoa(iaxs[fr.callno]->addr.sin_addr));
+ iax_destroy(fr.callno);
+ }
+ break;
+ case AST_IAX_COMMAND_AUTHREP:
+ ((char *)f.data)[f.datalen] = '\0';
+ if (authenticate_verify(iaxs[fr.callno], (char *)f.data)) {
+ ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), iaxs[fr.callno]->username);
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1);
+ /* XXX Not guaranteed to work, but probably does XXX */
+ pthread_mutex_lock(&iaxq.lock);
+ send_packet(iaxq.tail);
+ pthread_mutex_unlock(&iaxq.lock);
+ iax_destroy(fr.callno);
+ break;
+ }
+ /* Authentication is fine, go ahead */
+ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, NULL, 0, -1);
+ iaxs[fr.callno]->state |= IAX_STATE_STARTED;
+ if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING)))
+ iax_destroy(fr.callno);
+ else
+ c->format = iaxs[fr.callno]->peerformats;
+ break;
+ case AST_IAX_COMMAND_INVAL:
+ iaxs[fr.callno]->error = ENOTCONN;
+ iax_destroy(fr.callno);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno);
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr.callno, iaxs[fr.callno]->peercallno);
+ }
+ /* Don't actually pass these frames along */
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ } else {
+ /* A mini frame */
+ f.frametype = AST_FRAME_VOICE;
+ if (iaxs[fr.callno]->voiceformat > 0)
+ f.subclass = iaxs[fr.callno]->voiceformat;
+ else {
+ ast_log(LOG_WARNING, "Received mini frame before first full voice frame\n ");
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ f.datalen = res - sizeof(struct ast_iax_mini_hdr);
+ if (f.datalen < 0) {
+ ast_log(LOG_WARNING, "Datalen < 0?\n");
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ if (f.datalen)
+ f.data = buf + sizeof(struct ast_iax_mini_hdr);
+ else
+ f.data = NULL;
+ fr.ts = (iaxs[fr.callno]->last & 0xFFFF0000L) | ntohs(mh->ts);
+ }
+ /* Don't pass any packets until we're started */
+ if (!(iaxs[fr.callno]->state & IAX_STATE_STARTED)) {
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+ }
+ /* Common things */
+ snprintf(src, sizeof(src), "IAX/%s/%d", inet_ntoa(sin.sin_addr),fr.callno);
+ f.src = src;
+ f.mallocd = 0;
+ f.offset = 0;
+ fr.f = &f;
+ if (f.datalen && (f.frametype == AST_FRAME_VOICE))
+ f.timelen = get_timelen(&f);
+ else
+ f.timelen = 0;
+
+ /* If this is our most recent packet, use it as our basis for timestamping */
+ if (iaxs[fr.callno]->last < fr.ts) {
+ iaxs[fr.callno]->last = fr.ts;
+ fr.outoforder = 0;
+ } else {
+ ast_log(LOG_DEBUG, "Received out of order packet... (type=%d, subclass %d, ts = %d, last = %d)\n", f.frametype, f.subclass, fr.ts, iaxs[fr.callno]->last);
+ fr.outoforder = -1;
+ }
+ schedule_delivery(iaxfrdup2(&fr, 0));
+ /* Always run again */
+ pthread_mutex_unlock(&iaxs_lock);
+ return 1;
+}
+
+static void free_ha(struct iax_ha *ha)
+{
+ struct iax_ha *hal;
+ while(ha) {
+ hal = ha;
+ ha = ha->next;
+ free(hal);
+ }
+}
+
+static void free_context(struct iax_context *con)
+{
+ struct iax_context *conl;
+ while(con) {
+ conl = con;
+ con = con->next;
+ free(conl);
+ }
+}
+
+static struct ast_channel *iax_request(char *type, int format, void *data)
+{
+ int callno;
+ struct sockaddr_in sin;
+ char s[256];
+ char *st;
+ struct ast_channel *c;
+ strncpy(s, (char *)data, sizeof(s));
+ strtok(s, ":");
+ strtok(s, "@");
+ st = strtok(NULL, "@");
+ if (!st)
+ st = s;
+ /* Populate our address from the given */
+ if (create_addr(&sin, st)) {
+ ast_log(LOG_WARNING, "Unable to assign address\n");
+ return NULL;
+ }
+ pthread_mutex_lock(&iaxs_lock);
+ callno = find_callno(-1, -1, &sin, NEW_FORCE);
+ if (callno < 0) {
+ ast_log(LOG_WARNING, "Unable to create call\n");
+ return NULL;
+ }
+ c = ast_iax_new(iaxs[callno], AST_STATE_DOWN);
+ if (c) {
+ /* Choose a format we can live with */
+ if (c->format & format)
+ c->format &= format;
+ else
+ c->format = ast_translator_best_choice(format, c->format);
+ }
+ pthread_mutex_unlock(&iaxs_lock);
+ return c;
+}
+
+static void *network_thread(void *ignore)
+{
+ /* Our job is simple: Send queued messages, retrying if necessary. Read frames
+ from the network, and queue them for delivery to the channels */
+ int res;
+ struct ast_iax_frame *f, *freeme;
+ /* Establish I/O callback for socket read */
+ ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
+ pthread_mutex_lock(&iaxs_lock);
+ for(;;) {
+ /* Go through the queue, sending messages which have not yet been
+ sent, and scheduling retransmissions if appropriate */
+ pthread_mutex_lock(&iaxq.lock);
+ f = iaxq.head;
+ while(f) {
+ freeme = NULL;
+ if (!f->sentyet) {
+ f->sentyet++;
+ /* Send a copy immediately */
+ if (iaxs[f->callno]) {
+ send_packet(f);
+ }
+ if (f->retries < 0) {
+ /* This is not supposed to be retransmitted */
+ if (f->prev)
+ f->prev->next = f->next;
+ else
+ iaxq.head = f->next;
+ if (f->next)
+ f->next->prev = f->prev;
+ else
+ iaxq.tail = f->prev;
+ iaxq.count--;
+ /* Free the frame */
+ ast_frfree(f->f);
+ /* Free the iax frame */
+ freeme = f;
+ } else {
+ /* We need reliable delivery. Schedule a retransmission */
+ f->retries++;
+ ast_sched_add(sched, f->retrytime, attempt_transmit, f);
+ }
+ }
+ f = f->next;
+ if (freeme)
+ free(freeme);
+ }
+ pthread_mutex_unlock(&iaxq.lock);
+ pthread_mutex_unlock(&iaxs_lock);
+ res = ast_sched_wait(sched);
+ res = ast_io_wait(io, res);
+ pthread_mutex_lock(&iaxs_lock);
+ if (res >= 0) {
+ ast_sched_runq(sched);
+ }
+ }
+}
+
+static int start_network_thread()
+{
+ return pthread_create(&netthreadid, NULL, network_thread, NULL);
+}
+
+static struct iax_context *build_context(char *context)
+{
+ struct iax_context *con = malloc(sizeof(struct iax_context));
+ if (con) {
+ strncpy(con->context, context, sizeof(con->context));
+ con->next = NULL;
+ }
+ return con;
+}
+
+static struct iax_ha *build_ha(char *sense, char *stuff)
+{
+ struct iax_ha *ha = malloc(sizeof(struct iax_ha));
+ char *nm;
+ if (ha) {
+ strtok(stuff, "/");
+ nm = strtok(NULL, "/");
+ if (!nm)
+ nm = "255.255.255.255";
+ if (!inet_aton(stuff, &ha->netaddr)) {
+ ast_log(LOG_WARNING, "%s not a valid IP\n", stuff);
+ free(ha);
+ return NULL;
+ }
+ if (!inet_aton(nm, &ha->netmask)) {
+ ast_log(LOG_WARNING, "%s not a valid netmask\n", nm);
+ free(ha);
+ return NULL;
+ }
+ ha->netaddr.s_addr &= ha->netmask.s_addr;
+ if (!strcasecmp(sense, "a")) {
+ ha->sense = IAX_SENSE_ALLOW;
+ } else {
+ ha->sense = IAX_SENSE_DENY;
+ }
+ ha->next = NULL;
+ }
+ return ha;
+}
+
+static struct iax_peer *build_peer(char *name, struct ast_variable *v)
+{
+ struct iax_peer *peer;
+ int maskfound=0;
+ struct hostent *hp;
+ peer = malloc(sizeof(struct iax_peer));
+ if (peer) {
+ memset(peer, 0, sizeof(struct iax_peer));
+ strncpy(peer->name, name, sizeof(peer->name));
+ peer->addr.sin_port = htons(AST_DEFAULT_IAX_PORTNO);
+ while(v) {
+ if (!strcasecmp(v->name, "secret"))
+ strncpy(peer->secret, v->value, sizeof(peer->secret));
+ else if (!strcasecmp(v->name, "host")) {
+ hp = gethostbyname(v->value);
+ if (hp) {
+ memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+ } else {
+ ast_log(LOG_WARNING, "Unable to lookup '%s'\n", v->value);
+ free(peer);
+ return NULL;
+ }
+ if (!maskfound)
+ inet_aton("255.255.255.255", &peer->mask);
+ }
+ else if (!strcasecmp(v->name, "mask")) {
+ maskfound++;
+ inet_aton(v->value, &peer->mask);
+ } else if (!strcasecmp(v->name, "port"))
+ peer->addr.sin_port = htons(atoi(v->value));
+ else if (!strcasecmp(v->name, "username"))
+ strncpy(peer->username, v->value, sizeof(peer->username));
+ v=v->next;
+ }
+ }
+ return peer;
+}
+
+static struct iax_user *build_user(char *name, struct ast_variable *v)
+{
+ struct iax_user *user;
+ struct iax_context *con, *conl = NULL;
+ struct iax_ha *ha, *hal = NULL;
+ user = (struct iax_user *)malloc(sizeof(struct iax_user));
+ if (user) {
+ memset(user, 0, sizeof(struct iax_user));
+ strncpy(user->name, name, sizeof(user->name));
+ while(v) {
+ if (!strcasecmp(v->name, "context")) {
+ con = build_context(v->value);
+ if (con) {
+ if (conl)
+ conl->next = con;
+ else
+ user->contexts = con;
+ conl = con;
+ }
+ } else if (!strcasecmp(v->name, "allow") ||
+ !strcasecmp(v->name, "deny")) {
+ ha = build_ha(v->name, v->value);
+ if (ha) {
+ if (hal)
+ hal->next = ha;
+ else
+ user->ha = ha;
+ hal = ha;
+ }
+ } else if (!strcasecmp(v->name, "auth")) {
+ strncpy(user->methods, v->value, sizeof(user->methods));
+ } else if (!strcasecmp(v->name, "secret")) {
+ strncpy(user->secret, v->value, sizeof(user->secret));
+ }
+ v = v->next;
+ }
+ }
+ return user;
+}
+
+int load_module()
+{
+ int res = 0;
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ struct iax_user *user;
+ struct iax_peer *peer;
+ char *cat;
+ char *utype;
+ int format;
+
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = ntohs(AST_DEFAULT_IAX_PORTNO);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ io = io_context_create();
+ sched = sched_context_create();
+
+ if (!io || !sched) {
+ ast_log(LOG_ERROR, "Out of memory\n");
+ return -1;
+ }
+
+ pthread_mutex_init(&iaxq.lock, NULL);
+ pthread_mutex_init(&userl.lock, NULL);
+
+ ast_cli_register(&cli_show_users);
+ ast_cli_register(&cli_show_channels);
+ ast_cli_register(&cli_show_peers);
+ ast_cli_register(&cli_set_jitter);
+#ifdef IAX_SIMULATOR
+ ast_cli_register(&delay_cli);
+ ast_cli_register(&deviation_cli);
+ ast_cli_register(&reliability_cli);
+ ast_cli_register(&sim_show_cli);
+#endif
+ cfg = ast_load(config);
+
+ if (!cfg) {
+ ast_log(LOG_ERROR, "Unable to load config %s\n", config);
+ return -1;
+ }
+ v = ast_variable_browse(cfg, "general");
+ while(v) {
+ if (!strcasecmp(v->name, "port"))
+ sin.sin_port = ntohs(atoi(v->value));
+ else if (!strcasecmp(v->name, "pingtime"))
+ ping_time = atoi(v->value);
+ else if (!strcasecmp(v->name, "maxjitterbuffer"))
+ maxjitterbuffer = atoi(v->value);
+ else if (!strcasecmp(v->name, "maxexcessbuffer"))
+ max_jitter_buffer = atoi(v->value);
+ else if (!strcasecmp(v->name, "lagrqtime"))
+ lagrq_time = atoi(v->value);
+ else if (!strcasecmp(v->name, "dropcount"))
+ iax_dropcount = atoi(v->value);
+ else if (!strcasecmp(v->name, "bindaddr"))
+ inet_aton(v->value, &sin.sin_addr);
+ else if (!strcasecmp(v->name, "jitterbuffer"))
+ use_jitterbuffer = ast_true(v->value);
+ else if (!strcasecmp(v->name, "bandwidth")) {
+ if (!strcasecmp(v->value, "low")) {
+ iax_capability = IAX_CAPABILITY_LOWBANDWIDTH;
+ } else if (!strcasecmp(v->value, "medium")) {
+ iax_capability = IAX_CAPABILITY_MEDBANDWIDTH;
+ } else if (!strcasecmp(v->value, "high")) {
+ iax_capability = IAX_CAPABILITY_FULLBANDWIDTH;
+ } else
+ ast_log(LOG_WARNING, "bandwidth must be either low, medium, or high\n");
+ } else if (!strcasecmp(v->name, "allow")) {
+ format = ast_getformatbyname(v->value);
+ if (format < 1)
+ ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
+ else
+ iax_capability |= format;
+ } else if (!strcasecmp(v->name, "disallow")) {
+ format = ast_getformatbyname(v->value);
+ if (format < 1)
+ ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
+ else
+ iax_capability &= ~format;
+ }
+ v = v->next;
+ }
+ cat = ast_category_browse(cfg, NULL);
+ while(cat) {
+ if (strcasecmp(cat, "general")) {
+ utype = ast_variable_retrieve(cfg, cat, "type");
+ if (utype) {
+ if (!strcasecmp(utype, "user")) {
+ user = build_user(cat, ast_variable_browse(cfg, cat));
+ if (user) {
+ pthread_mutex_lock(&userl.lock);
+ user->next = userl.users;
+ userl.users = user;
+ pthread_mutex_unlock(&userl.lock);
+ }
+ } else if (!strcasecmp(utype, "peer")) {
+ peer = build_peer(cat, ast_variable_browse(cfg, cat));
+ if (peer) {
+ pthread_mutex_lock(&peerl.lock);
+ peer->next = peerl.peers;
+ peerl.peers = peer;
+ pthread_mutex_unlock(&peerl.lock);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, config);
+ }
+ } else
+ ast_log(LOG_WARNING, "Section '%s' lacks type\n", cat);
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+ ast_destroy(cfg);
+ if (ast_channel_register(type, tdesc, iax_capability, iax_request)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ unload_module();
+ return -1;
+ }
+
+ /* Make a UDP socket */
+ netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (netsocket < 0) {
+ ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+ return -1;
+ }
+ if (bind(netsocket, &sin, sizeof(sin))) {
+ ast_log(LOG_ERROR, "Unable to bind to %s port %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ return -1;
+ }
+
+ if (!res) {
+ res = start_network_thread();
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "IAX Ready and Listening on %s port %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ } else {
+ ast_log(LOG_ERROR, "Unable to start network thread\n");
+ close(netsocket);
+ }
+ return res;
+}
+
+char *description()
+{
+ return desc;
+}
+
+int unload_module()
+{
+ struct iax_user *user, *userlast;
+ struct iax_peer *peer, *peerlast;
+ int x;
+ /* Cancel the network thread, close the net socket */
+ pthread_cancel(netthreadid);
+ pthread_join(netthreadid, NULL);
+ close(netsocket);
+ for (x=0;x<AST_IAX_MAX_CALLS;x++)
+ if (iaxs[x])
+ iax_destroy(x);
+ ast_cli_unregister(&cli_show_users);
+ ast_cli_unregister(&cli_show_channels);
+ ast_cli_unregister(&cli_show_peers);
+ ast_cli_unregister(&cli_set_jitter);
+#ifdef IAX_SIMULATOR
+ ast_cli_unregister(&delay_cli);
+ ast_cli_unregister(&deviation_cli);
+ ast_cli_unregister(&reliability_cli);
+ ast_cli_unregister(&sim_show_cli);
+#endif
+ for (user=userl.users;user;) {
+ free_ha(user->ha);
+ free_context(user->contexts);
+ userlast = user;
+ user=user->next;
+ free(userlast);
+ }
+ for (peer=peerl.peers;peer;) {
+ peerlast = peer;
+ peer=peer->next;
+ free(peerlast);
+ }
+ return 0;
+}
+
+int usecount()
+{
+ int res;
+ pthread_mutex_lock(&usecnt_lock);
+ res = usecnt;
+ pthread_mutex_unlock(&usecnt_lock);
+ return res;
+}
+
diff --git a/channels/chan_modem.c b/channels/chan_modem.c
index 7c6fedbf2..bf4a89c60 100755
--- a/channels/chan_modem.c
+++ b/channels/chan_modem.c
@@ -419,7 +419,6 @@ struct ast_channel *ast_modem_new(struct ast_modem_pvt *i, int state)
snprintf(tmp->name, sizeof(tmp->name), "Modem[%s]/%s", i->mc->name, i->dev + 5);
tmp->type = type;
tmp->fd = i->fd;
- /* XXX Switching formats silently causes kernel panics XXX */
tmp->format = i->mc->formats;
tmp->state = state;
if (state == AST_STATE_RING)
@@ -433,7 +432,7 @@ struct ast_channel *ast_modem_new(struct ast_modem_pvt *i, int state)
tmp->pvt->write = modem_write;
strncpy(tmp->context, i->context, sizeof(tmp->context));
if (strlen(i->cid))
- strncpy(tmp->callerid, i->cid, sizeof(tmp->callerid));
+ tmp->callerid = strdup(i->cid);
i->owner = tmp;
pthread_mutex_lock(&usecnt_lock);
usecnt++;
@@ -443,6 +442,7 @@ struct ast_channel *ast_modem_new(struct ast_modem_pvt *i, int state)
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
ast_hangup(tmp);
+ tmp = NULL;
}
}
} else
@@ -454,6 +454,12 @@ static void modem_mini_packet(struct ast_modem_pvt *i)
{
struct ast_frame *fr;
fr = i->mc->read(i);
+ if (fr->frametype == AST_FRAME_CONTROL) {
+ if (fr->subclass == AST_CONTROL_RING) {
+ ast_modem_new(i, AST_STATE_RING);
+ restart_monitor();
+ }
+ }
}
static void *do_monitor(void *data)
@@ -701,10 +707,10 @@ int load_module()
ast_verbose(VERBOSE_PREFIX_2 "Loading modem driver %s", driver);
if (ast_load_resource(driver)) {
- ast_log(LOG_ERROR, "Failed to laod driver %s\n", driver);
+ ast_log(LOG_ERROR, "Failed to load driver %s\n", driver);
ast_destroy(cfg);
- unload_module();
pthread_mutex_unlock(&iflock);
+ unload_module();
return -1;
}
} else if (!strcasecmp(v->name, "mode")) {
@@ -742,8 +748,6 @@ int load_module()
return 0;
}
-
-
int unload_module()
{
struct ast_modem_pvt *p, *pl;
diff --git a/channels/chan_modem_aopen.c b/channels/chan_modem_aopen.c
index ae2b7e41a..f344cb149 100755
--- a/channels/chan_modem_aopen.c
+++ b/channels/chan_modem_aopen.c
@@ -194,9 +194,13 @@ static struct ast_frame *aopen_handle_escape(struct ast_modem_pvt *p, char esc)
ast_log(LOG_DEBUG, "Escaped character '%c'\n", esc);
switch(esc) {
+ case 'R': /* Pseudo ring */
+ p->fr.frametype = AST_FRAME_CONTROL;
+ p->fr.subclass = AST_CONTROL_RING;
+ return &p->fr;
case 'X': /* Pseudo connect */
p->fr.frametype = AST_FRAME_CONTROL;
- p->fr.subclass = AST_CONTROL_ANSWER;
+ p->fr.subclass = AST_CONTROL_RING;
if (p->owner)
p->owner->state = AST_STATE_UP;
if (aopen_startrec(p))
@@ -255,11 +259,14 @@ static struct ast_frame *aopen_read(struct ast_modem_pvt *p)
/* If we're in immediate mode, reply now */
if (p->mode == MODEM_MODE_IMMEDIATE)
return aopen_handle_escape(p, 'X');
- }
+ } else
if (!strcasecmp(result, "BUSY")) {
/* Same as a busy signal */
return aopen_handle_escape(p, 'b');
- }
+ } else
+ if (!strcasecmp(result, "RING")) {
+ return aopen_handle_escape(p, 'R');
+ } else
if (!strcasecmp(result, "NO DIALTONE")) {
/* There's no dialtone, so the line isn't working */
ast_log(LOG_WARNING, "Device '%s' lacking dialtone\n", p->dev);
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
new file mode 100755
index 000000000..caf2403c1
--- /dev/null
+++ b/channels/chan_oss.c
@@ -0,0 +1,791 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Use /dev/dsp as a channel, and the console to command it :).
+ *
+ * The full-duplex "simulation" is pretty weak. This is generally a
+ * VERY BADLY WRITTEN DRIVER so please don't use it as a model for
+ * writing a driver.
+ *
+ * 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 <asterisk/frame.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/module.h>
+#include <asterisk/channel_pvt.h>
+#include <asterisk/options.h>
+#include <asterisk/pbx.h>
+#include <asterisk/config.h>
+#include <asterisk/cli.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <linux/soundcard.h>
+
+/* Which device to use */
+#define DEV_DSP "/dev/dsp"
+
+/* Lets use 160 sample frames, just like GSM. */
+#define FRAME_SIZE 160
+
+/* When you set the frame size, you have to come up with
+ the right buffer format as well. */
+/* 5 64-byte frames = one frame */
+#define BUFFER_FMT ((buffersize * 5) << 16) | (0x0006);
+
+/* Don't switch between read/write modes faster than every 300 ms */
+#define MIN_SWITCH_TIME 600
+
+static struct timeval lasttime;
+
+static int usecnt;
+static int needanswer = 0;
+static int needhangup = 0;
+static int silencesuppression = 0;
+static int silencethreshold = 1000;
+
+static char digits[80] = "";
+
+static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static char *type = "Console";
+static char *desc = "OSS Console Channel Driver";
+static char *tdesc = "OSS Console Channel Driver";
+static char *config = "oss.conf";
+
+static char context[AST_MAX_EXTENSION] = "default";
+static char exten[AST_MAX_EXTENSION] = "s";
+
+/* Some pipes to prevent overflow */
+static int funnel[2];
+static pthread_mutex_t sound_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t silly;
+
+static struct chan_oss_pvt {
+ /* We only have one OSS structure -- near sighted perhaps, but it
+ keeps this driver as simple as possible -- as it should be. */
+ struct ast_channel *owner;
+ char exten[AST_MAX_EXTENSION];
+ char context[AST_MAX_EXTENSION];
+} oss;
+
+static int time_has_passed()
+{
+ struct timeval tv;
+ int ms;
+ gettimeofday(&tv, NULL);
+ ms = (tv.tv_sec - lasttime.tv_sec) * 1000 +
+ (tv.tv_usec - lasttime.tv_usec) / 1000;
+ if (ms > MIN_SWITCH_TIME)
+ return -1;
+ return 0;
+}
+
+/* Number of buffers... Each is FRAMESIZE/8 ms long. For example
+ with 160 sample frames, and a buffer size of 3, we have a 60ms buffer,
+ usually plenty. */
+
+
+#define MAX_BUFFER_SIZE 100
+static int buffersize = 3;
+
+static int full_duplex = 0;
+
+/* Are we reading or writing (simulated full duplex) */
+static int readmode = 1;
+
+/* File descriptor for sound device */
+static int sounddev = -1;
+
+static int autoanswer = 1;
+
+static int calc_loudness(short *frame)
+{
+ int sum = 0;
+ int x;
+ for (x=0;x<FRAME_SIZE;x++) {
+ if (frame[x] < 0)
+ sum -= frame[x];
+ else
+ sum += frame[x];
+ }
+ sum = sum/FRAME_SIZE;
+ return sum;
+}
+
+static int silence_suppress(short *buf)
+{
+#define SILBUF 3
+ int loudness;
+ static int silentframes = 0;
+ static char silbuf[FRAME_SIZE * 2 * SILBUF];
+ static int silbufcnt=0;
+ if (!silencesuppression)
+ return 0;
+ loudness = calc_loudness((short *)(buf));
+ if (option_debug)
+ ast_log(LOG_DEBUG, "loudness is %d\n", loudness);
+ if (loudness < silencethreshold) {
+ silentframes++;
+ silbufcnt++;
+ /* Keep track of the last few bits of silence so we can play
+ them as lead-in when the time is right */
+ if (silbufcnt >= SILBUF) {
+ /* Make way for more buffer */
+ memmove(silbuf, silbuf + FRAME_SIZE * 2, FRAME_SIZE * 2 * (SILBUF - 1));
+ silbufcnt--;
+ }
+ memcpy(silbuf + FRAME_SIZE * 2 * silbufcnt, buf, FRAME_SIZE * 2);
+ if (silentframes > 10) {
+ /* We've had plenty of silence, so compress it now */
+ return 1;
+ }
+ } else {
+ silentframes=0;
+ /* Write any buffered silence we have, it may have something
+ important */
+ if (silbufcnt) {
+ write(funnel[1], silbuf, silbufcnt * FRAME_SIZE);
+ silbufcnt = 0;
+ }
+ }
+ return 0;
+}
+
+static void *silly_thread(void *ignore)
+{
+ char buf[FRAME_SIZE * 2];
+ int pos=0;
+ int res=0;
+ /* Read from the sound device, and write to the pipe. */
+ for (;;) {
+ /* Give the writer a better shot at the lock */
+#if 0
+ usleep(1000);
+#endif
+ pthread_testcancel();
+ pthread_mutex_lock(&sound_lock);
+ res = read(sounddev, buf + pos, FRAME_SIZE * 2 - pos);
+ pthread_mutex_unlock(&sound_lock);
+ if (res > 0) {
+ pos += res;
+ if (pos == FRAME_SIZE * 2) {
+ if (needhangup || needanswer || strlen(digits) ||
+ !silence_suppress((short *)buf)) {
+ res = write(funnel[1], buf, sizeof(buf));
+ }
+ pos = 0;
+ }
+ } else {
+ close(funnel[1]);
+ break;
+ }
+ pthread_testcancel();
+ }
+ return NULL;
+}
+
+static int setformat(void)
+{
+ int fmt, desired, res, fd = sounddev;
+ static int warnedalready = 0;
+ static int warnedalready2 = 0;
+ pthread_mutex_lock(&sound_lock);
+ fmt = AFMT_S16_LE;
+ res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+ if (res >= 0) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n");
+ full_duplex = -1;
+ }
+ fmt = 0;
+ res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ /* 8000 Hz desired */
+ desired = 8000;
+ fmt = desired;
+ res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ if (fmt != desired) {
+ if (!warnedalready++)
+ ast_log(LOG_WARNING, "Requested %d Hz, got %d Hz -- sound may be choppy\n", desired, fmt);
+ }
+#if 1
+ fmt = BUFFER_FMT;
+ res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
+ if (res < 0) {
+ if (!warnedalready2++)
+ ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n");
+ }
+#endif
+ pthread_mutex_unlock(&sound_lock);
+ return 0;
+}
+
+static int soundcard_setoutput(int force)
+{
+ /* Make sure the soundcard is in output mode. */
+ int fd = sounddev;
+ if (full_duplex || (!readmode && !force))
+ return 0;
+ pthread_mutex_lock(&sound_lock);
+ readmode = 0;
+ if (force || time_has_passed()) {
+ ioctl(sounddev, SNDCTL_DSP_RESET);
+ /* Keep the same fd reserved by closing the sound device and copying stdin at the same
+ time. */
+ /* dup2(0, sound); */
+ close(sounddev);
+ fd = open(DEV_DSP, O_WRONLY);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to re-open DSP device: %s\n", strerror(errno));
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ /* dup2 will close the original and make fd be sound */
+ if (dup2(fd, sounddev) < 0) {
+ ast_log(LOG_WARNING, "dup2() failed: %s\n", strerror(errno));
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ if (setformat()) {
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ pthread_mutex_unlock(&sound_lock);
+ return 0;
+ }
+ pthread_mutex_unlock(&sound_lock);
+ return 1;
+}
+
+static int soundcard_setinput(int force)
+{
+ int fd = sounddev;
+ if (full_duplex || (readmode && !force))
+ return 0;
+ pthread_mutex_lock(&sound_lock);
+ readmode = -1;
+ if (force || time_has_passed()) {
+ ioctl(sounddev, SNDCTL_DSP_RESET);
+ close(sounddev);
+ /* dup2(0, sound); */
+ fd = open(DEV_DSP, O_RDONLY);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to re-open DSP device: %s\n", strerror(errno));
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ /* dup2 will close the original and make fd be sound */
+ if (dup2(fd, sounddev) < 0) {
+ ast_log(LOG_WARNING, "dup2() failed: %s\n", strerror(errno));
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ if (setformat()) {
+ pthread_mutex_unlock(&sound_lock);
+ return -1;
+ }
+ pthread_mutex_unlock(&sound_lock);
+ return 0;
+ }
+ pthread_mutex_unlock(&sound_lock);
+ return 1;
+}
+
+static int soundcard_init()
+{
+ /* Assume it's full duplex for starters */
+ int fd = open(DEV_DSP, O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_ERROR, "Unable to open %s: %s\n", DEV_DSP, strerror(errno));
+ return fd;
+ }
+ gettimeofday(&lasttime, NULL);
+ sounddev = fd;
+ setformat();
+ if (!full_duplex)
+ soundcard_setinput(1);
+ return sounddev;
+}
+
+static int oss_digit(struct ast_channel *c, char digit)
+{
+ ast_verbose( " << Console Received digit %c >> \n", digit);
+ return 0;
+}
+
+static int oss_call(struct ast_channel *c, char *dest, int timeout)
+{
+ ast_verbose( " << Call placed to '%s' on console >> \n", dest);
+ if (autoanswer) {
+ ast_verbose( " << Auto-answered >> \n" );
+ needanswer = 1;
+ } else {
+ ast_verbose( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
+ }
+ return 0;
+}
+
+static int oss_answer(struct ast_channel *c)
+{
+ ast_verbose( " << Console call has been answered >> \n");
+ c->state = AST_STATE_UP;
+ return 0;
+}
+
+static int oss_hangup(struct ast_channel *c)
+{
+ c->pvt->pvt = NULL;
+ oss.owner = NULL;
+ ast_verbose( " << Hangup on console >> \n");
+ pthread_mutex_lock(&usecnt_lock);
+ usecnt--;
+ pthread_mutex_unlock(&usecnt_lock);
+ needhangup = 0;
+ needanswer = 0;
+ return 0;
+}
+
+static int soundcard_writeframe(short *data)
+{
+ /* Write an exactly FRAME_SIZE sized of frame */
+ static int bufcnt = 0;
+ static char buffer[FRAME_SIZE * 2 * MAX_BUFFER_SIZE * 5];
+ struct audio_buf_info info;
+ int res;
+ int fd = sounddev;
+ static int warned=0;
+ pthread_mutex_lock(&sound_lock);
+ if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)) {
+ if (!warned)
+ ast_log(LOG_WARNING, "Error reading output space\n");
+ bufcnt = buffersize;
+ warned++;
+ }
+ if ((info.fragments >= buffersize * 5) && (bufcnt == buffersize)) {
+ /* We've run out of stuff, buffer again */
+ bufcnt = 0;
+ }
+ if (bufcnt == buffersize) {
+ /* Write sample immediately */
+ res = write(fd, ((void *)data), FRAME_SIZE * 2);
+ } else {
+ /* Copy the data into our buffer */
+ res = FRAME_SIZE * 2;
+ memcpy(buffer + (bufcnt * FRAME_SIZE * 2), data, FRAME_SIZE * 2);
+ bufcnt++;
+ if (bufcnt == buffersize) {
+ res = write(fd, ((void *)buffer), FRAME_SIZE * 2 * buffersize);
+ }
+ }
+ pthread_mutex_unlock(&sound_lock);
+ return res;
+}
+
+
+static int oss_write(struct ast_channel *chan, struct ast_frame *f)
+{
+ int res;
+ static char sizbuf[8000];
+ static int sizpos = 0;
+ int len = sizpos;
+ int pos;
+ if (!full_duplex && (strlen(digits) || needhangup || needanswer)) {
+ /* If we're half duplex, we have to switch to read mode
+ to honor immediate needs if necessary */
+ res = soundcard_setinput(1);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set device to input mode\n");
+ return -1;
+ }
+ return 0;
+ }
+ res = soundcard_setoutput(0);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set output device\n");
+ return -1;
+ } else if (res > 0) {
+ /* The device is still in read mode, and it's too soon to change it,
+ so just pretend we wrote it */
+ return 0;
+ }
+ /* We have to digest the frame in 160-byte portions */
+ if (f->datalen > sizeof(sizbuf) - sizpos) {
+ ast_log(LOG_WARNING, "Frame too large\n");
+ return -1;
+ }
+ memcpy(sizbuf + sizpos, f->data, f->datalen);
+ len += f->datalen;
+ pos = 0;
+ while(len - pos > FRAME_SIZE * 2) {
+ soundcard_writeframe((short *)(sizbuf + pos));
+ pos += FRAME_SIZE * 2;
+ }
+ if (len - pos)
+ memmove(sizbuf, sizbuf + pos, len - pos);
+ sizpos = len - pos;
+ return 0;
+}
+
+static struct ast_frame *oss_read(struct ast_channel *chan)
+{
+ static struct ast_frame f;
+ static char buf[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];
+ static int readpos = 0;
+ int res;
+
+#if 0
+ ast_log(LOG_DEBUG, "oss_read()\n");
+#endif
+
+ f.frametype = AST_FRAME_NULL;
+ f.subclass = 0;
+ f.timelen = 0;
+ f.datalen = 0;
+ f.data = NULL;
+ f.offset = 0;
+ f.src = type;
+ f.mallocd = 0;
+
+ if (needhangup) {
+ return NULL;
+ }
+ if (strlen(digits)) {
+ f.frametype = AST_FRAME_DTMF;
+ f.subclass = digits[0];
+ for (res=0;res<strlen(digits);res++)
+ digits[res] = digits[res + 1];
+ return &f;
+ }
+
+ if (needanswer) {
+ needanswer = 0;
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ chan->state = AST_STATE_UP;
+ return &f;
+ }
+
+ res = soundcard_setinput(0);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set input mode\n");
+ return NULL;
+ }
+ if (res > 0) {
+ /* Theoretically shouldn't happen, but anyway, return a NULL frame */
+ return &f;
+ }
+ res = read(funnel[0], buf + AST_FRIENDLY_OFFSET + readpos, FRAME_SIZE * 2 - readpos);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Error reading from sound device: %s\n", strerror(errno));
+ return NULL;
+ }
+ readpos += res;
+
+ if (readpos == FRAME_SIZE * 2) {
+ /* A real frame */
+ readpos = 0;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.timelen = FRAME_SIZE / 8;
+ f.datalen = FRAME_SIZE * 2;
+ f.data = buf + AST_FRIENDLY_OFFSET;
+ f.offset = AST_FRIENDLY_OFFSET;
+ f.src = type;
+ f.mallocd = 0;
+ }
+ return &f;
+}
+
+static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state)
+{
+ struct ast_channel *tmp;
+ tmp = ast_channel_alloc();
+ if (tmp) {
+ snprintf(tmp->name, sizeof(tmp->name), "OSS/%s", DEV_DSP + 5);
+ tmp->type = type;
+ tmp->fd = funnel[0];
+ tmp->format = AST_FORMAT_SLINEAR;
+ tmp->pvt->pvt = p;
+ tmp->pvt->send_digit = oss_digit;
+ tmp->pvt->hangup = oss_hangup;
+ tmp->pvt->answer = oss_answer;
+ tmp->pvt->read = oss_read;
+ tmp->pvt->write = oss_write;
+ if (strlen(p->context))
+ strncpy(tmp->context, p->context, sizeof(tmp->context));
+ if (strlen(p->exten))
+ strncpy(tmp->exten, p->exten, sizeof(tmp->exten));
+ p->owner = tmp;
+ tmp->state = state;
+ pthread_mutex_lock(&usecnt_lock);
+ usecnt++;
+ pthread_mutex_unlock(&usecnt_lock);
+ ast_update_use_count();
+ 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;
+ }
+ }
+ }
+ return tmp;
+}
+
+static struct ast_channel *oss_request(char *type, int format, void *data)
+{
+ int oldformat = format;
+ format &= AST_FORMAT_SLINEAR;
+ if (!format) {
+ ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
+ return NULL;
+ }
+ if (oss.owner) {
+ ast_log(LOG_NOTICE, "Already have a call on the OSS channel\n");
+ return NULL;
+ }
+ return oss_new(&oss, AST_STATE_DOWN);
+}
+
+static int console_autoanswer(int fd, int argc, char *argv[])
+{
+ if ((argc != 1) && (argc != 2))
+ return RESULT_SHOWUSAGE;
+ if (argc == 1) {
+ ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
+ return RESULT_SUCCESS;
+ } else {
+ if (!strcasecmp(argv[1], "on"))
+ autoanswer = -1;
+ else if (!strcasecmp(argv[1], "off"))
+ autoanswer = 0;
+ else
+ return RESULT_SHOWUSAGE;
+ }
+ return RESULT_SUCCESS;
+}
+
+static char *autoanswer_complete(char *line, char *word, int pos, int state)
+{
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+ switch(state) {
+ case 0:
+ if (strlen(word) && !strncasecmp(word, "on", MIN(strlen(word), 2)))
+ return strdup("on");
+ case 1:
+ if (strlen(word) && !strncasecmp(word, "off", MIN(strlen(word), 3)))
+ return strdup("off");
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static char autoanswer_usage[] =
+"Usage: autoanswer [on|off]\n"
+" Enables or disables autoanswer feature. If used without\n"
+" argument, displays the current on/off status of autoanswer.\n"
+" The default value of autoanswer is in 'oss.conf'.\n";
+
+static int console_answer(int fd, int argc, char *argv[])
+{
+ if (argc != 1)
+ return RESULT_SHOWUSAGE;
+ if (!oss.owner) {
+ ast_cli(fd, "No one is calling us\n");
+ return RESULT_FAILURE;
+ }
+ needanswer++;
+ return RESULT_SUCCESS;
+}
+
+static char answer_usage[] =
+"Usage: answer\n"
+" Answers an incoming call on the console (OSS) channel.\n";
+
+static int console_hangup(int fd, int argc, char *argv[])
+{
+ if (argc != 1)
+ return RESULT_SHOWUSAGE;
+ if (!oss.owner) {
+ ast_cli(fd, "No call to hangup up\n");
+ return RESULT_FAILURE;
+ }
+ needhangup++;
+ return RESULT_SUCCESS;
+}
+
+static char hangup_usage[] =
+"Usage: hangup\n"
+" Hangs up any call currently placed on the console.\n";
+
+
+static int console_dial(int fd, int argc, char *argv[])
+{
+ char tmp[256], *tmp2;
+ char *mye, *myc;
+ if ((argc != 1) && (argc != 2))
+ return RESULT_SHOWUSAGE;
+ if (oss.owner) {
+ if (argc == 2)
+ strncat(digits, argv[1], sizeof(digits) - strlen(digits));
+ else {
+ ast_cli(fd, "You're already in a call. You can use this only to dial digits until you hangup\n");
+ return RESULT_FAILURE;
+ }
+ return RESULT_SUCCESS;
+ }
+ mye = exten;
+ myc = context;
+ if (argc == 2) {
+ strncpy(tmp, argv[1], sizeof(tmp));
+ strtok(tmp, "@");
+ tmp2 = strtok(NULL, "@");
+ if (strlen(tmp))
+ mye = tmp;
+ if (tmp2 && strlen(tmp2))
+ myc = tmp2;
+ }
+ if (ast_exists_extension(NULL, myc, mye, 1)) {
+ strncpy(oss.exten, mye, sizeof(oss.exten));
+ strncpy(oss.context, myc, sizeof(oss.context));
+ oss_new(&oss, AST_STATE_UP);
+ } else
+ ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
+ return RESULT_SUCCESS;
+}
+
+static char dial_usage[] =
+"Usage: dial [extension[@context]]\n"
+" Dials a given extensison (";
+
+
+static struct ast_cli_entry myclis[] = {
+ { { "answer", NULL }, console_answer, "Answer an incoming console call", answer_usage },
+ { { "hangup", NULL }, console_hangup, "Hangup a call on the console", hangup_usage },
+ { { "dial", NULL }, console_dial, "Dial an extension on the console", dial_usage },
+ { { "autoanswer", NULL }, console_autoanswer, "Sets/displays autoanswer", autoanswer_usage, autoanswer_complete }
+};
+
+int load_module()
+{
+ int res;
+ int x;
+ int flags;
+ struct ast_config *cfg = ast_load(config);
+ struct ast_variable *v;
+ res = pipe(funnel);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to create pipe\n");
+ return -1;
+ }
+ /* We make the funnel so that writes to the funnel don't block...
+ Our "silly" thread can read to its heart content, preventing
+ recording overruns */
+ flags = fcntl(funnel[1], F_GETFL);
+#if 0
+ fcntl(funnel[0], F_SETFL, flags | O_NONBLOCK);
+#endif
+ fcntl(funnel[1], F_SETFL, flags | O_NONBLOCK);
+ res = soundcard_init();
+ if (res < 0) {
+ close(funnel[1]);
+ close(funnel[0]);
+ return -1;
+ }
+ if (!full_duplex)
+ ast_log(LOG_WARNING, "XXX I don't work right with non-full duplex sound cards XXX\n");
+ pthread_create(&silly, NULL, silly_thread, NULL);
+ res = ast_channel_register(type, tdesc, AST_FORMAT_SLINEAR, oss_request);
+ if (res < 0) {
+ ast_log(LOG_ERROR, "Unable to register channel class '%s'\n", type);
+ return -1;
+ }
+ for (x=0;x<sizeof(myclis)/sizeof(struct ast_cli_entry); x++)
+ ast_cli_register(myclis + x);
+ if (cfg) {
+ v = ast_variable_browse(cfg, "general");
+ while(v) {
+ if (!strcasecmp(v->name, "autoanswer"))
+ autoanswer = ast_true(v->value);
+ else if (!strcasecmp(v->name, "silencesuppression"))
+ silencesuppression = ast_true(v->value);
+ else if (!strcasecmp(v->name, "silencethreshold"))
+ silencethreshold = atoi(v->value);
+ else if (!strcasecmp(v->name, "context"))
+ strncpy(context, v->value, sizeof(context));
+ else if (!strcasecmp(v->name, "extension"))
+ strncpy(exten, v->value, sizeof(exten));
+ v=v->next;
+ }
+ ast_destroy(cfg);
+ }
+ return 0;
+}
+
+
+
+int unload_module()
+{
+ int x;
+ for (x=0;x<sizeof(myclis)/sizeof(struct ast_cli_entry); x++)
+ ast_cli_unregister(myclis + x);
+ close(sounddev);
+ if (funnel[0] > 0) {
+ close(funnel[0]);
+ close(funnel[1]);
+ }
+ if (silly) {
+ pthread_cancel(silly);
+ pthread_join(silly, NULL);
+ }
+ if (oss.owner)
+ ast_softhangup(oss.owner);
+ if (oss.owner)
+ return -1;
+ return 0;
+}
+
+char *description()
+{
+ return desc;
+}
+
+int usecount()
+{
+ int res;
+ pthread_mutex_lock(&usecnt_lock);
+ res = usecnt;
+ pthread_mutex_unlock(&usecnt_lock);
+ return res;
+}
diff --git a/channels/chan_ixj.c b/channels/chan_phone.c
index c8cdb91dd..26826d233 100755
--- a/channels/chan_ixj.c
+++ b/channels/chan_phone.c
@@ -1,7 +1,7 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
- * QuickNet Internet Phone Jack Channel
+ * Generic Linux Telephony Interface driver
*
* Copyright (C) 1999, Mark Spencer
*
@@ -29,27 +29,32 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <linux/if_packet.h>
-#include <linux/if_ether.h>
-#include "ixjuser.h"
+#include <linux/telephony.h>
+/* Still use some IXJ specific stuff */
+#include <linux/ixjuser.h>
#include "DialTone.h"
-#define IXJ_MAX_BUF 480
+#define phone_MAX_BUF 480
-static char *desc = "QuickNet Internet Phone Jack";
-static char *type = "PhoneJack";
-static char *tdesc = "QuickNet Internet Phone Jack";
-static char *config = "ixj.conf";
+static char *desc = "Linux Telephony API Support";
+static char *type = "Phone";
+static char *tdesc = "Standard Linux Telephony API Driver";
+static char *config = "phone.conf";
/* Default context for dialtone mode */
static char context[AST_MAX_EXTENSION] = "default";
-char *ignore_rcs_id_for_chan_ixj = ixjuser_h_rcsid;
-
static int usecnt =0;
+
+static int echocancel = AEC_OFF;
+
+static int silencesupression = 0;
+
+static int prefformat = AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR;
+
static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
-/* Protect the interface list (of ixj_pvt's) */
+/* Protect the interface list (of phone_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
@@ -68,7 +73,7 @@ static int restart_monitor(void);
#define MODE_DIALTONE 1
#define MODE_IMMEDIATE 2
-static struct ixj_pvt {
+static struct phone_pvt {
int fd; /* Raw file descriptor for this device */
struct ast_channel *owner; /* Channel we belong to, possibly NULL */
int mode; /* Is this in the */
@@ -76,20 +81,21 @@ static struct ixj_pvt {
int lastinput; /* Last input format */
int ministate; /* Miniature state, for dialtone mode */
char dev[256]; /* Device name */
- struct ixj_pvt *next; /* Next channel in list */
+ struct phone_pvt *next; /* Next channel in list */
struct ast_frame fr; /* Frame */
char offset[AST_FRIENDLY_OFFSET];
- char buf[IXJ_MAX_BUF]; /* Static buffer for reading frames */
+ char buf[phone_MAX_BUF]; /* Static buffer for reading frames */
int obuflen;
int dialtone;
+ int silencesupression;
char context[AST_MAX_EXTENSION];
- char obuf[IXJ_MAX_BUF * 2];
+ char obuf[phone_MAX_BUF * 2];
char ext[AST_MAX_EXTENSION];
} *iflist = NULL;
-static int ixj_digit(struct ast_channel *ast, char digit)
+static int phone_digit(struct ast_channel *ast, char digit)
{
- struct ixj_pvt *p;
+ struct phone_pvt *p;
int outdigit;
p = ast->pvt->pvt;
switch(digit) {
@@ -115,57 +121,60 @@ static int ixj_digit(struct ast_channel *ast, char digit)
ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
return -1;
}
- ioctl(p->fd, IXJCTL_PLAY_TONE, digit);
+ ioctl(p->fd, PHONE_PLAY_TONE, digit);
return 0;
}
-static int ixj_call(struct ast_channel *ast, char *dest, int timeout)
+static int phone_call(struct ast_channel *ast, char *dest, int timeout)
{
- struct ixj_pvt *p;
+ struct phone_pvt *p;
p = ast->pvt->pvt;
if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
- ast_log(LOG_WARNING, "ixj_call called on %s, neither down nor reserved\n", ast->name);
+ ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
/* When we call, it just works, really, there's no destination... Just
ring the phone and wait for someone to answer */
if (option_debug)
ast_log(LOG_DEBUG, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fd);
- ioctl(p->fd, IXJCTL_RING_START);
+ ioctl(p->fd, PHONE_RING_START);
ast->state = AST_STATE_RINGING;
return 0;
}
-static int ixj_hangup(struct ast_channel *ast)
+static int phone_hangup(struct ast_channel *ast)
{
- struct ixj_pvt *p;
+ struct phone_pvt *p;
p = ast->pvt->pvt;
if (option_debug)
- ast_log(LOG_DEBUG, "ixj_hangup(%s)\n", ast->name);
+ ast_log(LOG_DEBUG, "phone_hangup(%s)\n", ast->name);
if (!ast->pvt->pvt) {
ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
return 0;
}
/* XXX Is there anything we can do to really hang up except stop recording? */
ast->state = AST_STATE_DOWN;
- if (ioctl(p->fd, IXJCTL_REC_STOP))
+ if (ioctl(p->fd, PHONE_REC_STOP))
ast_log(LOG_WARNING, "Failed to stop recording\n");
- if (ioctl(p->fd, IXJCTL_PLAY_STOP))
+ if (ioctl(p->fd, PHONE_PLAY_STOP))
ast_log(LOG_WARNING, "Failed to stop playing\n");
- if (ioctl(p->fd, IXJCTL_RING_STOP))
+ if (ioctl(p->fd, PHONE_RING_STOP))
ast_log(LOG_WARNING, "Failed to stop ringing\n");
- if (ioctl(p->fd, IXJCTL_CPT_STOP))
+ if (ioctl(p->fd, PHONE_CPT_STOP))
ast_log(LOG_WARNING, "Failed to stop sounds\n");
/* If they're off hook, give a busy signal */
- if (ioctl(p->fd, IXJCTL_HOOKSTATE))
- ioctl(p->fd, IXJCTL_BUSY);
+ if (ioctl(p->fd, PHONE_HOOKSTATE)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Got hunghup, giving busy signal\n");
+ ioctl(p->fd, PHONE_BUSY);
+ }
p->lastformat = -1;
p->lastinput = -1;
p->ministate = 0;
p->obuflen = 0;
p->dialtone = 0;
memset(p->ext, 0, sizeof(p->ext));
- ((struct ixj_pvt *)(ast->pvt->pvt))->owner = NULL;
+ ((struct phone_pvt *)(ast->pvt->pvt))->owner = NULL;
pthread_mutex_lock(&usecnt_lock);
usecnt--;
if (usecnt < 0)
@@ -180,27 +189,27 @@ static int ixj_hangup(struct ast_channel *ast)
return 0;
}
-static int ixj_setup(struct ast_channel *ast)
+static int phone_setup(struct ast_channel *ast)
{
- struct ixj_pvt *p;
+ struct phone_pvt *p;
p = ast->pvt->pvt;
- ioctl(p->fd, IXJCTL_CPT_STOP);
+ ioctl(p->fd, PHONE_CPT_STOP);
/* Nothing to answering really, just start recording */
if (ast->format & AST_FORMAT_G723_1) {
/* Prefer g723 */
- ioctl(p->fd, IXJCTL_REC_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
if (p->lastinput != AST_FORMAT_G723_1) {
p->lastinput = AST_FORMAT_G723_1;
- if (ioctl(p->fd, IXJCTL_REC_CODEC, G723_63)) {
+ if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
ast_log(LOG_WARNING, "Failed to set codec to g723.1\n");
return -1;
}
}
} else if (ast->format & AST_FORMAT_SLINEAR) {
- ioctl(p->fd, IXJCTL_REC_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
if (p->lastinput != AST_FORMAT_SLINEAR) {
p->lastinput = AST_FORMAT_SLINEAR;
- if (ioctl(p->fd, IXJCTL_REC_CODEC, LINEAR16)) {
+ if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
ast_log(LOG_WARNING, "Failed to set codec to signed linear 16\n");
return -1;
}
@@ -209,24 +218,24 @@ static int ixj_setup(struct ast_channel *ast)
ast_log(LOG_WARNING, "Can't do format %d\n", ast->format);
return -1;
}
- if (ioctl(p->fd, IXJCTL_REC_START)) {
+ if (ioctl(p->fd, PHONE_REC_START)) {
ast_log(LOG_WARNING, "Failed to start recording\n");
return -1;
}
return 0;
}
-static int ixj_answer(struct ast_channel *ast)
+static int phone_answer(struct ast_channel *ast)
{
- ixj_setup(ast);
+ phone_setup(ast);
if (option_debug)
- ast_log(LOG_DEBUG, "ixj_answer(%s)\n", ast->name);
+ ast_log(LOG_DEBUG, "phone_answer(%s)\n", ast->name);
ast->rings = 0;
ast->state = AST_STATE_UP;
return 0;
}
-static char ixj_2digit(char c)
+static char phone_2digit(char c)
{
if (c == 12)
return '#';
@@ -238,11 +247,11 @@ static char ixj_2digit(char c)
return '?';
}
-static struct ast_frame *ixj_read(struct ast_channel *ast)
+static struct ast_frame *phone_read(struct ast_channel *ast)
{
int res;
- IXJ_EXCEPTION ixje;
- struct ixj_pvt *p = ast->pvt->pvt;
+ union telephony_exception phonee;
+ struct phone_pvt *p = ast->pvt->pvt;
char digit;
/* Some nice norms */
@@ -253,16 +262,16 @@ static struct ast_frame *ixj_read(struct ast_channel *ast)
p->fr.offset = 0;
p->fr.mallocd=0;
- ixje.bytes = ioctl(p->fd, IXJCTL_EXCEPTION);
- if (ixje.bits.dtmf_ready) {
+ phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION);
+ if (phonee.bits.dtmf_ready) {
/* We've got a digit -- Just handle this nicely and easily */
- digit = ioctl(p->fd, IXJCTL_GET_DTMF_ASCII);
+ digit = ioctl(p->fd, PHONE_GET_DTMF_ASCII);
p->fr.subclass = digit;
p->fr.frametype = AST_FRAME_DTMF;
return &p->fr;
}
- if (ixje.bits.hookstate) {
- res = ioctl(p->fd, IXJCTL_HOOKSTATE);
+ if (phonee.bits.hookstate) {
+ res = ioctl(p->fd, PHONE_HOOKSTATE);
/* See if we've gone on hook, if so, notify by returning NULL */
if (!res)
return NULL;
@@ -271,23 +280,23 @@ static struct ast_frame *ixj_read(struct ast_channel *ast)
/* They've picked up the phone */
p->fr.frametype = AST_FRAME_CONTROL;
p->fr.subclass = AST_CONTROL_ANSWER;
- ixj_setup(ast);
+ phone_setup(ast);
ast->state = AST_STATE_UP;
return &p->fr;
} else
- ast_log(LOG_WARNING, "Got off hook in weird state\n");
+ ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->state);
}
}
#if 0
- if (ixje.bits.pstn_ring)
+ if (phonee.bits.pstn_ring)
ast_verbose("Unit is ringing\n");
- if (ixje.bits.caller_id) {
+ if (phonee.bits.caller_id) {
ast_verbose("We have caller ID: %s\n");
}
#endif
/* Try to read some data... */
CHECK_BLOCKING(ast);
- res = read(p->fd, p->buf, IXJ_MAX_BUF);
+ res = read(p->fd, p->buf, phone_MAX_BUF);
ast->blocking = 0;
if (res < 0) {
#if 0
@@ -302,6 +311,17 @@ static struct ast_frame *ixj_read(struct ast_channel *ast)
return NULL;
}
p->fr.data = p->buf;
+ switch(p->buf[0] & 0x3) {
+ case '0':
+ case '1':
+ /* Normal */
+ break;
+ case '2':
+ case '3':
+ /* VAD/CNG, only send two words */
+ res = 4;
+ break;
+ }
p->fr.datalen = res;
p->fr.frametype = AST_FRAME_VOICE;
p->fr.subclass = p->lastinput;
@@ -309,7 +329,7 @@ static struct ast_frame *ixj_read(struct ast_channel *ast)
return &p->fr;
}
-static int ixj_write_buf(struct ixj_pvt *p, char *buf, int len, int frlen)
+static int phone_write_buf(struct phone_pvt *p, char *buf, int len, int frlen)
{
int res;
/* Store as much of the buffer as we can, then write fixed frames */
@@ -342,14 +362,15 @@ static int ixj_write_buf(struct ixj_pvt *p, char *buf, int len, int frlen)
return len;
}
-static int ixj_write(struct ast_channel *ast, struct ast_frame *frame)
+static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
{
- struct ixj_pvt *p = ast->pvt->pvt;
+ struct phone_pvt *p = ast->pvt->pvt;
int res;
int maxfr=0;
char *pos;
int sofar;
int expected;
+ char tmpbuf[4];
/* 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);
@@ -361,14 +382,25 @@ static int ixj_write(struct ast_channel *ast, struct ast_frame *frame)
ast_frfree(frame);
return -1;
}
+ /* If we're not in up mode, go into up mode now */
+ if (ast->state != AST_STATE_UP) {
+ ast->state = AST_STATE_UP;
+ phone_setup(ast);
+ }
if (frame->subclass == AST_FORMAT_G723_1) {
if (p->lastformat != AST_FORMAT_G723_1) {
- ioctl(p->fd, IXJCTL_PLAY_STOP);
- if (ioctl(p->fd, IXJCTL_PLAY_CODEC, G723_63)) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, G723_63)) {
+ ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
return -1;
}
p->lastformat = AST_FORMAT_G723_1;
+ p->lastinput = AST_FORMAT_G723_1;
/* Reset output buffer */
p->obuflen = 0;
}
@@ -379,18 +411,28 @@ static int ixj_write(struct ast_channel *ast, struct ast_frame *frame)
maxfr = 24;
} else if (frame->subclass == AST_FORMAT_SLINEAR) {
if (p->lastformat != AST_FORMAT_SLINEAR) {
- ioctl(p->fd, IXJCTL_PLAY_STOP);
- if (ioctl(p->fd, IXJCTL_PLAY_CODEC, LINEAR16)) {
+ ioctl(p->fd, PHONE_PLAY_STOP);
+ ioctl(p->fd, PHONE_REC_STOP);
+ if (ioctl(p->fd, PHONE_PLAY_CODEC, LINEAR16)) {
+ ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
return -1;
}
p->lastformat = AST_FORMAT_SLINEAR;
+ p->lastinput = AST_FORMAT_SLINEAR;
/* Reset output buffer */
p->obuflen = 0;
}
maxfr = 480;
}
- if (ioctl(p->fd, IXJCTL_PLAY_START)) {
+ if (ioctl(p->fd, PHONE_PLAY_START)) {
+ ast_log(LOG_WARNING, "Failed to start playback\n");
+ return -1;
+ }
+ if (ioctl(p->fd, PHONE_REC_START)) {
ast_log(LOG_WARNING, "Failed to start recording\n");
return -1;
}
@@ -402,45 +444,54 @@ static int ixj_write(struct ast_channel *ast, struct ast_frame *frame)
expected = frame->datalen - sofar;
if (maxfr < expected)
expected = maxfr;
- /* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX */
- if (frame->datalen != 4) {
- res = ixj_write_buf(p, pos, expected, maxfr);
- if (res != expected) {
- if (res < 0)
- ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
- else
- ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
- return -1;
+ /* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX
+ we have to pad it to 24 bytes still. */
+ if (frame->datalen == 4) {
+ if (p->silencesupression) {
+ memset(tmpbuf + 4, 0, sizeof(tmpbuf) - 4);
+ memcpy(tmpbuf, frame->data, 4);
+ expected = 24;
+ res = phone_write_buf(p, tmpbuf, expected, maxfr);
}
- sofar += res;
- pos += res;
- } else
- sofar += 4;
+ res = 4;
+ expected=4;
+ } else {
+ res = phone_write_buf(p, pos, expected, maxfr);
+ }
+ if (res != expected) {
+ if (res < 0)
+ ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
+ else
+ ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
+ return -1;
+ }
+ sofar += res;
+ pos += res;
}
return 0;
}
-static struct ast_channel *ixj_new(struct ixj_pvt *i, int state)
+static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *context)
{
struct ast_channel *tmp;
tmp = ast_channel_alloc();
if (tmp) {
- snprintf(tmp->name, sizeof(tmp->name), "PhoneJack/%s", i->dev + 5);
+ snprintf(tmp->name, sizeof(tmp->name), "Phone/%s", i->dev + 5);
tmp->type = type;
tmp->fd = i->fd;
/* XXX Switching formats silently causes kernel panics XXX */
- tmp->format = AST_FORMAT_G723_1 /* | AST_FORMAT_SLINEAR */;
+ tmp->format = prefformat;
tmp->state = state;
if (state == AST_STATE_RING)
tmp->rings = 1;
tmp->pvt->pvt = i;
- tmp->pvt->send_digit = ixj_digit;
- tmp->pvt->call = ixj_call;
- tmp->pvt->hangup = ixj_hangup;
- tmp->pvt->answer = ixj_answer;
- tmp->pvt->read = ixj_read;
- tmp->pvt->write = ixj_write;
- strncpy(tmp->context, i->context, sizeof(tmp->context));
+ tmp->pvt->send_digit = phone_digit;
+ tmp->pvt->call = phone_call;
+ tmp->pvt->hangup = phone_hangup;
+ tmp->pvt->answer = phone_answer;
+ tmp->pvt->read = phone_read;
+ tmp->pvt->write = phone_write;
+ strncpy(tmp->context, context, sizeof(tmp->context));
if (strlen(i->ext))
strncpy(tmp->exten, i->ext, sizeof(tmp->exten));
i->owner = tmp;
@@ -449,8 +500,9 @@ static struct ast_channel *ixj_new(struct ixj_pvt *i, int state)
pthread_mutex_unlock(&usecnt_lock);
ast_update_use_count();
if (state != AST_STATE_DOWN) {
- if (state == AST_STATE_RING)
- ioctl(tmp->fd, IXJCTL_RINGBACK);
+ if (state == AST_STATE_RING) {
+ ioctl(tmp->fd, PHONE_RINGBACK);
+ }
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
ast_hangup(tmp);
@@ -461,7 +513,7 @@ static struct ast_channel *ixj_new(struct ixj_pvt *i, int state)
return tmp;
}
-static void ixj_mini_packet(struct ixj_pvt *i)
+static void phone_mini_packet(struct phone_pvt *i)
{
int res;
char buf[1024];
@@ -473,75 +525,77 @@ static void ixj_mini_packet(struct ixj_pvt *i)
}
}
-static void ixj_check_exception(struct ixj_pvt *i)
+static void phone_check_exception(struct phone_pvt *i)
{
int offhook=0;
char digit[2] = {0 , 0};
- IXJ_EXCEPTION ixje;
+ union telephony_exception phonee;
/* XXX Do something XXX */
#if 0
ast_log(LOG_DEBUG, "Exception!\n");
#endif
- ixje.bytes = ioctl(i->fd, IXJCTL_EXCEPTION);
- if (ixje.bits.dtmf_ready) {
- digit[0] = ioctl(i->fd, IXJCTL_GET_DTMF_ASCII);
+ phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION);
+ if (phonee.bits.dtmf_ready) {
+ digit[0] = ioctl(i->fd, PHONE_GET_DTMF_ASCII);
if (i->mode == MODE_DIALTONE) {
- ioctl(i->fd, IXJCTL_PLAY_STOP);
- ioctl(i->fd, IXJCTL_REC_STOP);
- ioctl(i->fd, IXJCTL_CPT_STOP);
+ ioctl(i->fd, PHONE_PLAY_STOP);
+ ioctl(i->fd, PHONE_REC_STOP);
+ ioctl(i->fd, PHONE_CPT_STOP);
i->dialtone = 0;
if (strlen(i->ext) < AST_MAX_EXTENSION - 1)
strcat(i->ext, digit);
if (ast_exists_extension(NULL, i->context, i->ext, 1)) {
/* It's a valid extension in its context, get moving! */
- ixj_new(i, AST_STATE_UP);
+ phone_new(i, AST_STATE_RING, i->context);
/* No need to restart monitor, we are the monitor */
if (i->owner) {
pthread_mutex_lock(&usecnt_lock);
usecnt--;
pthread_mutex_unlock(&usecnt_lock);
ast_update_use_count();
- ixj_setup(i->owner);
}
- } else if (ast_exists_extension(NULL, "default", i->ext, 1)) {
- /* Check the default, too... */
- /* XXX This should probably be justified better XXX */
- strncpy(i->context, "default", sizeof(i->context));
- ixj_new(i, AST_STATE_UP);
- if (i->owner) {
- pthread_mutex_lock(&usecnt_lock);
- usecnt--;
- pthread_mutex_unlock(&usecnt_lock);
- ast_update_use_count();
- ixj_setup(i->owner);
+ } else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1)) {
+ /* There is nothing in the specified extension that can match anymore.
+ Try the default */
+ if (ast_exists_extension(NULL, "default", i->ext, 1)) {
+ /* Check the default, too... */
+ phone_new(i, AST_STATE_RING, "default");
+ if (i->owner) {
+ pthread_mutex_lock(&usecnt_lock);
+ usecnt--;
+ pthread_mutex_unlock(&usecnt_lock);
+ ast_update_use_count();
+ }
+ /* XXX This should probably be justified better XXX */
+ } else if (!ast_canmatch_extension(NULL, "default", i->ext, 1)) {
+ /* It's not a valid extension, give a busy signal */
+ if (option_debug)
+ ast_log(LOG_DEBUG, "%s can't match anything in %s or default\n", i->ext, i->context);
+ ioctl(i->fd, PHONE_BUSY);
}
- } else if ((strlen(i->ext) >= ast_pbx_longest_extension(i->context)) &&
- (strlen(i->ext) >= ast_pbx_longest_extension("default"))) {
- if (option_debug)
- ast_log(LOG_DEBUG, "%s is too long\n", i->ext);
- /* It's not a valid extension, give a busy signal */
- ioctl(i->fd, IXJCTL_BUSY);
}
#if 0
ast_verbose("Extension is %s\n", i->ext);
#endif
}
}
- if (ixje.bits.hookstate) {
- offhook = ioctl(i->fd, IXJCTL_HOOKSTATE);
+ if (phonee.bits.hookstate) {
+ offhook = ioctl(i->fd, PHONE_HOOKSTATE);
if (offhook) {
if (i->mode == MODE_IMMEDIATE) {
- ixj_new(i, AST_STATE_RING);
+ phone_new(i, AST_STATE_RING, i->context);
} else if (i->mode == MODE_DIALTONE) {
pthread_mutex_lock(&usecnt_lock);
usecnt++;
pthread_mutex_unlock(&usecnt_lock);
ast_update_use_count();
+ /* Reset the extension */
+ i->ext[0] = '\0';
/* Play the dialtone */
i->dialtone++;
- ioctl(i->fd, IXJCTL_PLAY_STOP);
- ioctl(i->fd, IXJCTL_PLAY_CODEC, ULAW);
- ioctl(i->fd, IXJCTL_PLAY_START);
+ ioctl(i->fd, PHONE_PLAY_STOP);
+ ioctl(i->fd, PHONE_PLAY_CODEC, ULAW);
+ ioctl(i->fd, PHONE_PLAY_START);
}
} else {
if (i->dialtone) {
@@ -551,15 +605,15 @@ static void ixj_check_exception(struct ixj_pvt *i)
ast_update_use_count();
}
memset(i->ext, 0, sizeof(i->ext));
- ioctl(i->fd, IXJCTL_CPT_STOP);
- ioctl(i->fd, IXJCTL_PLAY_STOP);
- ioctl(i->fd, IXJCTL_REC_STOP);
+ ioctl(i->fd, PHONE_CPT_STOP);
+ ioctl(i->fd, PHONE_PLAY_STOP);
+ ioctl(i->fd, PHONE_REC_STOP);
i->dialtone = 0;
}
}
- if (ixje.bits.pstn_ring)
+ if (phonee.bits.pstn_ring)
ast_verbose("Unit is ringing\n");
- if (ixje.bits.caller_id)
+ if (phonee.bits.caller_id)
ast_verbose("We have caller ID\n");
@@ -569,7 +623,7 @@ static void *do_monitor(void *data)
{
fd_set rfds, efds;
int n, res;
- struct ixj_pvt *i;
+ struct phone_pvt *i;
int tonepos = 0;
/* The tone we're playing this round */
struct timeval tv = {0,0};
@@ -595,7 +649,7 @@ static void *do_monitor(void *data)
return NULL;
}
/* Build the stuff we're going to select on, that is the socket of every
- ixj_pvt that does not have an associated owner channel */
+ phone_pvt that does not have an associated owner channel */
n = -1;
FD_ZERO(&rfds);
FD_ZERO(&efds);
@@ -668,14 +722,14 @@ static void *do_monitor(void *data)
ast_log(LOG_WARNING, "Whoa.... I'm owned but found (%d, %s)...\n", i->fd, i->dev);
continue;
}
- ixj_mini_packet(i);
+ phone_mini_packet(i);
}
if (FD_ISSET(i->fd, &efds)) {
if (i->owner) {
ast_log(LOG_WARNING, "Whoa.... I'm owned but found (%d, %s)...\n", i->fd, i->dev);
continue;
}
- ixj_check_exception(i);
+ phone_check_exception(i);
}
i=i->next;
}
@@ -716,15 +770,15 @@ static int restart_monitor()
return 0;
}
-static struct ixj_pvt *mkif(char *iface, int mode)
+static struct phone_pvt *mkif(char *iface, int mode)
{
- /* Make a ixj_pvt structure for this interface */
- struct ixj_pvt *tmp;
+ /* Make a phone_pvt structure for this interface */
+ struct phone_pvt *tmp;
#if 0
int flags;
#endif
- tmp = malloc(sizeof(struct ixj_pvt));
+ tmp = malloc(sizeof(struct phone_pvt));
if (tmp) {
tmp->fd = open(iface, O_RDWR);
if (tmp->fd < 0) {
@@ -732,10 +786,16 @@ static struct ixj_pvt *mkif(char *iface, int mode)
free(tmp);
return NULL;
}
- ioctl(tmp->fd, IXJCTL_PLAY_STOP);
- ioctl(tmp->fd, IXJCTL_REC_STOP);
- ioctl(tmp->fd, IXJCTL_RING_STOP);
- ioctl(tmp->fd, IXJCTL_CPT_STOP);
+ ioctl(tmp->fd, PHONE_PLAY_STOP);
+ ioctl(tmp->fd, PHONE_REC_STOP);
+ ioctl(tmp->fd, PHONE_RING_STOP);
+ ioctl(tmp->fd, PHONE_CPT_STOP);
+ ioctl(tmp->fd, PHONE_REC_DEPTH, 4);
+ if (echocancel != AEC_OFF)
+ ioctl(tmp->fd, IXJCTL_AEC_START, echocancel);
+ if (silencesupression)
+ tmp->silencesupression = 1;
+ ioctl(tmp->fd, PHONE_VAD, tmp->silencesupression);
tmp->mode = mode;
#if 0
flags = fcntl(tmp->fd, F_GETFL);
@@ -755,10 +815,10 @@ static struct ixj_pvt *mkif(char *iface, int mode)
return tmp;
}
-static struct ast_channel *ixj_request(char *type, int format, void *data)
+static struct ast_channel *phone_request(char *type, int format, void *data)
{
int oldformat;
- struct ixj_pvt *p;
+ struct phone_pvt *p;
struct ast_channel *tmp = NULL;
char *name = data;
@@ -777,7 +837,7 @@ static struct ast_channel *ixj_request(char *type, int format, void *data)
while(p) {
if (!strcmp(name, p->dev + 5)) {
if (!p->owner) {
- tmp = ixj_new(p, AST_STATE_DOWN);
+ tmp = phone_new(p, AST_STATE_DOWN, p->context);
break;
}
}
@@ -792,7 +852,7 @@ int load_module()
{
struct ast_config *cfg;
struct ast_variable *v;
- struct ixj_pvt *tmp;
+ struct phone_pvt *tmp;
int mode = MODE_IMMEDIATE;
cfg = ast_load(config);
@@ -822,6 +882,8 @@ int load_module()
unload_module();
return -1;
}
+ } else if (!strcasecmp(v->name, "silencesupression")) {
+ silencesupression = ast_true(v->value);
} else if (!strcasecmp(v->name, "mode")) {
if (!strncasecmp(v->value, "di", 2))
mode = MODE_DIALTONE;
@@ -831,12 +893,30 @@ int load_module()
ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value);
} else if (!strcasecmp(v->name, "context")) {
strncpy(context, v->value, sizeof(context));
+ } else if (!strcasecmp(v->name, "format")) {
+ if (!strcasecmp(v->value, "g723.1")) {
+ prefformat = AST_FORMAT_G723_1;
+ } else if (!strcasecmp(v->value, "slinear")) {
+ prefformat = AST_FORMAT_SLINEAR;
+ } else
+ ast_log(LOG_WARNING, "Unknown format '%s'\n", v->value);
+ } else if (!strcasecmp(v->name, "echocancel")) {
+ if (!strcasecmp(v->value, "off")) {
+ echocancel = AEC_OFF;
+ } else if (!strcasecmp(v->value, "low")) {
+ echocancel = AEC_LOW;
+ } else if (!strcasecmp(v->value, "medium")) {
+ echocancel = AEC_MED;
+ } else if (!strcasecmp(v->value, "high")) {
+ echocancel = AEC_HIGH;
+ } else
+ ast_log(LOG_WARNING, "Unknown echo cancellation '%s'\n", v->value);
}
v = v->next;
}
pthread_mutex_unlock(&iflock);
- /* Make sure we can register our Adtranixj channel type */
- if (ast_channel_register(type, tdesc, AST_FORMAT_G723_1, ixj_request)) {
+ /* Make sure we can register our Adtranphone channel type */
+ if (ast_channel_register(type, tdesc, AST_FORMAT_G723_1, phone_request)) {
ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
ast_destroy(cfg);
unload_module();
@@ -852,7 +932,7 @@ int load_module()
int unload_module()
{
- struct ixj_pvt *p, *pl;
+ struct phone_pvt *p, *pl;
/* First, take us out of the channel loop */
ast_channel_unregister(type);
if (!pthread_mutex_lock(&iflock)) {
diff --git a/channels/ixjuser.h b/channels/ixjuser.h
index 2de8abd1a..188485ca1 100755
--- a/channels/ixjuser.h
+++ b/channels/ixjuser.h
@@ -1,8 +1,8 @@
/******************************************************************************
$Id$
$Log$
-Revision 1.15 1999/12/01 05:25:58 markster
-Version 0.3.0 from FTP
+Revision 1.3 1999/12/01 05:25:58 markster
+Version 0.1.2 from FTP
Revision 1.1 1999/12/01 05:25:58 markster
Start on the Internet Phone Jack channel
diff --git a/codecs/Makefile b/codecs/Makefile
index 0e015cf92..baec412a5 100755
--- a/codecs/Makefile
+++ b/codecs/Makefile
@@ -26,8 +26,9 @@ LIBG723=g723.1/libg723.a
LIBG723B=g723.1b/libg723b.a
LIBGSM=gsm/lib/libgsm.a
LIBMP3=mp3/libmp3.a
+LIBLPC10=lpc10/liblpc10.a
-CODECS+=$(MODG723) codec_gsm.so codec_mp3_d.so
+CODECS+=$(MODG723) codec_gsm.so codec_mp3_d.so codec_lpc10.so
all: $(CODECS)
@@ -37,6 +38,7 @@ clean:
! [ -d g723.1b ] || make -C g723.1b clean
make -C gsm clean
make -C mp3 clean
+ make -C lpc10 clean
$(LIBG723):
make -C g723.1 all
@@ -50,11 +52,14 @@ $(LIBG723B):
$(LIBMP3):
make -C mp3 all
+$(LIBLPC10):
+ make -C lpc10 all
+
codec_g723_1.so : codec_g723_1.o $(LIBG723)
$(CC) -shared -Xlinker -x -o $@ $< $(LIBG723)
codec_g723_1b.o : codec_g723_1.c
- $(CC) -c -o $@ $(CFLAGS) -DANNEX_B $<
+ $(CC) -c -o $@ $(CFLAGS) -DANNEX_B -Dsingle $<
codec_g723_1b.so : codec_g723_1b.o $(LIBG723B)
$(CC) -shared -Xlinker -x -o $@ $< $(LIBG723B) -lm
@@ -62,6 +67,9 @@ codec_g723_1b.so : codec_g723_1b.o $(LIBG723B)
codec_gsm.so: codec_gsm.o $(LIBGSM)
$(CC) -shared -Xlinker -x -o $@ $< $(LIBGSM)
+codec_lpc10.so: codec_lpc10.o $(LIBLPC10)
+ $(CC) -shared -Xlinker -x -o $@ $< $(LIBLPC10) -lm
+
codec_mp3_d.so: codec_mp3_d.o $(LIBMP3)
$(CC) -shared -Xlinker -x -o $@ $< $(LIBMP3)
diff --git a/codecs/codec_lpc10.c b/codecs/codec_lpc10.c
new file mode 100755
index 000000000..6316241dd
--- /dev/null
+++ b/codecs/codec_lpc10.c
@@ -0,0 +1,348 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Translate between signed linear and LPC10 (Linear Predictor Code)
+ *
+ * The lpc10 code is from a library used by nautilus, modified to be a bit
+ * nicer to the compiler.
+ *
+ * See http://www.arl.wustl.edu/~jaf/
+ *
+ * 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 <asterisk/translate.h>
+#include <asterisk/module.h>
+#include <asterisk/logger.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "lpc10/lpc10.h"
+
+/* Sample frame data */
+#include "slin_lpc10_ex.h"
+#include "lpc10_slin_ex.h"
+
+/* We use a very strange format here... I have no idea why... The frames are 180
+ samples long, which isn't even an even number of milliseconds... Not only that
+ but we hvae to waste two bits of each frame to keep them ending on a byte boundary
+ because the frames are 54 bits long */
+
+#define LPC10_BYTES_IN_COMPRESSED_FRAME (LPC10_BITS_IN_COMPRESSED_FRAME + 7)/8
+
+static pthread_mutex_t localuser_lock = PTHREAD_MUTEX_INITIALIZER;
+static int localusecnt=0;
+
+static char *tdesc = "LPC10 2.4kbps (signed linear) Voice Coder";
+
+struct ast_translator_pvt {
+ union {
+ struct lpc10_encoder_state *enc;
+ struct lpc10_decoder_state *dec;
+ } lpc10;
+ struct ast_frame f;
+ /* Space to build offset */
+ char offset[AST_FRIENDLY_OFFSET];
+ /* Buffer for our outgoing frame */
+ short outbuf[LPC10_SAMPLES_PER_FRAME];
+ /* Enough to store a full second */
+ short buf[8000];
+ int tail;
+ int longer;
+};
+
+#define lpc10_coder_pvt ast_translator_pvt
+
+static struct ast_translator_pvt *lpc10_enc_new()
+{
+ struct lpc10_coder_pvt *tmp;
+ tmp = malloc(sizeof(struct lpc10_coder_pvt));
+ if (tmp) {
+ if (!(tmp->lpc10.enc = create_lpc10_encoder_state())) {
+ free(tmp);
+ tmp = NULL;
+ }
+ tmp->tail = 0;
+ tmp->longer = 0;
+ localusecnt++;
+ }
+ return tmp;
+}
+
+static struct ast_translator_pvt *lpc10_dec_new()
+{
+ struct lpc10_coder_pvt *tmp;
+ tmp = malloc(sizeof(struct lpc10_coder_pvt));
+ if (tmp) {
+ if (!(tmp->lpc10.dec = create_lpc10_decoder_state())) {
+ free(tmp);
+ tmp = NULL;
+ }
+ tmp->tail = 0;
+ tmp->longer = 0;
+ localusecnt++;
+ }
+ return tmp;
+}
+static struct ast_frame *lintolpc10_sample()
+{
+ static struct ast_frame f;
+ static int longer = 0;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_SLINEAR;
+ f.datalen = sizeof(slin_lpc10_ex);
+ /* Assume 8000 Hz */
+ f.timelen = LPC10_SAMPLES_PER_FRAME/8;
+ f.timelen += longer;
+ longer = 1- longer;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = slin_lpc10_ex;
+ return &f;
+}
+
+static struct ast_frame *lpc10tolin_sample()
+{
+ static struct ast_frame f;
+ f.frametype = AST_FRAME_VOICE;
+ f.subclass = AST_FORMAT_LPC10;
+ f.datalen = sizeof(lpc10_slin_ex);
+ /* All frames are 22 ms long (maybe a little more -- why did he choose
+ LPC10_SAMPLES_PER_FRAME sample frames anyway?? */
+ f.timelen = LPC10_SAMPLES_PER_FRAME/8;
+ f.mallocd = 0;
+ f.offset = 0;
+ f.src = __PRETTY_FUNCTION__;
+ f.data = lpc10_slin_ex;
+ return &f;
+}
+
+static struct ast_frame *lpc10tolin_frameout(struct ast_translator_pvt *tmp)
+{
+ if (!tmp->tail)
+ return NULL;
+ /* Signed linear is no particular frame size, so just send whatever
+ we have in the buffer in one lump sum */
+ tmp->f.frametype = AST_FRAME_VOICE;
+ tmp->f.subclass = AST_FORMAT_SLINEAR;
+ tmp->f.datalen = tmp->tail * 2;
+ /* Assume 8000 Hz */
+ tmp->f.timelen = tmp->tail / 8;
+ tmp->f.mallocd = 0;
+ tmp->f.offset = AST_FRIENDLY_OFFSET;
+ tmp->f.src = __PRETTY_FUNCTION__;
+ tmp->f.data = tmp->buf;
+ /* Reset tail pointer */
+ tmp->tail = 0;
+
+#if 0
+ /* Save a sample frame */
+ { static int samplefr = 0;
+ if (samplefr == 80) {
+ int fd;
+ fd = open("lpc10.example", O_WRONLY | O_CREAT, 0644);
+ write(fd, tmp->f.data, tmp->f.datalen);
+ close(fd);
+ }
+ samplefr++;
+ }
+#endif
+ return &tmp->f;
+}
+
+static void extract_bits(INT32 *bits, unsigned char *c)
+{
+ int x;
+ for (x=0;x<LPC10_BITS_IN_COMPRESSED_FRAME;x++) {
+ if (*c & (0x80 >> (x & 7)))
+ bits[x] = 1;
+ else
+ bits[x] = 0;
+ if ((x & 7) == 7)
+ c++;
+ }
+}
+
+static void build_bits(unsigned char *c, INT32 *bits)
+{
+ unsigned char mask=0x80;
+ int x;
+ *c = 0;
+ for (x=0;x<LPC10_BITS_IN_COMPRESSED_FRAME;x++) {
+ if (bits[x])
+ *c |= mask;
+ mask = mask >> 1;
+ if ((x % 8)==7) {
+ c++;
+ *c = 0;
+ mask = 0x80;
+ }
+ }
+}
+
+static int lpc10tolin_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
+{
+ /* Assuming there's space left, decode into the current buffer at
+ the tail location */
+ int x;
+ float tmpbuf[LPC10_SAMPLES_PER_FRAME];
+ short *sd;
+ INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];
+ if (tmp->tail + LPC10_SAMPLES_PER_FRAME < sizeof(tmp->buf)/2) {
+ sd = tmp->buf + tmp->tail;
+ extract_bits(bits, f->data);
+ if (lpc10_decode(bits, tmpbuf, tmp->lpc10.dec)) {
+ ast_log(LOG_WARNING, "Invalid lpc10 data\n");
+ return -1;
+ }
+ for (x=0;x<LPC10_SAMPLES_PER_FRAME;x++) {
+ /* Convert to a real between -1.0 and 1.0 */
+ sd[x] = 32768.0 * tmpbuf[x];
+ }
+
+ tmp->tail+=LPC10_SAMPLES_PER_FRAME;
+ } else {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int lintolpc10_framein(struct ast_translator_pvt *tmp, struct ast_frame *f)
+{
+ /* Just add the frames to our stream */
+ /* XXX We should look at how old the rest of our stream is, and if it
+ is too old, then we should overwrite it entirely, otherwise we can
+ get artifacts of earlier talk that do not belong */
+ if (tmp->tail + f->datalen < sizeof(tmp->buf) / 2) {
+ memcpy((tmp->buf + tmp->tail), f->data, f->datalen);
+ tmp->tail += f->datalen/2;
+ } else {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_frame *lintolpc10_frameout(struct ast_translator_pvt *tmp)
+{
+ int x;
+ float tmpbuf[LPC10_SAMPLES_PER_FRAME];
+ INT32 bits[LPC10_BITS_IN_COMPRESSED_FRAME];
+ /* We can't work on anything less than a frame in size */
+ if (tmp->tail < LPC10_SAMPLES_PER_FRAME)
+ return NULL;
+ /* Encode a frame of data */
+ for (x=0;x<LPC10_SAMPLES_PER_FRAME;x++) {
+ tmpbuf[x] = (float)tmp->buf[x] / 32768.0;
+ }
+ lpc10_encode(tmpbuf, bits, tmp->lpc10.enc);
+ build_bits((unsigned char *)tmp->outbuf, bits);
+ tmp->f.frametype = AST_FRAME_VOICE;
+ tmp->f.subclass = AST_FORMAT_LPC10;
+ tmp->f.datalen = LPC10_BYTES_IN_COMPRESSED_FRAME;
+ tmp->f.timelen = 22;
+ /* We alternate between 22 and 23 ms to simulate 22.5 ms */
+ tmp->f.timelen += tmp->longer;
+ /* Use one of the two left over bits to record if this is a 22 or 23 ms frame...
+ important for IAX use */
+ tmp->longer = 1 - tmp->longer;
+ tmp->f.mallocd = 0;
+ tmp->f.offset = AST_FRIENDLY_OFFSET;
+ tmp->f.src = __PRETTY_FUNCTION__;
+ tmp->f.data = tmp->outbuf;
+ ((char *)(tmp->f.data))[LPC10_BYTES_IN_COMPRESSED_FRAME - 1] |= tmp->longer;
+ tmp->tail -= LPC10_SAMPLES_PER_FRAME;
+ /* Move the data at the end of the buffer to the front */
+ if (tmp->tail)
+ memmove(tmp->buf, tmp->buf + LPC10_SAMPLES_PER_FRAME, tmp->tail * 2);
+#if 0
+ /* Save a sample frame */
+ { static int samplefr = 0;
+ if (samplefr == 0) {
+ int fd;
+ fd = open("lpc10.example", O_WRONLY | O_CREAT, 0644);
+ write(fd, tmp->f.data, tmp->f.datalen);
+ close(fd);
+ }
+ samplefr++;
+ }
+#endif
+ return &tmp->f;
+}
+
+static void lpc10_destroy(struct ast_translator_pvt *pvt)
+{
+ /* Enc and DEC are both just allocated, so they can be freed */
+ free(pvt->lpc10.enc);
+ free(pvt);
+ localusecnt--;
+}
+
+static struct ast_translator lpc10tolin =
+ { "lpc10tolin",
+ AST_FORMAT_LPC10, AST_FORMAT_SLINEAR,
+ lpc10_dec_new,
+ lpc10tolin_framein,
+ lpc10tolin_frameout,
+ lpc10_destroy,
+ lpc10tolin_sample
+ };
+
+static struct ast_translator lintolpc10 =
+ { "lintolpc10",
+ AST_FORMAT_SLINEAR, AST_FORMAT_LPC10,
+ lpc10_enc_new,
+ lintolpc10_framein,
+ lintolpc10_frameout,
+ lpc10_destroy,
+ lintolpc10_sample
+ };
+
+int unload_module(void)
+{
+ int res;
+ pthread_mutex_lock(&localuser_lock);
+ res = ast_unregister_translator(&lintolpc10);
+ if (!res)
+ res = ast_unregister_translator(&lpc10tolin);
+ if (localusecnt)
+ res = -1;
+ pthread_mutex_unlock(&localuser_lock);
+ return res;
+}
+
+int load_module(void)
+{
+ int res;
+ res=ast_register_translator(&lpc10tolin);
+ if (!res)
+ res=ast_register_translator(&lintolpc10);
+ else
+ ast_unregister_translator(&lpc10tolin);
+ return res;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
diff --git a/codecs/lpc10/lpc10.h b/codecs/lpc10/lpc10.h
index 5804fc57b..4194ac64e 100755
--- a/codecs/lpc10/lpc10.h
+++ b/codecs/lpc10/lpc10.h
@@ -1,8 +1,8 @@
/*
$Log$
-Revision 1.13 2000/01/05 00:20:06 markster
-Version 0.3.0 from FTP
+Revision 1.1 2000/01/05 00:20:06 markster
+Version 0.1.2 from FTP
Revision 1.1 2000/01/05 00:20:06 markster
Add broken lpc10 code... It's not too far from working I don't think...
diff --git a/codecs/lpc10_slin_ex.h b/codecs/lpc10_slin_ex.h
new file mode 100755
index 000000000..802c3f785
--- /dev/null
+++ b/codecs/lpc10_slin_ex.h
@@ -0,0 +1,13 @@
+/*
+ * 8-bit raw data
+ *
+ * Source: example.lpc10
+ *
+ * Copyright (C) 1999, Mark Spencer and Linux Support Services
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static unsigned char lpc10_slin_ex[] = {
+0x1, 0x8, 0x31, 0x8, 0x31, 0x80, 0x30 };
diff --git a/codecs/slin_lpc10_ex.h b/codecs/slin_lpc10_ex.h
new file mode 100755
index 000000000..d738ae41e
--- /dev/null
+++ b/codecs/slin_lpc10_ex.h
@@ -0,0 +1,21 @@
+/*
+ * Signed 16-bit audio data
+ *
+ * Source: example.slin
+ *
+ * Copyright (C) 1999, Mark Spencer and Linux Support Services
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+static signed short slin_lpc10_ex[] = {
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000,
+000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000, 000000 };
diff --git a/config.c b/config.c
index 3d81ebda7..f56eb069c 100755
--- a/config.c
+++ b/config.c
@@ -70,6 +70,8 @@ void ast_destroy(struct ast_config *ast)
int ast_true(char *s)
{
+ if (!s)
+ return 0;
/* Determine if this is a true value */
if (!strcasecmp(s, "yes") ||
!strcasecmp(s, "true") ||
diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample
new file mode 100755
index 000000000..62307d5b9
--- /dev/null
+++ b/configs/iax.conf.sample
@@ -0,0 +1,72 @@
+;
+; Inter-Asterisk eXchange driver definition
+;
+;
+; General settings, like port number to bind to, and
+; an option address (the default is to bind to all
+; local addresses).
+;
+[general]
+port=5036
+;bindaddr=192.168.0.1
+;
+; Specify bandwidth of low, medium, or high to control which codecs are used
+; in general.
+;
+bandwidth=low
+;
+; You can also fine tune codecs here using "allow" and "disallow" clauses
+; with specific codecs. Use "all" to represent all formats.
+;
+;allow=all ; same as bandwidth=high
+;disallow=g723.1 ; Hm... Proprietary, don't use it...
+disallow=lpc10 ; Icky sound quality... Mr. Roboto.
+;allow=gsm ; Always allow GSM, it's cool :)
+;
+; You can also adjust several parameters relating to the jitter
+; buffer. Specifically, you can provide a maximum jitter buffer,
+; you can turn it off entirely, and you can specify an acceptable
+; drop rate (per MEMORY_SIZE, by default 3 of 100). Disabling the
+; jitter buffer is not recommended. Finally, you can specify the maximum
+; excess jitter buffer, which if exceeded, causes the jitter buffer to
+; slowly shrink in order to improve latency.
+;
+;jitterbuffer=no
+;dropcount=3
+;maxjitterbuffer=500
+;maxexccessbuffer=100
+
+;
+; Guest sections for unauthenticated connection attempts. Just
+; specify an empty secret, or provide no secret section.
+;
+[guest]
+type=user
+context=default
+;
+; Further user sections may be added, specifying a context and a
+; secret used for connections with that given authentication name.
+; Limited IP based access control is allowed by use of "allow" and
+; "deny" keywords. Multiple rules are permitted. Multiple permitted
+; contexts may be specified, in which case the first will be the default.
+;
+;[markster]
+;type=user
+;context=default
+;context=local
+;auth=md5,cleartext
+;secret=markpasswd
+;deny=0.0.0.0/0.0.0.0
+;allow=209.16.236.73/255.255.255.0
+;
+; Peers may also be specified, with a secret and
+; a remote hostname.
+;
+[demo]
+type=peer
+username=asterisk
+secret=supersecret
+host=209.16.236.91
+;host=asterisk.linux-support.net
+;port=5036
+;mask=255.255.255.255
diff --git a/configs/ixj.conf.sample b/configs/ixj.conf.sample
deleted file mode 100755
index ed6be96c1..000000000
--- a/configs/ixj.conf.sample
+++ /dev/null
@@ -1,19 +0,0 @@
-;
-; Internet Phone Jack
-;
-; Configuration file
-;
-[interfaces]
-;
-; Select a mode, either the line jack provides dialtone, reads digits,
-; then starts PBX with the given extension (dialtone mode), or
-; immediately provides the PBX without reading any digits or providing
-; any dialtone (this is the immediate mode, the default)
-;
-;mode=immediate
-mode=dialtone
-;
-; List all devices we can use.
-;
-context=local
-device=/dev/ixj0
diff --git a/configs/oss.conf.sample b/configs/oss.conf.sample
new file mode 100755
index 000000000..138a7376b
--- /dev/null
+++ b/configs/oss.conf.sample
@@ -0,0 +1,23 @@
+;
+; Open Sound System Console Driver Configuration File
+;
+[general]
+;
+; Automatically answer incoming calls on the console? Choose yes if
+; for example you want to use this as an intercom.
+;
+autoanswer=yes
+;
+; Default context (is overridden with @context syntax)
+;
+;context=local
+;
+; Default extension to call
+;
+extension=s
+;
+; Silence supression can be enabled when sound is over a certain threshold.
+; The value for the threshold should probably be between 500 and 2000 or so,
+; but your mileage may vary. Use the echo test to evaluate the best setting.
+;silencesuppression = yes
+;silencethreshold = 1000
diff --git a/configs/phone.conf.sample b/configs/phone.conf.sample
new file mode 100755
index 000000000..72c5393ab
--- /dev/null
+++ b/configs/phone.conf.sample
@@ -0,0 +1,35 @@
+;
+; Linux Telephony Interface
+;
+; Configuration file
+;
+[interfaces]
+;
+; Select a mode, either the line jack provides dialtone, reads digits,
+; then starts PBX with the given extension (dialtone mode), or
+; immediately provides the PBX without reading any digits or providing
+; any dialtone (this is the immediate mode, the default)
+;
+;mode=immediate
+mode=dialtone
+;
+; You can decide which format to use by default, "g723.1" or "slinear".
+; XXX Be careful, sometimes the card causes kernel panics when running
+; in signed linear mode for some reason... XXX
+;
+;format=slinear
+format=g723.1
+;
+; And set the echo cancellation to "off", "low", "medium", and "high".
+; This is not supported on all phones.
+;
+echocancel=medium
+;
+; You can optionally use VAD/CNG silence supression
+;
+;silencesupression=yes
+;
+; List all devices we can use. Contexts may also be specified
+;
+;context=local
+;device=/dev/ixj0
diff --git a/frame.c b/frame.c
index 856e7fd89..9e593bfbb 100755
--- a/frame.c
+++ b/frame.c
@@ -13,7 +13,9 @@
#include <asterisk/frame.h>
#include <asterisk/logger.h>
+#include <asterisk/options.h>
#include <stdlib.h>
+#include <unistd.h>
#include <string.h>
/*
@@ -74,16 +76,106 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
} else
out->src = fr->src;
if (!(fr->mallocd & AST_MALLOCD_DATA)) {
- out->data = malloc(fr->datalen + fr->offset);
- out->data += fr->offset;
- out->offset = fr->offset;
- out->datalen = fr->datalen;
- memcpy(out->data, fr->data, fr->datalen);
+ out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET);
if (!out->data) {
+ free(out);
ast_log(LOG_WARNING, "Out of memory\n");
return NULL;
}
+ out->data += AST_FRIENDLY_OFFSET;
+ out->offset = AST_FRIENDLY_OFFSET;
+ out->datalen = fr->datalen;
+ memcpy(out->data, fr->data, fr->datalen);
}
out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
return out;
}
+
+struct ast_frame *ast_frdup(struct ast_frame *f)
+{
+ struct ast_frame *ret;
+ int p;
+ p = f->mallocd;
+ f->mallocd = 0;
+ /* Make frisolate think this is a 100% static frame, and make a duplicate */
+ ret = ast_frisolate(f);
+ /* Restore its true malloc status */
+ f->mallocd = p;
+ return ret;
+}
+
+struct ast_frame *ast_fr_fdread(int fd)
+{
+ char buf[4096];
+ int res;
+ struct ast_frame *f = (struct ast_frame *)buf;
+ /* Read a frame directly from there. They're always in the
+ right format. */
+
+ if (read(fd, buf, sizeof(struct ast_frame))
+ == sizeof(struct ast_frame)) {
+ /* read the frame header */
+ f->mallocd = 0;
+ /* Re-write data position */
+ f->data = buf + sizeof(struct ast_frame);
+ f->offset = 0;
+ /* Forget about being mallocd */
+ f->mallocd = 0;
+ /* Re-write the source */
+ f->src = __FUNCTION__;
+ if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) {
+ /* Really bad read */
+ ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
+ return NULL;
+ }
+ if (f->datalen) {
+ if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
+ /* Bad read */
+ ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
+ return NULL;
+ }
+ }
+ return ast_frisolate(f);
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "NULL or invalid header\n");
+ /* Null if there was an error */
+ return NULL;
+}
+
+/* Some convenient routines for sending frames to/from stream or datagram
+ sockets, pipes, etc (maybe even files) */
+
+int ast_fr_fdwrite(int fd, struct ast_frame *frame)
+{
+ /* Write the frame exactly */
+ if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
+ ast_log(LOG_WARNING, "Write error\n");
+ return -1;
+ }
+ if (write(fd, frame->data, frame->datalen) != frame->datalen) {
+ ast_log(LOG_WARNING, "Write error\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ast_getformatbyname(char *name)
+{
+ if (!strcasecmp(name, "g723.1"))
+ return AST_FORMAT_G723_1;
+ else if (!strcasecmp(name, "gsm"))
+ return AST_FORMAT_GSM;
+ else if (!strcasecmp(name, "ulaw"))
+ return AST_FORMAT_ULAW;
+ else if (!strcasecmp(name, "alaw"))
+ return AST_FORMAT_ALAW;
+ else if (!strcasecmp(name, "mp3"))
+ return AST_FORMAT_MP3;
+ else if (!strcasecmp(name, "slinear"))
+ return AST_FORMAT_SLINEAR;
+ else if (!strcasecmp(name, "lpc10"))
+ return AST_FORMAT_LPC10;
+ else if (!strcasecmp(name, "all"))
+ return 0x7FFFFFFF;
+ return 0;
+}
diff --git a/pbx.c b/pbx.c
index 23dd88a84..3c8c17c6b 100755
--- a/pbx.c
+++ b/pbx.c
@@ -82,6 +82,8 @@ struct ast_app {
struct ast_app *next;
};
+static int pbx_builtin_prefix(struct ast_channel *, void *);
+static int pbx_builtin_stripmsd(struct ast_channel *, void *);
static int pbx_builtin_answer(struct ast_channel *, void *);
static int pbx_builtin_goto(struct ast_channel *, void *);
static int pbx_builtin_hangup(struct ast_channel *, void *);
@@ -104,6 +106,8 @@ static struct pbx_builtin {
{ "ResponseTimeout", pbx_builtin_rtimeout },
{ "BackGround", pbx_builtin_background },
{ "Wait", pbx_builtin_wait },
+ { "StripMSD", pbx_builtin_stripmsd },
+ { "Prefix", pbx_builtin_prefix },
};
/* Lock for the application list */
@@ -155,6 +159,7 @@ static int pbx_exec(struct ast_channel *c, /* Channel */
#define HELPER_EXISTS 0
#define HELPER_SPAWN 1
#define HELPER_EXEC 2
+#define HELPER_CANMATCH 3
static struct ast_app *pbx_findapp(char *app)
{
@@ -213,6 +218,42 @@ static int extension_match(char *pattern, char *data)
return match;
}
+static int extension_close(char *pattern, char *data)
+{
+ int match;
+ /* If "data" is longer, it can'be a subset of pattern */
+ if (strlen(pattern) < strlen(data))
+ return 0;
+
+ if (!strncasecmp(pattern, data, strlen(data))) {
+ return 1;
+ }
+ /* All patterns begin with _ */
+ if (pattern[0] != '_')
+ return 0;
+ /* Start optimistic */
+ match=1;
+ pattern++;
+ while(match && *data && *pattern) {
+ switch(toupper(*pattern)) {
+ case 'N':
+ if ((*data < '2') || (*data > '9'))
+ match=0;
+ break;
+ case 'X':
+ if ((*data < '0') || (*data > '9'))
+ match = 0;
+ break;
+ default:
+ if (*data != *pattern)
+ match =0;
+ }
+ data++;
+ pattern++;
+ }
+ return match;
+}
+
static int pbx_extension_helper(struct ast_channel *c, char *context, char *exten, int priority, int action)
{
struct ast_context *tmp;
@@ -222,7 +263,7 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
int res;
if (pthread_mutex_lock(&conlock)) {
ast_log(LOG_WARNING, "Unable to obtain lock\n");
- if (action == HELPER_EXISTS)
+ if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH))
return 0;
else
return -1;
@@ -230,27 +271,32 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
tmp = contexts;
while(tmp) {
if (!strcasecmp(tmp->name, context)) {
+#if 0
/* By locking tmp, not only can the state of its entries not
change, but it cannot be destroyed either. */
pthread_mutex_lock(&tmp->lock);
- /* But we can relieve the conlock, as tmp will not change */
- pthread_mutex_unlock(&conlock);
+#endif
e = tmp->root;
while(e) {
- if (extension_match(e->exten, exten)) {
+ if (extension_match(e->exten, exten) ||
+ ((action == HELPER_CANMATCH) && extension_close(e->exten, exten))) {
while(e) {
if (e->priority == priority) {
- pthread_mutex_unlock(&tmp->lock);
/* We have a winner! Maybe there are some races
in here though. XXX */
switch(action) {
+ case HELPER_CANMATCH:
+ pthread_mutex_unlock(&conlock);
+ return -1;
case HELPER_EXISTS:
+ pthread_mutex_unlock(&conlock);
return -1;
case HELPER_SPAWN:
newstack++;
/* Fall through */
case HELPER_EXEC:
app = pbx_findapp(e->app);
+ pthread_mutex_unlock(&conlock);
if (app) {
strncpy(c->context, context, sizeof(c->context));
strncpy(c->exten, exten, sizeof(c->exten));
@@ -265,6 +311,7 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
res = pbx_exec(c, app->execute, e->data, newstack);
c->appl = NULL;
c->data = NULL;
+ pthread_mutex_unlock(&conlock);
return res;
} else {
ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
@@ -277,20 +324,25 @@ static int pbx_extension_helper(struct ast_channel *c, char *context, char *exte
e = e->peer;
}
pthread_mutex_unlock(&tmp->lock);
- if (action != HELPER_EXISTS) {
+ if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH)) {
ast_log(LOG_WARNING, "No such priority '%d' in '%s' in '%s'\n", priority, exten, context);
+ pthread_mutex_unlock(&conlock);
return -1;
- } else
+ } else {
+ pthread_mutex_unlock(&conlock);
return 0;
+ }
}
e = e->next;
}
- pthread_mutex_unlock(&tmp->lock);
- if (action != HELPER_EXISTS) {
+ if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH)) {
+ pthread_mutex_unlock(&conlock);
ast_log(LOG_WARNING, "No such extension '%s' in '%s'\n", exten, context);
return -1;
- } else
+ } else {
+ pthread_mutex_unlock(&conlock);
return 0;
+ }
}
tmp = tmp->next;
}
@@ -338,6 +390,11 @@ int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int
return pbx_extension_helper(c, context, exten, priority, HELPER_EXISTS);
}
+int ast_canmatch_extension(struct ast_channel *c, char *context, char *exten, int priority)
+{
+ return pbx_extension_helper(c, context, exten, priority, HELPER_CANMATCH);
+}
+
int ast_spawn_extension(struct ast_channel *c, char *context, char *exten, int priority)
{
return pbx_extension_helper(c, context, exten, priority, HELPER_SPAWN);
@@ -382,14 +439,13 @@ static void *pbx_thread(void *data)
goto out;
}
/* If we're playing something in the background, wait for it to finish or for a digit */
- if (c->stream) {
+ if (c->stream || (c->trans && c->trans->stream)) {
digit = ast_waitstream(c, AST_DIGIT_ANY);
ast_stopstream(c);
/* Hang up if something goes wrong */
if (digit < 0)
goto out;
else if (digit) {
- ast_stopstream(c);
exten[pos++] = digit;
break;
}
@@ -402,8 +458,8 @@ static void *pbx_thread(void *data)
waittime = c->pbx->dtimeout;
else
waittime = c->pbx->rtimeout;
- while(!ast_exists_extension(c, c->context, exten, 1) && (
- strlen(exten) < ast_pbx_longest_extension(c->context))) {
+ while(!ast_exists_extension(c, c->context, exten, 1) &&
+ ast_canmatch_extension(c, c->context, exten, 1)) {
/* As long as we're willing to wait, and as long as it's not defined,
keep reading digits until we can't possibly get a right answer anymore. */
digit = ast_waitfordigit(c, waittime * 1000);
@@ -770,6 +826,32 @@ int pbx_builtin_hangup(struct ast_channel *chan, void *data)
return -1;
}
+int pbx_builtin_stripmsd(struct ast_channel *chan, void *data)
+{
+ char newexten[AST_MAX_EXTENSION] = "";
+ if (!data || !atoi(data)) {
+ ast_log(LOG_DEBUG, "Ignoring, since number of digits to strip is 0\n");
+ return 0;
+ }
+ if (strlen(chan->exten) > atoi(data)) {
+ strncpy(newexten, chan->exten + atoi(data), sizeof(newexten));
+ }
+ strncpy(chan->exten, newexten, sizeof(chan->exten));
+ return 0;
+}
+
+int pbx_builtin_prefix(struct ast_channel *chan, void *data)
+{
+ char newexten[AST_MAX_EXTENSION] = "";
+ if (!data || !strlen(data)) {
+ ast_log(LOG_DEBUG, "Ignoring, since there is no prefix to add\n");
+ return 0;
+ }
+ snprintf(newexten, sizeof(newexten), "%s%s", (char *)data, chan->exten);
+ strncpy(chan->exten, newexten, sizeof(chan->exten));
+ return 0;
+}
+
int pbx_builtin_wait(struct ast_channel *chan, void *data)
{
/* Wait for "n" seconds */
diff --git a/sounds/demo-moreinfo.gsm b/sounds/demo-moreinfo.gsm
new file mode 100755
index 000000000..f31b31230
--- /dev/null
+++ b/sounds/demo-moreinfo.gsm
Binary files differ
diff --git a/translate.c b/translate.c
index c2264cc4f..7546b59a2 100755
--- a/translate.c
+++ b/translate.c
@@ -16,6 +16,9 @@
#include <asterisk/logger.h>
#include <asterisk/translate.h>
#include <asterisk/options.h>
+#include <asterisk/frame.h>
+#include <asterisk/sched.h>
+#include <asterisk/cli.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
@@ -24,6 +27,16 @@
#include <string.h>
#include <stdio.h>
+/* Uncomment the EXPERIMENTAL_TRANSLATION to enable a more complicated, but probably more
+ correct way of handling full duplex translation */
+
+/*
+#define EXPERIMENTAL_TRANSLATION
+*/
+
+/* This could all be done more efficiently *IF* we chained packets together
+ by default, but it would also complicate virtually every application. */
+
static char *type = "Trans";
static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -34,6 +47,15 @@ struct ast_translator_dir {
int cost; /* Complete cost to destination */
};
+struct ast_frame_delivery {
+ struct ast_frame *f;
+ struct ast_channel *chan;
+ int fd;
+ struct translator_pvt *owner;
+ struct ast_frame_delivery *prev;
+ struct ast_frame_delivery *next;
+};
+
static struct ast_translator_dir tr_matrix[MAX_FORMAT][MAX_FORMAT];
struct ast_trans_pvt {
@@ -59,9 +81,69 @@ struct translator_pvt {
struct ast_trans_pvt *system;
struct ast_trans_pvt *rsystem;
struct timeval lastpass;
+#ifdef EXPERIMENTAL_TRANSLATION
+ struct ast_frame_delivery *head;
+ struct ast_frame_delivery *tail;
+ struct sched_context *sched;
+#endif
pthread_t threadid;
};
+
+#ifdef EXPERIMENTAL_TRANSLATION
+static int deliver(void *data)
+{
+ struct ast_frame_delivery *del = data;
+ ast_log(LOG_DEBUG, "Delivering a packet\n");
+ if (del->f) {
+ if (del->chan)
+ ast_write(del->chan, del->f);
+ else
+ ast_fr_fdwrite(del->fd, del->f);
+ ast_frfree(del->f);
+ }
+ /* Take us out of the list */
+ if (del->prev)
+ del->prev->next = del->next;
+ else
+ del->owner->head = del->next;
+ if (del->next)
+ del->next->prev = del->prev;
+ else
+ del->owner->tail = del->prev;
+ /* Free used memory */
+ free(del);
+ /* Never run again */
+ return 0;
+}
+
+/* Schedule the delivery of a packet in the near future, using the given context */
+static int schedule_delivery(struct sched_context *sched, struct translator_pvt *p, struct ast_channel *c,
+ int fd, struct ast_frame *f, int ms)
+{
+ struct ast_frame_delivery *del;
+ ast_log(LOG_DEBUG, "Scheduling a packet delivery\n");
+ del = malloc(sizeof(struct ast_frame_delivery));
+ if (del) {
+ del->f = ast_frdup(f);
+ del->chan = c;
+ del->fd = fd;
+ del->owner = p;
+ if (p->tail) {
+ del->prev = p->tail;
+ p->tail = del;
+ del->next = NULL;
+ } else {
+ p->head = p->tail = del;
+ del->next = NULL;
+ del->prev = NULL;
+ }
+ ast_sched_add(sched, ms, deliver, del);
+ return 0;
+ } else
+ return -1;
+}
+#endif
static int translator_hangup(struct ast_channel *chan)
{
ast_log(LOG_WARNING, "Explicit hangup on '%s' not recommended! Call translator_destroy() instead.\n", chan->name);
@@ -107,6 +189,18 @@ void ast_translator_free_path(struct ast_trans_pvt *p)
static void ast_translator_free(struct translator_pvt *pvt)
{
+#ifdef EXPERIMENTAL_TRANSLATION
+ struct ast_frame_delivery *d, *dl;
+ if (pvt->sched)
+ sched_context_destroy(pvt->sched);
+ d = pvt->head;
+ while(d) {
+ dl = d;
+ d = d->next;
+ ast_frfree(dl->f);
+ free(dl);
+ }
+#endif
ast_translator_free_path(pvt->system);
ast_translator_free_path(pvt->rsystem);
if (pvt->comm[0] > -1)
@@ -150,71 +244,23 @@ struct ast_trans_pvt *ast_translator_build_path(int source, int dest)
ast_log(LOG_WARNING, "Out of memory\n");
return NULL;
}
+ } else {
+ /* We shouldn't have allocated any memory */
+ ast_log(LOG_WARNING, "No translator path from %d to %d\n", source, dest);
+ return NULL;
}
}
return tmpr;
}
-static struct ast_frame *fd_read(int fd)
-{
- char buf[4096];
- int res;
- struct ast_frame *f = (struct ast_frame *)buf;
- /* Read a frame directly from there. They're always in the
- right format. */
-
- if (read(fd, buf, sizeof(struct ast_frame))
- == sizeof(struct ast_frame)) {
- /* read the frame header */
- f->mallocd = 0;
- /* Re-write data position */
- f->data = buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET;
- f->offset = AST_FRIENDLY_OFFSET;
- /* Forget about being mallocd */
- f->mallocd = 0;
- /* Re-write the source */
- f->src = __FUNCTION__;
- if (f->datalen > sizeof(buf) - sizeof(struct ast_frame) - AST_FRIENDLY_OFFSET) {
- /* Really bad read */
- ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen);
- return NULL;
- }
- if (f->datalen) {
- if ((res = read(fd, f->data, f->datalen)) != f->datalen) {
- /* Bad read */
- ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res);
- return NULL;
- }
- }
- return ast_frisolate(f);
- } else if (option_debug)
- ast_log(LOG_DEBUG, "NULL or invalid header\n");
- /* Null if there was an error */
- return NULL;
-}
-
static struct ast_frame *translator_read(struct ast_channel *chan)
{
- return fd_read(chan->fd);
-}
-
-static int fd_write(int fd, struct ast_frame *frame)
-{
- /* Write the frame exactly */
- if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) {
- ast_log(LOG_WARNING, "Write error\n");
- return -1;
- }
- if (write(fd, frame->data, frame->datalen) != frame->datalen) {
- ast_log(LOG_WARNING, "Write error\n");
- return -1;
- }
- return 0;
+ return ast_fr_fdread(chan->fd);
}
static int translator_write(struct ast_channel *chan, struct ast_frame *frame)
{
- return fd_write(chan->fd, frame);
+ return ast_fr_fdwrite(chan->fd, frame);
}
struct ast_frame_chain *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f)
@@ -256,9 +302,9 @@ struct ast_frame_chain *ast_translate(struct ast_trans_pvt *path, struct ast_fra
return outc;
}
-#define FUDGE 2
+#define FUDGE 0
-static void translator_apply(struct ast_trans_pvt *path, struct ast_frame *f, int fd, struct ast_channel *c, struct timeval *last)
+static void translator_apply(struct translator_pvt *pvt, struct ast_trans_pvt *path, struct ast_frame *f, int fd, struct ast_channel *c, struct timeval *last)
{
struct ast_trans_pvt *p;
struct ast_frame *out;
@@ -279,15 +325,33 @@ static void translator_apply(struct ast_trans_pvt *path, struct ast_frame *f, in
gettimeofday(&tv, NULL);
ms = 1000 * (tv.tv_sec - last->tv_sec) +
(tv.tv_usec - last->tv_usec) / 1000;
+#ifdef EXPERIMENTAL_TRANSLATION
+ if (ms + FUDGE < out->timelen)
+ schedule_delivery(pvt->sched, pvt,
+ c, fd, out, ms);
+ else {
+ if (c)
+ ast_write(c, out);
+ else
+ ast_fr_fdwrite(fd, out);
+ }
+ last->tv_sec = tv.tv_sec;
+ last->tv_usec = tv.tv_usec;
+ /* Schedule this packet to be delivered at the
+ right time */
+ } else
+#else
+ /* XXX Not correct in the full duplex case XXX */
if (ms + FUDGE < out->timelen)
usleep((out->timelen - ms - FUDGE) * 1000);
last->tv_sec = tv.tv_sec;
last->tv_usec = tv.tv_usec;
}
+#endif
if (c)
ast_write(c, out);
else
- fd_write(fd, out);
+ ast_fr_fdwrite(fd, out);
}
ast_frfree(out);
}
@@ -331,14 +395,14 @@ static void *translator_thread(void *data)
}
if (f->frametype == AST_FRAME_VOICE) {
if (pvt->system)
- translator_apply(pvt->system, f, fd, NULL, &pvt->lastpass);
+ translator_apply(pvt, pvt->system, f, fd, NULL, &pvt->lastpass);
} else {
/* If it's not voice, just pass it along */
- fd_write(fd, f);
+ ast_fr_fdwrite(fd, f);
}
ast_frfree(f);
} else {
- f = fd_read(res);
+ f = ast_fr_fdread(res);
if (!f) {
if (option_debug)
ast_log(LOG_DEBUG, "Empty (hangup) frame\n");
@@ -347,7 +411,7 @@ static void *translator_thread(void *data)
if (f->frametype == AST_FRAME_VOICE) {
if (pvt->rsystem)
- translator_apply(pvt->rsystem, f, -1, real, &pvt->lastpass);
+ translator_apply(pvt, pvt->rsystem, f, -1, real, &pvt->lastpass);
} else {
ast_write(real, f);
}
@@ -384,9 +448,21 @@ struct ast_channel *ast_translator_create(struct ast_channel *real, int format,
pvt->comm[1] = -1;
pvt->lastpass.tv_usec = 0;
pvt->lastpass.tv_sec = 0;
+
+#ifdef EXPERIMENTAL_TRANSLATION
+ pvt->head = NULL;
+ pvt->tail = NULL;
+ pvt->sched = sched_context_create();
+ if (!pvt->sched) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ ast_translator_free(pvt);
+ return NULL;
+ }
+#endif
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pvt->comm)) {
ast_log(LOG_WARNING, "Unable to create UNIX domain socket on '%s'\n", real->name);
ast_translator_free(pvt);
+ return NULL;
}
/* In to the system */
if (direction & AST_DIRECTION_IN)
@@ -534,6 +610,46 @@ static void calc_cost(struct ast_translator *t)
t->cost = cost;
}
+static int show_translation(int fd, int argc, char *argv[])
+{
+#define SHOW_TRANS 14
+ int x,y;
+ char line[80];
+ if (argc != 2)
+ return RESULT_SHOWUSAGE;
+ ast_cli(fd, " Translation times between formats (in milliseconds)\n");
+ ast_cli(fd, " Destination Format\n");
+ pthread_mutex_lock(&list_lock);
+ for (x=0;x<SHOW_TRANS; x++) {
+ if (x == 1)
+ strcpy(line, " Src ");
+ else if (x == 2)
+ strcpy(line, " Fmt ");
+ else
+ strcpy(line, " ");
+ for (y=0;y<SHOW_TRANS;y++) {
+ if (tr_matrix[x][y].step)
+ snprintf(line + strlen(line), sizeof(line) - strlen(line), " %4d", tr_matrix[x][y].cost);
+ else
+ snprintf(line + strlen(line), sizeof(line) - strlen(line), " n/a");
+ }
+ snprintf(line + strlen(line), sizeof(line) - strlen(line), "\n");
+ ast_cli(fd, line);
+ }
+ pthread_mutex_unlock(&list_lock);
+ return RESULT_SUCCESS;
+}
+
+static int added_cli = 0;
+
+static char show_trans_usage[] =
+"Usage: show translation\n"
+" Displays known codec translators and the cost associated\n"
+"with each conversion.\n";
+
+static struct ast_cli_entry show_trans =
+{ { "show", "translation", NULL }, show_translation, "Display translation matrix", show_trans_usage };
+
int ast_register_translator(struct ast_translator *t)
{
t->srcfmt = powerof(t->srcfmt);
@@ -546,6 +662,10 @@ int ast_register_translator(struct ast_translator *t)
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Registered translator '%s' from format %d to %d, cost %d\n", t->name, t->srcfmt, t->dstfmt, t->cost);
pthread_mutex_lock(&list_lock);
+ if (!added_cli) {
+ ast_cli_register(&show_trans);
+ added_cli++;
+ }
t->next = list;
list = t;
rebuild_matrix();
@@ -566,6 +686,7 @@ int ast_unregister_translator(struct ast_translator *t)
list = u->next;
break;
}
+ ul = u;
u = u->next;
}
rebuild_matrix();
@@ -599,20 +720,23 @@ void ast_translator_destroy(struct ast_channel *trans)
int ast_translator_best_choice(int dst, int srcs)
{
/* Calculate our best source format, given costs, and a desired destination */
- int x;
+ int x,y;
int best=-1;
+ int cur = 1;
int besttime=999999999;
- dst = powerof(dst);
pthread_mutex_lock(&list_lock);
- for (x=0;x<MAX_FORMAT;x++) {
- if (tr_matrix[x][dst].step && /* There's a step */
- (tr_matrix[x][dst].cost < besttime) && /* We're better than what exists now */
- (srcs & (1 << x))) /* x is a valid source format */
- {
- best = 1 << x;
- besttime = tr_matrix[x][dst].cost;
+ for (y=0;y<MAX_FORMAT;y++) {
+ if (cur & dst)
+ for (x=0;x<MAX_FORMAT;x++) {
+ if (tr_matrix[x][y].step && /* There's a step */
+ (tr_matrix[x][y].cost < besttime) && /* We're better than what exists now */
+ (srcs & (1 << x))) /* x is a valid source format */
+ {
+ best = 1 << x;
+ besttime = tr_matrix[x][dst].cost;
+ }
}
-
+ cur = cur << 1;
}
pthread_mutex_unlock(&list_lock);
return best;