SS7 marked the start of an open season for trunk again but here's something minor...
[asterisk/asterisk.git] / main / channel.c
index f2cc5fa..3df9191 100644 (file)
@@ -102,6 +102,9 @@ unsigned long global_fin = 0, global_fout = 0;
 AST_THREADSTORAGE(state2str_threadbuf, state2str_threadbuf_init);
 #define STATE2STR_BUFSIZE   32
 
+/* XXX 100ms ... this won't work with wideband support */
+#define AST_DEFAULT_EMULATE_DTMF_SAMPLES 800
+
 struct chanlist {
        const struct ast_channel_tech *tech;
        AST_LIST_ENTRY(chanlist) list;
@@ -240,7 +243,8 @@ static int show_channeltype(int fd, int argc, char *argv[])
                "    Indication: %s\n"
                "     Transfer : %s\n"
                "  Capabilities: %d\n"
-               "    Send Digit: %s\n"
+               "   Digit Begin: %s\n"
+               "     Digit End: %s\n"
                "    Send HTML : %s\n"
                " Image Support: %s\n"
                "  Text Support: %s\n",
@@ -249,7 +253,8 @@ static int show_channeltype(int fd, int argc, char *argv[])
                (cl->tech->indicate) ? "yes" : "no",
                (cl->tech->transfer) ? "yes" : "no",
                (cl->tech->capabilities) ? cl->tech->capabilities : -1,
-               (cl->tech->send_digit) ? "yes" : "no",
+               (cl->tech->send_digit_begin) ? "yes" : "no",
+               (cl->tech->send_digit_end) ? "yes" : "no",
                (cl->tech->send_html) ? "yes" : "no",
                (cl->tech->send_image) ? "yes" : "no",
                (cl->tech->send_text) ? "yes" : "no"
@@ -283,18 +288,32 @@ static char *complete_channeltypes(const char *line, const char *word, int pos,
 }
 
 static char show_channeltypes_usage[] =
-"Usage: show channeltypes\n"
-"       Shows available channel types registered in your Asterisk server.\n";
+"Usage: channeltype list\n"
+"       Lists available channel types registered in your Asterisk server.\n";
 
 static char show_channeltype_usage[] =
-"Usage: show channeltype <name>\n"
+"Usage: channeltype show <name>\n"
 "      Show details about the specified channel type, <name>.\n";
 
-static struct ast_cli_entry cli_show_channeltypes =
-       { { "show", "channeltypes", NULL }, show_channeltypes, "Show available channel types", show_channeltypes_usage };
+static struct ast_cli_entry cli_show_channeltypes_deprecated = {
+       { "show", "channeltypes", NULL },
+       show_channeltypes, NULL,
+       NULL };
+
+static struct ast_cli_entry cli_show_channeltype_deprecated = {
+       { "show", "channeltype", NULL },
+       show_channeltype, NULL,
+       NULL, complete_channeltypes };
 
-static struct ast_cli_entry cli_show_channeltype =
-       { { "show", "channeltype", NULL }, show_channeltype, "Give more details on that channel type", show_channeltype_usage, complete_channeltypes };
+static struct ast_cli_entry cli_channel[] = {
+       { { "channeltype", "list", NULL },
+       show_channeltypes, "List available channel types",
+       show_channeltypes_usage, NULL, &cli_show_channeltypes_deprecated },
+
+       { { "channeltype", "show", NULL },
+       show_channeltype, "Give more details on that channel type",
+       show_channeltype_usage, complete_channeltypes, &cli_show_channeltype_deprecated },
+};
 
 /*! \brief Checks to see if a channel is needing hang up */
 int ast_check_hangup(struct ast_channel *chan)
@@ -706,7 +725,7 @@ struct ast_channel *ast_channel_alloc(int needqueue)
 int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
 {
        struct ast_frame *f;
-       struct ast_frame *prev, *cur;
+       struct ast_frame *cur;
        int blah = 1;
        int qlen = 0;
 
@@ -716,17 +735,19 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
                return -1;
        }
        ast_channel_lock(chan);
-       prev = NULL;
-       for (cur = chan->readq; cur; cur = cur->next) {
-               if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
-                       /* Don't bother actually queueing anything after a hangup */
-                       ast_frfree(f);
-                       ast_channel_unlock(chan);
-                       return 0;
-               }
-               prev = cur;
+
+       /* See if the last frame on the queue is a hangup, if so don't queue anything */
+       if ((cur = AST_LIST_LAST(&chan->readq)) && (cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
+               ast_frfree(f);
+               ast_channel_unlock(chan);
+               return 0;
+       }
+
+       /* Count how many frames exist on the queue */
+       AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
                qlen++;
        }
+
        /* Allow up to 96 voice frames outstanding, and up to 128 total frames */
        if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen  > 128)) {
                if (fin->frametype != AST_FRAME_VOICE) {
@@ -739,10 +760,7 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
                        return 0;
                }
        }
