Version 0.1.12 from FTP
[asterisk/asterisk.git] / channels / chan_phone.c
index 26826d2..b7f82ea 100755 (executable)
@@ -34,7 +34,7 @@
 #include <linux/ixjuser.h>
 #include "DialTone.h"
 
-#define phone_MAX_BUF 480
+#define PHONE_MAX_BUF 480
 
 static char *desc = "Linux Telephony API Support";
 static char *type = "Phone";
@@ -44,6 +44,8 @@ static char *config = "phone.conf";
 /* Default context for dialtone mode */
 static char context[AST_MAX_EXTENSION] = "default";
 
+/* Default language */
+static char language[MAX_LANGUAGE] = "";
 static int usecnt =0;
 
 static int echocancel = AEC_OFF;
@@ -72,6 +74,7 @@ static int restart_monitor(void);
    
 #define MODE_DIALTONE  1
 #define MODE_IMMEDIATE 2
+#define MODE_FXO               3
    
 static struct phone_pvt {
        int fd;                                                 /* Raw file descriptor for this device */
@@ -84,13 +87,14 @@ static struct phone_pvt {
        struct phone_pvt *next;                 /* Next channel in list */
        struct ast_frame fr;                    /* Frame */
        char offset[AST_FRIENDLY_OFFSET];
-       char buf[phone_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[phone_MAX_BUF * 2];
+       char obuf[PHONE_MAX_BUF * 2];
        char ext[AST_MAX_EXTENSION];
+       char language[MAX_LANGUAGE];
 } *iflist = NULL;
 
 static int phone_digit(struct ast_channel *ast, char digit)
@@ -122,6 +126,7 @@ static int phone_digit(struct ast_channel *ast, char digit)
                return -1;
        }
        ioctl(p->fd, PHONE_PLAY_TONE, digit);
+       p->lastformat = -1;
        return 0;
 }
 
@@ -162,6 +167,13 @@ static int phone_hangup(struct ast_channel *ast)
                ast_log(LOG_WARNING, "Failed to stop ringing\n");
        if (ioctl(p->fd, PHONE_CPT_STOP))
                ast_log(LOG_WARNING, "Failed to stop sounds\n");
+
+       /* If it's an FXO, hang them up */
+       if (p->mode == MODE_FXO) {
+               if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK)) 
+                       ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",ast->name, strerror(errno));
+       }
+
        /* If they're off hook, give a busy signal */
        if (ioctl(p->fd, PHONE_HOOKSTATE)) {
                if (option_debug)
@@ -195,7 +207,7 @@ static int phone_setup(struct ast_channel *ast)
        p = ast->pvt->pvt;
        ioctl(p->fd, PHONE_CPT_STOP);
        /* Nothing to answering really, just start recording */
-       if (ast->format & AST_FORMAT_G723_1) {
+       if (ast->pvt->rawreadformat == AST_FORMAT_G723_1) {
                /* Prefer g723 */
                ioctl(p->fd, PHONE_REC_STOP);
                if (p->lastinput != AST_FORMAT_G723_1) {
@@ -205,7 +217,7 @@ static int phone_setup(struct ast_channel *ast)
                                return -1;
                        }
                }
-       } else if (ast->format & AST_FORMAT_SLINEAR) {
+       } else if (ast->pvt->rawreadformat == AST_FORMAT_SLINEAR) {
                ioctl(p->fd, PHONE_REC_STOP);
                if (p->lastinput != AST_FORMAT_SLINEAR) {
                        p->lastinput = AST_FORMAT_SLINEAR;
@@ -215,7 +227,7 @@ static int phone_setup(struct ast_channel *ast)
                        }
                }
        } else {
-               ast_log(LOG_WARNING, "Can't do format %d\n", ast->format);
+               ast_log(LOG_WARNING, "Can't do format %d\n", ast->pvt->rawreadformat);
                return -1;
        }
        if (ioctl(p->fd, PHONE_REC_START)) {
@@ -227,6 +239,15 @@ static int phone_setup(struct ast_channel *ast)
 
 static int phone_answer(struct ast_channel *ast)
 {
+       struct phone_pvt *p;
+       p = ast->pvt->pvt;
+       /* In case it's a LineJack, take it off hook */
+       if (p->mode == MODE_FXO) {
+               if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK)) 
+                       ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n", ast->name, strerror(errno));
+               else
+                       ast_log(LOG_DEBUG, "Took linejack off hook\n");
+       }
        phone_setup(ast);
        if (option_debug)
                ast_log(LOG_DEBUG, "phone_answer(%s)\n", ast->name);
