Version 0.3.0 from FTP
[asterisk/asterisk.git] / channels / chan_oss.c
index caf2403..aeba669 100755 (executable)
@@ -15,6 +15,7 @@
  * the GNU General Public License
  */
 
  * the GNU General Public License
  */
 
+#include <asterisk/lock.h>
 #include <asterisk/frame.h>
 #include <asterisk/logger.h>
 #include <asterisk/channel.h>
 #include <asterisk/frame.h>
 #include <asterisk/logger.h>
 #include <asterisk/channel.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <linux/soundcard.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <linux/soundcard.h>
+#include "busy.h"
+#include "ringtone.h"
+#include "ring10.h"
+#include "answer.h"
 
 /* Which device to use */
 #define DEV_DSP "/dev/dsp"
 
 /* Which device to use */
 #define DEV_DSP "/dev/dsp"
@@ -43,7 +48,7 @@
 /* When you set the frame size, you have to come up with
    the right buffer format as well. */
 /* 5 64-byte frames = one frame */
 /* 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);
+#define BUFFER_FMT ((buffersize * 10) << 16) | (0x0006);
 
 /* Don't switch between read/write modes faster than every 300 ms */
 #define MIN_SWITCH_TIME 600
 
 /* 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 struct timeval lasttime;
 
 static int usecnt;
-static int needanswer = 0;
-static int needhangup = 0;
 static int silencesuppression = 0;
 static int silencethreshold = 1000;
 
 static int silencesuppression = 0;
 static int silencethreshold = 1000;
 
-static char digits[80] = "";
 
 
-static pthread_mutex_t usecnt_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
 
 static char *type = "Console";
 static char *desc = "OSS Console Channel Driver";
 
 static char *type = "Console";
 static char *desc = "OSS Console Channel Driver";
@@ -66,12 +68,32 @@ static char *tdesc = "OSS Console Channel Driver";
 static char *config = "oss.conf";
 
 static char context[AST_MAX_EXTENSION] = "default";
 static char *config = "oss.conf";
 
 static char context[AST_MAX_EXTENSION] = "default";
+static char language[MAX_LANGUAGE] = "";
 static char exten[AST_MAX_EXTENSION] = "s";
 
 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;
+int hookstate=0;
+
+static short silence[FRAME_SIZE] = {0, };
+
+struct sound {
+       int ind;
+       short *data;
+       int datalen;
+       int samplen;
+       int silencelen;
+       int repeat;
+};
+
+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 },
+};
+
+/* Sound command pipe */
+static int sndcmd[2];
 
 static struct chan_oss_pvt {
        /* We only have one OSS structure -- near sighted perhaps, but it
 
 static struct chan_oss_pvt {
        /* We only have one OSS structure -- near sighted perhaps, but it
@@ -97,6 +119,7 @@ static int time_has_passed()
    with 160 sample frames, and a buffer size of 3, we have a 60ms buffer, 
    usually plenty. */
 
    with 160 sample frames, and a buffer size of 3, we have a 60ms buffer, 
    usually plenty. */
 
+pthread_t sthread;
 
 #define MAX_BUFFER_SIZE 100
 static int buffersize = 3;
 
 #define MAX_BUFFER_SIZE 100
 static int buffersize = 3;
@@ -125,6 +148,109 @@ static int calc_loudness(short *frame)
        return sum;
 }
 
        return sum;
 }
 