-       if (prev)
-               prev->next = f;
-       else
-               chan->readq = f;
+       AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list);
        if (chan->alertpipe[1] > -1) {
                if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
                        ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
@@ -973,7 +991,7 @@ void ast_channel_free(struct ast_channel *chan)
 {
        int fd;
        struct ast_var_t *vardata;
-       struct ast_frame *f, *fp;
+       struct ast_frame *f;
        struct varshead *headp;
        struct ast_datastore *datastore = NULL;
        char name[AST_CHANNEL_NAME];
@@ -1024,13 +1042,8 @@ void ast_channel_free(struct ast_channel *chan)
                close(fd);
        if ((fd = chan->timingfd) > -1)
                close(fd);
-       f = chan->readq;
-       chan->readq = NULL;
-       while(f) {
-               fp = f;
-               f = f->next;
-               ast_frfree(fp);
-       }
+       while ((f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list)))
+               ast_frfree(f);
        
        /* Get rid of each of the data stores on the channel */
        while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
@@ -1204,22 +1217,61 @@ int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
        return 0;
 }
 
+/* Clean up a channel's spy information */
+static void spy_cleanup(struct ast_channel *chan)
+{
+       if (!AST_LIST_EMPTY(&chan->spies->list))
+               return;
+       if (chan->spies->read_translator.path)
+               ast_translator_free_path(chan->spies->read_translator.path);
+       if (chan->spies->write_translator.path)
+               ast_translator_free_path(chan->spies->write_translator.path);
+       free(chan->spies);
+       chan->spies = NULL;
+       return;
+}
+
+/* Detach a spy from it's channel */
+static void spy_detach(struct ast_channel_spy *spy, struct ast_channel *chan)
+{
+       ast_mutex_lock(&spy->lock);
+
+       /* We only need to poke them if they aren't already done */
+       if (spy->status != CHANSPY_DONE) {
+               /* Indicate to the spy to stop */
+               spy->status = CHANSPY_STOP;
+               spy->chan = NULL;
+               /* Poke the spy if needed */
+               if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
+                       ast_cond_signal(&spy->trigger);
+       }
+
+       /* Print it out while we still have a lock so the structure can't go away (if signalled above) */
+       ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n", spy->type, chan->name);
+
+       ast_mutex_unlock(&spy->lock);
+
+       return;
+}
+
 void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
 {
-       struct ast_channel_spy *spy;
+       struct ast_channel_spy *spy = NULL;
        
        if (!chan->spies)
                return;
 
-       AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
                ast_mutex_lock(&spy->lock);
                if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
-                       spy->status = CHANSPY_STOP;
-                       if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
-                               ast_cond_signal(&spy->trigger);
-               }
-               ast_mutex_unlock(&spy->lock);
+                       ast_mutex_unlock(&spy->lock);
+                       AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
+                       spy_detach(spy, chan);
+               } else
+                       ast_mutex_unlock(&spy->lock);
        }
+       AST_LIST_TRAVERSE_SAFE_END
+       spy_cleanup(chan);
 }
 
 void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