@@ -247,7 +268,7 @@ static char phone_2digit(char c)
                return '?';
 }
 
-static struct ast_frame  *phone_read(struct ast_channel *ast)
+static struct ast_frame  *phone_exception(struct ast_channel *ast)
 {
        int res;
        union telephony_exception phonee;
@@ -264,6 +285,9 @@ static struct ast_frame  *phone_read(struct ast_channel *ast)
 
        phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION);
        if (phonee.bits.dtmf_ready)  {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "phone_exception(): DTMF\n");
+       
                /* We've got a digit -- Just handle this nicely and easily */
                digit =  ioctl(p->fd, PHONE_GET_DTMF_ASCII);
                p->fr.subclass = digit;
@@ -271,9 +295,13 @@ static struct ast_frame  *phone_read(struct ast_channel *ast)
                return &p->fr;
        }
        if (phonee.bits.hookstate) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Hookstate changed\n");
                res = ioctl(p->fd, PHONE_HOOKSTATE);
                /* See if we've gone on hook, if so, notify by returning NULL */
-               if (!res)
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "New hookstate: %d\n", res);
+               if (!res && (p->mode != MODE_FXO))
                        return NULL;
                else {
                        if (ast->state == AST_STATE_RINGING) {
@@ -287,16 +315,37 @@ static struct ast_frame  *phone_read(struct ast_channel *ast)
                                ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->state);
                }
        }
-#if 0
+#if 1
        if (phonee.bits.pstn_ring)
                ast_verbose("Unit is ringing\n");
        if (phonee.bits.caller_id) {
                ast_verbose("We have caller ID: %s\n");
        }
+       if (phonee.bits.pstn_wink)
+               ast_verbose("Detected Wink\n");
 #endif
+       /* Strange -- nothing there.. */
+       p->fr.frametype = AST_FRAME_NULL;
+       p->fr.subclass = 0;
+       return &p->fr;
+}
+
+static struct ast_frame  *phone_read(struct ast_channel *ast)
+{
+       int res;
+       struct phone_pvt *p = ast->pvt->pvt;
+
+       /* Some nice norms */
+       p->fr.datalen = 0;
+       p->fr.timelen = 0;
+       p->fr.data =  NULL;
+       p->fr.src = type;
+       p->fr.offset = 0;
+       p->fr.mallocd=0;
+
        /* Try to read some data... */
        CHECK_BLOCKING(ast);
-       res = read(p->fd, p->buf, phone_MAX_BUF);
+       res = read(p->fd, p->buf, PHONE_MAX_BUF);
        ast->blocking = 0;
        if (res < 0) {
 #if 0
@@ -370,6 +419,7 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
        char *pos;
        int sofar;
        int expected;
+       int codecset = 0;
        char tmpbuf[4];
        /* Write a frame of (presumably voice) data */
        if (frame->frametype != AST_FRAME_VOICE) {
@@ -403,6 +453,7 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
                        p->lastinput = AST_FORMAT_G723_1;
                        /* Reset output buffer */
                        p->obuflen = 0;
+                       codecset = 1;
                }
                if (frame->datalen > 24) {
                        ast_log(LOG_WARNING, "Frame size too large for G.723.1 (%d bytes)\n", frame->datalen);
@@ -423,18 +474,23 @@ static int phone_write(struct ast_channel *ast, struct ast_frame *frame)
                        }
                        p->lastformat = AST_FORMAT_SLINEAR;
                        p->lastinput = AST_FORMAT_SLINEAR;
+                       codecset = 1;
                        /* Reset output buffer */
                        p->obuflen = 0;
                }
                maxfr = 480;
        }
-       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;
+       if (codecset) {
+               ioctl(p->fd, PHONE_REC_DEPTH, 3);
+               ioctl(p->fd, PHONE_PLAY_DEPTH, 3);
+               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;
+               }
        }
        /* If we get here, we have a voice frame of Appropriate data */
        sofar = 0;