+static int cursound = -1;
+static int sampsent = 0;
+static int silencelen=0;
+static int offset=0;
+static int nosound=0;
+
+static int send_sound(void)
+{
+       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;
+                               }
+                       }
+               }
+               if (frame)
+                       res = write(sounddev, frame, res * 2);
+               if (res > 0)
+                       return 0;
+               return res;
+       }
+       return 0;
+}
+
+static void *sound_thread(void *unused)
+{
+       fd_set rfds;
+       fd_set wfds;
+       int max;
+       int res;
+       for(;;) {
+               FD_ZERO(&rfds);
+               FD_ZERO(&wfds);
+               max = sndcmd[0];
+               FD_SET(sndcmd[0], &rfds);
+               if (cursound > -1) {
+                       FD_SET(sounddev, &wfds);
+                       if (sounddev > max)
+                               max = sounddev;
+               }
+               res = 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(sounddev, &wfds))
+                       if (send_sound())
+                               ast_log(LOG_WARNING, "Failed to write sound\n");
+       }
+       /* Never reached */
+       return NULL;
+}
+
+#if 0
 static int silence_suppress(short *buf)
 {
 #define SILBUF 3
 static int silence_suppress(short *buf)
 {
 #define SILBUF 3
@@ -157,57 +283,23 @@ static int silence_suppress(short *buf)
                /* Write any buffered silence we have, it may have something
                   important */
                if (silbufcnt) {
                /* Write any buffered silence we have, it may have something
                   important */
                if (silbufcnt) {
-                       write(funnel[1], silbuf, silbufcnt * FRAME_SIZE);
+                       write(sounddev, silbuf, silbufcnt * FRAME_SIZE);
                        silbufcnt = 0;
                }
        }
        return 0;
 }
                        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;
-}
+#endif
 
 static int setformat(void)
 {
        int fmt, desired, res, fd = sounddev;
        static int warnedalready = 0;
        static int warnedalready2 = 0;
 
 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");
        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);
                return -1;
        }
        res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
@@ -220,7 +312,6 @@ static int setformat(void)
        res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
        if (res < 0) {
                ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
        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 */
                return -1;
        }
        /* 8000 Hz desired */
@@ -229,7 +320,6 @@ static int setformat(void)
        res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
        if (res < 0) {
                ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
        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) {
                return -1;
        }
        if (fmt != desired) {
@@ -244,7 +334,6 @@ static int setformat(void)
                        ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n");
        }
 #endif
                        ast_log(LOG_WARNING, "Unable to set fragment size -- sound may be choppy\n");
        }
 #endif
-       pthread_mutex_unlock(&sound_lock);
        return 0;
 }
 
        return 0;
 }
 
@@ -254,7 +343,6 @@ static int soundcard_setoutput(int force)
        int fd = sounddev;
        if (full_duplex || (!readmode && !force))
                return 0;
        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);
        readmode = 0;
        if (force || time_has_passed()) {
                ioctl(sounddev, SNDCTL_DSP_RESET);
@@ -262,26 +350,21 @@ static int soundcard_setoutput(int force)
                   time. */
                /* dup2(0, sound); */ 
                close(sounddev);
                   time. */
                /* dup2(0, sound); */ 
                close(sounddev);
-               fd = open(DEV_DSP, O_WRONLY);
+               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));
                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));
                        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()) {
                        return -1;
                }
                if (setformat()) {
-                       pthread_mutex_unlock(&sound_lock);
                        return -1;
                }
                        return -1;
                }
-               pthread_mutex_unlock(&sound_lock);
                return 0;
        }
                return 0;
        }
-       pthread_mutex_unlock(&sound_lock);
        return 1;
 }
 
        return 1;
 }
 
@@ -290,41 +373,35 @@ static int soundcard_setinput(int force)
        int fd = sounddev;
        if (full_duplex || (readmode && !force))
                return 0;
        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); */
        readmode = -1;
        if (force || time_has_passed()) {
                ioctl(sounddev, SNDCTL_DSP_RESET);
                close(sounddev);
                /* dup2(0, sound); */
-               fd = open(DEV_DSP, O_RDONLY);
+               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));
                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));
                        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()) {
                        return -1;
                }
                if (setformat()) {
-                       pthread_mutex_unlock(&sound_lock);
                        return -1;
                }
                        return -1;
                }
-               pthread_mutex_unlock(&sound_lock);
                return 0;
        }
                return 0;
        }
-       pthread_mutex_unlock(&sound_lock);
        return 1;
 }
 
 static int soundcard_init()
 {
        /* Assume it's full duplex for starters */
        return 1;
 }
 
 static int soundcard_init()
 {
        /* Assume it's full duplex for starters */
-       int fd = open(DEV_DSP,  O_RDWR);
+       int fd = open(DEV_DSP,  O_RDWR | O_NONBLOCK);
        if (fd < 0) {
        if (fd < 0) {
-               ast_log(LOG_ERROR, "Unable to open %s: %s\n", DEV_DSP, strerror(errno));
+               ast_log(LOG_WARNING, "Unable to open %s: %s\n", DEV_DSP, strerror(errno));
                return fd;
        }
        gettimeofday(&lasttime, NULL);
                return fd;
        }
        gettimeofday(&lasttime, NULL);
@@ -341,35 +418,71 @@ static int oss_digit(struct ast_channel *c, char digit)
        return 0;
 }
 
        return 0;
 }
 