@@ -1236,65 +1288,54 @@ void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
 
 void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
 {
-       struct ast_frame *f;
-
        if (!chan->spies)
                return;
 
        AST_LIST_REMOVE(&chan->spies->list, spy, list);
+       spy_detach(spy, chan);
+       spy_cleanup(chan);
+}
 
-       ast_mutex_lock(&spy->lock);
+void ast_channel_spy_free(struct ast_channel_spy *spy)
+{
+       struct ast_frame *f = NULL;
+
+       if (spy->status == CHANSPY_DONE)
+               return;
 
-       spy->chan = NULL;
+       /* Switch status to done in case we get called twice */
+       spy->status = CHANSPY_DONE;
 
-       for (f = spy->read_queue.head; f; f = spy->read_queue.head) {
-               spy->read_queue.head = f->next;
+       /* Drop any frames in the queue */
+       while ((f = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list)))
                ast_frfree(f);
-       }
-       for (f = spy->write_queue.head; f; f = spy->write_queue.head) {
-               spy->write_queue.head = f->next;
+       while ((f = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list)))
                ast_frfree(f);
-       }
 
+       /* Destroy the condition if in use */
        if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
                ast_cond_destroy(&spy->trigger);
 
-       ast_mutex_unlock(&spy->lock);
+       /* Destroy our mutex since it is no longer in use */
+       ast_mutex_destroy(&spy->lock);
 
-       ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n",
-               spy->type, chan->name);
-
-       if (AST_LIST_EMPTY(&chan->spies->list)) {
-               if (chan->spies->read_translator.path)
-                       ast_translator_free_path(chan->spies->read_translator.path);
-               if (chan->spies->write_translator.path)
-                       ast_translator_free_path(chan->spies->write_translator.path);
-               free(chan->spies);
-               chan->spies = NULL;
-       }
+       return;
 }
 
 static void detach_spies(struct ast_channel *chan)
 {
-       struct ast_channel_spy *spy;
+       struct ast_channel_spy *spy = NULL;
 
        if (!chan->spies)
                return;
 
-       /* Marking the spies as done is sufficient.  Chanspy or spy users will get the picture. */
-       AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
-               ast_mutex_lock(&spy->lock);
-               spy->chan = NULL;
-               if (spy->status == CHANSPY_RUNNING)
-                       spy->status = CHANSPY_DONE;
-               if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
-                       ast_cond_signal(&spy->trigger);
-               ast_mutex_unlock(&spy->lock);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list) {
+               AST_LIST_REMOVE_CURRENT(&chan->spies->list, list);
+               spy_detach(spy, chan);
        }
+       AST_LIST_TRAVERSE_SAFE_END
 
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list)
-               ast_channel_spy_remove(chan, spy);
-       AST_LIST_TRAVERSE_SAFE_END;
+       spy_cleanup(chan);
 }
 
 /*! \brief Softly hangup a channel, don't lock */
@@ -1337,9 +1378,8 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
        trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
 
        AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
-               struct ast_frame *last;
-               struct ast_frame *f1;   /* the frame to append */
                struct ast_channel_spy_queue *queue;
+               struct ast_frame *duped_fr;
 
                ast_mutex_lock(&spy->lock);
 
@@ -1370,26 +1410,17 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
                                        break;
                                }
                        }
-                       f1 = translated_frame;
-               } else {
-                       if (f->subclass != queue->format) {
-                               ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
-                                       spy->type, chan->name,
-                                       ast_getformatname(queue->format), ast_getformatname(f->subclass));
-                               ast_mutex_unlock(&spy->lock);
-                               continue;
-                       }
-                       f1 = f;
-               }
-               /* duplicate and append f1 to the tail */
-               f1 = ast_frdup(f1);
+                       duped_fr = ast_frdup(translated_frame);
+               } else if (f->subclass != queue->format) {
+                       ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
+                               spy->type, chan->name,
+                               ast_getformatname(queue->format), ast_getformatname(f->subclass));
+                       ast_mutex_unlock(&spy->lock);
+                       continue;
+               } else
+                       duped_fr = ast_frdup(f);
 
