mirror of
https://github.com/asterisk/asterisk.git
synced 2025-08-18 20:16:32 +00:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bbe290b904 | ||
|
d6a447055d | ||
|
39e1748aea |
15
CHANGES
Executable file → Normal file
15
CHANGES
Executable file → Normal file
@@ -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
|
||||
|
64
Makefile
Executable file → Normal file
64
Makefile
Executable file → Normal file
@@ -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
|
||||
|
56
README
Executable file → Normal file
56
README
Executable file → Normal file
@@ -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
|
||||
|
0
apps/Makefile
Executable file → Normal file
0
apps/Makefile
Executable file → Normal file
0
apps/app_dial.c
Executable file → Normal file
0
apps/app_dial.c
Executable file → Normal file
0
apps/app_directory.c
Executable file → Normal file
0
apps/app_directory.c
Executable file → Normal file
0
apps/app_echo.c
Executable file → Normal file
0
apps/app_echo.c
Executable file → Normal file
0
apps/app_intercom.c
Executable file → Normal file
0
apps/app_intercom.c
Executable file → Normal file
0
apps/app_mp3.c
Executable file → Normal file
0
apps/app_mp3.c
Executable file → Normal file
0
apps/app_playback.c
Executable file → Normal file
0
apps/app_playback.c
Executable file → Normal file
0
apps/app_skel.c
Executable file → Normal file
0
apps/app_skel.c
Executable file → Normal file
0
apps/app_system.c
Executable file → Normal file
0
apps/app_system.c
Executable file → Normal file
0
apps/app_voicemail.c
Executable file → Normal file
0
apps/app_voicemail.c
Executable file → Normal file
0
asterisk.c
Executable file → Normal file
0
asterisk.c
Executable file → Normal file
0
asterisk.h
Executable file → Normal file
0
asterisk.h
Executable file → Normal file
0
audiofile.patch
Executable file → Normal file
0
audiofile.patch
Executable file → Normal file
0
channels/DialTone.h
Executable file → Normal file
0
channels/DialTone.h
Executable file → Normal file
0
channels/Makefile
Executable file → Normal file
0
channels/Makefile
Executable file → Normal file
0
channels/adtranvofr.h
Executable file → Normal file
0
channels/adtranvofr.h
Executable file → Normal file
2238
channels/chan_iax.c
Normal file
2238
channels/chan_iax.c
Normal file
File diff suppressed because it is too large
Load Diff
16
channels/chan_modem.c
Executable file → Normal file
16
channels/chan_modem.c
Executable file → Normal file
@@ -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;
|
||||
|
13
channels/chan_modem_aopen.c
Executable file → Normal file
13
channels/chan_modem_aopen.c
Executable file → Normal file
@@ -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);
|
||||
|
791
channels/chan_oss.c
Normal file
791
channels/chan_oss.c
Normal file
@@ -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;
|
||||
}
|
378
channels/chan_ixj.c → channels/chan_phone.c
Executable file → Normal file
378
channels/chan_ixj.c → channels/chan_phone.c
Executable file → Normal file
@@ -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)) {
|
0
channels/chan_vofr.c
Executable file → Normal file
0
channels/chan_vofr.c
Executable file → Normal file
0
channels/iax.h
Executable file → Normal file
0
channels/iax.h
Executable file → Normal file
4
channels/ixjuser.h
Executable file → Normal file
4
channels/ixjuser.h
Executable file → Normal file
@@ -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
|
||||
|
12
codecs/Makefile
Executable file → Normal file
12
codecs/Makefile
Executable file → Normal file
@@ -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)
|
||||
|
||||
|
0
codecs/codec_g723_1.c
Executable file → Normal file
0
codecs/codec_g723_1.c
Executable file → Normal file
0
codecs/codec_gsm.c
Executable file → Normal file
0
codecs/codec_gsm.c
Executable file → Normal file
348
codecs/codec_lpc10.c
Normal file
348
codecs/codec_lpc10.c
Normal file
@@ -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;
|
||||
}
|
0
codecs/codec_mp3_d.c
Executable file → Normal file
0
codecs/codec_mp3_d.c
Executable file → Normal file
0
codecs/g723_slin_ex.h
Executable file → Normal file
0
codecs/g723_slin_ex.h
Executable file → Normal file
0
codecs/gsm/COPYRIGHT
Executable file → Normal file
0
codecs/gsm/COPYRIGHT
Executable file → Normal file
0
codecs/gsm/Makefile
Executable file → Normal file
0
codecs/gsm/Makefile
Executable file → Normal file
0
codecs/gsm/README
Executable file → Normal file
0
codecs/gsm/README
Executable file → Normal file
0
codecs/gsm/inc/config.h
Executable file → Normal file
0
codecs/gsm/inc/config.h
Executable file → Normal file
0
codecs/gsm/inc/gsm.h
Executable file → Normal file
0
codecs/gsm/inc/gsm.h
Executable file → Normal file
0
codecs/gsm/inc/private.h
Executable file → Normal file
0
codecs/gsm/inc/private.h
Executable file → Normal file
0
codecs/gsm/inc/proto.h
Executable file → Normal file
0
codecs/gsm/inc/proto.h
Executable file → Normal file
0
codecs/gsm/inc/unproto.h
Executable file → Normal file
0
codecs/gsm/inc/unproto.h
Executable file → Normal file
0
codecs/gsm/src/add.c
Executable file → Normal file
0
codecs/gsm/src/add.c
Executable file → Normal file
0
codecs/gsm/src/code.c
Executable file → Normal file
0
codecs/gsm/src/code.c
Executable file → Normal file
0
codecs/gsm/src/debug.c
Executable file → Normal file
0
codecs/gsm/src/debug.c
Executable file → Normal file
0
codecs/gsm/src/decode.c
Executable file → Normal file
0
codecs/gsm/src/decode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_create.c
Executable file → Normal file
0
codecs/gsm/src/gsm_create.c
Executable file → Normal file
0
codecs/gsm/src/gsm_decode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_decode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_destroy.c
Executable file → Normal file
0
codecs/gsm/src/gsm_destroy.c
Executable file → Normal file
0
codecs/gsm/src/gsm_encode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_encode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_explode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_explode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_implode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_implode.c
Executable file → Normal file
0
codecs/gsm/src/gsm_option.c
Executable file → Normal file
0
codecs/gsm/src/gsm_option.c
Executable file → Normal file
0
codecs/gsm/src/gsm_print.c
Executable file → Normal file
0
codecs/gsm/src/gsm_print.c
Executable file → Normal file
0
codecs/gsm/src/long_term.c
Executable file → Normal file
0
codecs/gsm/src/long_term.c
Executable file → Normal file
0
codecs/gsm/src/lpc.c
Executable file → Normal file
0
codecs/gsm/src/lpc.c
Executable file → Normal file
0
codecs/gsm/src/preprocess.c
Executable file → Normal file
0
codecs/gsm/src/preprocess.c
Executable file → Normal file
0
codecs/gsm/src/rpe.c
Executable file → Normal file
0
codecs/gsm/src/rpe.c
Executable file → Normal file
0
codecs/gsm/src/short_term.c
Executable file → Normal file
0
codecs/gsm/src/short_term.c
Executable file → Normal file
0
codecs/gsm/src/table.c
Executable file → Normal file
0
codecs/gsm/src/table.c
Executable file → Normal file
0
codecs/gsm_slin_ex.h
Executable file → Normal file
0
codecs/gsm_slin_ex.h
Executable file → Normal file
0
codecs/lpc10/Makefile
Executable file → Normal file
0
codecs/lpc10/Makefile
Executable file → Normal file
0
codecs/lpc10/README
Executable file → Normal file
0
codecs/lpc10/README
Executable file → Normal file
0
codecs/lpc10/analys.c
Executable file → Normal file
0
codecs/lpc10/analys.c
Executable file → Normal file
0
codecs/lpc10/bsynz.c
Executable file → Normal file
0
codecs/lpc10/bsynz.c
Executable file → Normal file
0
codecs/lpc10/chanwr.c
Executable file → Normal file
0
codecs/lpc10/chanwr.c
Executable file → Normal file
0
codecs/lpc10/dcbias.c
Executable file → Normal file
0
codecs/lpc10/dcbias.c
Executable file → Normal file
0
codecs/lpc10/decode.c
Executable file → Normal file
0
codecs/lpc10/decode.c
Executable file → Normal file
0
codecs/lpc10/deemp.c
Executable file → Normal file
0
codecs/lpc10/deemp.c
Executable file → Normal file
0
codecs/lpc10/difmag.c
Executable file → Normal file
0
codecs/lpc10/difmag.c
Executable file → Normal file
0
codecs/lpc10/dyptrk.c
Executable file → Normal file
0
codecs/lpc10/dyptrk.c
Executable file → Normal file
0
codecs/lpc10/encode.c
Executable file → Normal file
0
codecs/lpc10/encode.c
Executable file → Normal file
0
codecs/lpc10/energy.c
Executable file → Normal file
0
codecs/lpc10/energy.c
Executable file → Normal file
0
codecs/lpc10/f2c.h
Executable file → Normal file
0
codecs/lpc10/f2c.h
Executable file → Normal file
0
codecs/lpc10/f2clib.c
Executable file → Normal file
0
codecs/lpc10/f2clib.c
Executable file → Normal file
0
codecs/lpc10/ham84.c
Executable file → Normal file
0
codecs/lpc10/ham84.c
Executable file → Normal file
0
codecs/lpc10/hp100.c
Executable file → Normal file
0
codecs/lpc10/hp100.c
Executable file → Normal file
0
codecs/lpc10/invert.c
Executable file → Normal file
0
codecs/lpc10/invert.c
Executable file → Normal file
0
codecs/lpc10/irc2pc.c
Executable file → Normal file
0
codecs/lpc10/irc2pc.c
Executable file → Normal file
0
codecs/lpc10/ivfilt.c
Executable file → Normal file
0
codecs/lpc10/ivfilt.c
Executable file → Normal file
4
codecs/lpc10/lpc10.h
Executable file → Normal file
4
codecs/lpc10/lpc10.h
Executable file → Normal file
@@ -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...
|
||||
|
0
codecs/lpc10/lpcdec.c
Executable file → Normal file
0
codecs/lpc10/lpcdec.c
Executable file → Normal file
0
codecs/lpc10/lpcenc.c
Executable file → Normal file
0
codecs/lpc10/lpcenc.c
Executable file → Normal file
0
codecs/lpc10/lpcini.c
Executable file → Normal file
0
codecs/lpc10/lpcini.c
Executable file → Normal file
0
codecs/lpc10/lpfilt.c
Executable file → Normal file
0
codecs/lpc10/lpfilt.c
Executable file → Normal file
0
codecs/lpc10/median.c
Executable file → Normal file
0
codecs/lpc10/median.c
Executable file → Normal file
0
codecs/lpc10/mload.c
Executable file → Normal file
0
codecs/lpc10/mload.c
Executable file → Normal file
0
codecs/lpc10/onset.c
Executable file → Normal file
0
codecs/lpc10/onset.c
Executable file → Normal file
0
codecs/lpc10/pitsyn.c
Executable file → Normal file
0
codecs/lpc10/pitsyn.c
Executable file → Normal file
0
codecs/lpc10/placea.c
Executable file → Normal file
0
codecs/lpc10/placea.c
Executable file → Normal file
0
codecs/lpc10/placev.c
Executable file → Normal file
0
codecs/lpc10/placev.c
Executable file → Normal file
0
codecs/lpc10/preemp.c
Executable file → Normal file
0
codecs/lpc10/preemp.c
Executable file → Normal file
0
codecs/lpc10/prepro.c
Executable file → Normal file
0
codecs/lpc10/prepro.c
Executable file → Normal file
0
codecs/lpc10/random.c
Executable file → Normal file
0
codecs/lpc10/random.c
Executable file → Normal file
0
codecs/lpc10/rcchk.c
Executable file → Normal file
0
codecs/lpc10/rcchk.c
Executable file → Normal file
0
codecs/lpc10/synths.c
Executable file → Normal file
0
codecs/lpc10/synths.c
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user