@@ -480,7 +536,7 @@ static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *conte
                tmp->type = type;
                tmp->fd = i->fd;
                /* XXX Switching formats silently causes kernel panics XXX */
-               tmp->format = prefformat;
+               tmp->nativeformats = prefformat;
                tmp->state = state;
                if (state == AST_STATE_RING)
                        tmp->rings = 1;
@@ -491,9 +547,12 @@ static struct ast_channel *phone_new(struct phone_pvt *i, int state, char *conte
                tmp->pvt->answer = phone_answer;
                tmp->pvt->read = phone_read;
                tmp->pvt->write = phone_write;
+               tmp->pvt->exception = phone_exception;
                strncpy(tmp->context, context, sizeof(tmp->context));
                if (strlen(i->ext))
                        strncpy(tmp->exten, i->ext, sizeof(tmp->exten));
+               if (strlen(i->language))
+                       strncpy(tmp->language, i->language, sizeof(tmp->language));
                i->owner = tmp;
                pthread_mutex_lock(&usecnt_lock);
                usecnt++;
@@ -596,6 +655,7 @@ static void phone_check_exception(struct phone_pvt *i)
                                ioctl(i->fd, PHONE_PLAY_STOP);
                                ioctl(i->fd, PHONE_PLAY_CODEC, ULAW);
                                ioctl(i->fd, PHONE_PLAY_START);
+                               i->lastformat = -1;
                        }
                } else {
                        if (i->dialtone) {
@@ -609,10 +669,13 @@ static void phone_check_exception(struct phone_pvt *i)
                        ioctl(i->fd, PHONE_PLAY_STOP);
                        ioctl(i->fd, PHONE_REC_STOP);
                        i->dialtone = 0;
+                       i->lastformat = -1;
                }
        }
-       if (phonee.bits.pstn_ring)
+       if (phonee.bits.pstn_ring) {
                ast_verbose("Unit is ringing\n");
+               phone_new(i, AST_STATE_RING, i->context);
+       }
        if (phonee.bits.caller_id)
                ast_verbose("We have caller ID\n");
        
@@ -786,16 +849,23 @@ static struct phone_pvt *mkif(char *iface, int mode)
                        free(tmp);
                        return NULL;
                }
+               if (mode == MODE_FXO) {
+                       if (ioctl(tmp->fd, IXJCTL_PORT, PORT_PSTN)) 
+                               ast_log(LOG_DEBUG, "Unable to set port to PSTN\n");
+               }
                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 (ioctl(tmp->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK)) 
+                       ast_log(LOG_DEBUG, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",iface, strerror(errno));
                if (echocancel != AEC_OFF)
                        ioctl(tmp->fd, IXJCTL_AEC_START, echocancel);
                if (silencesupression) 
                        tmp->silencesupression = 1;
+#ifdef PHONE_VAD
                ioctl(tmp->fd, PHONE_VAD, tmp->silencesupression);
+#endif
                tmp->mode = mode;
 #if 0
                flags = fcntl(tmp->fd, F_GETFL);
@@ -806,6 +876,7 @@ static struct phone_pvt *mkif(char *iface, int mode)
                tmp->lastinput = -1;
                tmp->ministate = 0;
                memset(tmp->ext, 0, sizeof(tmp->ext));
+               strncpy(tmp->language, language, sizeof(tmp->language));
                strncpy(tmp->dev, iface, sizeof(tmp->dev));
                strncpy(tmp->context, context, sizeof(tmp->context));
                tmp->next = NULL;
@@ -884,11 +955,15 @@ int load_module()
                                }
                } else if (!strcasecmp(v->name, "silencesupression")) {
                        silencesupression = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "language")) {
+                       strncpy(language, v->value, sizeof(language));
                } else if (!strcasecmp(v->name, "mode")) {
                        if (!strncasecmp(v->value, "di", 2)) 
                                mode = MODE_DIALTONE;
                        else if (!strncasecmp(v->value, "im", 2))
                                mode = MODE_IMMEDIATE;
+                       else if (!strncasecmp(v->value, "fx", 2))
+                               mode = MODE_FXO;
                        else
                                ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value);
                } else if (!strcasecmp(v->name, "context")) {
@@ -997,3 +1072,7 @@ char *description()
        return desc;
 }
 
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}