-               for (last = queue->head; last && last->next; last = last->next)
-                       ;
-               if (last)
-                       last->next = f1;
-               else
-                       queue->head = f1;
+               AST_LIST_INSERT_TAIL(&queue->list, duped_fr, frame_list);
 
                queue->samples += f->samples;
 
@@ -1425,10 +1456,8 @@ static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f,
                                        ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n",
                                                spy->type, chan->name, (dir == SPY_READ) ? "read" : "write");
                                while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
-                                       struct ast_frame *drop = queue->head;
-                                       
+                                       struct ast_frame *drop = AST_LIST_REMOVE_HEAD(&queue->list, frame_list);
                                        queue->samples -= drop->samples;
-                                       queue->head = drop->next;
                                        ast_frfree(drop);
                                }
                        }
@@ -1705,7 +1734,7 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
        /* Wait full interval */
        rms = *ms;
        if (whentohangup) {
-               rms = (whentohangup - now) * 1000;      /* timeout in milliseconds */
+               rms = whentohangup * 1000;              /* timeout in milliseconds */
                if (*ms >= 0 && *ms < rms)              /* original *ms still smaller */
                        rms =  *ms;
        }
@@ -1879,13 +1908,17 @@ int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
                                        break;
                                default:
                                        ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", f->subclass);
+                                       break;
                                }
+                               break;
                        case AST_FRAME_VOICE:
                                /* Write audio if appropriate */
                                if (audiofd > -1)
                                        write(audiofd, f->data, f->datalen);
+                       default:
+                               /* Ignore */
+                               break;
                        }
-                       /* Ignore */
                        ast_frfree(f);
                }
        }
@@ -1903,11 +1936,10 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
         */
        ast_channel_lock(chan);
        if (chan->masq) {
-               if (ast_do_masquerade(chan)) {
+               if (ast_do_masquerade(chan))
                        ast_log(LOG_WARNING, "Failed to perform masquerade\n");
-               } else {
+               else
                        f =  &ast_null_frame;
-               }
                goto done;
        }
 
@@ -1919,13 +1951,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        }
        prestate = chan->_state;
 
-       if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !ast_strlen_zero(chan->dtmfq)) {
+       if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) && 
+           !ast_strlen_zero(chan->dtmfq)) {
                /* We have DTMF that has been deferred.  Return it now */
-               chan->dtmff.frametype = AST_FRAME_DTMF;
+               chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN;
                chan->dtmff.subclass = chan->dtmfq[0];
                /* Drop first digit from the buffer */
                memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
                f = &chan->dtmff;
+               ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+               chan->emulate_dtmf_digit = f->subclass;
+               chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES;
                goto done;
        }
        