+static int oss_text(struct ast_channel *c, char *text)
+{
+       ast_verbose( " << Console Received text %s >> \n", text);
+       return 0;
+}
+
 static int oss_call(struct ast_channel *c, char *dest, int timeout)
 {
 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) {
                ast_verbose( " << Auto-answered >> \n" );
        ast_verbose( " << Call placed to '%s' on console >> \n", dest);
        if (autoanswer) {
                ast_verbose( " << Auto-answered >> \n" );
-               needanswer = 1;
+               f.frametype = AST_FRAME_CONTROL;
+               f.subclass = AST_CONTROL_ANSWER;
+               ast_queue_frame(c, &f, 0);
        } else {
        } else {
+               nosound = 1;
                ast_verbose( " << Type 'answer' to answer, or use 'autoanswer' for future calls >> \n");
                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, 0);
+               write(sndcmd[1], &res, sizeof(res));
        }
        return 0;
 }
 
        }
        return 0;
 }
 
+static void answer_sound(void)
+{
+       int res;
+       nosound = 1;
+       res = 4;
+       write(sndcmd[1], &res, sizeof(res));
+       
+}
+
 static int oss_answer(struct ast_channel *c)
 {
        ast_verbose( " << Console call has been answered >> \n");
 static int oss_answer(struct ast_channel *c)
 {
        ast_verbose( " << Console call has been answered >> \n");
-       c->state = AST_STATE_UP;
+       answer_sound();
+       ast_setstate(c, AST_STATE_UP);
+       cursound = -1;
        return 0;
 }
 
 static int oss_hangup(struct ast_channel *c)
 {
        return 0;
 }
 
 static int oss_hangup(struct ast_channel *c)
 {
+       int res = 0;
+       cursound = -1;
        c->pvt->pvt = NULL;
        oss.owner = NULL;
        ast_verbose( " << Hangup on console >> \n");
        c->pvt->pvt = NULL;
        oss.owner = NULL;
        ast_verbose( " << Hangup on console >> \n");
-       pthread_mutex_lock(&usecnt_lock);
+       ast_pthread_mutex_lock(&usecnt_lock);
        usecnt--;
        usecnt--;
-       pthread_mutex_unlock(&usecnt_lock);
-       needhangup = 0;
-       needanswer = 0;
+       ast_pthread_mutex_unlock(&usecnt_lock);
+       if (hookstate) {
+               if (autoanswer) {
+                       /* Assume auto-hangup too */
+                       hookstate = 0;
+               } else {
+                       /* Make congestion noise */
+                       res = 2;
+                       write(sndcmd[1], &res, sizeof(res));
+               }
+       }
        return 0;
 }
 
        return 0;
 }
 
@@ -377,12 +490,11 @@ static int soundcard_writeframe(short *data)
 {      
        /* Write an exactly FRAME_SIZE sized of frame */
        static int bufcnt = 0;
 {      
        /* Write an exactly FRAME_SIZE sized of frame */
        static int bufcnt = 0;
-       static char buffer[FRAME_SIZE * 2 * MAX_BUFFER_SIZE * 5];
+       static short buffer[FRAME_SIZE * MAX_BUFFER_SIZE * 5];
        struct audio_buf_info info;
        int res;
        int fd = sounddev;
        static int warned=0;
        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");
        if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info)) {
                if (!warned)
                        ast_log(LOG_WARNING, "Error reading output space\n");
@@ -399,13 +511,12 @@ static int soundcard_writeframe(short *data)
        } else {
                /* Copy the data into our buffer */
                res = FRAME_SIZE * 2;
        } else {
                /* Copy the data into our buffer */
                res = FRAME_SIZE * 2;
-               memcpy(buffer + (bufcnt * FRAME_SIZE * 2), data, 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);
                }
        }
                bufcnt++;
                if (bufcnt == buffersize) {
                        res = write(fd, ((void *)buffer), FRAME_SIZE * 2 * buffersize);
                }
        }
-       pthread_mutex_unlock(&sound_lock);
        return res;
 }
 
        return res;
 }
 
