aboutsummaryrefslogtreecommitdiff
path: root/net/asterisk-bristuff
diff options
context:
space:
mode:
authorMaxim Sobolev <sobomax@FreeBSD.org>2005-04-22 18:25:41 +0000
committerMaxim Sobolev <sobomax@FreeBSD.org>2005-04-22 18:25:41 +0000
commitb6be6b2cd93321c7a986065262e428fb91289e54 (patch)
treeec93640739c9d5010e4a5a8d444e3ec0af185286 /net/asterisk-bristuff
parent609c61c08d14d0f53911a0234ad02ffd131b97b5 (diff)
downloadports-b6be6b2cd93321c7a986065262e428fb91289e54.tar.gz
ports-b6be6b2cd93321c7a986065262e428fb91289e54.zip
Notes
Diffstat (limited to 'net/asterisk-bristuff')
-rw-r--r--net/asterisk-bristuff/Makefile8
-rw-r--r--net/asterisk-bristuff/files/patch-channels::chan_oss.c1167
2 files changed, 1172 insertions, 3 deletions
diff --git a/net/asterisk-bristuff/Makefile b/net/asterisk-bristuff/Makefile
index 52f177b905d8..bf9289fc189c 100644
--- a/net/asterisk-bristuff/Makefile
+++ b/net/asterisk-bristuff/Makefile
@@ -7,7 +7,7 @@
PORTNAME= asterisk
PORTVERSION= 1.0.7
-PORTREVISION= 1
+PORTREVISION= 2
CATEGORIES= net
MASTER_SITES= ftp://ftp.asterisk.org/pub/telephony/asterisk/ \
ftp://ftp.asterisk.org/pub/telephony/asterisk/old-releases/
@@ -20,10 +20,12 @@ PATCH_DIST_STRIP= -p1
MAINTAINER= sobomax@FreeBSD.org
COMMENT= An Open Source PBX and telephony toolkit
-BUILD_DEPENDS= ${LOCALBASE}/lib/mysql/libmysqlclient.a:${PORTSDIR}/databases/mysql40-client
+BUILD_DEPENDS= ${LOCALBASE}/lib/mysql/libmysqlclient.a:${PORTSDIR}/databases/mysql40-client \
+ mpg123:${PORTSDIR}/audio/mpg123
LIB_DEPENDS= speex.3:${PORTSDIR}/audio/speex \
newt.51:${PORTSDIR}/devel/newt
-RUN_DEPENDS= ${LOCALBASE}/lib/mysql/libmysqlclient.a:${PORTSDIR}/databases/mysql40-client
+RUN_DEPENDS= ${LOCALBASE}/lib/mysql/libmysqlclient.a:${PORTSDIR}/databases/mysql40-client \
+ mpg123:${PORTSDIR}/audio/mpg123
ONLY_FOR_ARCHS= i386 sparc64
diff --git a/net/asterisk-bristuff/files/patch-channels::chan_oss.c b/net/asterisk-bristuff/files/patch-channels::chan_oss.c
new file mode 100644
index 000000000000..ef8cfc11d711
--- /dev/null
+++ b/net/asterisk-bristuff/files/patch-channels::chan_oss.c
@@ -0,0 +1,1167 @@
+
+$FreeBSD$
+
+--- channels/chan_oss.c
++++ channels/chan_oss.c
+@@ -13,6 +13,8 @@
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
++ *
++ * FreeBSD changes by Luigi Rizzo, 2005.04.18
+ */
+
+ #include <asterisk/lock.h>
+@@ -54,21 +56,30 @@
+ #endif
+
+ /* Lets use 160 sample frames, just like GSM. */
+-#define FRAME_SIZE 160
++/* this corresponds to 20ms of audio. */
++#define FRAME_SIZE 160 // was 160
+
+-/* When you set the frame size, you have to come up with
+- the right buffer format as well. */
++/*
++ * When you set the frame size, you have to come up with
++ * the right buffer format as well.
++ * OSS lets you define a 'block' size (which should be a power of 2,
++ * which power is specified in the lower 16 bits) and the number of
++ * blocks allowed in the buffer (to avoid that the queue grows too large).
++ * The latter is specified in the top 16 bits.
++ * We use a block of 64 bytes (0x6), 5 blocks make a frame each sample
++ * being 2 bytes, and we make room to store two buffers.
++ * XXX the '10' is magic
++ */
++
++#define N_BLOCKS (buffersize * 5 * 2)
+ /* 5 64-byte frames = one frame */
+-#define BUFFER_FMT ((buffersize * 10) << 16) | (0x0006);
++#define BUFFER_FMT (N_BLOCKS << 16) | (0x0006);
+
+ /* Don't switch between read/write modes faster than every 300 ms */
+-#define MIN_SWITCH_TIME 600
++#define MIN_SWITCH_TIME 300
+
+-static struct timeval lasttime;
+
+ static int usecnt;
+-static int silencesuppression = 0;
+-static int silencethreshold = 1000;
+
+
+ AST_MUTEX_DEFINE_STATIC(usecnt_lock);
+@@ -78,16 +89,15 @@
+ static char *tdesc = "OSS Console Channel Driver";
+ static char *config = "oss.conf";
+
+-static char context[AST_MAX_EXTENSION] = "default";
++static char default_context[AST_MAX_EXTENSION] = "default";
+ static char language[MAX_LANGUAGE] = "";
+-static char exten[AST_MAX_EXTENSION] = "s";
++static char oss_exten[AST_MAX_EXTENSION] = "s";
+
+-static int hookstate=0;
+
+-static short silence[FRAME_SIZE] = {0, };
+
+ struct sound {
+ int ind;
++ char *desc;
+ short *data;
+ int datalen;
+ int samplen;
+@@ -96,136 +106,178 @@
+ };
+
+ static struct sound sounds[] = {
+- { AST_CONTROL_RINGING, ringtone, sizeof(ringtone)/2, 16000, 32000, 1 },
+- { AST_CONTROL_BUSY, busy, sizeof(busy)/2, 4000, 4000, 1 },
+- { AST_CONTROL_CONGESTION, busy, sizeof(busy)/2, 2000, 2000, 1 },
+- { AST_CONTROL_RING, ring10, sizeof(ring10)/2, 16000, 32000, 1 },
+- { AST_CONTROL_ANSWER, answer, sizeof(answer)/2, 2200, 0, 0 },
++ { AST_CONTROL_RINGING, "RINGING", ringtone, sizeof(ringtone)/2, 16000, 32000, 1 },
++ { AST_CONTROL_BUSY, "BUSY", busy, sizeof(busy)/2, 4000, 4000, 1 },
++ { AST_CONTROL_CONGESTION, "CONGESTION", busy, sizeof(busy)/2, 2000, 2000, 1 },
++ { AST_CONTROL_RING, "RING10", ring10, sizeof(ring10)/2, 16000, 32000, 1 },
++ { AST_CONTROL_ANSWER, "ANSWER", answer, sizeof(answer)/2, 2200, 0, 0 },
++ { -1, NULL, 0, 0, 0, 0 }, /* end marker */
+ };
+
+-/* Sound command pipe */
+-static int sndcmd[2];
++
+
+ 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. */
++ /*
++ * cursound indicates which in struct sound we play. -1 means nothing,
++ * any other value is a valid sound, in which case sampsent indicates
++ * the next sample to send in [0..samplen + silencelen]
++ * nosound is set to disable the audio data from the channel
++ * (so we can play the tones etc.).
++ */
++ int sndcmd[2]; /* Sound command pipe */
++ int cursound; /* index of sound to send */
++ int sampsent; /* # of sound samples sent */
++ int nosound;
++
++ int total_blocks; /* total blocks in the output device */
++ int sounddev;
++ enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex;
++ int autoanswer;
++ int autohangup;
++ int hookstate;
++ struct timeval lasttime; /* last setformat */
++
++ int silencesuppression;
++ int silencethreshold;
++ char device[64]; /* device to open */
++
++ pthread_t sthread;
++
+ struct ast_channel *owner;
+ char exten[AST_MAX_EXTENSION];
+ char context[AST_MAX_EXTENSION];
+-} oss;
++} oss = {
++ .cursound = -1,
++ .sounddev = -1,
++ .duplex = M_UNSET, /* XXX check this */
++ .autoanswer = 1,
++ .autohangup = 1,
++ .silencethreshold = 1000,
++};
+
+-static int time_has_passed(void)
++/*
++ * returns true if too early to switch
++ */
++static int too_early(struct chan_oss_pvt *o)
+ {
+ 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)
++ ms = (tv.tv_sec - o->lasttime.tv_sec) * 1000 +
++ (tv.tv_usec - o->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. */
+-
+-static pthread_t sthread;
+-
+-#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;
+-
+-#if 0
+-static int calc_loudness(short *frame)
++/*
++ * Returns the number of blocks used in the audio output channel
++ */
++static int
++used_blocks(struct chan_oss_pvt *o)
+ {
+- int sum = 0;
+- int x;
+- for (x=0;x<FRAME_SIZE;x++) {
+- if (frame[x] < 0)
+- sum -= frame[x];
+- else
+- sum += frame[x];
++ struct audio_buf_info info;
++ static int warned=0;
++ if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) {
++ if (!warned) {
++ ast_log(LOG_WARNING, "Error reading output space\n");
++ warned++;
+ }
+- sum = sum/FRAME_SIZE;
+- return sum;
++ return 1;
++ }
++ if (o->total_blocks == 0) {
++ ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n",
++ info.fragstotal,
++ info.fragsize,
++ info.fragments);
++ o->total_blocks = info.fragments;
++ }
++ return o->total_blocks - info.fragments;
+ }
+-#endif
+
+-static int cursound = -1;
+-static int sampsent = 0;
+-static int silencelen=0;
+-static int offset=0;
+-static int nosound=0;
++static int soundcard_writeframe(struct chan_oss_pvt *o, short *data)
++{
++ /* Write an exactly FRAME_SIZE sized of frame */
++ int res;
++ static int errors = 0;
+
+-static int send_sound(void)
++ /*
++ * nothing spectacular.
++ * If the buffer is full just drop the extra, otherwise write
++ */
++ res = used_blocks(o);
++ if (res > 10) { /* no room to write a block */
++ errors ++;
++ if (errors == 0)
++ ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, errors);
++ return 0;
++ }
++ errors = 0;
++ res = write(o->sounddev, ((void *)data), FRAME_SIZE * 2);
++ return res;
++}
++
++/*
++ * handler for 'sound writable' events from the sound thread.
++ * Builds a frame from the high level description of the sounds,
++ * (tone+silence) and passes it to the audio device.
++ */
++static int send_sound(struct chan_oss_pvt *o)
+ {
+ short myframe[FRAME_SIZE];
+- int total = FRAME_SIZE;
+- short *frame = NULL;
+- int amt=0;
+- int res;
+- int myoff;
+- audio_buf_info abi;
+- if (cursound > -1) {
+- res = ioctl(sounddev, SNDCTL_DSP_GETOSPACE ,&abi);
+- if (res) {
+- ast_log(LOG_WARNING, "Unable to read output space\n");
+- return -1;
+- }
+- /* Calculate how many samples we can send, max */
+- if (total > (abi.fragments * abi.fragsize / 2))
+- total = abi.fragments * abi.fragsize / 2;
+- res = total;
+- if (sampsent < sounds[cursound].samplen) {
+- myoff=0;
+- while(total) {
+- amt = total;
+- if (amt > (sounds[cursound].datalen - offset))
+- amt = sounds[cursound].datalen - offset;
+- memcpy(myframe + myoff, sounds[cursound].data + offset, amt * 2);
+- total -= amt;
+- offset += amt;
+- sampsent += amt;
+- myoff += amt;
+- if (offset >= sounds[cursound].datalen)
+- offset = 0;
+- }
+- /* Set it up for silence */
+- if (sampsent >= sounds[cursound].samplen)
+- silencelen = sounds[cursound].silencelen;
+- frame = myframe;
+- } else {
+- if (silencelen > 0) {
+- frame = silence;
+- silencelen -= res;
+- } else {
+- if (sounds[cursound].repeat) {
+- /* Start over */
+- sampsent = 0;
+- offset = 0;
+- } else {
+- cursound = -1;
+- nosound = 0;
+- }
+- }
++ int ofs = 0;
++ int l_sampsent = o->sampsent;
++ int l;
++ struct sound *s;
++
++ if (o->cursound < 0) /* no sound to send */
++ return 0;
++ s = &sounds[o->cursound];
++ /*
++ * prepare a frame
++ */
++
++ for (ofs = 0; ofs < FRAME_SIZE; ofs += l) {
++ /* take chunks of sound and data until the buffer is full */
++ l = s->samplen - l_sampsent; /* sound available */
++ if (l > 0) {
++ if (l > FRAME_SIZE - ofs)
++ l = FRAME_SIZE - ofs;
++ if (0)
++ ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n",
++ l_sampsent, l, s->samplen, ofs);
++ bcopy(s->data + l_sampsent, myframe + ofs, l*2);
++ l_sampsent += l;
++ } else { /* no sound, maybe some silence */
++ static short silence[FRAME_SIZE] = {0, };
++
++ l += s->silencelen;
++ if (l > 0) {
++ if (l > FRAME_SIZE - ofs)
++ l = FRAME_SIZE - ofs;
++ if (0)
++ ast_log(LOG_WARNING, "send_sound silence %d/%d of %d into %d\n",
++ l_sampsent - s->samplen, l, s->silencelen, ofs);
++ bcopy(silence, myframe + ofs, l*2);
++ l_sampsent += l;
++ } else { /* silence is over, restart sound if loop */
++ if (s->repeat == 0) { /* last block */
++ ast_log(LOG_WARNING, "send_sound last block\n");
++ o->cursound = -1;
++ o->nosound = 0; /* allow audio data */
++ if (ofs < FRAME_SIZE) /* pad with silence */
++ bcopy(silence, myframe + ofs, (FRAME_SIZE - ofs)*2);
++ }
++ l_sampsent = 0;
+ }
+- if (frame)
+- res = write(sounddev, frame, res * 2);
+- if (res > 0)
+- return 0;
+- return res;
++ }
+ }
+- return 0;
++ l = soundcard_writeframe(o, myframe);
++ if (l > 0)
++ o->sampsent = l_sampsent; /* update status */
++ return 0; /* fake success */
+ }
+
+ static void *sound_thread(void *unused)
+@@ -235,41 +287,53 @@
+ int max;
+ int res;
+ char ign[4096];
+- if (read(sounddev, ign, sizeof(sounddev)) < 0)
++ if (read(oss.sounddev, ign, sizeof(ign)) < 0)
+ ast_log(LOG_WARNING, "Read error on sound device: %s\n", strerror(errno));
+ for(;;) {
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+- max = sndcmd[0];
+- FD_SET(sndcmd[0], &rfds);
++ max = oss.sndcmd[0];
++ FD_SET(oss.sndcmd[0], &rfds);
+ if (!oss.owner) {
+- FD_SET(sounddev, &rfds);
+- if (sounddev > max)
+- max = sounddev;
++ FD_SET(oss.sounddev, &rfds);
++ if (oss.sounddev > max)
++ max = oss.sounddev;
+ }
+- if (cursound > -1) {
+- FD_SET(sounddev, &wfds);
+- if (sounddev > max)
+- max = sounddev;
++ if (oss.cursound > -1) {
++ FD_SET(oss.sounddev, &wfds);
++ if (oss.sounddev > max)
++ max = oss.sounddev;
+ }
+ res = ast_select(max + 1, &rfds, &wfds, NULL, NULL);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno));
+ continue;
+ }
+- if (FD_ISSET(sndcmd[0], &rfds)) {
+- read(sndcmd[0], &cursound, sizeof(cursound));
+- silencelen = 0;
+- offset = 0;
+- sampsent = 0;
++ if (FD_ISSET(oss.sndcmd[0], &rfds)) { /* read which sound to play from the pipe */
++ int i, what;
++
++ read(oss.sndcmd[0], &what, sizeof(what));
++ for (i = 0; sounds[i].ind != -1; i++)
++ if (sounds[i].ind == what) {
++ oss.cursound = i;
++ oss.sampsent = 0;
++ oss.nosound = 1; /* block other audio */
++ ast_log(LOG_WARNING, "play %s\n", sounds[i].desc);
++ break;
++ }
++ if (sounds[i].ind == -1)
++ oss.cursound = -1;
++ ast_log(LOG_WARNING, "cursound %d samplen %d silencelen %d\n",
++ oss.cursound, oss.cursound >=0 ? sounds[oss.cursound].samplen : -1,
++ oss.cursound >=0 ? sounds[oss.cursound].silencelen : -1);
+ }
+- if (FD_ISSET(sounddev, &rfds)) {
++ if (FD_ISSET(oss.sounddev, &rfds)) {
+ /* Ignore read */
+- if (read(sounddev, ign, sizeof(ign)) < 0)
++ if (read(oss.sounddev, ign, sizeof(ign)) < 0)
+ ast_log(LOG_WARNING, "Read error on sound device: %s\n", strerror(errno));
+ }
+- if (FD_ISSET(sounddev, &wfds))
+- if (send_sound())
++ if (FD_ISSET(oss.sounddev, &wfds))
++ if (send_sound(&oss) < 0)
+ ast_log(LOG_WARNING, "Failed to write sound\n");
+ }
+ /* Never reached */
+@@ -277,6 +341,20 @@
+ }
+
+ #if 0
++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
+@@ -284,7 +362,7 @@
+ static int silentframes = 0;
+ static char silbuf[FRAME_SIZE * 2 * SILBUF];
+ static int silbufcnt=0;
+- if (!silencesuppression)
++ if (!oss.silencesuppression)
+ return 0;
+ loudness = calc_loudness((short *)(buf));
+ if (option_debug)
+@@ -309,7 +387,7 @@
+ /* Write any buffered silence we have, it may have something
+ important */
+ if (silbufcnt) {
+- write(sounddev, silbuf, silbufcnt * FRAME_SIZE);
++ write(oss.sounddev, silbuf, silbufcnt * FRAME_SIZE);
+ silbufcnt = 0;
+ }
+ }
+@@ -317,27 +395,55 @@
+ }
+ #endif
+
+-static int setformat(void)
++/*
++ * reset and close the device if opened,
++ * then open and initialize it in the desired mode,
++ * trigger reads and writes so we can start using it.
++ */
++static int setformat(struct chan_oss_pvt *o, int mode)
+ {
+- int fmt, desired, res, fd = sounddev;
++ int fmt, desired, res, fd;
+ static int warnedalready = 0;
+ static int warnedalready2 = 0;
++
++ if (o->sounddev >= 0) {
++ ioctl(o->sounddev, SNDCTL_DSP_RESET, 0);
++ close(o->sounddev);
++ o->duplex = M_UNSET;
++ }
++ fd = o->sounddev = open(o->device, mode |O_NONBLOCK);
++ if (o->sounddev < 0) {
++ ast_log(LOG_WARNING, "Unable to re-open DSP device: %s\n",
++ strerror(errno));
++ return -1;
++ }
++
++ gettimeofday(&o->lasttime, NULL);
+ 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");
+ return -1;
+ }
+- res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+-
+- /* Check to see if duplex set (FreeBSD Bug)*/
+- res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt);
+-
+- if ((fmt & DSP_CAP_DUPLEX) && !res) {
+- if (option_verbose > 1)
++ switch (mode) {
++ case O_RDWR:
++ res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
++ /* Check to see if duplex set (FreeBSD Bug)*/
++ res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt);
++ if (res == 0 && (fmt & DSP_CAP_DUPLEX)) {
++ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n");
+- full_duplex = -1;
++ o->duplex = M_FULL;
++ };
++ break;
++ case O_WRONLY:
++ o->duplex = M_WRITE;
++ break;
++ case O_RDONLY:
++ o->duplex = M_READ;
++ break;
+ }
++
+ fmt = 0;
+ res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
+ if (res < 0) {
+@@ -348,6 +454,7 @@
+ 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");
+ return -1;
+@@ -357,89 +464,54 @@
+ ast_log(LOG_WARNING, "Requested %d Hz, got %d Hz -- sound may be choppy\n", desired, fmt);
+ }
+ #if 1
+- fmt = BUFFER_FMT;
++ /*
++ * on freebsd, SETFRAGMENT does not work very well on some cards.
++ * Better leave it out
++ */
++
++ // fmt = BUFFER_FMT;
++ fmt = 0x8; // 256-bytes fragment
+ 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
++ /* XXX on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */
++ res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
++ res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res);
++ /* it may fail if we are in half duplex, never mind */
+ return 0;
+ }
+
++/*
++ * make sure output mode is available. Returns 0 if done,
++ * 1 if too early to switch, -1 if error
++ */
+ static int soundcard_setoutput(int force)
+ {
+- /* Make sure the soundcard is in output mode. */
+- int fd = sounddev;
+- if (full_duplex || (!readmode && !force))
+- return 0;
+- readmode = 0;
+- if (force || time_has_passed()) {
+- ioctl(sounddev, SNDCTL_DSP_RESET, 0);
+- /* 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 |O_NONBLOCK);
+- if (fd < 0) {
+- ast_log(LOG_WARNING, "Unable to re-open DSP device: %s\n", strerror(errno));
+- 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));
+- return -1;
+- }
+- if (setformat()) {
+- return -1;
+- }
++ if (oss.duplex == M_FULL || (oss.duplex == M_WRITE && !force))
+ return 0;
+- }
+- return 1;
++ if (!force && too_early(&oss))
++ return 1;
++ if (setformat(&oss, O_WRONLY))
++ return -1;
++ return 0;
+ }
+
++/*
++ * make sure input mode is available. Returns 0 if done
++ * 1 if too early to switch, -1 if error
++ */
+ static int soundcard_setinput(int force)
+ {
+- int fd = sounddev;
+- if (full_duplex || (readmode && !force))
+- return 0;
+- readmode = -1;
+- if (force || time_has_passed()) {
+- ioctl(sounddev, SNDCTL_DSP_RESET, 0);
+- close(sounddev);
+- /* dup2(0, sound); */
+- fd = open(DEV_DSP, O_RDONLY | O_NONBLOCK);
+- if (fd < 0) {
+- ast_log(LOG_WARNING, "Unable to re-open DSP device: %s\n", strerror(errno));
+- 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));
+- return -1;
+- }
+- if (setformat()) {
+- return -1;
+- }
++ if (oss.duplex == M_FULL || (oss.duplex == M_READ && !force))
+ return 0;
+- }
+- return 1;
+-}
+-
+-static int soundcard_init(void)
+-{
+- /* Assume it's full duplex for starters */
+- int fd = open(DEV_DSP, O_RDWR | O_NONBLOCK);
+- if (fd < 0) {
+- ast_log(LOG_WARNING, "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;
++ if (!force && too_early(&oss))
++ return 1;
++ if (setformat(&oss, O_RDONLY))
++ return -1;
++ return 0;
+ }
+
+ static int oss_digit(struct ast_channel *c, char digit)
+@@ -454,120 +526,81 @@
+ return 0;
+ }
+
++/* request to play a sound on the speaker */
++#define RING(x) { int what = x; write(oss.sndcmd[1], &what, sizeof(what)); }
++
+ static int oss_call(struct ast_channel *c, char *dest, int timeout)
+ {
+- int res = 3;
+ struct ast_frame f = { 0, };
+ ast_verbose( " << Call placed to '%s' on console >> \n", dest);
+- if (autoanswer) {
++ if (oss.autoanswer) {
+ ast_verbose( " << Auto-answered >> \n" );
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_ANSWER;
+ ast_queue_frame(c, &f);
+ } else {
+- nosound = 1;
+ ast_verbose( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
+ f.frametype = AST_FRAME_CONTROL;
+ f.subclass = AST_CONTROL_RINGING;
+ ast_queue_frame(c, &f);
+- write(sndcmd[1], &res, sizeof(res));
++ RING(AST_CONTROL_RING);
+ }
+ return 0;
+ }
+
+ static void answer_sound(void)
+ {
+- int res;
+- nosound = 1;
+- res = 4;
+- write(sndcmd[1], &res, sizeof(res));
+-
++ RING(AST_CONTROL_ANSWER);
+ }
+
+ static int oss_answer(struct ast_channel *c)
+ {
+ ast_verbose( " << Console call has been answered >> \n");
+- answer_sound();
++ answer_sound(); /* XXX do we really need it ? considering we shut down immediately... */
+ ast_setstate(c, AST_STATE_UP);
+- cursound = -1;
+- nosound=0;
++ oss.cursound = -1;
++ oss.nosound=0;
+ return 0;
+ }
+
+ static int oss_hangup(struct ast_channel *c)
+ {
+- int res = 0;
+- cursound = -1;
++ oss.cursound = -1;
+ c->pvt->pvt = NULL;
+ oss.owner = NULL;
+ ast_verbose( " << Hangup on console >> \n");
+ ast_mutex_lock(&usecnt_lock);
+ usecnt--;
+ ast_mutex_unlock(&usecnt_lock);
+- if (hookstate) {
+- if (autoanswer) {
++ if (oss.hookstate) {
++ if (oss.autoanswer || oss.autohangup) {
+ /* Assume auto-hangup too */
+- hookstate = 0;
++ oss.hookstate = 0;
+ } else {
+ /* Make congestion noise */
+- res = 2;
+- write(sndcmd[1], &res, sizeof(res));
++ RING(AST_CONTROL_CONGESTION);
+ }
+ }
+ return 0;
+ }
+
+-static int soundcard_writeframe(short *data)
+-{
+- /* Write an exactly FRAME_SIZE sized of frame */
+- static int bufcnt = 0;
+- static short buffer[FRAME_SIZE * MAX_BUFFER_SIZE * 5];
+- struct audio_buf_info info;
+- int res;
+- int fd = sounddev;
+- static int warned=0;
+- 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), data, FRAME_SIZE * 2);
+- bufcnt++;
+- if (bufcnt == buffersize) {
+- res = write(fd, ((void *)buffer), FRAME_SIZE * 2 * buffersize);
+- }
+- }
+- return res;
+-}
+-
+-
++/* used for data coming from the network */
+ 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;
++ int src;
++
++ // ast_log(LOG_WARNING, "oss_write size %d\n", f->datalen);
+ /* Immediately return if no sound is enabled */
+- if (nosound)
++ if (oss.nosound)
+ return 0;
+ /* Stop any currently playing sound */
+- cursound = -1;
+- if (!full_duplex) {
++ oss.cursound = -1;
++ if (oss.duplex != M_FULL) {
++ /* XXX check this, looks weird! */
+ /* If we're half duplex, we have to switch to read mode
+ to honor immediate needs if necessary */
+- res = soundcard_setinput(1);
++ res = soundcard_setinput(1); /* force set if not full_duplex */
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set device to input mode\n");
+ return -1;
+@@ -583,21 +616,30 @@
+ 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;
++ /*
++ * we could receive a sample which is not a multiple of our FRAME_SIZE,
++ * so we buffer it locally and write to the device in FRAME_SIZE
++ * chunks, keeping the residue stored for future use.
++ */
++
++ src = 0; /* read position into f->data */
++ while ( src < f->datalen ) {
++ static char buf[FRAME_SIZE*2];
++ static int dst = 0;
++ int l = sizeof(buf) - dst; /* how much room in the buffer */
++
++ if (f->datalen - src >= l) { /* enough to fill a frame */
++ memcpy(buf + dst, f->data + src, l);
++ soundcard_writeframe(&oss, (short *)buf);
++ src += l;
++ dst = 0;
++ } else { /* copy residue */
++ l = f->datalen - src;
++ memcpy(buf + dst, f->data + src, l);
++ src += l; /* but really, we are done */
++ dst += l;
++ }
+ }
+- if (len - pos)
+- memmove(sizbuf, sizbuf + pos, len - pos);
+- sizpos = len - pos;
+ return 0;
+ }
+
+@@ -628,18 +670,15 @@
+ ast_log(LOG_WARNING, "Unable to set input mode\n");
+ return NULL;
+ }
+- if (res > 0) {
++ if (res > 0) { /* too early to switch ? */
+ /* Theoretically shouldn't happen, but anyway, return a NULL frame */
+ return &f;
+ }
+- res = read(sounddev, buf + AST_FRIENDLY_OFFSET + readpos, FRAME_SIZE * 2 - readpos);
+- if (res < 0) {
+- ast_log(LOG_WARNING, "Error reading from sound device (If you're running 'artsd' then kill it): %s\n", strerror(errno));
+-#if 0
+- CRASH;
+-#endif
+- return NULL;
+- }
++
++ res = read(oss.sounddev, buf + AST_FRIENDLY_OFFSET + readpos, FRAME_SIZE * 2 - readpos);
++ // ast_log(LOG_WARNING, "oss_read() fd %d got %d\n", oss.sounddev, res);
++ if (res < 0) /* audio data not ready, return a NULL frame */
++ return &f;
+ readpos += res;
+
+ if (readpos >= FRAME_SIZE * 2) {
+@@ -682,64 +721,66 @@
+ int res;
+ switch(cond) {
+ case AST_CONTROL_BUSY:
+- res = 1;
+- break;
+ case AST_CONTROL_CONGESTION:
+- res = 2;
+- break;
+ case AST_CONTROL_RINGING:
+- res = 0;
++ res = cond;
+ break;
+ case -1:
+- cursound = -1;
++ oss.cursound = -1;
+ return 0;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, chan->name);
+ return -1;
+ }
+ if (res > -1) {
+- write(sndcmd[1], &res, sizeof(res));
++ RING(res);
+ }
+ return 0;
+ }
+
+-static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state)
++static struct ast_channel *oss_new(struct chan_oss_pvt *oss, int state)
+ {
+ struct ast_channel *tmp;
++ struct ast_channel_pvt *pvt;
++
+ tmp = ast_channel_alloc(1);
+- if (tmp) {
+- snprintf(tmp->name, sizeof(tmp->name), "OSS/%s", DEV_DSP + 5);
+- tmp->type = type;
+- tmp->fds[0] = sounddev;
+- tmp->nativeformats = AST_FORMAT_SLINEAR;
+- tmp->pvt->pvt = p;
+- tmp->pvt->send_digit = oss_digit;
+- tmp->pvt->send_text = oss_text;
+- tmp->pvt->hangup = oss_hangup;
+- tmp->pvt->answer = oss_answer;
+- tmp->pvt->read = oss_read;
+- tmp->pvt->call = oss_call;
+- tmp->pvt->write = oss_write;
+- tmp->pvt->indicate = oss_indicate;
+- tmp->pvt->fixup = oss_fixup;
+- if (strlen(p->context))
+- strncpy(tmp->context, p->context, sizeof(tmp->context)-1);
+- if (strlen(p->exten))
+- strncpy(tmp->exten, p->exten, sizeof(tmp->exten)-1);
+- if (strlen(language))
+- strncpy(tmp->language, language, sizeof(tmp->language)-1);
+- p->owner = tmp;
+- ast_setstate(tmp, state);
+- ast_mutex_lock(&usecnt_lock);
+- usecnt++;
+- ast_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;
+- }
++ if (tmp == NULL)
++ return NULL;
++ snprintf(tmp->name, sizeof(tmp->name), "OSS/%s", oss->device + 5);
++ tmp->type = type;
++ tmp->fds[0] = oss->sounddev;
++ tmp->nativeformats = AST_FORMAT_SLINEAR;
++ pvt = tmp->pvt;
++ pvt->pvt = oss;
++#if 1
++ pvt->send_digit = oss_digit;
++ pvt->send_text = oss_text;
++ pvt->hangup = oss_hangup;
++ pvt->answer = oss_answer;
++ pvt->read = oss_read;
++ pvt->call = oss_call;
++ pvt->write = oss_write;
++ pvt->indicate = oss_indicate;
++ pvt->fixup = oss_fixup;
++#endif
++ if (strlen(oss->context))
++ strncpy(tmp->context, oss->context, sizeof(tmp->context)-1);
++ if (strlen(oss->exten))
++ strncpy(tmp->exten, oss->exten, sizeof(tmp->exten)-1);
++ if (strlen(language))
++ strncpy(tmp->language, language, sizeof(tmp->language)-1);
++ oss->owner = tmp;
++ ast_setstate(tmp, state);
++ ast_mutex_lock(&usecnt_lock);
++ usecnt++;
++ ast_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;
++ /* XXX what about oss->owner and the channel itself ? */
+ }
+ }
+ return tmp;
+@@ -770,13 +811,13 @@
+ if ((argc != 1) && (argc != 2))
+ return RESULT_SHOWUSAGE;
+ if (argc == 1) {
+- ast_cli(fd, "Auto answer is %s.\n", autoanswer ? "on" : "off");
++ ast_cli(fd, "Auto answer is %s.\n", oss.autoanswer ? "on" : "off");
+ return RESULT_SUCCESS;
+ } else {
+ if (!strcasecmp(argv[1], "on"))
+- autoanswer = -1;
++ oss.autoanswer = -1;
+ else if (!strcasecmp(argv[1], "off"))
+- autoanswer = 0;
++ oss.autoanswer = 0;
+ else
+ return RESULT_SHOWUSAGE;
+ }
+@@ -788,12 +829,14 @@
+ #ifndef MIN
+ #define MIN(a,b) ((a) < (b) ? (a) : (b))
+ #endif
++ int l = strlen(word);
++
+ switch(state) {
+ case 0:
+- if (strlen(word) && !strncasecmp(word, "on", MIN(strlen(word), 2)))
++ if (l && !strncasecmp(word, "on", MIN(l, 2)))
+ return strdup("on");
+ case 1:
+- if (strlen(word) && !strncasecmp(word, "off", MIN(strlen(word), 3)))
++ if (l && !strncasecmp(word, "off", MIN(l, 3)))
+ return strdup("off");
+ default:
+ return NULL;
+@@ -816,8 +859,8 @@
+ ast_cli(fd, "No one is calling us\n");
+ return RESULT_FAILURE;
+ }
+- hookstate = 1;
+- cursound = -1;
++ oss.hookstate = 1;
++ oss.cursound = -1;
+ ast_queue_frame(oss.owner, &f);
+ answer_sound();
+ return RESULT_SUCCESS;
+@@ -863,12 +906,12 @@
+ {
+ if (argc != 1)
+ return RESULT_SHOWUSAGE;
+- cursound = -1;
+- if (!oss.owner && !hookstate) {
++ oss.cursound = -1;
++ if (!oss.owner && !oss.hookstate) {
+ ast_cli(fd, "No call to hangup up\n");
+ return RESULT_FAILURE;
+ }
+- hookstate = 0;
++ oss.hookstate = 0;
+ if (oss.owner) {
+ ast_queue_hangup(oss.owner);
+ }
+@@ -900,8 +943,8 @@
+ }
+ return RESULT_SUCCESS;
+ }
+- mye = exten;
+- myc = context;
++ mye = oss_exten;
++ myc = default_context;
+ if (argc == 2) {
+ char *stringp=NULL;
+ strncpy(tmp, argv[1], sizeof(tmp)-1);
+@@ -916,7 +959,7 @@
+ if (ast_exists_extension(NULL, myc, mye, 1, NULL)) {
+ strncpy(oss.exten, mye, sizeof(oss.exten)-1);
+ strncpy(oss.context, myc, sizeof(oss.context)-1);
+- hookstate = 1;
++ oss.hookstate = 1;
+ oss_new(&oss, AST_STATE_RINGING);
+ } else
+ ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
+@@ -974,21 +1017,47 @@
+ int res;
+ int x;
+ struct ast_config *cfg;
+- struct ast_variable *v;
+- res = pipe(sndcmd);
++
++ res = pipe(oss.sndcmd);
+ if (res) {
+ ast_log(LOG_ERROR, "Unable to create pipe\n");
+ return -1;
+ }
+- res = soundcard_init();
+- if (res < 0) {
++ /* load config file */
++ if ((cfg = ast_load(config))) {
++ struct ast_variable *v = ast_variable_browse(cfg, "general");
++ while(v) {
++ if (!strcasecmp(v->name, "autoanswer"))
++ oss.autoanswer = ast_true(v->value);
++ else if (!strcasecmp(v->name, "autohangup"))
++ oss.autohangup = ast_true(v->value);
++ else if (!strcasecmp(v->name, "oss.silencesuppression"))
++ oss.silencesuppression = ast_true(v->value);
++ else if (!strcasecmp(v->name, "silencethreshold"))
++ oss.silencethreshold = atoi(v->value);
++ else if (!strcasecmp(v->name, "device"))
++ strncpy(oss.device, v->value, sizeof(oss.device)-1);
++ else if (!strcasecmp(v->name, "context"))
++ strncpy(default_context, v->value, sizeof(default_context)-1);
++ else if (!strcasecmp(v->name, "language"))
++ strncpy(language, v->value, sizeof(language)-1);
++ else if (!strcasecmp(v->name, "extension"))
++ strncpy(oss_exten, v->value, sizeof(oss_exten)-1);
++ v=v->next;
++ }
++ ast_destroy(cfg);
++ }
++ if (!strlen(oss.device))
++ strncpy(oss.device, DEV_DSP, sizeof(oss.device)-1);
++ if (setformat(&oss, O_RDWR) < 0) { /* open device */
+ if (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2 "No sound card detected -- console channel will be unavailable\n");
+ ast_verbose(VERBOSE_PREFIX_2 "Turn off OSS support by adding 'noload=chan_oss.so' in /etc/asterisk/modules.conf\n");
+ }
+ return 0;
+ }
+- if (!full_duplex)
++ soundcard_setinput(1); /* force set if not full_duplex */
++ if (oss.duplex != M_FULL)
+ ast_log(LOG_WARNING, "XXX I don't work right with non-full duplex sound cards XXX\n");
+ res = ast_channel_register(type, tdesc, AST_FORMAT_SLINEAR, oss_request);
+ if (res < 0) {
+@@ -997,26 +1066,7 @@
+ }
+ for (x=0;x<sizeof(myclis)/sizeof(struct ast_cli_entry); x++)
+ ast_cli_register(myclis + x);
+- if ((cfg = ast_load(config))) {
+- 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)-1);
+- else if (!strcasecmp(v->name, "language"))
+- strncpy(language, v->value, sizeof(language)-1);
+- else if (!strcasecmp(v->name, "extension"))
+- strncpy(exten, v->value, sizeof(exten)-1);
+- v=v->next;
+- }
+- ast_destroy(cfg);
+- }
+- ast_pthread_create(&sthread, NULL, sound_thread, NULL);
++ ast_pthread_create(&oss.sthread, NULL, sound_thread, NULL);
+ return 0;
+ }
+
+@@ -1027,15 +1077,16 @@
+ int x;
+ for (x=0;x<sizeof(myclis)/sizeof(struct ast_cli_entry); x++)
+ ast_cli_unregister(myclis + x);
+- close(sounddev);
+- if (sndcmd[0] > 0) {
+- close(sndcmd[0]);
+- close(sndcmd[1]);
++ close(oss.sounddev);
++ if (oss.sndcmd[0] > 0) {
++ close(oss.sndcmd[0]);
++ close(oss.sndcmd[1]);
+ }
+ if (oss.owner)
+ ast_softhangup(oss.owner, AST_SOFTHANGUP_APPUNLOAD);
+ if (oss.owner)
+ return -1;
++ /* XXX what about the thread ? */
+ return 0;
+ }
+