@@ -1946,7 +1982,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                        blah = ZT_EVENT_TIMER_EXPIRED;
 
                if (blah == ZT_EVENT_TIMER_PING) {
-                       if (!chan->readq || !chan->readq->next) {
+                       if (AST_LIST_EMPTY(&chan->readq) || !AST_LIST_NEXT(AST_LIST_FIRST(&chan->readq), frame_list)) {
                                /* Acknowledge PONG unless we need it again */
                                if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
                                        ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
@@ -1985,10 +2021,8 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        }
 
        /* Check for pending read queue */
-       if (chan->readq) {
-               f = chan->readq;
-               chan->readq = f->next;
-               f->next = NULL;
+       if (!AST_LIST_EMPTY(&chan->readq)) {
+               f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list);
                /* Interpret hangup and return NULL */
                /* XXX why not the same for frames from the channel ? */
                if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
@@ -2014,11 +2048,13 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
 
        if (f) {
                /* if the channel driver returned more than one frame, stuff the excess
-                  into the readq for the next ast_read call
+                  into the readq for the next ast_read call (note that we can safely assume
+                  that the readq is empty, because otherwise we would not have called into
+                  the channel driver and f would be only a single frame)
                */
-               if (f->next) {
-                       chan->readq = f->next;
-                       f->next = NULL;
+               if (AST_LIST_NEXT(f, frame_list)) {
+                       AST_LIST_HEAD_SET_NOLOCK(&chan->readq, AST_LIST_NEXT(f, frame_list));
+                       AST_LIST_NEXT(f, frame_list) = NULL;
                }
 
                switch (f->frametype) {
@@ -2039,27 +2075,57 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                }
                        }
                        break;
-               case AST_FRAME_DTMF:
-                       ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
-                       if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
+               case AST_FRAME_DTMF_END:
+                       ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
+                       if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) {
                                if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
                                        chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
                                else
                                        ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
                                ast_frfree(f);
                                f = &ast_null_frame;
-                       }
+                       } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+                               f->frametype = AST_FRAME_DTMF_BEGIN;
+                               ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+                               chan->emulate_dtmf_digit = f->subclass;
+                               if (f->samples)
+                                       chan->emulate_dtmf_samples = f->samples;
+                               else
+                                       chan->emulate_dtmf_samples = AST_DEFAULT_EMULATE_DTMF_SAMPLES;
+                       } else 
+                               ast_clear_flag(chan, AST_FLAG_IN_DTMF);
                        break;
                case AST_FRAME_DTMF_BEGIN:
                        ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
-                       break;
-               case AST_FRAME_DTMF_END:
-                       ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
+                       if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
+                               ast_frfree(f);
+                               f = &ast_null_frame;
+                       } else 
+                               ast_set_flag(chan, AST_FLAG_IN_DTMF);
                        break;
                case AST_FRAME_VOICE:
-                       if (dropaudio) {
+                       /* The EMULATE_DTMF flag must be cleared here as opposed to when the samples
+                        * first get to zero, because we want to make sure we pass at least one
+                        * voice frame through before starting the next digit, to ensure a gap
+                        * between DTMF digits. */
+                       if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_samples) {
+                               ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+                               chan->emulate_dtmf_digit = 0;
+                       }
+
+                       if (dropaudio || ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
                                ast_frfree(f);
                                f = &ast_null_frame;
+                       } else if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) {
+                               if (f->samples >= chan->emulate_dtmf_samples) {
+                                       chan->emulate_dtmf_samples = 0;
+                                       f->frametype = AST_FRAME_DTMF_END;
+                                       f->subclass = chan->emulate_dtmf_digit;
+                               } else {
+                                       chan->emulate_dtmf_samples -= f->samples;
+                                       ast_frfree(f);
+                                       f = &ast_null_frame;
+                               }
                        } else if (!(f->subclass & chan->nativeformats)) {
                                /* This frame can't be from the current native formats -- drop it on the
                                   floor */
@@ -2128,6 +2194,9 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                                        }
                                }
                        }
+               default:
+                       /* Just pass it on! */
+                       break;
                }
        } else {
                /* Make sure we always return NULL in the future */
@@ -2280,12 +2349,13 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
        return res;
 }
 