@@ -417,7 +528,12 @@ static int oss_write(struct ast_channel *chan, struct ast_frame *f)
        static int sizpos = 0;
        int len = sizpos;
        int pos;
        static int sizpos = 0;
        int len = sizpos;
        int pos;
-       if (!full_duplex && (strlen(digits) || needhangup || needanswer)) {
+       /* Immediately return if no sound is enabled */
+       if (nosound)
+               return 0;
+       /* Stop any currently playing sound */
+       cursound = -1;
+       if (!full_duplex) {
                /* If we're half duplex, we have to switch to read mode
                   to honor immediate needs if necessary */
                res = soundcard_setinput(1);
                /* If we're half duplex, we have to switch to read mode
                   to honor immediate needs if necessary */
                res = soundcard_setinput(1);
@@ -464,7 +580,7 @@ static struct ast_frame *oss_read(struct ast_channel *chan)
 #if 0
        ast_log(LOG_DEBUG, "oss_read()\n");
 #endif
 #if 0
        ast_log(LOG_DEBUG, "oss_read()\n");
 #endif
-       
+               
        f.frametype = AST_FRAME_NULL;
        f.subclass = 0;
        f.timelen = 0;
        f.frametype = AST_FRAME_NULL;
        f.subclass = 0;
        f.timelen = 0;
@@ -474,25 +590,6 @@ static struct ast_frame *oss_read(struct ast_channel *chan)
        f.src = type;
        f.mallocd = 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");
        res = soundcard_setinput(0);
        if (res < 0) {
                ast_log(LOG_WARNING, "Unable to set input mode\n");
@@ -502,16 +599,23 @@ static struct ast_frame *oss_read(struct ast_channel *chan)
                /* Theoretically shouldn't happen, but anyway, return a NULL frame */
                return &f;
        }
                /* 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);
+       res = read(sounddev, buf + AST_FRIENDLY_OFFSET + readpos, FRAME_SIZE * 2 - readpos);
        if (res < 0) {
        if (res < 0) {
-               ast_log(LOG_WARNING, "Error reading from sound device: %s\n", strerror(errno));
+               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;
        }
        readpos += res;
        
                return NULL;
        }
        readpos += res;
        
-       if (readpos == FRAME_SIZE * 2) {
+       if (readpos >= FRAME_SIZE * 2) {
                /* A real frame */
                readpos = 0;
                /* A real frame */
                readpos = 0;
+               if (chan->_state != AST_STATE_UP) {
+                       /* Don't transmit unless it's up */
+                       return &f;
+               }
                f.frametype = AST_FRAME_VOICE;
                f.subclass = AST_FORMAT_SLINEAR;
                f.timelen = FRAME_SIZE / 8;
                f.frametype = AST_FRAME_VOICE;
                f.subclass = AST_FORMAT_SLINEAR;
                f.timelen = FRAME_SIZE / 8;
@@ -520,34 +624,77 @@ static struct ast_frame *oss_read(struct ast_channel *chan)
                f.offset = AST_FRIENDLY_OFFSET;
                f.src = type;
                f.mallocd = 0;
                f.offset = AST_FRIENDLY_OFFSET;
                f.src = type;
                f.mallocd = 0;
+#if 0
+               { static int fd = -1;
+                 if (fd < 0)
+                       fd = open("output.raw", O_RDWR | O_TRUNC | O_CREAT);
+                 write(fd, f.data, f.datalen);
+               }
+#endif         
        }
        return &f;
 }
 
        }
        return &f;
 }
 