-static int do_senddigit(struct ast_channel *chan, char digit)
+int ast_senddigit_begin(struct ast_channel *chan, char digit)
 {
        int res = -1;
 
-       if (chan->tech->send_digit)
-               res = chan->tech->send_digit(chan, digit);
+       if (chan->tech->send_digit_begin)
+               res = chan->tech->send_digit_begin(chan, digit);
+
        if (res) {
                /*
                 * Device does not support DTMF tones, lets fake
@@ -2321,12 +2391,30 @@ static int do_senddigit(struct ast_channel *chan, char digit)
                        ast_log(LOG_DEBUG, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name);
                }
        }
+
+       return 0;
+}
+
+int ast_senddigit_end(struct ast_channel *chan, char digit)
+{
+       int res = -1;
+
+       if (chan->tech->send_digit_end)
+               res = chan->tech->send_digit_end(chan, digit);
+
+       if (res && chan->generator)
+               ast_playtones_stop(chan);
+       
        return 0;
 }
 
 int ast_senddigit(struct ast_channel *chan, char digit)
 {
-       return do_senddigit(chan, digit);
+       ast_senddigit_begin(chan, digit);
+       
+       ast_safe_sleep(chan, 100); /* XXX 100ms ... probably should be configurable */
+       
+       return ast_senddigit_end(chan, digit);
 }
 
 int ast_prod(struct ast_channel *chan)
@@ -2394,17 +2482,16 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                        chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
                break;
        case AST_FRAME_DTMF_BEGIN:
-               res = (chan->tech->send_digit_begin == NULL) ? 0 :
-                       chan->tech->send_digit_begin(chan, fr->subclass);
+               ast_clear_flag(chan, AST_FLAG_BLOCKING);
+               ast_channel_unlock(chan);
+               res = ast_senddigit_begin(chan, fr->subclass);
+               ast_channel_lock(chan);
+               CHECK_BLOCKING(chan);
                break;
        case AST_FRAME_DTMF_END:
-               res = (chan->tech->send_digit_end == NULL) ? 0 :
-                       chan->tech->send_digit_end(chan);
-               break;
-       case AST_FRAME_DTMF:
                ast_clear_flag(chan, AST_FLAG_BLOCKING);
                ast_channel_unlock(chan);
-               res = do_senddigit(chan,fr->subclass);
+               res = ast_senddigit_end(chan, fr->subclass);
                ast_channel_lock(chan);
                CHECK_BLOCKING(chan);
                break;
@@ -2489,7 +2576,15 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 
                        res = chan->tech->write(chan, f);
                }
-               break;  
+               break;
+       case AST_FRAME_NULL:
+       case AST_FRAME_IAX:
+               /* Ignore these */
+               res = 0;
+               break;
+       default:
+               res = chan->tech->write(chan, f);
+               break;
        }
 
        if (f && f != fr)
@@ -2533,7 +2628,7 @@ static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *fo
        /* Now we have a good choice for both. */
        ast_channel_lock(chan);
 
-       if ((*rawformat == native) && (*format == fmt)) {
+       if ((*rawformat == native) && (*format == fmt) && ((*rawformat == *format) || (*trans))) {
                /* the channel is already in these formats, so nothing to do */
                ast_channel_unlock(chan);
                return 0;
@@ -3068,11 +3163,13 @@ int ast_do_masquerade(struct ast_channel *original)
        int x,i;
        int res=0;
        int origstate;
-       struct ast_frame *cur, *prev;
+       struct ast_frame *cur;
        const struct ast_channel_tech *t;
        void *t_pvt;
        struct ast_callerid tmpcid;
        struct ast_channel *clone = original->masq;
+       struct ast_channel_spy_list *spy_list = NULL;
+       struct ast_channel_spy *spy = NULL;
        int rformat = original->readformat;
        int wformat = original->writeformat;
        char newn[100];
@@ -3132,9 +3229,9 @@ int ast_do_masquerade(struct ast_channel *original)
        clone->tech_pvt = t_pvt;
 
        /* Swap the readq's */
-       cur = original->readq;
-       original->readq = clone->readq;
-       clone->readq = cur;
+       cur = AST_LIST_FIRST(&original->readq);
+       AST_LIST_HEAD_SET_NOLOCK(&original->readq, AST_LIST_FIRST(&clone->readq));
+       AST_LIST_HEAD_SET_NOLOCK(&clone->readq, cur);
 
        /* Swap the alertpipes */
        for (i = 0; i < 2; i++) {
@@ -3151,25 +3248,45 @@ int ast_do_masquerade(struct ast_channel *original)
        original->rawwriteformat = clone->rawwriteformat;
        clone->rawwriteformat = x;
 
+       /* Swap the spies */
+       spy_list = original->spies;
+       original->spies = clone->spies;
+       clone->spies = spy_list;
+
+       /* Update channel on respective spy lists if present */
+       if (original->spies) {
+               AST_LIST_TRAVERSE(&original->spies->list, spy, list) {
+                       ast_mutex_lock(&spy->lock);
+                       spy->chan = original;
+                       ast_mutex_unlock(&spy->lock);
+               }
+       }
+       if (clone->spies) {
+               AST_LIST_TRAVERSE(&clone->spies->list, spy, list) {
+                       ast_mutex_lock(&spy->lock);
+                       spy->chan = clone;
+                       ast_mutex_unlock(&spy->lock);
+               }
+       }
+
        /* Save any pending frames on both sides.  Start by counting
         * how many we're going to need... */
-       prev = NULL;
        x = 0;
-       for (cur = clone->readq; cur; cur = cur->next) {
-               x++;
-               prev = cur;
+       if (original->alertpipe[1] > -1) {
+               AST_LIST_TRAVERSE(&clone->readq, cur, frame_list)
+                       x++;
        }
-       /* If we had any, prepend them to the ones already in the queue, and
+
+       /* If we had any, prepend them to the ones already in the queue, and 
         * load up the alertpipe */
-       if (prev) {
-               prev->next = original->readq;
-               original->readq = clone->readq;
-               clone->readq = NULL;
-               if (original->alertpipe[1] > -1) {
-                       for (i = 0; i < x; i++)
-                               write(original->alertpipe[1], &x, sizeof(x));
-               }
+       if (AST_LIST_FIRST(&clone->readq)) {
+               AST_LIST_INSERT_TAIL(&clone->readq, AST_LIST_FIRST(&original->readq), frame_list);
+               AST_LIST_HEAD_SET_NOLOCK(&original->readq, AST_LIST_FIRST(&clone->readq));
+               AST_LIST_HEAD_SET_NOLOCK(&clone->readq, NULL);
+               for (i = 0; i < x; i++)
+                       write(original->alertpipe[1], &x, sizeof(x));
        }
+       
        clone->_softhangup = AST_SOFTHANGUP_DEV;
 
 
@@ -3519,6 +3636,7 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
                                break;
                }
                if ((f->frametype == AST_FRAME_VOICE) ||
+                   (f->frametype == AST_FRAME_DTMF_BEGIN) ||
                    (f->frametype == AST_FRAME_DTMF) ||
                    (f->frametype == AST_FRAME_VIDEO) ||
                    (f->frametype == AST_FRAME_IMAGE) ||
@@ -3528,10 +3646,14 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
                        /* monitored dtmf causes exit from bridge */
                        int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf;
 
-                       if (f->frametype == AST_FRAME_DTMF && monitored_source) {
+                       if (monitored_source && 
+                               (f->frametype == AST_FRAME_DTMF_END || 
+                               f->frametype == AST_FRAME_DTMF_BEGIN)) {
                                *fo = f;
                                *rc = who;
-                               ast_log(LOG_DEBUG, "Got DTMF on channel (%s)\n", who->name);
+                               ast_log(LOG_DEBUG, "Got DTMF %s on channel (%s)\n", 
+                                       f->frametype == AST_FRAME_DTMF_END ? "end" : "begin",
+                                       who->name);
                                break;
                        }
                        /* Write immediately frames, not passed through jb */
@@ -3553,6 +3675,16 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
        return res;
 }
 
+/*! \brief Bridge two channels together (early) */
+int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
+{
+       /* Make sure we can early bridge, if not error out */
+       if (!c0->tech->early_bridge || (c1 && (!c1->tech->early_bridge || c0->tech->early_bridge != c1->tech->early_bridge)))
+               return -1;
+
+       return c0->tech->early_bridge(c0, c1);
+}
+
 /*! \brief Bridge two channels together */
 enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1,
                                          struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc)
@@ -4041,8 +4173,7 @@ void ast_moh_cleanup(struct ast_channel *chan)
 
 void ast_channels_init(void)
 {
-       ast_cli_register(&cli_show_channeltypes);
-       ast_cli_register(&cli_show_channeltype);
+       ast_cli_register_multiple(cli_channel, sizeof(cli_channel) / sizeof(struct ast_cli_entry));
 }
 
 /*! \brief Print call group and pickup group ---*/
@@ -4086,9 +4217,7 @@ static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf
        int bytestocopy;
 
        while (samples) {
-               f = queue->head;
-
-               if (!f) {
+               if (!(f = AST_LIST_FIRST(&queue->list))) {
                        ast_log(LOG_ERROR, "Ran out of frames before buffer filled!\n");
                        break;
                }
@@ -4103,10 +4232,9 @@ static void copy_data_from_queue(struct ast_channel_spy_queue *queue, short *buf
                f->datalen -= bytestocopy;
                f->offset += bytestocopy;
                queue->samples -= tocopy;
-               if (!f->samples) {
-                       queue->head = f->next;
-                       ast_frfree(f);
-               }
+
+               if (!f->samples)
+                       ast_frfree(AST_LIST_REMOVE_HEAD(&queue->list, frame_list));
        }
 }
 
@@ -4136,19 +4264,19 @@ struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsign
        if (ast_test_flag(spy, CHANSPY_TRIGGER_FLUSH)) {
                if (spy->read_queue.samples > spy->write_queue.samples) {
                        if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST)) {
-                               for (result = spy->read_queue.head; result; result = result->next)
+                               AST_LIST_TRAVERSE(&spy->read_queue.list, result, frame_list)
                                        ast_frame_adjust_volume(result, spy->read_vol_adjustment);
                        }
-                       result = spy->read_queue.head;
-                       spy->read_queue.head = NULL;
+                       result = AST_LIST_FIRST(&spy->read_queue.list);
+                       AST_LIST_HEAD_SET_NOLOCK(&spy->read_queue.list, NULL);
                        spy->read_queue.samples = 0;
                } else {
                        if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST)) {
-                               for (result = spy->write_queue.head; result; result = result->next)
+                               AST_LIST_TRAVERSE(&spy->write_queue.list, result, frame_list)
                                        ast_frame_adjust_volume(result, spy->write_vol_adjustment);
                        }
-                       result = spy->write_queue.head;
-                       spy->write_queue.head = NULL;
+                       result = AST_LIST_FIRST(&spy->write_queue.list);
+                       AST_LIST_HEAD_SET_NOLOCK(&spy->write_queue.list, NULL);
                        spy->write_queue.samples = 0;
                }
                ast_clear_flag(spy, CHANSPY_TRIGGER_FLUSH);
@@ -4159,15 +4287,10 @@ struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsign
                return NULL;
 
        /* short-circuit if both head frames have exactly what we want */
-       if ((spy->read_queue.head->samples == samples) &&
-           (spy->write_queue.head->samples == samples)) {
-               read_frame = spy->read_queue.head;
-               spy->read_queue.head = read_frame->next;
-               read_frame->next = NULL;
-
-               write_frame = spy->write_queue.head;
-               spy->write_queue.head = write_frame->next;
-               write_frame->next = NULL;
+       if ((AST_LIST_FIRST(&spy->read_queue.list)->samples == samples) &&
+           (AST_LIST_FIRST(&spy->write_queue.list)->samples == samples)) {
+               read_frame = AST_LIST_REMOVE_HEAD(&spy->read_queue.list, frame_list);
+               write_frame = AST_LIST_REMOVE_HEAD(&spy->write_queue.list, frame_list);
 
                spy->read_queue.samples -= samples;
                spy->write_queue.samples -= samples;
@@ -4200,10 +4323,10 @@ struct ast_frame *ast_channel_spy_read_frame(struct ast_channel_spy *spy, unsign
        } else {
                if (need_dup) {
                        result = ast_frdup(read_frame);
-                       result->next = ast_frdup(write_frame);
+                       AST_LIST_NEXT(result, frame_list) = ast_frdup(write_frame);
                } else {
                        result = read_frame;
-                       result->next = write_frame;
+                       AST_LIST_NEXT(result, frame_list) = write_frame;
                }
        }