+static int oss_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+       struct chan_oss_pvt *p = newchan->pvt->pvt;
+       p->owner = newchan;
+       return 0;
+}
+
+static int oss_indicate(struct ast_channel *chan, int cond)
+{
+       int res;
+       switch(cond) {
+       case AST_CONTROL_BUSY:
+               res = 1;
+               break;
+       case AST_CONTROL_CONGESTION:
+               res = 2;
+               break;
+       case AST_CONTROL_RINGING:
+               res = 0;
+               break;
+       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));
+       }
+       return 0;       
+}
+
 static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state)
 {
        struct ast_channel *tmp;
 static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state)
 {
        struct ast_channel *tmp;
-       tmp = ast_channel_alloc();
+       tmp = ast_channel_alloc(1);
        if (tmp) {
                snprintf(tmp->name, sizeof(tmp->name), "OSS/%s", DEV_DSP + 5);
                tmp->type = type;
        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->fds[0] = sounddev;
+               tmp->nativeformats = AST_FORMAT_SLINEAR;
                tmp->pvt->pvt = p;
                tmp->pvt->send_digit = oss_digit;
                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->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->write = oss_write;
+               tmp->pvt->indicate = oss_indicate;
+               tmp->pvt->fixup = oss_fixup;
                if (strlen(p->context))
                if (strlen(p->context))
-                       strncpy(tmp->context, p->context, sizeof(tmp->context));
+                       strncpy(tmp->context, p->context, sizeof(tmp->context)-1);
                if (strlen(p->exten))
                if (strlen(p->exten))
-                       strncpy(tmp->exten, p->exten, sizeof(tmp->exten));
+                       strncpy(tmp->exten, p->exten, sizeof(tmp->exten)-1);
+               if (strlen(language))
+                       strncpy(tmp->language, language, sizeof(tmp->language)-1);
                p->owner = tmp;
                p->owner = tmp;
-               tmp->state = state;
-               pthread_mutex_lock(&usecnt_lock);
+               ast_setstate(tmp, state);
+               ast_pthread_mutex_lock(&usecnt_lock);
                usecnt++;
                usecnt++;
-               pthread_mutex_unlock(&usecnt_lock);
+               ast_pthread_mutex_unlock(&usecnt_lock);
                ast_update_use_count();
                if (state != AST_STATE_DOWN) {
                        if (ast_pbx_start(tmp)) {
                ast_update_use_count();
                if (state != AST_STATE_DOWN) {
                        if (ast_pbx_start(tmp)) {
@@ -563,6 +710,7 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state)
 static struct ast_channel *oss_request(char *type, int format, void *data)
 {
        int oldformat = format;
 static struct ast_channel *oss_request(char *type, int format, void *data)
 {
        int oldformat = format;
+       struct ast_channel *tmp;
        format &= AST_FORMAT_SLINEAR;
        if (!format) {
                ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
        format &= AST_FORMAT_SLINEAR;
        if (!format) {
                ast_log(LOG_NOTICE, "Asked to get a channel of format '%d'\n", oldformat);
@@ -572,7 +720,11 @@ static struct ast_channel *oss_request(char *type, int format, void *data)
                ast_log(LOG_NOTICE, "Already have a call on the OSS channel\n");
                return NULL;
        }
                ast_log(LOG_NOTICE, "Already have a call on the OSS channel\n");
                return NULL;
        }
-       return oss_new(&oss, AST_STATE_DOWN);
+       tmp= oss_new(&oss, AST_STATE_DOWN);
+       if (!tmp) {
+               ast_log(LOG_WARNING, "Unable to create new OSS channel\n");
+       }
+       return tmp;
 }
 
 static int console_autoanswer(int fd, int argc, char *argv[])
 }
 
 static int console_autoanswer(int fd, int argc, char *argv[])
@@ -619,13 +771,49 @@ static char autoanswer_usage[] =
 
 static int console_answer(int fd, int argc, char *argv[])
 {
 
 static int console_answer(int fd, int argc, char *argv[])
 {
+       struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
        if (argc != 1)
                return RESULT_SHOWUSAGE;
        if (!oss.owner) {
                ast_cli(fd, "No one is calling us\n");
                return RESULT_FAILURE;
        }
        if (argc != 1)
                return RESULT_SHOWUSAGE;
        if (!oss.owner) {
                ast_cli(fd, "No one is calling us\n");
                return RESULT_FAILURE;
        }
-       needanswer++;
+       hookstate = 1;
+       cursound = -1;
+       ast_queue_frame(oss.owner, &f, 1);
+       answer_sound();
+       return RESULT_SUCCESS;
+}
+
+static char sendtext_usage[] =
+"Usage: send text <message>\n"
+"       Sends a text message for display on the remote terminal.\n";
+
+static int console_sendtext(int fd, int argc, char *argv[])
+{
+       int tmparg = 1;
+       char text2send[256];
+       struct ast_frame f = { 0, };
+       if (argc < 1)
+               return RESULT_SHOWUSAGE;
+       if (!oss.owner) {
+               ast_cli(fd, "No one is calling us\n");
+               return RESULT_FAILURE;
+       }
+       if (strlen(text2send))
+               ast_cli(fd, "Warning: message already waiting to be sent, overwriting\n");
+       strcpy(text2send, "");
+       while(tmparg <= argc) {
+               strncat(text2send, argv[tmparg++], sizeof(text2send) - strlen(text2send));
+               strncat(text2send, " ", sizeof(text2send) - strlen(text2send));
+       }
+       if (strlen(text2send)) {
+               f.frametype = AST_FRAME_TEXT;
+               f.subclass = 0;
+               f.data = text2send;
+               f.datalen = strlen(text2send);
+               ast_queue_frame(oss.owner, &f, 1);
+       }
        return RESULT_SUCCESS;
 }
 
        return RESULT_SUCCESS;
 }
 
@@ -637,11 +825,15 @@ static int console_hangup(int fd, int argc, char *argv[])
 {
        if (argc != 1)
                return RESULT_SHOWUSAGE;
 {
        if (argc != 1)
                return RESULT_SHOWUSAGE;
-       if (!oss.owner) {
+       cursound = -1;
+       if (!oss.owner && !hookstate) {
                ast_cli(fd, "No call to hangup up\n");
                return RESULT_FAILURE;
        }
                ast_cli(fd, "No call to hangup up\n");
                return RESULT_FAILURE;
        }
-       needhangup++;
+       hookstate = 0;
+       if (oss.owner) {
+               ast_queue_hangup(oss.owner, 1);
+       }
        return RESULT_SUCCESS;
 }
 
        return RESULT_SUCCESS;
 }
 
@@ -654,12 +846,17 @@ static int console_dial(int fd, int argc, char *argv[])
 {
        char tmp[256], *tmp2;
        char *mye, *myc;
 {
        char tmp[256], *tmp2;
        char *mye, *myc;
+       int x;
+       struct ast_frame f = { AST_FRAME_DTMF, 0 };
        if ((argc != 1) && (argc != 2))
                return RESULT_SHOWUSAGE;
        if (oss.owner) {
        if ((argc != 1) && (argc != 2))
                return RESULT_SHOWUSAGE;
        if (oss.owner) {
-               if (argc == 2)
-                       strncat(digits, argv[1], sizeof(digits) - strlen(digits));
-               else {
+               if (argc == 2) {
+                       for (x=0;x<strlen(argv[1]);x++) {
+                               f.subclass = argv[1][x];
+                               ast_queue_frame(oss.owner, &f, 1);
+                       }
+               } 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;
                }
                        ast_cli(fd, "You're already in a call.  You can use this only to dial digits until you hangup\n");
                        return RESULT_FAILURE;
                }
@@ -668,7 +865,7 @@ static int console_dial(int fd, int argc, char *argv[])
        mye = exten;
        myc = context;
        if (argc == 2) {
        mye = exten;
        myc = context;
        if (argc == 2) {
-               strncpy(tmp, argv[1], sizeof(tmp));
+               strncpy(tmp, argv[1], sizeof(tmp)-1);
                strtok(tmp, "@");
                tmp2 = strtok(NULL, "@");
                if (strlen(tmp))
                strtok(tmp, "@");
                tmp2 = strtok(NULL, "@");
                if (strlen(tmp))
@@ -676,10 +873,11 @@ static int console_dial(int fd, int argc, char *argv[])
                if (tmp2 && strlen(tmp2))
                        myc = tmp2;
        }
                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);
+       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_new(&oss, AST_STATE_RINGING);
        } else
                ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
        return RESULT_SUCCESS;
        } else
                ast_cli(fd, "No such extension '%s' in context '%s'\n", mye, myc);
        return RESULT_SUCCESS;
@@ -689,11 +887,45 @@ static char dial_usage[] =
 "Usage: dial [extension[@context]]\n"
 "       Dials a given extensison (";
 
 "Usage: dial [extension[@context]]\n"
 "       Dials a given extensison (";
 
+static int console_transfer(int fd, int argc, char *argv[])
+{
+       char tmp[256];
+       char *context;
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       if (oss.owner && oss.owner->bridge) {
+               strncpy(tmp, argv[1], sizeof(tmp) - 1);
+               context = strchr(tmp, '@');
+               if (context) {
+                       *context = '\0';
+                       context++;
+               } else
+                       context = oss.owner->context;
+               if (ast_exists_extension(oss.owner->bridge, context, tmp, 1, oss.owner->bridge->callerid)) {
+                       ast_cli(fd, "Whee, transferring %s to %s@%s.\n", 
+                                       oss.owner->bridge->name, tmp, context);
+                       if (ast_async_goto(oss.owner->bridge, context, tmp, 1, 1))
+                               ast_cli(fd, "Failed to transfer :(\n");
+               } else {
+                       ast_cli(fd, "No such extension exists\n");
+               }
+       } else {
+               ast_cli(fd, "There is no call to transfer\n");
+       }
+       return RESULT_SUCCESS;
+}
+
+static char transfer_usage[] =
+"Usage: transfer <extension>[@context]\n"
+"       Transfers the currently connected call to the given extension (and\n"
+"context if specified)\n";
 
 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 },
 
 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 },
+       { { "transfer", NULL }, console_transfer, "Transfer a call to a different extension", transfer_usage },
+       { { "send text", NULL }, console_sendtext, "Send text to the remote device", sendtext_usage },
        { { "autoanswer", NULL }, console_autoanswer, "Sets/displays autoanswer", autoanswer_usage, autoanswer_complete }
 };
 
        { { "autoanswer", NULL }, console_autoanswer, "Sets/displays autoanswer", autoanswer_usage, autoanswer_complete }
 };
 
@@ -701,31 +933,23 @@ int load_module()
 {
        int res;
        int x;
 {
        int res;
        int x;
-       int flags;
        struct ast_config *cfg = ast_load(config);
        struct ast_variable *v;
        struct ast_config *cfg = ast_load(config);
        struct ast_variable *v;
-       res = pipe(funnel);
+       res = pipe(sndcmd);
        if (res) {
                ast_log(LOG_ERROR, "Unable to create pipe\n");
                return -1;
        }
        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) {
        res = soundcard_init();
        if (res < 0) {
-               close(funnel[1]);
-               close(funnel[0]);
-               return -1;
+               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)
                ast_log(LOG_WARNING, "XXX I don't work right with non-full duplex sound cards XXX\n");
        }
        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);
        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);
@@ -743,13 +967,16 @@ int load_module()
                        else if (!strcasecmp(v->name, "silencethreshold"))
                                silencethreshold = atoi(v->value);
                        else if (!strcasecmp(v->name, "context"))
                        else if (!strcasecmp(v->name, "silencethreshold"))
                                silencethreshold = atoi(v->value);
                        else if (!strcasecmp(v->name, "context"))
-                               strncpy(context, v->value, sizeof(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"))
                        else if (!strcasecmp(v->name, "extension"))
-                               strncpy(exten, v->value, sizeof(exten));
+                               strncpy(exten, v->value, sizeof(exten)-1);
                        v=v->next;
                }
                ast_destroy(cfg);
        }
                        v=v->next;
                }
                ast_destroy(cfg);
        }
+       pthread_create(&sthread, NULL, sound_thread, NULL);
        return 0;
 }
 
        return 0;
 }
 
@@ -761,16 +988,12 @@ int unload_module()
        for (x=0;x<sizeof(myclis)/sizeof(struct ast_cli_entry); x++)
                ast_cli_unregister(myclis + x);
        close(sounddev);
        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 (sndcmd[0] > 0) {
+               close(sndcmd[0]);
+               close(sndcmd[1]);
        }
        if (oss.owner)
        }
        if (oss.owner)
-               ast_softhangup(oss.owner);
+               ast_softhangup(oss.owner, AST_SOFTHANGUP_APPUNLOAD);
        if (oss.owner)
                return -1;
        return 0;
        if (oss.owner)
                return -1;
        return 0;
@@ -784,8 +1007,13 @@ char *description()
 int usecount()
 {
        int res;
 int usecount()
 {
        int res;
-       pthread_mutex_lock(&usecnt_lock);
+       ast_pthread_mutex_lock(&usecnt_lock);
        res = usecnt;
        res = usecnt;
-       pthread_mutex_unlock(&usecnt_lock);
+       ast_pthread_mutex_unlock(&usecnt_lock);
        return res;
 }
        return res;
 }
+
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}