--- /dev/null
+--- a/README
++++ b/README
+@@ -4,6 +4,8 @@ and the Asterisk.org developer community
+
+ Copyright (C) 2001-2006 Digium, Inc.
+ and other copyright holders.
++Copyright (C) 2002-2005 Junghanns.NET GmbH
++and other copyright holders.
+ ================================================================
+
+ * SECURITY
+--- a/LICENSE
++++ b/LICENSE
+@@ -1,7 +1,7 @@
+-Asterisk is distributed under the GNU General Public License version 2
+-and is also available under alternative licenses negotiated directly
+-with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL
+-applies to all loadable Asterisk modules used on your system as well,
++BRIstuffed Asterisk is distributed under the GNU General Public License version 2
++and is not available under any alternative licenses.
++If you obtained BRIstuffed Asterisk under the GPL, then the GPL
++applies to all loadable BRIstuffed Asterisk modules used on your system as well,
+ except as defined below. The GPL (version 2) is included in this
+ source tree in the file COPYING.
+
+--- a/doc/hardware.txt
++++ b/doc/hardware.txt
+@@ -31,6 +31,19 @@ Zaptel compatible hardware
+ * Wildcard TE410P - Quad T1/E1 switchable interface. Supports PRI and
+ RBS signalling, as well as PPP, FR, and HDLC data modes.
+
++-- Junghanns.NET (Primary author of BRIstuff)
++ http://www.junghanns.net
++
++ * quadBRI PCI ISDN - 4port BRI ISDN interface, supports NT and TE mode
++
++ * octoBRI PCI ISDN - 8port BRI ISDN interface, supports NT and TE mode
++
++ * singleE1 PCI ISDN - Single E1 interface
++
++ * doubleE1 PCI ISDN - Double E1 interface
++
++ * uno/duo/quad GSM PCI - 1/2/4 channel GSM interface cards
++
+ Non-zaptel compatible hardware
+ ==============================
+
+--- a/build_tools/make_defaults_h
++++ b/build_tools/make_defaults_h
+@@ -17,6 +17,7 @@ cat << END
+ #define AST_KEY_DIR "${INSTALL_PATH}${ASTDATADIR}/keys"
+ #define AST_DB "${INSTALL_PATH}${ASTVARLIBDIR}/astdb"
+ #define AST_TMP_DIR "${INSTALL_PATH}${ASTSPOOLDIR}/tmp"
++#define AST_SYSTEM_NAME "asterisk"
+
+ #define AST_CONFIG_FILE "${INSTALL_PATH}${ASTCONFPATH}"
+
+--- a/main/asterisk.c
++++ b/main/asterisk.c
+@@ -2427,6 +2427,7 @@ static void ast_readconfig(void)
+ ast_copy_string(ast_config_AST_PID, AST_PID, sizeof(ast_config_AST_PID));
+ ast_copy_string(ast_config_AST_SOCKET, AST_SOCKET, sizeof(ast_config_AST_SOCKET));
+ ast_copy_string(ast_config_AST_RUN_DIR, AST_RUN_DIR, sizeof(ast_config_AST_RUN_DIR));
++ ast_copy_string(ast_config_AST_SYSTEM_NAME, AST_SYSTEM_NAME, sizeof(ast_config_AST_SYSTEM_NAME));
+
+ /* no asterisk.conf? no problem, use buildtime config! */
+ if (!cfg) {
+@@ -2551,6 +2552,8 @@ static void ast_readconfig(void)
+ ast_copy_string(ast_config_AST_RUN_GROUP, v->value, sizeof(ast_config_AST_RUN_GROUP));
+ } else if (!strcasecmp(v->name, "systemname")) {
+ ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME));
++ } else if (!strcasecmp(v->name, "uniquename")) {
++ ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME));
+ } else if (!strcasecmp(v->name, "languageprefix")) {
+ ast_language_is_prefix = ast_true(v->value);
+ }
+--- a/include/asterisk/agi.h
++++ b/include/asterisk/agi.h
+@@ -30,6 +30,7 @@ extern "C" {
+ typedef struct agi_state {
+ int fd; /* FD for general output */
+ int audio; /* FD for audio output */
++ int audio_in; /* FD for audio output */
+ int ctrl; /* FD for input control */
+ unsigned int fast:1; /* flag for fast agi or not */
+ } AGI;
+--- a/res/res_agi.c
++++ b/res/res_agi.c
+@@ -11,6 +11,9 @@
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
++ * Copyright (C) 2005 Junghanns.NET GmbH
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+@@ -75,16 +78,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+
+ static char *app = "AGI";
+
++static char *xapp = "XAGI";
++
+ static char *eapp = "EAGI";
+
+ static char *deadapp = "DeadAGI";
+
+ static char *synopsis = "Executes an AGI compliant application";
++static char *xsynopsis = "Executes an XAGI compliant application";
+ static char *esynopsis = "Executes an EAGI compliant application";
+ static char *deadsynopsis = "Executes AGI on a hungup channel";
+
+ static char *descrip =
+-" [E|Dead]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
++" [E|Dead|X]AGI(command|args): Executes an Asterisk Gateway Interface compliant\n"
+ "program on a channel. AGI allows Asterisk to launch external programs\n"
+ "written in any language to control a telephony channel, play audio,\n"
+ "read DTMF digits, etc. by communicating with the AGI protocol on stdin\n"
+@@ -97,6 +103,8 @@ static char *descrip =
+ "variable to \"no\" before executing the AGI application.\n"
+ " Using 'EAGI' provides enhanced AGI, with incoming audio available out of band\n"
+ "on file descriptor 3\n\n"
++"Using 'XAGI' provides enhanced AGI, with incoming audio available out of band"
++" on file descriptor 3 and outgoing audio available out of band on file descriptor 4\n\n"
+ " Use the CLI command 'agi show' to list available agi commands\n"
+ " This application sets the following channel variable upon completion:\n"
+ " AGISTATUS The status of the attempt to the run the AGI script\n"
+@@ -236,13 +244,14 @@ static enum agi_result launch_netscript(
+ return AGI_RESULT_SUCCESS_FAST;
+ }
+
+-static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *opid)
++static enum agi_result launch_script(char *script, char *argv[], int *fds, int *efd, int *efd2, int *opid)
+ {
+ char tmp[256];
+ int pid;
+ int toast[2];
+ int fromast[2];
+ int audio[2];
++ int audio2[2];
+ int x;
+ int res;
+ sigset_t signal_set, old_set;
+@@ -287,6 +296,33 @@ static enum agi_result launch_script(cha
+ return AGI_RESULT_FAILURE;
+ }
+ }
++ if (efd2) {
++ if (pipe(audio2)) {
++ ast_log(LOG_WARNING, "unable to create audio pipe: %s\n", strerror(errno));
++ close(fromast[0]);
++ close(fromast[1]);
++ close(toast[0]);
++ close(toast[1]);
++ close(audio[0]);
++ close(audio[1]);
++ return AGI_RESULT_FAILURE;
++ }
++ res = fcntl(audio2[0], F_GETFL);
++ if (res > -1)
++ res = fcntl(audio2[0], F_SETFL, res | O_NONBLOCK);
++ if (res < 0) {
++ ast_log(LOG_WARNING, "unable to set audio pipe parameters: %s\n", strerror(errno));
++ close(fromast[0]);
++ close(fromast[1]);
++ close(toast[0]);
++ close(toast[1]);
++ close(audio[0]);
++ close(audio[1]);
++ close(audio2[0]);
++ close(audio2[1]);
++ return AGI_RESULT_FAILURE;
++ }
++ }
+
+ /* Block SIGHUP during the fork - prevents a race */
+ sigfillset(&signal_set);
+@@ -322,6 +358,11 @@ static enum agi_result launch_script(cha
+ } else {
+ close(STDERR_FILENO + 1);
+ }
++ if (efd2) {
++ dup2(audio2[1], STDERR_FILENO + 2);
++ } else {
++ close(STDERR_FILENO + 2);
++ }
+
+ /* Before we unblock our signals, return our trapped signals back to the defaults */
+ signal(SIGHUP, SIG_DFL);
+@@ -339,7 +380,7 @@ static enum agi_result launch_script(cha
+ }
+
+ /* Close everything but stdin/out/error */
+- for (x=STDERR_FILENO + 2;x<1024;x++)
++ for (x=STDERR_FILENO + 3;x<1024;x++)
+ close(x);
+
+ /* Execute script */
+@@ -359,12 +400,19 @@ static enum agi_result launch_script(cha
+ if (efd) {
+ *efd = audio[1];
+ }
++ if (efd2) {
++ *efd2 = audio2[0];
++ }
+ /* close what we're not using in the parent */
+ close(toast[1]);
+ close(fromast[0]);
+
+- if (efd)
++ if (efd) {
+ close(audio[0]);
++ }
++ if (efd2) {
++ close(audio2[1]);
++ }
+
+ *opid = pid;
+ return AGI_RESULT_SUCCESS;
+@@ -394,7 +442,7 @@ static void setup_env(struct ast_channel
+ fdprintf(fd, "agi_context: %s\n", chan->context);
+ fdprintf(fd, "agi_extension: %s\n", chan->exten);
+ fdprintf(fd, "agi_priority: %d\n", chan->priority);
+- fdprintf(fd, "agi_enhanced: %s\n", enhanced ? "1.0" : "0.0");
++ fdprintf(fd, "agi_enhanced: %d%s\n", enhanced, ".0");
+
+ /* User information */
+ fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
+@@ -1835,8 +1883,13 @@ static enum agi_result run_agi(struct as
+ int ms;
+ enum agi_result returnstatus = AGI_RESULT_SUCCESS;
+ struct ast_frame *f;
++ struct ast_frame fr;
+ char buf[AGI_BUF_LEN];
++ char audiobuf[AGI_BUF_LEN];
+ char *res = NULL;
++ int audiobytes;
++ int fds[2];
++ int enhanced = 0;
+ FILE *readf;
+ /* how many times we'll retry if ast_waitfor_nandfs will return without either
+ channel or file descriptor in case select is interrupted by a system call (EINTR) */
+@@ -1850,10 +1903,22 @@ static enum agi_result run_agi(struct as
+ return AGI_RESULT_FAILURE;
+ }
+ setlinebuf(readf);
+- setup_env(chan, request, agi->fd, (agi->audio > -1));
++ if (agi->audio > -1) {
++ enhanced = 1;
++ }
++ if (agi->audio_in > -1) {
++ enhanced++;
++ }
++ setup_env(chan, request, agi->fd, enhanced);
++ fds[0] = agi->ctrl;
++ fds[1] = agi->audio_in;
+ for (;;) {
+ ms = -1;
+- c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
++ if (agi->audio_in > -1) {
++ c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, fds, 2, NULL, &outfd, &ms);
++ } else {
++ c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
++ }
+ if (c) {
+ retry = AGI_NANDFS_RETRY;
+ /* Idle the channel until we get a command */
+@@ -1871,6 +1936,16 @@ static enum agi_result run_agi(struct as
+ ast_frfree(f);
+ }
+ } else if (outfd > -1) {
++ if ((agi->audio_in > -1) && (outfd == agi->audio_in)) {
++ audiobytes = read(agi->audio_in, audiobuf, sizeof(audiobuf));
++ if (audiobytes > 0) {
++ fr.frametype = AST_FRAME_VOICE;
++ fr.subclass = AST_FORMAT_SLINEAR;
++ fr.datalen = audiobytes;
++ fr.data = audiobuf;
++ ast_write(chan, &fr);
++ }
++ } else {
+ size_t len = sizeof(buf);
+ size_t buflen = 0;
+
+@@ -1922,6 +1997,7 @@ static enum agi_result run_agi(struct as
+ if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
+ break;
+ }
++ }
+ } else {
+ if (--retry <= 0) {
+ ast_log(LOG_WARNING, "No channel, no fd?\n");
+@@ -2030,6 +2106,7 @@ static int agi_exec_full(struct ast_chan
+ int argc = 0;
+ int fds[2];
+ int efd = -1;
++ int efd2 = -1;
+ int pid;
+ char *stringp;
+ AGI agi;
+@@ -2056,12 +2133,13 @@ static int agi_exec_full(struct ast_chan
+ }
+ #endif
+ ast_replace_sigchld();
+- res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
++ res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, (enhanced == 2) ? &efd2 : NULL, &pid);
+ if (res == AGI_RESULT_SUCCESS || res == AGI_RESULT_SUCCESS_FAST) {
+ int status = 0;
+ agi.fd = fds[1];
+ agi.ctrl = fds[0];
+ agi.audio = efd;
++ agi.audio_in = efd2;
+ agi.fast = (res == AGI_RESULT_SUCCESS_FAST) ? 1 : 0;
+ res = run_agi(chan, argv[0], &agi, pid, &status, dead);
+ /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
+@@ -2071,6 +2149,8 @@ static int agi_exec_full(struct ast_chan
+ close(fds[1]);
+ if (efd > -1)
+ close(efd);
++ if (efd2 > -1)
++ close(efd2);
+ }
+ ast_unreplace_sigchld();
+ ast_module_user_remove(u);
+@@ -2119,6 +2199,35 @@ static int eagi_exec(struct ast_channel
+ return res;
+ }
+
++static int xagi_exec(struct ast_channel *chan, void *data)
++{
++ int readformat, writeformat;
++ int res;
++
++ if (chan->_softhangup)
++ ast_log(LOG_WARNING, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
++ readformat = chan->readformat;
++ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
++ ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
++ return -1;
++ }
++ writeformat = chan->writeformat;
++ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
++ ast_log(LOG_WARNING, "Unable to set channel '%s' to linear mode\n", chan->name);
++ return -1;
++ }
++ res = agi_exec_full(chan, data, 2, 0);
++ if (!res) {
++ if (ast_set_read_format(chan, readformat)) {
++ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(readformat));
++ }
++ if (ast_set_write_format(chan, writeformat)) {
++ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(writeformat));
++ }
++ }
++ return res;
++}
++
+ static int deadagi_exec(struct ast_channel *chan, void *data)
+ {
+ if (!ast_check_hangup(chan))
+@@ -2174,6 +2283,7 @@ static int unload_module(void)
+ {
+ ast_module_user_hangup_all();
+ ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
++ ast_unregister_application(xapp);
+ ast_unregister_application(eapp);
+ ast_unregister_application(deadapp);
+ return ast_unregister_application(app);
+@@ -2184,6 +2294,7 @@ static int load_module(void)
+ ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
+ ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
+ ast_register_application(eapp, eagi_exec, esynopsis, descrip);
++ ast_register_application(xapp, xagi_exec, xsynopsis, descrip);
+ return ast_register_application(app, agi_exec, synopsis, descrip);
+ }
+
+--- /dev/null
++++ b/agi/xagi-test.c
+@@ -0,0 +1,175 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * XAGI sample script
++ *
++ * Copyright (C) 2005 Junghanns.NET GmbH
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * based on eagi-test.c
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ */
++
++#include <stdio.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <string.h>
++#include <sys/select.h>
++#ifdef SOLARIS
++#include <solaris-compat/compat.h>
++#endif
++
++#define AUDIO_FILENO_IN (STDERR_FILENO + 1)
++#define AUDIO_FILENO_OUT (STDERR_FILENO + 2)
++
++static int read_environment(void)
++{
++ char buf[256];
++ char *val;
++ /* Read environment */
++ for(;;) {
++ fgets(buf, sizeof(buf), stdin);
++ if (feof(stdin))
++ return -1;
++ buf[strlen(buf) - 1] = '\0';
++ /* Check for end of environment */
++ if (!strlen(buf))
++ return 0;
++ val = strchr(buf, ':');
++ if (!val) {
++ fprintf(stderr, "Invalid environment: '%s'\n", buf);
++ return -1;
++ }
++ *val = '\0';
++ val++;
++ val++;
++ /* Skip space */
++ // fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val);
++
++ /* Load into normal environment */
++ setenv(buf, val, 1);
++
++ }
++ /* Never reached */
++ return 0;
++}
++
++static void app_echo(void)
++{
++ fd_set fds;
++ int res;
++ int bytes = 0;
++ static char astresp[256];
++ char audiobuf[16000]; /* 1 second of audio */
++ for (;;) {
++ FD_ZERO(&fds);
++ FD_SET(STDIN_FILENO, &fds);
++ FD_SET(AUDIO_FILENO_IN, &fds);
++ /* Wait for *some* sort of I/O */
++ res = select(AUDIO_FILENO_IN + 1, &fds, NULL, NULL, NULL);
++ if (res < 0) {
++ fprintf(stderr, "Error in select: %s\n", strerror(errno));
++ return;
++ }
++ if (FD_ISSET(STDIN_FILENO, &fds)) {
++ fgets(astresp, sizeof(astresp), stdin);
++ if (feof(stdin)) {
++ return;
++ }
++ astresp[strlen(astresp) - 1] = '\0';
++ fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp);
++ return;
++ }
++ if (FD_ISSET(AUDIO_FILENO_IN, &fds)) {
++ /* what goes in.... */
++ res = read(AUDIO_FILENO_IN, audiobuf, sizeof(audiobuf));
++ if (res > 0) {
++ bytes = res;
++ /* must come out */
++ write(AUDIO_FILENO_OUT, audiobuf, bytes);
++ }
++ }
++ }
++}
++
++static char *wait_result(void)
++{
++ fd_set fds;
++ int res;
++ static char astresp[256];
++ char audiobuf[4096];
++ for (;;) {
++ FD_ZERO(&fds);
++ FD_SET(STDIN_FILENO, &fds);
++ FD_SET(AUDIO_FILENO_IN, &fds);
++ /* Wait for *some* sort of I/O */
++ res = select(AUDIO_FILENO_IN + 1, &fds, NULL, NULL, NULL);
++ if (res < 0) {
++ fprintf(stderr, "Error in select: %s\n", strerror(errno));
++ return NULL;
++ }
++ if (FD_ISSET(STDIN_FILENO, &fds)) {
++ fgets(astresp, sizeof(astresp), stdin);
++ if (feof(stdin)) {
++ fprintf(stderr, "Got hungup on apparently\n");
++ return NULL;
++ }
++ astresp[strlen(astresp) - 1] = '\0';
++ fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp);
++ return astresp;
++ }
++ if (FD_ISSET(AUDIO_FILENO_IN, &fds)) {
++ res = read(AUDIO_FILENO_IN, audiobuf, sizeof(audiobuf));
++ /* drop it, like it's hot */
++ }
++ }
++
++}
++
++static char *run_command(char *command)
++{
++ fprintf(stdout, "%s\n", command);
++ return wait_result();
++}
++
++
++static int run_script(void)
++{
++ char *res;
++ res = run_command("STREAM FILE demo-echotest \"\"");
++ if (!res) {
++ fprintf(stderr, "Failed to execute command\n");
++ return -1;
++ }
++ app_echo();
++ return 0;
++}
++
++int main(int argc, char *argv[])
++{
++ char *tmp;
++ int ver = 0;
++ int subver = 0;
++ /* Setup stdin/stdout for line buffering */
++ setlinebuf(stdin);
++ setlinebuf(stdout);
++ if (read_environment()) {
++ fprintf(stderr, "Failed to read environment: %s\n", strerror(errno));
++ exit(1);
++ }
++ tmp = getenv("agi_enhanced");
++ if (tmp) {
++ if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
++ ver = 0;
++ }
++ if (ver < 2) {
++ fprintf(stderr, "No XAGI services available. Use XAGI, not AGI or EAGI\n");
++ exit(1);
++ }
++ if (run_script())
++ return -1;
++ exit(0);
++}
+--- a/agi/Makefile
++++ b/agi/Makefile
+@@ -13,7 +13,7 @@
+
+ .PHONY: clean all uninstall
+
+-AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi
++AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi xagi-test
+
+ ifeq ($(OSARCH),SunOS)
+ LIBS+=-lsocket -lnsl
+@@ -38,7 +38,7 @@ uninstall:
+ for x in $(AGIS); do rm -f $(DESTDIR)$(AGI_DIR)/$$x ; done
+
+ clean:
+- rm -f *.so *.o look eagi-test eagi-sphinx-test
++ rm -f *.so *.o look eagi-test eagi-sphinx-test xagi-test
+ rm -f .*.o.d .*.oo.d *.s *.i
+ rm -f strcompat.c
+
+--- /dev/null
++++ b/apps/app_segfault.c
+@@ -0,0 +1,55 @@
++/*
++ * Segfault application
++ *
++ * An application to provoke a segmentation fault from the dialplan.
++ * (I know what you are thinking now...., but since Asterisk is too stable...
++ * I needed something to test my failover switches.)
++ *
++ * Copyright (C) 2005 Junghanns.NET GmbH
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License. THIS APPLICATION _WILL_ CRASH YOUR
++ * ASTERISK SERVER SO OF COURSE THERE IS NOT LIABILITY FOR NOTHING!
++ */
++
++#include "asterisk.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdio.h>
++#include <asterisk/lock.h>
++#include <asterisk/file.h>
++#include <asterisk/logger.h>
++#include <asterisk/channel.h>
++#include <asterisk/pbx.h>
++#include <asterisk/module.h>
++
++static char *app = "Segfault";
++
++static char *synopsis = "This application will crash Asterisk with a segmentation fault.";
++
++static char *descrip =
++" Segfault(): Crash with a segfault. Never returns nufin.\n";
++
++static int segfault_exec(struct ast_channel *chan, void *data)
++{
++ ((char *)0)[0] = 0;
++ return 0;
++}
++
++static int unload_module(void)
++{
++ return ast_unregister_application(app);
++}
++
++static int load_module(void)
++{
++ return ast_register_application(app, segfault_exec, synopsis, descrip);
++}
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Application for crashing Asterisk with a segmentation fault",
++ .load = load_module,
++ .unload = unload_module,
++);
+--- a/apps/app_directed_pickup.c
++++ b/apps/app_directed_pickup.c
+@@ -45,7 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+
+ #define PICKUPMARK "PICKUPMARK"
+
+-static const char *app = "Pickup";
++static const char *app = "DPickup";
+ static const char *synopsis = "Directed Call Pickup";
+ static const char *descrip =
+ " Pickup(extension[@context][&extension2@context...]): This application can pickup any ringing channel\n"
+--- /dev/null
++++ b/apps/app_pickup.c
+@@ -0,0 +1,300 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Pickup, channel independent call pickup
++ *
++ * Copyright (C) 2004, Junghanns.NET GmbH
++ *
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * Copyright (C) 2004, Florian Overkamp <florian@obsimref.com>
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ */
++
++#include "asterisk.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdio.h>
++#include <signal.h>
++#include <pthread.h>
++#include <asterisk/lock.h>
++#include <asterisk/file.h>
++#include <asterisk/logger.h>
++#include <asterisk/channel.h>
++#include <asterisk/pbx.h>
++#include <asterisk/module.h>
++#include <asterisk/features.h>
++#include <asterisk/options.h>
++
++
++static char *app = "PickUp";
++
++static char *synopsis = "Channel independent call pickup.";
++
++static char *descrip =
++" PickDown([group]): Tries to pickup the first ringing channel with callgroup == group.\n"
++" If called without the group argument, the pickupgroup of the channel will be used.\n";
++
++static char *app2 = "Steal";
++
++static char *synopsis2 = "Channel independent call stealing. Just like pickup but for answered channels.";
++
++static char *descrip2 =
++" Steal([group]): Tries to steal the first bridged channel with callgroup == group.\n"
++" If called without the group argument, the pickupgroup of the channel will be used.\n";
++
++static char *app3 = "PickDown";
++
++static char *synopsis3 = "Channel independent call pickdown.";
++
++static char *descrip3 =
++" PickDown([group]): Tries to hangup the first ringing channel with callgroup == group.\n"
++" If called without the group argument, the pickupgroup of the channel will be used.\n";
++
++static char *app4 = "PickupChan";
++
++static char *synopsis4 = "Channel independent call pickup.";
++
++static char *descrip4 =
++" PickupChan(Technology/resource[&Technology2/resource2...]): Tries to pickup the first ringing channel in the parameter list.\n";
++
++static char *app5 = "StealChan";
++
++static char *synopsis5 = "Channel independent call stealing. Just like pickup but for answered channels.";
++
++static char *descrip5 =
++" StealChan(Technology/resource[&Technology2/resource2...]): Tries to steal the first ringing channel in the parameter list.\n";
++
++
++static int my_pickup_call(struct ast_channel *chan, unsigned int pickupgroup, int chanstate, int bridge) {
++ struct ast_channel *cur;
++ int res = -1;
++ cur = ast_channel_walk_locked(NULL);
++ while(cur) {
++ if ((cur != chan) &&
++ (pickupgroup & cur->callgroup) &&
++ (cur->_state == chanstate)) {
++ break;
++ }
++ ast_mutex_unlock(&cur->lock);
++ cur = ast_channel_walk_locked(cur);
++ }
++ if (cur) {
++ if(option_verbose > 2) {
++ if (chanstate == AST_STATE_RINGING) {
++ if (bridge == 1) {
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s picked up ringing channel %s\n",chan->name,cur->name);
++ } else {
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s hung up ringing channel %s\n",chan->name,cur->name);
++ }
++ } else {
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s stole channel %s\n",chan->name,cur->name);
++ }
++ }
++ if (bridge == 1) {
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
++ if (ast_channel_masquerade(cur, chan)) {
++ ast_log(LOG_ERROR, "unable to masquerade\n");
++ }
++ ast_mutex_unlock(&cur->lock);
++ ast_mutex_unlock(&chan->lock);
++ } else {
++ cur->_softhangup = AST_SOFTHANGUP_DEV;
++ ast_mutex_unlock(&cur->lock);
++ }
++ } else {
++ if(option_verbose > 2) {
++ ast_verbose(VERBOSE_PREFIX_3 "No channel found %d.\n",pickupgroup);
++ }
++ }
++ return res;
++}
++
++static int my_pickup_channel(struct ast_channel *chan, void *data, int chanstate, int bridge) {
++ struct ast_channel *cur;
++ char channels[256];
++ char evalchan[256];
++ char *endptr;
++ int res = -1;
++ cur = ast_channel_walk_locked(NULL);
++ strncpy(channels, (char *)data, sizeof(channels) - 1);
++ while(cur) {
++ if ((cur != chan) &&
++ (cur->_state == chanstate)) {
++ /* This call is a candidate (correct ringstate and not ourselves), now check if the channel is in our list */
++ strncpy(evalchan, (char *)cur->name, sizeof(evalchan) - 1);
++ /* strip the subchannel tag */
++ endptr = strrchr(evalchan, '-');
++ if(endptr) {
++ *endptr = '\0';
++ }
++ /* check for each of the members if they match (probably a stristr will do ?) */
++ /* if we match the code, break */
++ if(strstr(channels, evalchan) != NULL) {
++ ast_verbose(VERBOSE_PREFIX_1 "Nice channel, I'll take it: %s\n",evalchan);
++ break;
++ }
++ }
++ ast_mutex_unlock(&cur->lock);
++ cur = ast_channel_walk_locked(cur);
++ }
++ if (cur) {
++ if(option_verbose > 2) {
++ if (chanstate == AST_STATE_RINGING) {
++ if (bridge == 1) {
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s picked up ringing channel %s\n",chan->name,cur->name);
++ } else {
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s hung up ringing channel %s\n",chan->name,cur->name);
++ }
++ } else {
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s stole channel %s\n",chan->name,cur->name);
++ }
++ }
++ if (bridge == 1) {
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
++ if (ast_channel_masquerade(cur, chan)) {
++ ast_log(LOG_ERROR, "unable to masquerade\n");
++ }
++ ast_mutex_unlock(&cur->lock);
++ ast_mutex_unlock(&chan->lock);
++ } else {
++ cur->_softhangup = AST_SOFTHANGUP_DEV;
++ ast_mutex_unlock(&cur->lock);
++ }
++ } else {
++ if(option_verbose > 2) {
++ ast_verbose(VERBOSE_PREFIX_3 "No channel found %s.\n",channels);
++ }
++ }
++ return res;
++}
++
++
++static int pickup_exec(struct ast_channel *chan, void *data)
++{
++ int res=0;
++ unsigned int pickupgroup=0;
++ struct ast_module_user *u;
++ if (!data || !strlen(data)) {
++ pickupgroup = chan->pickupgroup;
++ } else {
++ pickupgroup = ast_get_group(data);
++ }
++ u = ast_module_user_add(chan);
++ if (!res) {
++ res = my_pickup_call(chan, pickupgroup, AST_STATE_RINGING, 1);
++ }
++ if (res > 0)
++ res = 0;
++ ast_module_user_remove(u);
++ return res;
++}
++
++static int steal_exec(struct ast_channel *chan, void *data)
++{
++ int res=0;
++ unsigned int pickupgroup=0;
++ struct ast_module_user *u;
++ if (!data || !strlen(data)) {
++ pickupgroup = chan->pickupgroup;
++ } else {
++ pickupgroup = ast_get_group(data);
++ }
++ u = ast_module_user_add(chan);
++ if (!res) {
++ res = my_pickup_call(chan, pickupgroup, AST_STATE_UP, 1);
++ }
++ if (res > 0)
++ res = 0;
++ ast_module_user_remove(u);
++ return res;
++}
++
++static int pickdown_exec(struct ast_channel *chan, void *data)
++{
++ int res=0;
++ unsigned int pickupgroup=0;
++ struct ast_module_user *u;
++ if (!data || !strlen(data)) {
++ pickupgroup = chan->pickupgroup;
++ } else {
++ pickupgroup = ast_get_group(data);
++ }
++ u = ast_module_user_add(chan);
++ if (!res) {
++ res = my_pickup_call(chan, pickupgroup, AST_STATE_RINGING, 0);
++ }
++ if (res > 0)
++ res = 0;
++ ast_module_user_remove(u);
++ return res;
++}
++
++static int pickupchan_exec(struct ast_channel *chan, void *data) {
++ int res=0;
++ struct ast_module_user *u;
++ if (!data) {
++ ast_log(LOG_WARNING, "PickupChan requires an argument (technology1/number1&technology2/number2...)\n");
++ return -1;
++ }
++ u = ast_module_user_add(chan);
++ if (!res) {
++ res = my_pickup_channel(chan, data, AST_STATE_RINGING, 1);
++ }
++ if (res > 0)
++ res = 0;
++ ast_module_user_remove(u);
++ return res;
++}
++
++static int stealchan_exec(struct ast_channel *chan, void *data)
++{
++ int res=0;
++ struct ast_module_user *u;
++ if (!data) {
++ ast_log(LOG_WARNING, "StealChan requires an argument (technology1/number1&technology2/number2...)\n");
++ return -1;
++ }
++
++ u = ast_module_user_add(chan);
++ if (!res) {
++ res = my_pickup_channel(chan, data, AST_STATE_UP, 1);
++ }
++ if (res > 0)
++ res = 0;
++ ast_module_user_remove(u);
++ return res;
++}
++
++
++static int unload_module(void)
++{
++ ast_module_user_hangup_all();
++ ast_unregister_application(app5);
++ ast_unregister_application(app4);
++ ast_unregister_application(app3);
++ ast_unregister_application(app2);
++ return ast_unregister_application(app);
++}
++
++static int load_module(void)
++{
++ ast_register_application(app5, stealchan_exec, synopsis5, descrip5);
++ ast_register_application(app4, pickupchan_exec, synopsis4, descrip4);
++ ast_register_application(app3, pickdown_exec, synopsis3, descrip3);
++ ast_register_application(app2, steal_exec, synopsis2, descrip2);
++ return ast_register_application(app, pickup_exec, synopsis, descrip);
++}
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PickUp/PickDown/Steal/PickupChan/StealChan",
++ .load = load_module,
++ .unload = unload_module,
++);
+--- /dev/null
++++ b/apps/app_devstate.c
+@@ -0,0 +1,202 @@
++/*
++ * Devstate application
++ *
++ * Since we like the snom leds so much, a little app to
++ * light the lights on the snom on demand ....
++ *
++ * Copyright (C) 2005, Druid Software
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ */
++
++#include "asterisk.h"
++
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdio.h>
++
++#include "asterisk/lock.h"
++#include "asterisk/file.h"
++#include "asterisk/logger.h"
++#include "asterisk/channel.h"
++#include "asterisk/pbx.h"
++#include "asterisk/astdb.h"
++#include "asterisk/cli.h"
++#include "asterisk/manager.h"
++#include "asterisk/devicestate.h"
++#include "asterisk/module.h"
++
++
++static char type[] = "DS";
++static char tdesc[] = "Application for sending device state messages";
++
++static char app[] = "Devstate";
++
++static char synopsis[] = "Generate a device state change event given the input parameters";
++
++static char descrip[] = " Devstate(device|state): Generate a device state change event given the input parameters. Returns 0. State values match the asterisk device states. They are 0 = unknown, 1 = not inuse, 2 = inuse, 3 = busy, 4 = invalid, 5 = unavailable, 6 = ringing\n";
++
++static char devstate_cli_usage[] =
++"Usage: devstate device state\n"
++" Generate a device state change event given the input parameters.\n Mainly used for lighting the LEDs on the snoms.\n";
++
++static int devstate_cli(int fd, int argc, char *argv[]);
++static struct ast_cli_entry cli_dev_state =
++ { { "devstate", NULL }, devstate_cli, "Set the device state on one of the \"pseudo devices\".", devstate_cli_usage };
++
++
++static int devstate_cli(int fd, int argc, char *argv[])
++{
++ char devName[128];
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++
++ if (ast_db_put("DEVSTATES", argv[1], argv[2]))
++ {
++ ast_log(LOG_DEBUG, "ast_db_put failed\n");
++ }
++ snprintf(devName, sizeof(devName), "DS/%s", argv[1]);
++ ast_device_state_changed_literal(devName);
++ return RESULT_SUCCESS;
++}
++
++static int devstate_exec(struct ast_channel *chan, void *data)
++{
++ char *device, *state, *info;
++ char devName[128];
++ struct ast_module_user *u;
++
++
++ if (!(info = ast_strdupa(data))) {
++ ast_log(LOG_WARNING, "Unable to dupe data :(\n");
++ return -1;
++ }
++
++ u = ast_module_user_add(chan);
++ device = info;
++ state = strchr(info, '|');
++ if (state) {
++ *state = '\0';
++ state++;
++ }
++ else
++ {
++ ast_log(LOG_DEBUG, "No state argument supplied\n");
++ return -1;
++ }
++
++ if (ast_db_put("DEVSTATES", device, state))
++ {
++ ast_log(LOG_DEBUG, "ast_db_put failed\n");
++ }
++
++ snprintf(devName, sizeof(devName), "DS/%s", device);
++ ast_device_state_changed_literal(devName);
++
++ ast_module_user_remove(u);
++ return 0;
++}
++
++
++static int ds_devicestate(void *data)
++{
++ char *dest = data;
++ char stateStr[16];
++ if (ast_db_get("DEVSTATES", dest, stateStr, sizeof(stateStr)))
++ {
++ ast_log(LOG_DEBUG, "ds_devicestate couldnt get state in astdb\n");
++ return 0;
++ }
++ else
++ {
++ ast_log(LOG_DEBUG, "ds_devicestate dev=%s returning state %d\n",
++ dest, atoi(stateStr));
++ return (atoi(stateStr));
++ }
++}
++
++static struct ast_channel_tech devstate_tech = {
++ .type = type,
++ .description = tdesc,
++ .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
++ .devicestate = ds_devicestate,
++ .requester = NULL,
++ .send_digit_begin = NULL,
++ .send_digit_end = NULL,
++ .send_text = NULL,
++ .call = NULL,
++ .hangup = NULL,
++ .answer = NULL,
++ .read = NULL,
++ .write = NULL,
++ .bridge = NULL,
++ .exception = NULL,
++ .indicate = NULL,
++ .fixup = NULL,
++ .setoption = NULL,
++};
++
++static char mandescr_devstate[] =
++"Description: Put a value into astdb\n"
++"Variables: \n"
++" Family: ...\n"
++" Key: ...\n"
++" Value: ...\n";
++
++static int action_devstate(struct mansession *s, const struct message *m)
++{
++ const char *devstate = astman_get_header(m, "Devstate");
++ const char *value = astman_get_header(m, "Value");
++ const char *id = astman_get_header(m,"ActionID");
++ char devName[128];
++ char idText[256] = "";
++
++ if (!strlen(devstate)) {
++ astman_send_error(s, m, "No Devstate specified");
++ return 0;
++ }
++ if (!strlen(value)) {
++ astman_send_error(s, m, "No Value specified");
++ return 0;
++ }
++ if (!ast_strlen_zero(id))
++ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
++
++ if (!ast_db_put("DEVSTATES", devstate, (char *)value)) {
++ snprintf(devName, sizeof(devName), "DS/%s", devstate);
++ ast_device_state_changed_literal(devName);
++ astman_append(s, "Response: Success\r\n%s\r\n", idText);
++ } else {
++ ast_log(LOG_DEBUG, "ast_db_put failed\n");
++ astman_append(s, "Response: Failed\r\n%s\r\n", idText);
++ }
++ return 0;
++}
++
++static int load_module(void)
++{
++ if (ast_channel_register(&devstate_tech)) {
++ ast_log(LOG_DEBUG, "Unable to register channel class %s\n", type);
++ return -1;
++ }
++ ast_cli_register(&cli_dev_state);
++ ast_manager_register2( "Devstate", EVENT_FLAG_CALL, action_devstate, "Change a device state", mandescr_devstate );
++ return ast_register_application(app, devstate_exec, synopsis, descrip);
++}
++
++static int unload_module(void)
++{
++ int res = 0;
++
++ ast_module_user_hangup_all();
++ ast_manager_unregister( "Devstate");
++ ast_cli_unregister(&cli_dev_state);
++ res = ast_unregister_application(app);
++ ast_channel_unregister(&devstate_tech);
++ return res;
++}
++
++AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Devstate Application");
++
+--- /dev/null
++++ b/configs/watchdog.conf.sample
+@@ -0,0 +1,22 @@
++;
++; Configuration file for res_watchdog
++;
++; type = isdnguard | watchdog
++; device = /dev/...
++; interval = interval to trigger the watchdog in ms
++
++;[ISDNguard-direct]
++;type = isdnguard
++;device = /dev/ttyS0
++;interval = 200
++
++;[ISDNguard-with-daemon]
++;type = isdnguard
++;device = /var/run/guard.ctl
++;interval = 200
++
++;[kernel_watchdog]
++;type = watchdog
++;device = /dev/watchdog
++;interval = 100
++
+--- /dev/null
++++ b/res/res_watchdog.c
+@@ -0,0 +1,137 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Resource to make watchdogs happy
++ *
++ * Copyright (C) 2005, Junghanns.NET GmbH
++ *
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ */
++
++#include "asterisk.h"
++#include <stdlib.h>
++#include <errno.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <sys/time.h>
++#include <sys/signal.h>
++#include <netinet/in.h>
++
++#include <asterisk/logger.h>
++#include <asterisk/channel.h>
++#include <asterisk/pbx.h>
++#include <asterisk/options.h>
++#include <asterisk/module.h>
++#include <asterisk/translate.h>
++#include <asterisk/say.h>
++#include <asterisk/features.h>
++#include <asterisk/musiconhold.h>
++#include <asterisk/config.h>
++#include <asterisk/cli.h>
++#include <asterisk/manager.h>
++#include <asterisk/utils.h>
++#include <asterisk/lock.h>
++#include <asterisk/adsi.h>
++
++static struct watchdog_pvt *watchdogs = NULL;
++
++typedef struct watchdog_pvt {
++ char device[80];
++ int fd;
++ int type;
++ int interval;
++ pthread_t watchdog_thread;
++ struct watchdog_pvt *next;
++} watchdog_pvt;
++
++static void *do_watchdog_thread(void *data) {
++ struct watchdog_pvt *woof = (struct watchdog_pvt *)data;
++ for (;;) {
++ if (woof->fd) {
++ write(woof->fd, "PING\n", 5);
++ }
++ usleep(woof->interval * 1000);
++ }
++ return NULL;
++}
++
++
++static int load_module(void)
++{
++ int res = 0;
++ const char *cat, *utype, *udevice, *uinterval;
++ struct ast_config *cfg;
++ struct watchdog_pvt *woof = NULL;
++
++ cfg = ast_config_load("watchdog.conf");
++ if (cfg) {
++ cat = ast_category_browse(cfg, NULL);
++ while(cat) {
++ cat = ast_category_browse(cfg, cat);
++ utype = ast_variable_retrieve(cfg, cat, "type");
++/* if (utype) {
++ ast_log(LOG_NOTICE, "type = %s\n", utype);
++ } */
++ udevice = ast_variable_retrieve(cfg, cat, "device");
++/* if (udevice) {
++ ast_log(LOG_NOTICE, "device = %s\n", udevice);
++ } */
++ uinterval = ast_variable_retrieve(cfg, cat, "interval");
++/* if (uinterval) {
++ ast_log(LOG_NOTICE, "interval = %s\n", uinterval);
++ } */
++ if (uinterval && udevice && utype) {
++ woof = malloc(sizeof(struct watchdog_pvt));
++ if (!woof) {
++ ast_log(LOG_ERROR, "unable to malloc!\n");
++ return -1;
++ }
++ memset(woof, 0x0, sizeof(struct watchdog_pvt));
++ strncpy(woof->device, udevice, sizeof(woof->device) - 1);
++
++ woof->interval = atoi(uinterval);;
++ woof->next = watchdogs;
++ watchdogs = woof;
++ woof->fd = open(woof->device, O_WRONLY | O_SYNC);
++ if (woof->fd) {
++ if (!strncmp(utype, "isdnguard", sizeof(utype))) {
++ woof->type = 1;
++ write(woof->fd, "START\n", 6);
++ }
++ ast_pthread_create(&woof->watchdog_thread, NULL, do_watchdog_thread, woof);
++ } else {
++ ast_log(LOG_WARNING, "error opening watchdog device %s !\n", woof->device);
++ }
++ }
++ }
++ ast_config_destroy(cfg);
++ }
++ return res;
++}
++
++
++static int unload_module(void)
++{
++ struct watchdog_pvt *dogs, *woof;
++ dogs = watchdogs;
++ while (dogs) {
++ pthread_cancel(dogs->watchdog_thread);
++ pthread_join(dogs->watchdog_thread, NULL);
++ close(dogs->fd);
++ woof = dogs->next;
++ free(dogs);
++ dogs = woof;
++ }
++ return 0;
++}
++
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Watchdog Resource",
++ .load = load_module,
++ .unload = unload_module,
++);
+--- a/main/db.c
++++ b/main/db.c
+@@ -574,7 +574,7 @@ static int manager_dbget(struct mansessi
+ astman_append(s, "Event: DBGetResponse\r\n"
+ "Family: %s\r\n"
+ "Key: %s\r\n"
+- "Val: %s\r\n"
++ "Value: %s\r\n"
+ "%s"
+ "\r\n",
+ family, key, tmp, idText);
+@@ -582,11 +582,35 @@ static int manager_dbget(struct mansessi
+ return 0;
+ }
+
++static int manager_dbdel(struct mansession *s, const struct message *m)
++{
++ const char *family = astman_get_header(m, "Family");
++ const char *key = astman_get_header(m, "Key");
++
++ if (!strlen(family)) {
++ astman_send_error(s, m, "No family specified");
++ return 0;
++ }
++ if (!strlen(key)) {
++ astman_send_error(s, m, "No key specified");
++ return 0;
++ }
++
++ if (ast_db_del(family, key)) {
++ astman_send_error(s, m, "Failed to delete entry");
++ } else {
++ astman_send_ack(s, m, "Deleted entry successfully");
++ }
++
++ return 0;
++}
++
+ int astdb_init(void)
+ {
+ dbinit();
+ ast_cli_register_multiple(cli_database, sizeof(cli_database) / sizeof(struct ast_cli_entry));
+- ast_manager_register("DBGet", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry");
+- ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
++ ast_manager_register("DBget", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry");
++ ast_manager_register("DBput", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
++ ast_manager_register("DBdel", EVENT_FLAG_SYSTEM, manager_dbdel, "Delete DB Entry");
+ return 0;
+ }
+--- /dev/null
++++ b/configs/esel.conf.sample
+@@ -0,0 +1,12 @@
++;
++; Configuration file for res_esel
++;
++
++;[asterisk-2]
++;host = 192.168.0.1
++;port = 5038
++;username = manager
++;secret = donkey
++
++; export the extension snom in context phones to DS/100
++;export => snom@phones,100
+--- /dev/null
++++ b/res/res_esel.c
+@@ -0,0 +1,384 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Extension State Export Logic (E.S.E.L) (Sorry, i couldnt resist...)
++ *
++ * Resource to export extension states to other Asterisk servers
++ *
++ * Copyright (C) 2006, Junghanns.NET GmbH
++ *
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ */
++
++#include "asterisk.h"
++
++#include <stdlib.h>
++#include <errno.h>
++#include <unistd.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <sys/time.h>
++#include <sys/signal.h>
++#include <netinet/in.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++
++#include "asterisk/file.h"
++#include "asterisk/logger.h"
++#include "asterisk/channel.h"
++#include "asterisk/pbx.h"
++#include "asterisk/options.h"
++#include "asterisk/module.h"
++#include "asterisk/translate.h"
++#include "asterisk/say.h"
++#include "asterisk/features.h"
++#include "asterisk/musiconhold.h"
++#include "asterisk/config.h"
++#include "asterisk/cli.h"
++#include "asterisk/manager.h"
++#include "asterisk/utils.h"
++#include "asterisk/lock.h"
++#include "asterisk/adsi.h"
++
++
++AST_MUTEX_DEFINE_STATIC(listlock);
++
++typedef struct esel_extension_state {
++ char context[AST_MAX_EXTENSION];
++ char exten[AST_MAX_EXTENSION];
++ int state;
++ char devstate[AST_MAX_EXTENSION];
++ struct esel_extension_state *next;
++ struct esel_extension_state *prev;
++} esel_extension_state;
++
++typedef struct esel_export {
++ char context[AST_MAX_EXTENSION];
++ char exten[AST_MAX_EXTENSION];
++ char devstate[AST_MAX_EXTENSION];
++ struct esel_export *next;
++} esel_export;
++
++typedef struct esel_queue {
++ struct esel_extension_state *head;
++ struct esel_extension_state *tail;
++ int count;
++ ast_cond_t cond;
++ ast_mutex_t lock;
++} esel_queue;
++
++typedef struct esel_pvt {
++ char name[80];
++ char username[80];
++ char secret[80];
++ char host[80];
++ int port;
++ struct sockaddr_in raddr;
++ int sockfd;
++ int connected;
++ pthread_t esel_thread;
++
++ /* list of extensions to export */
++ struct esel_export *extensions;
++
++ /* queue */
++ struct esel_queue queue;
++
++ struct esel_pvt *next;
++} esel_pvt;
++
++static struct esel_pvt *donkeys = NULL;
++
++static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data) {
++ struct esel_extension_state *exstate = NULL;
++
++ exstate = malloc(sizeof(struct esel_extension_state));
++ if (!exstate) {
++ ast_log(LOG_ERROR, "Unable to malloc!\n");
++ return 1;
++ }
++ memset(exstate,0,sizeof(struct esel_extension_state));
++ exstate->next = NULL;
++ exstate->prev = NULL;
++
++ ast_mutex_lock(&queue->lock);
++ if (queue->count > 100) {
++ ast_mutex_unlock(&queue->lock);
++ free(exstate);
++ if (option_verbose > 5)
++ ast_log(LOG_WARNING, "E.S.E.L Queue too long.\n");
++ return -1;
++ }
++ ast_copy_string(exstate->exten, exten, sizeof(exstate->exten));
++ ast_copy_string(exstate->context, context, sizeof(exstate->context));
++ exstate->state = state;
++ if (!queue->head) {
++ /* Empty queue */
++ queue->head = exstate;
++ queue->tail = exstate;
++ } else {
++ /* Double link */
++ queue->tail->next = exstate;
++ exstate->prev = queue->tail;
++ queue->tail = exstate;
++ }
++ queue->count++;
++ ast_cond_signal(&queue->cond);
++ ast_mutex_unlock(&queue->lock);
++ return 0;
++}
++
++static int esel_is_exported(struct esel_export *extensions, struct esel_extension_state *exstate) {
++ struct esel_export *export = NULL;
++ export = extensions;
++ while (export) {
++ if ((!strcasecmp(export->exten, exstate->exten)) && (!strcasecmp(export->context, exstate->context))) {
++ /* copy mapping */
++ ast_copy_string(exstate->devstate, export->devstate, sizeof(exstate->devstate));
++ return 1;
++ }
++ export = export->next;
++ }
++ return 0;
++}
++
++static int esel_state2devstate(int state) {
++ switch(state) {
++ case 1:
++ return 2;
++ case 8:
++ return 6;
++ default:
++ return state;
++ }
++}
++
++static void esel_export_to_remote(struct esel_extension_state *exstate, struct esel_pvt *esel) {
++ char msg[1024];
++ int sent = 0;
++ memset(msg, 0x0, sizeof(msg));
++ snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state));
++ sent = send(esel->sockfd, msg, strlen(msg), 0);
++ if (sent == -1) {
++ esel->connected = 0;
++ }
++// ast_log(LOG_NOTICE, "%s", msg);
++}
++
++static void *do_esel_thread(void *data) {
++ struct esel_pvt *esel = (struct esel_pvt *)data;
++ struct esel_queue *queue = &esel->queue;
++ struct esel_extension_state *exstate = NULL;
++ char msg[1024];
++ char buf[1024];
++ int numbytes = 0;
++ int sent = 0;
++ int res = 0;
++ for (;;) {
++ if (esel->connected) {
++ ast_mutex_lock(&queue->lock);
++ if (queue->count == 0)
++ ast_cond_wait(&queue->cond, &queue->lock);
++ exstate = queue->head;
++ if (exstate) {
++ if (exstate->next) {
++ queue->head = exstate->next;
++ } else {
++ queue->head = NULL;
++ queue->tail = NULL;
++ }
++ queue->count--;
++ } else {
++ ast_log(LOG_ERROR, "I SHOULD NEVER HAPPEN! EXPECT SOME MAJOR KABOOM! DUCK AND COVER!\n");
++ }
++ ast_mutex_unlock(&queue->lock);
++
++ if (exstate) {
++ if (esel_is_exported(esel->extensions, exstate)) {
++ esel_export_to_remote(exstate, esel);
++ }
++ free(exstate);
++ exstate = NULL;
++ }
++ } else {
++ if (esel->sockfd > 0)
++ close(esel->sockfd);
++ if ((esel->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
++ ast_log(LOG_ERROR, "unable to request socket!\n");
++ return NULL;
++ }
++ /* try to connect */
++ res = connect(esel->sockfd, (struct sockaddr *)&esel->raddr, sizeof(struct sockaddr));
++ if (res) {
++ ast_log(LOG_NOTICE, "error connecting to %s:%d\n", esel->host, esel->port);
++ } else {
++ while (strncasecmp(buf, "Asterisk Call Manager:", 21)) {
++ if ((numbytes=recv(esel->sockfd, buf, sizeof(buf), 0)) == -1) {
++ esel->connected = 0;
++ continue;
++ }
++ buf[numbytes] = '\0';
++ // ast_log(LOG_NOTICE, "read: %s", buf);
++ }
++ /* log into remote manager */
++ memset(msg, 0x0, sizeof(msg));
++ snprintf(msg, sizeof(msg) - 1, "Action: Login\r\nUsername: %s\r\nSecret: %s\r\n\r\n", esel->username, esel->secret);
++ sent = send(esel->sockfd, msg, strlen(msg), 0);
++
++ while (strncasecmp(buf, "Response:", 9)) {
++ if ((numbytes=recv(esel->sockfd, buf, sizeof(buf), 0)) == -1) {
++ continue;
++ }
++ buf[numbytes] = '\0';
++ // ast_log(LOG_NOTICE, "read: %s", buf);
++ }
++
++ if (!strncasecmp(buf, "Response: Success", 17)) {
++ esel->connected = 1;
++ } else {
++ ast_log(LOG_ERROR, "error login into remote asterisk %s\n", esel->name);
++ }
++ }
++ /* time heals everything... */
++ sleep(10);
++ }
++ }
++ return NULL;
++}
++
++static int esel_state_cb(char *context, char *exten, int state, void *data) {
++ struct esel_pvt *esel;
++
++ esel = donkeys;
++ ast_mutex_lock(&listlock);
++ while (esel) {
++ esel_queue_extension_state(&esel->queue, context, exten, state, data);
++ esel = esel->next;
++ }
++ ast_mutex_unlock(&listlock);
++ return 0;
++}
++
++
++static int load_module(void)
++{
++ int res = 0;
++ const char *cat, *host, *port, *username, *secret, *name;
++ struct ast_config *cfg;
++ struct ast_variable *var;
++ struct esel_pvt *esel = NULL;
++ struct esel_export *export = NULL;
++ struct hostent *he;
++ struct ast_hostent h;
++
++ cfg = ast_config_load("esel.conf");
++ if (cfg) {
++ cat = ast_category_browse(cfg, NULL);
++ while(cat) {
++ name = cat;
++ host = ast_variable_retrieve(cfg, cat, "host");
++ username = ast_variable_retrieve(cfg, cat, "username");
++ secret = ast_variable_retrieve(cfg, cat, "secret");
++ port = ast_variable_retrieve(cfg, cat, "port");
++
++ if (name && host && username && secret && port) {
++ esel = malloc(sizeof(struct esel_pvt));
++ if (!esel) {
++ ast_log(LOG_ERROR, "unable to malloc!\n");
++ return -1;
++ }
++ memset(esel, 0x0, sizeof(struct esel_pvt));
++ ast_copy_string(esel->name, name, sizeof(esel->name));
++ ast_copy_string(esel->host, host, sizeof(esel->host));
++ ast_copy_string(esel->username, username, sizeof(esel->username));
++ ast_copy_string(esel->secret, secret, sizeof(esel->secret));
++
++ esel->port = atoi(port);
++ if ((he=ast_gethostbyname(host, &h)) == NULL) {
++ ast_log(LOG_ERROR, "unknown host!\n");
++ return -1;
++ }
++
++ esel->raddr.sin_family = AF_INET;
++ esel->raddr.sin_port = htons(esel->port);
++ esel->raddr.sin_addr = *((struct in_addr *)he->h_addr);
++ bzero(&(esel->raddr.sin_zero), 8);
++
++ esel->connected = 0;
++
++ ast_mutex_init(&esel->queue.lock);
++ ast_cond_init(&esel->queue.cond, NULL);
++
++
++ /* read exports */
++ var = ast_variable_browse(cfg, cat);
++ while (var) {
++ if (!strcasecmp(var->name, "export")) {
++ char *extenp = NULL, *contextp = NULL, *devstatep = NULL;
++ extenp = var->value;
++ devstatep = strchr(var->value, ',') + 1;
++ contextp = strchr(var->value, '@') + 1;
++ if (devstatep && contextp) {
++ export = malloc(sizeof(struct esel_export));
++ if (!export) {
++ ast_log(LOG_ERROR, "unable to malloc!\n");
++ return -1;
++ }
++ memset(export, 0x0, sizeof(struct esel_export));
++ ast_copy_string(export->exten, extenp, contextp - extenp);
++ ast_copy_string(export->context, contextp, devstatep - contextp);
++ ast_copy_string(export->devstate, devstatep, sizeof(export->devstate));
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "exporting %s @ %s as %s to %s\n", export->exten, export->context , export->devstate , esel->name);
++ export->next = esel->extensions;
++ esel->extensions = export;
++ export = NULL;
++ }
++ }
++ var = var->next;
++ }
++
++
++
++ esel->next = donkeys;
++ donkeys = esel;
++
++ ast_pthread_create(&esel->esel_thread, NULL, do_esel_thread, esel);
++
++ }
++ cat = ast_category_browse(cfg, cat);
++ }
++ ast_config_destroy(cfg);
++ }
++ ast_extension_state_add(NULL, NULL, esel_state_cb, NULL);
++ return res;
++}
++
++
++static int unload_module(void)
++{
++ struct esel_pvt *esel, *eseln;
++ ast_module_user_hangup_all();
++ esel = donkeys;
++ ast_mutex_lock(&listlock);
++ while (esel) {
++ pthread_cancel(esel->esel_thread);
++ pthread_join(esel->esel_thread, NULL);
++ ast_mutex_destroy(&esel->queue.lock);
++ close(esel->sockfd);
++ eseln = esel->next;
++ free(esel);
++ esel = eseln;
++ }
++ ast_mutex_unlock(&listlock);
++ return 0;
++}
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Extension State Export Logic (E.S.E.L.) Resource",
++ .load = load_module,
++ .unload = unload_module,
++);
+--- a/channels/chan_iax2.c
++++ b/channels/chan_iax2.c
+@@ -11,6 +11,9 @@
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
++ * Hangup cause signalling implementation by
++ * Levent Guendogdu <levon@feature-it.com>
++ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+@@ -3362,7 +3365,7 @@ static int iax2_hangup(struct ast_channe
+ ast_mutex_lock(&iaxsl[callno]);
+ if (callno && iaxs[callno]) {
+ if (option_debug)
+- ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name);
++ ast_log(LOG_DEBUG, "We're hanging up %s with cause %i now...\n", c->name, c->hangupcause);
+ alreadygone = ast_test_flag(iaxs[callno], IAX_ALREADYGONE);
+ /* Send the hangup unless we have had a transmission error or are already gone */
+ iax_ie_append_byte(&ied, IAX_IE_CAUSECODE, (unsigned char)c->hangupcause);
+@@ -3416,7 +3419,8 @@ static int iax2_setoption(struct ast_cha
+
+ static struct ast_frame *iax2_read(struct ast_channel *c)
+ {
+- ast_log(LOG_NOTICE, "I should never be called!\n");
++ if (option_verbose > 3)
++ ast_log(LOG_NOTICE, "I should never be called!\n");
+ return &ast_null_frame;
+ }
+
+--- a/apps/app_zapras.c
++++ b/apps/app_zapras.c
+@@ -183,7 +183,7 @@ static void run_ras(struct ast_channel *
+ }
+ }
+ /* Throw back into audio mode */
+- x = 1;
++ x = 0;
+ ioctl(chan->fds[0], ZT_AUDIOMODE, &x);
+
+ /* Restore saved values */
+--- a/main/channel.c
++++ b/main/channel.c
+@@ -3898,6 +3898,10 @@ enum ast_bridge_result ast_channel_bridg
+ c1->name, c1->_bridge->name);
+ return -1;
+ }
++
++ if (IS_DIGITAL(c0->transfercapability) || IS_DIGITAL(c1->transfercapability)) {
++ config->flags = 0;
++ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
+--- a/apps/app_meetme.c
++++ b/apps/app_meetme.c
+@@ -1405,8 +1405,9 @@ static int conf_run(struct ast_channel *
+ char members[10] = "";
+ int dtmf, opt_waitmarked_timeout = 0;
+ time_t timeout = 0;
++ int dyna_buff = CONF_SIZE;
+ ZT_BUFFERINFO bi;
+- char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
++ char __buf[ZT_MAX_BUF_SPACE / ZT_DEFAULT_NUM_BUFS + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+ int setusercount = 0;
+
+@@ -1615,7 +1616,7 @@ static int conf_run(struct ast_channel *
+ }
+ /* Setup buffering information */
+ memset(&bi, 0, sizeof(bi));
+- bi.bufsize = CONF_SIZE/2;
++ bi.bufsize = dyna_buff / 2;
+ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ bi.numbufs = audio_buffers;
+@@ -1926,6 +1927,14 @@ static int conf_run(struct ast_channel *
+ f = ast_read(c);
+ if (!f)
+ break;
++ if (f->datalen && f->datalen != dyna_buff) {
++ ast_log(LOG_NOTICE, "Audio bytes: %d Buffer size: %d\n", f->datalen, dyna_buff);
++ if (f->datalen < ZT_MAX_BUF_SPACE/audio_buffers) { /* skip too large frame to avoid overflow */
++ dyna_buff = f->datalen;
++ close(fd);
++ goto zapretry;
++ }
++ }
+ if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
+ if (user->talk.actual)
+ ast_frame_adjust_volume(f, user->talk.actual);
+Answer the channel before saying things (SayNumber, SayDigits,
+SayCharacters, SayPhonetic).
+
+--- a/main/pbx.c
++++ b/main/pbx.c
+@@ -6076,6 +6076,9 @@ static int pbx_builtin_saynumber(struct
+ return -1;
+ }
+ }
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
+
+ if (ast_say_number(chan, atoi(tmp), "", chan->language, options)) {
+ ast_log(LOG_WARNING, "We were unable to say the number %s, is it too large?\n", tmp);
+@@ -6088,8 +6091,12 @@ static int pbx_builtin_saydigits(struct
+ {
+ int res = 0;
+
+- if (data)
++ if (data) {
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
+ res = ast_say_digit_str(chan, data, "", chan->language);
++ }
+ return res;
+ }
+
+@@ -6097,8 +6104,12 @@ static int pbx_builtin_saycharacters(str
+ {
+ int res = 0;
+
+- if (data)
++ if (data) {
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
+ res = ast_say_character_str(chan, data, "", chan->language);
++ }
+ return res;
+ }
+
+@@ -6106,8 +6117,12 @@ static int pbx_builtin_sayphonetic(struc
+ {
+ int res = 0;
+
+- if (data)
++ if (data) {
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
+ res = ast_say_phonetic_str(chan, data, "", chan->language);
++ }
+ return res;
+ }
+
+--- a/apps/app_dial.c
++++ b/apps/app_dial.c
+@@ -11,6 +11,10 @@
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
++ * Copyright (C) 2004, Junghanns.NET GmbH
++ *
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+@@ -130,7 +134,8 @@ static char *descrip =
+ " H - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
+ " i - Asterisk will ignore any forwarding requests it may receive on this\n"
+ " dial attempt.\n"
+-" j - Jump to priority n+101 if all of the requested channels were busy.\n"
++" j - Jump to priority n+101 if the called party was busy.\n"
++" Jump to priority n+201 if all of the requested channels were busy.\n"
+ " k - Allow the called party to enable parking of the call by sending\n"
+ " the DTMF sequence defined for call parking in features.conf.\n"
+ " K - Allow the calling party to enable parking of the call by sending\n"
+@@ -1292,14 +1297,16 @@ static int dial_exec_full(struct ast_cha
+ }
+
+ if (!outgoing) {
+- strcpy(status, "CHANUNAVAIL");
++ ast_copy_string(status, "CHANUNAVAIL", sizeof(status));
+ if(fulldial == num_dialed) {
+ res = -1;
+ goto out;
+ }
++ /* See if there is a special message */
++ ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201);
+ } else {
+ /* Our status will at least be NOANSWER */
+- strcpy(status, "NOANSWER");
++ ast_copy_string(status, "NOANSWER", sizeof(status));
+ if (ast_test_flag(outgoing, OPT_MUSICBACK)) {
+ moh = 1;
+ if (!ast_strlen_zero(opt_args[OPT_ARG_MUSICBACK])) {
+--- a/apps/app_dial.c
++++ b/apps/app_dial.c
+@@ -71,6 +71,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ #include "asterisk/privacy.h"
+ #include "asterisk/stringfields.h"
+ #include "asterisk/global_datastores.h"
++#include "asterisk/transcap.h"
+
+ static char *app = "Dial";
+
+@@ -1652,23 +1653,25 @@ static int dial_exec_full(struct ast_cha
+ ast_set_flag(&(config.features_caller), AST_FEATURE_PLAY_WARNING);
+ if (play_to_callee)
+ ast_set_flag(&(config.features_callee), AST_FEATURE_PLAY_WARNING);
+- if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER))
++ if ((chan->transfercapability != AST_TRANS_CAP_DIGITAL) && (chan->transfercapability != AST_TRANS_CAP_RESTRICTED_DIGITAL)) {
++ /* only non-digital calls are allowed to go through userspace */
++ if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
+- if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER))
++ if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
+- if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP))
++ if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT);
+- if (ast_test_flag(peerflags, OPT_CALLER_HANGUP))
++ if (ast_test_flag(peerflags, OPT_CALLER_HANGUP))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT);
+- if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR))
++ if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON);
+- if (ast_test_flag(peerflags, OPT_CALLER_MONITOR))
++ if (ast_test_flag(peerflags, OPT_CALLER_MONITOR))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON);
+- if (ast_test_flag(peerflags, OPT_CALLEE_PARK))
++ if (ast_test_flag(peerflags, OPT_CALLEE_PARK))
+ ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
+- if (ast_test_flag(peerflags, OPT_CALLER_PARK))
++ if (ast_test_flag(peerflags, OPT_CALLER_PARK))
+ ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
+-
++ }
+ config.timelimit = timelimit;
+ config.play_warning = play_warning;
+ config.warning_freq = warning_freq;
+--- a/apps/app_dial.c
++++ b/apps/app_dial.c
+@@ -198,6 +198,8 @@ static char *descrip =
+ " family/key is not specified.\n"
+ " r - Indicate ringing to the calling party. Pass no audio to the calling\n"
+ " party until the called channel has answered.\n"
++" R - indicate ringing to the calling party when the called party indicates\n"
++" ringing, pass no audio until answered.\n"
+ " S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
+ " answered the call.\n"
+ " t - Allow the called party to transfer the calling party by sending the\n"
+@@ -254,6 +256,7 @@ enum {
+ OPT_CALLEE_PARK = (1 << 25),
+ OPT_CALLER_PARK = (1 << 26),
+ OPT_IGNORE_FORWARDING = (1 << 27),
++ OPT_NOINBAND = (1 << 28),
+ } dial_exec_option_flags;
+
+ #define DIAL_STILLGOING (1 << 30)
+@@ -297,6 +300,7 @@ AST_APP_OPTIONS(dial_exec_options, {
+ AST_APP_OPTION('p', OPT_SCREENING),
+ AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
+ AST_APP_OPTION('r', OPT_RINGBACK),
++ AST_APP_OPTION('R', OPT_NOINBAND),
+ AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
+ AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
+ AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+@@ -412,7 +416,7 @@ static struct ast_channel *wait_for_answ
+ int orig = *to;
+ struct ast_channel *peer = NULL;
+ /* single is set if only one destination is enabled */
+- int single = outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
++ int single = (outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK | OPT_NOINBAND));
+
+ if (single) {
+ /* Turn off hold music, etc */
+@@ -633,7 +637,7 @@ static struct ast_channel *wait_for_answ
+ /* Setup early media if appropriate */
+ if (single && CAN_EARLY_BRIDGE(peerflags))
+ ast_rtp_early_bridge(in, c);
+- if (!ast_test_flag(outgoing, OPT_RINGBACK))
++ if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_NOINBAND))
+ ast_indicate(in, AST_CONTROL_PROGRESS);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+@@ -651,7 +655,7 @@ static struct ast_channel *wait_for_answ
+ ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", c->name, in->name);
+ if (single && CAN_EARLY_BRIDGE(peerflags))
+ ast_rtp_early_bridge(in, c);
+- if (!ast_test_flag(outgoing, OPT_RINGBACK))
++ if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_NOINBAND))
+ ast_indicate(in, AST_CONTROL_PROCEEDING);
+ break;
+ case AST_CONTROL_HOLD:
+@@ -669,7 +673,7 @@ static struct ast_channel *wait_for_answ
+ /* Ignore going off hook and flash */
+ break;
+ case -1:
+- if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
++ if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK | OPT_NOINBAND)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s stopped sounds\n", c->name);
+ ast_indicate(in, -1);
+@@ -1091,7 +1095,7 @@ static int dial_exec_full(struct ast_cha
+ outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
+ }
+
+- ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING);
++ ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_NOINBAND);
+ /* loop through the list of dial destinations */
+ rest = args.peers;
+ while ((cur = strsep(&rest, "&")) ) {
+@@ -1116,7 +1120,7 @@ static int dial_exec_full(struct ast_cha
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ OPT_CALLEE_PARK | OPT_CALLER_PARK |
+- OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID);
++ OPT_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID | OPT_NOINBAND);
+ ast_set2_flag(tmp, args.url, DIAL_NOFORWARDHTML);
+ }
+ ast_copy_string(numsubst, number, sizeof(numsubst));
+@@ -1319,7 +1323,7 @@ static int dial_exec_full(struct ast_cha
+ ast_moh_start(chan, NULL, NULL);
+ }
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+- } else if (ast_test_flag(outgoing, OPT_RINGBACK)) {
++ } else if (ast_test_flag(outgoing, OPT_RINGBACK | OPT_NOINBAND)) {
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ sentringing++;
+ }
+@@ -1442,7 +1446,7 @@ static int dial_exec_full(struct ast_cha
+
+ if (ast_test_flag(&opts, OPT_MUSICBACK)) {
+ ast_moh_stop(chan);
+- } else if (ast_test_flag(&opts, OPT_RINGBACK)) {
++ } else if (ast_test_flag(&opts, OPT_RINGBACK | OPT_NOINBAND)) {
+ ast_indicate(chan, -1);
+ sentringing=0;
+ }
+--- a/apps/app_dial.c
++++ b/apps/app_dial.c
+@@ -201,7 +201,8 @@ static char *descrip =
+ " R - indicate ringing to the calling party when the called party indicates\n"
+ " ringing, pass no audio until answered.\n"
+ " S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
+-" answered the call.\n"
++" answered the call.\n"
++" c - callback initiation, ring once and hangup.\n"
+ " t - Allow the called party to transfer the calling party by sending the\n"
+ " DTMF sequence defined in features.conf.\n"
+ " T - Allow the calling party to transfer the called party by sending the\n"
+@@ -257,6 +258,7 @@ enum {
+ OPT_CALLER_PARK = (1 << 26),
+ OPT_IGNORE_FORWARDING = (1 << 27),
+ OPT_NOINBAND = (1 << 28),
++ OPT_CALLBACK_INIT = (1 << 29),
+ } dial_exec_option_flags;
+
+ #define DIAL_STILLGOING (1 << 30)
+@@ -301,6 +303,7 @@ AST_APP_OPTIONS(dial_exec_options, {
+ AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
+ AST_APP_OPTION('r', OPT_RINGBACK),
+ AST_APP_OPTION('R', OPT_NOINBAND),
++ AST_APP_OPTION('c', OPT_CALLBACK_INIT),
+ AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
+ AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
+ AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+@@ -621,14 +624,20 @@ static struct ast_channel *wait_for_answ
+ HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
+ break;
+ case AST_CONTROL_RINGING:
+- if (option_verbose > 2)
++ if (ast_test_flag(peerflags, OPT_CALLBACK_INIT)) {
++ if (option_verbose > 2)
++ ast_verbose( VERBOSE_PREFIX_3 "%s is ringing, hanging up.\n", o->chan->name);
++ return NULL;
++ } else {
++ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", c->name);
+- /* Setup early media if appropriate */
+- if (single && CAN_EARLY_BRIDGE(peerflags))
++ /* Setup early media if appropriate */
++ if (single && CAN_EARLY_BRIDGE(peerflags))
+ ast_rtp_early_bridge(in, c);
+- if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) {
++ if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) {
+ ast_indicate(in, AST_CONTROL_RINGING);
+ (*sentringing)++;
++ }
+ }
+ break;
+ case AST_CONTROL_PROGRESS:
+@@ -1095,7 +1104,7 @@ static int dial_exec_full(struct ast_cha
+ outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
+ }
+
+- ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_NOINBAND);
++ ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_CALLBACK_INIT | OPT_NOINBAND);
+ /* loop through the list of dial destinations */
+ rest = args.peers;
+ while ((cur = strsep(&rest, "&")) ) {
+# Allow as many spans as Zaptel can take (defualt: 128).
+# There is a static array of zt_pris allocated at this size.
+# For that reason upstream only sort-of merged it: made it a
+# compile-time option in 1.6 .
+
+--- a/channels/chan_zap.c
++++ b/channels/chan_zap.c
+@@ -190,7 +190,7 @@ static const char config[] = "zapata.con
+ #define SIG_GR303FXOKS (0x0100000 | ZT_SIG_FXOKS)
+ #define SIG_GR303FXSKS (0x0100000 | ZT_SIG_FXSKS)
+
+-#define NUM_SPANS 32
++#define NUM_SPANS ZT_MAX_SPANS
+ #define NUM_DCHANS 4 /*!< No more than 4 d-channels */
+ #define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */
+
+--- a/include/asterisk/channel.h
++++ b/include/asterisk/channel.h
+@@ -435,6 +435,7 @@ struct ast_channel {
+ unsigned int flags; /*!< channel flags of AST_FLAG_ type */
+ unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */
+ AST_LIST_HEAD_NOLOCK(, ast_frame) readq;
++ char lowlayercompat[16]; /*!< ISDN Low Layer Compatibility */
+ int alertpipe[2];
+
+ int nativeformats; /*!< Kinds of data this channel can natively handle */
+--- a/channels/chan_zap.c
++++ b/channels/chan_zap.c
+@@ -11,6 +11,10 @@
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
++ * Copyright (C) 2003-2006 Junghanns.NET GmbH
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+@@ -216,8 +220,6 @@ static struct ast_channel inuse;
+ #ifdef PRI_GETSET_TIMERS
+ static int pritimers[PRI_MAX_TIMERS];
+ #endif
+-static int pridebugfd = -1;
+-static char pridebugfilename[1024] = "";
+ #endif
+
+ /*! \brief Wait up to 16 seconds for first digit (FXO logic) */
+@@ -235,10 +237,6 @@ AST_MUTEX_DEFINE_STATIC(iflock);
+
+ static int ifcount = 0;
+
+-#ifdef HAVE_PRI
+-AST_MUTEX_DEFINE_STATIC(pridebugfdlock);
+-#endif
+-
+ /*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
+ when it's doing something critical. */
+ AST_MUTEX_DEFINE_STATIC(monlock);
+@@ -253,6 +251,7 @@ static enum ast_bridge_result zt_bridge(
+
+ static int zt_sendtext(struct ast_channel *c, const char *text);
+
++
+ /*! \brief Avoid the silly zt_getevent which ignores a bunch of events */
+ static inline int zt_get_event(int fd)
+ {
+@@ -297,6 +296,14 @@ static int ringt_base = DEFAULT_RINGT;
+ #define PRI_SPAN(p) (((p) >> 8) & 0xff)
+ #define PRI_EXPLICIT(p) (((p) >> 16) & 0x01)
+
++struct zt_suspended_call {
++ ast_mutex_t lock; /* Mutex */
++ char msn[AST_MAX_EXTENSION]; /* the MSN to which this parked call belongs */
++ char callid[10]; /* the callID provided by the user */
++ int parked_at; /* extension in the call parking context */
++ struct zt_suspended_call *next;
++};
++
+ struct zt_pri {
+ pthread_t master; /*!< Thread of master */
+ ast_mutex_t lock; /*!< Mutex */
+@@ -310,6 +317,8 @@ struct zt_pri {
+ int nsf; /*!< Network-Specific Facilities */
+ int dialplan; /*!< Dialing plan */
+ int localdialplan; /*!< Local dialing plan */
++ char nocid[AST_MAX_EXTENSION]; /*!< CallerID string to use if none provided */
++ char withheldcid[AST_MAX_EXTENSION]; /*!< CallerID string to use if CallerID is withheld */
+ char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */
+ char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */
+ char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */
+@@ -321,6 +330,7 @@ struct zt_pri {
+ int prilogicalspan; /*!< Logical span number within trunk group */
+ int numchans; /*!< Num of channels we represent */
+ int overlapdial; /*!< In overlap dialing mode */
++ int usercid;
+ int facilityenable; /*!< Enable facility IEs */
+ struct pri *dchans[NUM_DCHANS]; /*!< Actual d-channels */
+ int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */
+@@ -336,6 +346,8 @@ struct zt_pri {
+ struct zt_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
+ struct zt_pvt *crvs; /*!< Member CRV structs */
+ struct zt_pvt *crvend; /*!< Pointer to end of CRV structs */
++ struct zt_suspended_call *suspended_calls; /* Calls parked with SUSPEND messages */
++ int debugfd;
+ };
+
+
+@@ -453,6 +465,8 @@ static struct zt_pvt {
+ unsigned int echocanbridged:1;
+ unsigned int echocanon:1;
+ unsigned int faxhandled:1; /*!< Has a fax tone already been handled? */
++ /*!< KPJ: i will abuse this flag to implement a zapata option for dialing out
++ on a zap channel with EC to be off no matter what happens. */
+ unsigned int firstradio:1;
+ unsigned int hanguponpolarityswitch:1;
+ unsigned int hardwaredtmf:1;
+@@ -467,7 +481,8 @@ static struct zt_pvt {
+ unsigned int overlapdial:1;
+ unsigned int permcallwaiting:1;
+ unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */
+- unsigned int priindication_oob:1;
++ unsigned int priindication_oob:2;
++ unsigned int pritransfer:2;
+ unsigned int priexclusive:1;
+ unsigned int pulse:1;
+ unsigned int pulsedial:1; /*!< whether a pulse dial phone is detected */
+@@ -504,6 +519,7 @@ static struct zt_pvt {
+ #endif
+ char cid_num[AST_MAX_EXTENSION];
+ int cid_ton; /*!< Type Of Number (TON) */
++ int cid_pres; /*!< Calling Presentation */
+ char cid_name[AST_MAX_EXTENSION];
+ char lastcid_num[AST_MAX_EXTENSION];
+ char lastcid_name[AST_MAX_EXTENSION];
+@@ -569,6 +585,8 @@ static struct zt_pvt {
+ struct zt_pvt *bearer;
+ struct zt_pvt *realcall;
+ q931_call *call;
++ int tei; /* channel in use by this tei */
++ q931_call *holdedcall;
+ int prioffset;
+ int logicalspan;
+ #endif
+@@ -614,11 +632,14 @@ static struct zt_chan_conf zt_chan_conf_
+ .minunused = 2,
+ .idleext = "",
+ .idledial = "",
++ .nocid = "No CID available",
++ .withheldcid = "CID withheld",
+ .internationalprefix = "",
+ .nationalprefix = "",
+ .localprefix = "",
+ .privateprefix = "",
+ .unknownprefix = "",
++ .usercid = 0,
+
+ .resetinterval = 3600
+ },
+@@ -630,6 +651,8 @@ static struct zt_chan_conf zt_chan_conf_
+ .mohinterpret = "default",
+ .mohsuggest = "",
+ .transfertobusy = 1,
++ .priindication_oob = 0,
++ .pritransfer = 0,
+
+ .cid_signalling = CID_SIG_BELL,
+ .cid_start = CID_START_RING,
+@@ -684,6 +707,8 @@ static int zt_indicate(struct ast_channe
+ static int zt_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+ static int zt_setoption(struct ast_channel *chan, int option, void *data, int datalen);
+ static int zt_func_read(struct ast_channel *chan, char *function, char *data, char *buf, size_t len);
++static void enable_dtmf_detect(struct zt_pvt *p);
++static void disable_dtmf_detect(struct zt_pvt *p);
+
+ static const struct ast_channel_tech zap_tech = {
+ .type = "Zap",
+@@ -715,6 +740,13 @@ static const struct ast_channel_tech zap
+ struct zt_pvt *round_robin[32];
+
+ #ifdef HAVE_PRI
++struct app_tmp {
++ char app[256];
++ char data[256];
++ struct ast_channel *chan;
++ pthread_t t;
++};
++
+ static inline int pri_grab(struct zt_pvt *pvt, struct zt_pri *pri)
+ {
+ int res;
+@@ -1417,12 +1449,16 @@ static void zt_enable_ec(struct zt_pvt *
+ int res;
+ if (!p)
+ return;
++ if (p->faxhandled) {
++ ast_log(LOG_DEBUG, "Not enabling echo cancellation on a fax/modem call\n");
++ return;
++ }
+ if (p->echocanon) {
+ ast_log(LOG_DEBUG, "Echo cancellation already on\n");
+ return;
+ }
+ if (p->digital) {
+- ast_log(LOG_DEBUG, "Echo cancellation isn't required on digital connection\n");
++ ast_log(LOG_DEBUG, "Echo cancellation does not make any sense on digital connections!\n");
+ return;
+ }
+ if (p->echocancel) {
+@@ -1449,7 +1485,7 @@ static void zt_train_ec(struct zt_pvt *p
+ {
+ int x;
+ int res;
+- if (p && p->echocancel && p->echotraining) {
++ if (p && p->echocancel && p->echotraining && (!p->digital) && (!p->faxhandled)) {
+ x = p->echotraining;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_ECHOTRAIN, &x);
+ if (res)
+@@ -1810,7 +1846,12 @@ static int zt_call(struct ast_channel *a
+ ast_log(LOG_WARNING, "Unable to flush input on channel %d\n", p->channel);
+ p->outgoing = 1;
+
+- set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law);
++ if (IS_DIGITAL(ast->transfercapability)) {
++ set_actual_gain(p->subs[SUB_REAL].zfd, 0, 0, 0, p->law);
++ } else {
++ set_actual_gain(p->subs[SUB_REAL].zfd, 0, p->rxgain, p->txgain, p->law);
++ }
++
+
+ mysig = p->sig;
+ if (p->outsigmod > -1)
+@@ -2041,6 +2082,7 @@ static int zt_call(struct ast_channel *a
+ case SIG_PRI:
+ /* We'll get it in a moment -- but use dialdest to store pre-setup_ack digits */
+ p->dialdest[0] = '\0';
++ disable_dtmf_detect(p);
+ break;
+ default:
+ ast_log(LOG_DEBUG, "not yet implemented\n");
+@@ -2061,6 +2103,12 @@ static int zt_call(struct ast_channel *a
+ const char *rr_str;
+ int redirect_reason;
+
++ if ((p->pri->nodetype == BRI_NETWORK_PTMP) || (p->pri->nodetype == BRI_NETWORK)) {
++ // pass NO audio when ringing an isdn phone
++ p->dialing = 1;
++ // maybe we could allow passing audio when calling a p2p PBX, but well... ;-)
++ }
++
+ c = strchr(dest, '/');
+ if (c)
+ c++;
+@@ -2083,6 +2131,7 @@ static int zt_call(struct ast_channel *a
+ ast_mutex_unlock(&p->lock);
+ return -1;
+ }
++ strncpy(p->dnid, (c + p->stripmsd), sizeof(p->dnid)-1);
+ if (mysig != SIG_FXSKS) {
+ p->dop.op = ZT_DIAL_OP_REPLACE;
+ s = strchr(c + p->stripmsd, 'w');
+@@ -2106,6 +2155,8 @@ static int zt_call(struct ast_channel *a
+ pri_rel(p->pri);
+ ast_mutex_unlock(&p->lock);
+ return -1;
++ } else {
++ // ast_log(LOG_NOTICE, "call %d\n", p->call);
+ }
+ if (!(sr = pri_sr_new())) {
+ ast_log(LOG_WARNING, "Failed to allocate setup request channel %d\n", p->channel);
+@@ -2135,7 +2186,7 @@ static int zt_call(struct ast_channel *a
+ pri_sr_set_channel(sr, p->bearer ? PVT_TO_CHANNEL(p->bearer) : PVT_TO_CHANNEL(p), exclusive, 1);
+ pri_sr_set_bearer(sr, p->digital ? PRI_TRANS_CAP_DIGITAL : ast->transfercapability,
+ (p->digital ? -1 :
+- ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW)));
++ ((p->law == ZT_LAW_ALAW) ? PRI_LAYER_1_ALAW : PRI_LAYER_1_ULAW)), ast->lowlayercompat);
+ if (p->pri->facilityenable)
+ pri_facility_enable(p->pri->pri);
+
+@@ -2399,8 +2450,10 @@ static int pri_find_dchan(struct zt_pri
+ }
+ if (newslot < 0) {
+ newslot = 0;
+- ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n",
++ if (pri->nodetype != BRI_CPE_PTMP) {
++ ast_log(LOG_WARNING, "No D-channels available! Using Primary channel %d as D-channel anyway!\n",
+ pri->dchannels[newslot]);
++ }
+ }
+ if (old && (oldslot != newslot))
+ ast_log(LOG_NOTICE, "Switching from from d-channel %d to channel %d!\n",
+@@ -2410,6 +2463,16 @@ static int pri_find_dchan(struct zt_pri
+ }
+ #endif
+
++static int zt_setlaw(int zfd, int law)
++{
++ int res;
++ res = ioctl(zfd, ZT_SETLAW, &law);
++ if (res)
++ return res;
++ return 0;
++}
++
++
+ static int zt_hangup(struct ast_channel *ast)
+ {
+ int res;
+@@ -2457,8 +2520,7 @@ static int zt_hangup(struct ast_channel
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Hangup: channel: %d index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+ p->channel, index, p->subs[SUB_REAL].zfd, p->subs[SUB_CALLWAIT].zfd, p->subs[SUB_THREEWAY].zfd);
+- p->ignoredtmf = 0;
+-
++
+ if (index > -1) {
+ /* Real channel, do some fixup */
+ p->subs[index].owner = NULL;
+@@ -2560,6 +2622,7 @@ static int zt_hangup(struct ast_channel
+ }
+
+ if (!p->subs[SUB_REAL].owner && !p->subs[SUB_CALLWAIT].owner && !p->subs[SUB_THREEWAY].owner) {
++ int outgoing = p->outgoing;
+ p->owner = NULL;
+ p->ringt = 0;
+ p->distinctivering = 0;
+@@ -2602,7 +2665,7 @@ static int zt_hangup(struct ast_channel
+ pri_call_set_useruser(p->call, useruser);
+ #endif
+
+- pri_hangup(p->pri->pri, p->call, -1);
++ pri_hangup(p->pri->pri, p->call, -1, -1);
+ p->call = NULL;
+ if (p->bearer)
+ p->bearer->call = NULL;
+@@ -2622,7 +2685,28 @@ static int zt_hangup(struct ast_channel
+ if (atoi(cause))
+ icause = atoi(cause);
+ }
+- pri_hangup(p->pri->pri, p->call, icause);
++
++ pri_hangup(p->pri->pri, p->call, icause, -1);
++
++ /* if we send a rel9999ease complete we wont ge no hangup event, so clear the call here */
++ if (icause == 34 || icause == 44 || icause == 82 || icause == 1 || icause == 81 || icause == 17) {
++ if ((ast->_state == AST_STATE_RING) || (ast->_state == AST_STATE_RINGING) || (ast->_state == AST_STATE_DIALING) || (ast->_state == AST_STATE_RESERVED)) {
++ p->call = NULL;
++ } else {
++ ast_log(LOG_ERROR, "What is wrong with you? You cannot use cause %d number when in state %d!\n", icause, ast->_state);
++ icause = 16; /* Note, in pri_hangup() libpri will already override the cause */
++ }
++ }
++
++ if (p->pri->nodetype == BRI_NETWORK_PTMP) {
++ if ((icause == 16 || icause == -1) && (ast->_state != AST_STATE_UP)) {
++ if (outgoing) {
++ p->call = NULL;
++ }
++ }
++ }
++
++
+ }
+ if (res < 0)
+ ast_log(LOG_WARNING, "pri_disconnect failed\n");
+@@ -2806,10 +2890,14 @@ static int zt_answer(struct ast_channel
+ p->proceeding = 1;
+ res = pri_answer(p->pri->pri, p->call, 0, !p->digital);
+ pri_rel(p->pri);
++ /* stop ignoring inband dtmf */
++ enable_dtmf_detect(p);
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ res = -1;
+ }
++ /* the audio path is complete now, train the echo canceler */
++ zt_train_ec(p);
+ break;
+ #endif
+ case 0:
+@@ -3446,6 +3534,15 @@ static int zt_fixup(struct ast_channel *
+ {
+ struct zt_pvt *p = newchan->tech_pvt;
+ int x;
++ if (newchan && newchan->tech_pvt) {
++ p = newchan->tech_pvt;
++ }
++ if (!p) {
++ if (newchan) {
++ ast_log(LOG_ERROR, "channel %s has no tech_pvt structure\n", newchan->name);
++ }
++ return 0;
++ }
+ ast_mutex_lock(&p->lock);
+ ast_log(LOG_DEBUG, "New owner for channel %d is %s\n", p->channel, newchan->name);
+ if (p->owner == oldchan) {
+@@ -3655,8 +3752,10 @@ static void zt_handle_dtmfup(struct ast_
+ pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
+ if (ast_async_goto(ast, target_context, "fax", 1))
+ ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
+- } else
++ } else {
++ if (option_verbose > 2)
+ ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
++ }
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
+ } else if (option_debug)
+@@ -3815,7 +3914,7 @@ static struct ast_frame *zt_handle_event
+ if (p->call) {
+ if (p->pri && p->pri->pri) {
+ if (!pri_grab(p, p->pri)) {
+- pri_hangup(p->pri->pri, p->call, -1);
++ pri_hangup(p->pri->pri, p->call, -1, -1);
+ pri_destroycall(p->pri->pri, p->call);
+ p->call = NULL;
+ pri_rel(p->pri);
+@@ -4886,7 +4985,7 @@ static struct ast_frame *zt_read(struct
+ p->subs[index].f.data = NULL;
+ p->subs[index].f.datalen= 0;
+ }
+- if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) {
++ if (p->dsp && (!p->ignoredtmf || p->callwaitcas || p->busydetect || p->callprogress) && !index) {
+ /* Perform busy detection. etc on the zap line */
+ f = ast_dsp_process(ast, p->dsp, &p->subs[index].f);
+ if (f) {
+@@ -4898,8 +4997,9 @@ static struct ast_frame *zt_read(struct
+ }
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ #ifdef HAVE_PRI
+- if (!p->proceeding && p->sig==SIG_PRI && p->pri && p->pri->overlapdial) {
+- /* Don't accept in-band DTMF when in overlap dial mode */
++ if (p->sig==SIG_PRI && p->pri && p->pri->overlapdial && p->ignoredtmf) {
++ /* Don't accept in-band DTMF when in overlap dial mode
++ or when in non-overlap overlapdialing mode ... */
+ f->frametype = AST_FRAME_NULL;
+ f->subclass = 0;
+ }
+@@ -4974,7 +5074,9 @@ static int zt_write(struct ast_channel *
+ #endif
+ /* Write a frame of (presumably voice) data */
+ if (frame->frametype != AST_FRAME_VOICE) {
+- if (frame->frametype != AST_FRAME_IMAGE)
++ if (frame->frametype == AST_FRAME_TEXT) {
++ ast_log(LOG_NOTICE, "text\n");
++ } else if (frame->frametype != AST_FRAME_IMAGE)
+ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
+ return 0;
+ }
+@@ -5042,7 +5144,7 @@ static int zt_indicate(struct ast_channe
+ switch (condition) {
+ case AST_CONTROL_BUSY:
+ #ifdef HAVE_PRI
+- if (p->priindication_oob && p->sig == SIG_PRI) {
++ if ((p->priindication_oob == 1) && p->sig == SIG_PRI) {
+ chan->hangupcause = AST_CAUSE_USER_BUSY;
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ res = 0;
+@@ -5124,7 +5226,7 @@ static int zt_indicate(struct ast_channe
+ case AST_CONTROL_CONGESTION:
+ chan->hangupcause = AST_CAUSE_CONGESTION;
+ #ifdef HAVE_PRI
+- if (p->priindication_oob && p->sig == SIG_PRI) {
++ if ((p->priindication_oob == 1) && p->sig == SIG_PRI) {
+ chan->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ res = 0;
+@@ -5321,8 +5423,12 @@ static struct ast_channel *zt_new(struct
+ if (state == AST_STATE_RING)
+ tmp->rings = 1;
+ tmp->tech_pvt = i;
++#ifdef HAVE_PRI
++ if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS) || (i->sig == SIG_PRI)) {
++#else
+ if ((i->sig == SIG_FXOKS) || (i->sig == SIG_FXOGS) || (i->sig == SIG_FXOLS)) {
+- /* Only FXO signalled stuff can be picked up */
++#endif
++ /* Only FXO signalled stuff can be picked up */ /* i dont think so, mr. ulaw! we alaws like to pick up BRIs/PRIs */
+ tmp->callgroup = i->callgroup;
+ tmp->pickupgroup = i->pickupgroup;
+ }
+@@ -5452,6 +5558,7 @@ static void *ss_thread(void *data)
+ int len = 0;
+ int res;
+ int index;
++ int network;
+
+ /* in the bizarre case where the channel has become a zombie before we
+ even get started here, abort safely
+@@ -5480,10 +5587,17 @@ static void *ss_thread(void *data)
+ len = strlen(exten);
+ res = 0;
+ while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
+- if (len && !ast_ignore_pattern(chan->context, exten))
++ if (len && !ast_ignore_pattern(chan->context, exten)) {
+ tone_zone_play_tone(p->subs[index].zfd, -1);
+- else
+- tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
++ } else {
++ network = p->pri->nodetype == PRI_NETWORK || p->pri->nodetype == BRI_NETWORK || p->pri->nodetype == BRI_NETWORK_PTMP;
++ if (network) {
++ tone_zone_play_tone(p->subs[index].zfd, ZT_TONE_DIALTONE);
++ } else {
++ /* cpe be quiet */
++ tone_zone_play_tone(p->subs[index].zfd, -1);
++ }
++ }
+ if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num))
+ timeout = matchdigittimeout;
+ else
+@@ -6697,9 +6811,20 @@ static int handle_init_event(struct zt_p
+ case ZT_EVENT_NOALARM:
+ i->inalarm = 0;
+ if (!i->unknown_alarm) {
++#ifdef HAVE_PRI
++ if (i->pri) {
++ if ((i->pri->nodetype == BRI_CPE_PTMP) || (i->pri->nodetype == BRI_CPE)) {
++ /* dont annoy BRI TE mode users with layer2layer alarms */
++ } else {
++#endif
+ ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", i->channel);
+ manager_event(EVENT_FLAG_SYSTEM, "AlarmClear",
+ "Channel: %d\r\n", i->channel);
++#ifdef HAVE_PRI
++ }
++ }
++#endif
++
+ } else {
+ i->unknown_alarm = 0;
+ }
+@@ -6708,7 +6833,13 @@ static int handle_init_event(struct zt_p
+ i->inalarm = 1;
+ res = get_alarms(i);
+ do {
+- const char *alarm_str = alarm2str(res);
++#ifdef HAVE_PRI
++ if (i->pri) {
++ if ((i->pri->nodetype == BRI_CPE_PTMP) || (i->pri->nodetype == BRI_CPE)) {
++ /* dont annoy BRI TE mode users with layer2layer alarms */
++ } else {
++#endif
++ const char *alarm_str = alarm2str(res);
+
+ /* hack alert! Zaptel 1.4 now exposes FXO battery as an alarm, but asterisk 1.4
+ * doesn't know what to do with it. Don't confuse users with log messages. */
+@@ -6724,6 +6855,10 @@ static int handle_init_event(struct zt_p
+ "Alarm: %s\r\n"
+ "Channel: %d\r\n",
+ alarm_str, i->channel);
++#ifdef HAVE_PRI
++ }
++ }
++#endif
+ } while (0);
+ /* fall thru intentionally */
+ case ZT_EVENT_ONHOOK:
+@@ -6768,8 +6903,10 @@ static int handle_init_event(struct zt_p
+ zt_set_hook(i->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ break;
+ case SIG_PRI:
+- zt_disable_ec(i);
+- res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
++ if (event != ZT_EVENT_ALARM) {
++ zt_disable_ec(i);
++ res = tone_zone_play_tone(i->subs[SUB_REAL].zfd, -1);
++ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to handle on hook with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
+@@ -7062,6 +7199,8 @@ static int pri_resolve_span(int *span, i
+ } else {
+ if (si->totalchans == 31) { /* if it's an E1 */
+ pris[*span].dchannels[0] = 16 + offset;
++ } else if (si->totalchans == 3) { /* if it's an S0 ZAPBRI */
++ pris[*span].dchannels[0] = 3 + offset;
+ } else {
+ pris[*span].dchannels[0] = 24 + offset;
+ }
+@@ -7314,6 +7453,11 @@ static struct zt_pvt *mkintf(int channel
+ destroy_zt_pvt(&tmp);
+ return NULL;
+ }
++ if ((pris[span].localdialplan) && (pris[span].localdialplan != conf->pri.localdialplan)) {
++ ast_log(LOG_ERROR, "Span %d is already a %s local dialing plan\n", span + 1, dialplan2str(pris[span].localdialplan));
++ destroy_zt_pvt(&tmp);
++ return NULL;
++ }
+ if (pris[span].minunused && (pris[span].minunused != conf->pri.minunused)) {
+ ast_log(LOG_ERROR, "Span %d already has minunused of %d.\n", span + 1, conf->pri.minunused);
+ destroy_zt_pvt(&tmp);
+@@ -7331,6 +7475,11 @@ static struct zt_pvt *mkintf(int channel
+ return NULL;
+ }
+ pris[span].nodetype = conf->pri.nodetype;
++
++ if (conf->pri.nodetype == BRI_NETWORK_PTMP) {
++ pris[span].dchanavail[0] = DCHAN_AVAILABLE;
++ pri_find_dchan(&pris[span]);
++ }
+ pris[span].switchtype = myswitchtype;
+ pris[span].nsf = conf->pri.nsf;
+ pris[span].dialplan = conf->pri.dialplan;
+@@ -7339,9 +7488,13 @@ static struct zt_pvt *mkintf(int channel
+ pris[span].minunused = conf->pri.minunused;
+ pris[span].minidle = conf->pri.minidle;
+ pris[span].overlapdial = conf->pri.overlapdial;
++ pris[span].usercid = conf->pri.usercid;
++ pris[span].suspended_calls = NULL;
+ pris[span].facilityenable = conf->pri.facilityenable;
+ ast_copy_string(pris[span].idledial, conf->pri.idledial, sizeof(pris[span].idledial));
+ ast_copy_string(pris[span].idleext, conf->pri.idleext, sizeof(pris[span].idleext));
++ ast_copy_string(pris[span].nocid, conf->pri.nocid, sizeof(pris[span].nocid));
++ ast_copy_string(pris[span].withheldcid, conf->pri.withheldcid, sizeof(pris[span].withheldcid));
+ ast_copy_string(pris[span].internationalprefix, conf->pri.internationalprefix, sizeof(pris[span].internationalprefix));
+ ast_copy_string(pris[span].nationalprefix, conf->pri.nationalprefix, sizeof(pris[span].nationalprefix));
+ ast_copy_string(pris[span].localprefix, conf->pri.localprefix, sizeof(pris[span].localprefix));
+@@ -7483,6 +7636,7 @@ static struct zt_pvt *mkintf(int channel
+ tmp->restrictcid = conf->chan.restrictcid;
+ tmp->use_callingpres = conf->chan.use_callingpres;
+ tmp->priindication_oob = conf->chan.priindication_oob;
++ tmp->pritransfer = conf->chan.pritransfer;
+ tmp->priexclusive = conf->chan.priexclusive;
+ if (tmp->usedistinctiveringdetection) {
+ if (!tmp->use_callerid) {
+@@ -7765,7 +7919,7 @@ static int pri_find_empty_chan(struct zt
+ break;
+ if (!backwards && (x >= pri->numchans))
+ break;
+- if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner) {
++ if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner && !pri->pvts[x]->call) {
+ ast_log(LOG_DEBUG, "Found empty available channel %d/%d\n",
+ pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset);
+ return x;
+@@ -7961,6 +8115,11 @@ static struct ast_channel *zt_request(co
+ p->digital = 1;
+ if (tmp)
+ tmp->transfercapability = AST_TRANS_CAP_DIGITAL;
++ } else if (opt == 'm') {
++ /* If this is a modem/fax call, pretend to have the fax handled and dont do EC */
++ p->faxhandled = 1;
++ if (tmp)
++ tmp->transfercapability = AST_TRANS_CAP_3_1K_AUDIO;
+ } else {
+ ast_log(LOG_WARNING, "Unknown option '%c' in '%s'\n", opt, (char *)data);
+ }
+@@ -7994,13 +8153,14 @@ next:
+ *cause = AST_CAUSE_BUSY;
+ } else if (groupmatched) {
+ *cause = AST_CAUSE_CONGESTION;
++ } else {
++ *cause = AST_CAUSE_CONGESTION;
+ }
+ }
+
+ return tmp;
+ }
+
+-
+ #ifdef HAVE_PRI
+ static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv)
+ {
+@@ -8045,6 +8205,7 @@ static int pri_find_principle(struct zt_
+ static int pri_fixup_principle(struct zt_pri *pri, int principle, q931_call *c)
+ {
+ int x;
++ int res = 0;
+ struct zt_pvt *crv;
+ if (!c) {
+ if (principle < 0)
+@@ -8075,6 +8236,7 @@ static int pri_fixup_principle(struct zt
+ }
+ /* Fix it all up now */
+ new->owner = old->owner;
++ new->outgoing = old->outgoing;
+ old->owner = NULL;
+ if (new->owner) {
+ ast_string_field_build(new->owner, name,
+@@ -8094,6 +8256,34 @@ static int pri_fixup_principle(struct zt
+ new->dsp_features = old->dsp_features;
+ old->dsp = NULL;
+ old->dsp_features = 0;
++
++ /* Copy faxhandled/digial, alreadyhungup */
++ new->faxhandled = old->faxhandled;
++ new->digital = old->digital;
++ new->alreadyhungup = old->alreadyhungup;
++
++ /* Copy law, gains, etc */
++ new->law = old->law;
++ if (ioctl(new->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &new->law) == -1)
++ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", new->channel, new->law);
++ res = zt_setlaw(new->subs[SUB_REAL].zfd, new->law);
++ if (res < 0)
++ ast_log(LOG_WARNING, "Unable to set law on channel %d\n", new->channel);
++ if (!new->digital) {
++ res = set_actual_gain(new->subs[SUB_REAL].zfd, 0, new->rxgain, new->txgain, new->law);
++ } else {
++ res = set_actual_gain(new->subs[SUB_REAL].zfd, 0, 0, 0, new->law);
++ }
++ if (res < 0)
++ ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", new->channel);
++
++ /* Shutdown old channel */
++ zt_confmute(old, 0);
++ update_conf(old);
++ reset_conf(old);
++ restore_gains(old);
++ zt_disable_ec(old);
++ zt_setlinear(old->subs[SUB_REAL].zfd, 0);
+ }
+ return principle;
+ }
+@@ -8122,7 +8312,9 @@ static int pri_fixup_principle(struct zt
+ }
+ crv = crv->next;
+ }
+- ast_log(LOG_WARNING, "Call specified, but not found?\n");
++ if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) {
++ ast_log(LOG_WARNING, "Call specified, but not found?\n");
++ }
+ return -1;
+ }
+
+@@ -8181,86 +8373,21 @@ static void *do_idle_thread(void *vchan)
+ #ifndef PRI_RESTART
+ #error "Upgrade your libpri"
+ #endif
+-static void zt_pri_message(struct pri *pri, char *s)
++static void zt_pri_message(char *s, int span)
+ {
+- int x, y;
+- int dchan = -1, span = -1;
+- int dchancount = 0;
+-
+- if (pri) {
+- for (x = 0; x < NUM_SPANS; x++) {
+- for (y = 0; y < NUM_DCHANS; y++) {
+- if (pris[x].dchans[y])
+- dchancount++;
+-
+- if (pris[x].dchans[y] == pri)
+- dchan = y;
+- }
+- if (dchan >= 0) {
+- span = x;
+- break;
+- }
+- dchancount = 0;
+- }
+- if ((dchan >= 0) && (span >= 0)) {
+- if (dchancount > 1)
+- ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s);
+- else
+- ast_verbose("%s", s);
+- } else
+- ast_log(LOG_ERROR, "PRI debug error: could not find pri associated it with debug message output\n");
+- } else
+- ast_verbose("%s", s);
+-
+- ast_mutex_lock(&pridebugfdlock);
+-
+- if (pridebugfd >= 0)
+- write(pridebugfd, s, strlen(s));
+-
+- ast_mutex_unlock(&pridebugfdlock);
++ ast_verbose("%d %s", span, s);
+ }
+
+-static void zt_pri_error(struct pri *pri, char *s)
++static void zt_pri_error(char *s, int span)
+ {
+- int x, y;
+- int dchan = -1, span = -1;
+- int dchancount = 0;
+-
+- if (pri) {
+- for (x = 0; x < NUM_SPANS; x++) {
+- for (y = 0; y < NUM_DCHANS; y++) {
+- if (pris[x].dchans[y])
+- dchancount++;
+-
+- if (pris[x].dchans[y] == pri)
+- dchan = y;
+- }
+- if (dchan >= 0) {
+- span = x;
+- break;
+- }
+- dchancount = 0;
+- }
+- if ((dchan >= 0) && (span >= 0)) {
+- if (dchancount > 1)
+- ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s);
+- else
+- ast_log(LOG_ERROR, "%s", s);
+- } else
+- ast_log(LOG_ERROR, "PRI debug error: could not find pri associated it with debug message output\n");
+- } else
+- ast_log(LOG_ERROR, "%s", s);
+-
+- ast_mutex_lock(&pridebugfdlock);
+-
+- if (pridebugfd >= 0)
+- write(pridebugfd, s, strlen(s));
+-
+- ast_mutex_unlock(&pridebugfdlock);
++ ast_log(LOG_WARNING, "%d %s", span, s);
+ }
+
+ static int pri_check_restart(struct zt_pri *pri)
+ {
++ if ((pri->nodetype != PRI_NETWORK) && (pri->nodetype != PRI_CPE)) {
++ return 0;
++ }
+ do {
+ pri->resetpos++;
+ } while ((pri->resetpos < pri->numchans) &&
+@@ -8344,13 +8471,30 @@ static void apply_plan_to_number(char *b
+ }
+ }
+
+-static int zt_setlaw(int zfd, int law)
+-{
+- int res;
+- res = ioctl(zfd, ZT_SETLAW, &law);
+- if (res)
+- return res;
+- return 0;
++static void pri_make_callerid(struct zt_pri *pri, char *callerid, int callerid_len, char *callingnum, int callingnum_len, int callingplan, int callingpres, int stripmsd) {
++ if (callingnum && (callingnum_len > stripmsd)) {
++ callingnum += stripmsd;
++ }
++ switch (callingplan) {
++ case PRI_INTERNATIONAL_ISDN:
++ snprintf(callerid, callerid_len, "%s%s", pri->internationalprefix, callingnum);
++ break;
++ case PRI_NATIONAL_ISDN:
++ snprintf(callerid, callerid_len, "%s%s", pri->nationalprefix, callingnum);
++ break;
++ case PRI_LOCAL_ISDN:
++ snprintf(callerid, callerid_len, "%s%s", pri->localprefix, callingnum);
++ break;
++ case PRI_PRIVATE:
++ snprintf(callerid, callerid_len, "%s%s", pri->privateprefix, callingnum);
++ break;
++ case PRI_UNKNOWN:
++ snprintf(callerid, callerid_len, "%s%s", pri->unknownprefix, callingnum);
++ break;
++ default:
++ snprintf(callerid, callerid_len, "%s", callingnum);
++ break;
++ }
+ }
+
+ static void *pri_dchannel(void *vpri)
+@@ -8530,15 +8674,44 @@ static void *pri_dchannel(void *vpri)
+ /* Check for an event */
+ x = 0;
+ res = ioctl(pri->fds[which], ZT_GETEVENT, &x);
+- if (x)
++ if ((pri->nodetype != BRI_CPE) && (pri->nodetype != BRI_CPE_PTMP)) {
++ /* dont annoy BRI TE mode users with layer2layer alarms */
++ if (x)
+ ast_log(LOG_NOTICE, "PRI got event: %s (%d) on %s D-channel of span %d\n", event2str(x), x, pri_order(which), pri->span);
++ }
+ /* Keep track of alarm state */
+ if (x == ZT_EVENT_ALARM) {
+ pri->dchanavail[which] &= ~(DCHAN_NOTINALARM | DCHAN_UP);
+ pri_find_dchan(pri);
++ if ((pri->nodetype == BRI_CPE) || (pri->nodetype == BRI_CPE_PTMP)) {
++ if (pri->pri) {
++ for (i=0; i<pri->numchans; i++) {
++ struct zt_pvt *p = pri->pvts[i];
++ if (p) {
++ if (p->call) {
++ if (p->pri && p->pri->pri) {
++ pri_destroycall(p->pri->pri, p->call);
++ p->call = NULL;
++ p->tei = -1;
++ } else
++ ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n");
++ }
++ if (p->owner)
++ p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ p->inalarm = 1;
++ }
++ }
++ pri_shutdown(pri->pri);
++ }
++ }
+ } else if (x == ZT_EVENT_NOALARM) {
+- pri->dchanavail[which] |= DCHAN_NOTINALARM;
+- pri_restart(pri->dchans[which]);
++ if ((pri->nodetype == BRI_CPE) || (pri->nodetype == BRI_CPE_PTMP)) {
++ pri->dchanavail[which] |= DCHAN_NOTINALARM;
++ // pri->dchanavail[which] |= DCHAN_UP;
++ } else {
++ pri->dchanavail[which] |= DCHAN_NOTINALARM;
++ pri_restart(pri->dchans[which]);
++ }
+ }
+
+ if (option_debug)
+@@ -8550,8 +8723,7 @@ static void *pri_dchannel(void *vpri)
+ break;
+ }
+ } else if (errno != EINTR)
+- ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno));
+-
++ ast_log(LOG_WARNING, "pri_event returned error %d (%s) on span %d\n", errno, strerror(errno), pri->span);
+ if (e) {
+ if (pri->debug)
+ pri_dump_event(pri->dchans[which], e);
+@@ -8576,6 +8748,17 @@ static void *pri_dchannel(void *vpri)
+
+ switch (e->e) {
+ case PRI_EVENT_DCHAN_UP:
++ if (pri->nodetype == BRI_NETWORK_PTMP) {
++ if (option_verbose > 3)
++ ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up for TEI %d\n", pri_order(which), pri->span, e->gen.tei);
++ } else if (pri->nodetype == BRI_CPE_PTMP) {
++ if (option_verbose > 3)
++ ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span);
++ } else {
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d up\n", pri_order(which), pri->span);
++ }
++
+ if (!pri->pri) pri_find_dchan(pri);
+
+ /* Note presense of D-channel */
+@@ -8594,6 +8777,12 @@ static void *pri_dchannel(void *vpri)
+ }
+ break;
+ case PRI_EVENT_DCHAN_DOWN:
++ if (pri->nodetype == BRI_NETWORK_PTMP) {
++ if (option_verbose > 3)
++ ast_verbose(VERBOSE_PREFIX_2 "%s D-Channel on span %d down for TEI %d\n", pri_order(which), pri->span, e->gen.tei);
++ // PTMP BRIs have N dchans, handled by libpri
++ if (e->gen.tei == 0) break;
++ }
+ pri_find_dchan(pri);
+ if (!pri_is_up(pri)) {
+ pri->resetting = 0;
+@@ -8601,16 +8790,18 @@ static void *pri_dchannel(void *vpri)
+ for (i = 0; i < pri->numchans; i++) {
+ struct zt_pvt *p = pri->pvts[i];
+ if (p) {
++ if ((p->tei == e->gen.tei) || (pri->nodetype != BRI_NETWORK_PTMP)) {
+ if (!p->pri || !p->pri->pri || pri_get_timer(p->pri->pri, PRI_TIMER_T309) < 0) {
+ /* T309 is not enabled : hangup calls when alarm occurs */
+ if (p->call) {
+ if (p->pri && p->pri->pri) {
+- pri_hangup(p->pri->pri, p->call, -1);
++ pri_hangup(p->pri->pri, p->call, -1, -1);
+ pri_destroycall(p->pri->pri, p->call);
+ p->call = NULL;
+ } else
+ ast_log(LOG_WARNING, "The PRI Call have not been destroyed\n");
+ }
++ p->tei = -1;
+ if (p->realcall) {
+ pri_hangup_all(p->realcall, pri);
+ } else if (p->owner)
+@@ -8619,6 +8810,7 @@ static void *pri_dchannel(void *vpri)
+ p->inalarm = 1;
+ }
+ }
++ }
+ }
+ break;
+ case PRI_EVENT_RESTART:
+@@ -8653,8 +8845,8 @@ static void *pri_dchannel(void *vpri)
+ pri_destroycall(pri->pri, pri->pvts[x]->call);
+ pri->pvts[x]->call = NULL;
+ }
+- if (pri->pvts[chanpos]->realcall)
+- pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
++ if (pri->pvts[x]->realcall)
++ pri_hangup_all(pri->pvts[x]->realcall, pri);
+ else if (pri->pvts[x]->owner)
+ pri->pvts[x]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_mutex_unlock(&pri->pvts[x]->lock);
+@@ -8688,7 +8880,6 @@ static void *pri_dchannel(void *vpri)
+ }
+ }
+ break;
+-
+ case PRI_EVENT_INFO_RECEIVED:
+ chanpos = pri_find_principle(pri, e->ring.channel);
+ if (chanpos < 0) {
+@@ -8697,9 +8888,11 @@ static void *pri_dchannel(void *vpri)
+ } else {
+ chanpos = pri_fixup_principle(pri, chanpos, e->ring.call);
+ if (chanpos > -1) {
++// ast_log(LOG_NOTICE, "INFO received on channel %d/%d span %d\n",
++// PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ /* queue DTMF frame if the PBX for this call was already started (we're forwarding INFORMATION further on */
+- if (pri->overlapdial && pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) {
++ if (pri->pvts[chanpos]->call==e->ring.call && pri->pvts[chanpos]->owner) {
+ /* how to do that */
+ int digitlen = strlen(e->ring.callednum);
+ char digit;
+@@ -8711,6 +8904,14 @@ static void *pri_dchannel(void *vpri)
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ }
+ }
++ if (!pri->overlapdial) {
++ strncat(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten));
++ if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten + 1)) {
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1);
++ } else {
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
++ }
++ }
+ }
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+@@ -8718,36 +8919,59 @@ static void *pri_dchannel(void *vpri)
+ break;
+ case PRI_EVENT_RING:
+ crv = NULL;
+- if (e->ring.channel == -1)
++ if (e->ring.channel == -1) {
++ /* if no channel specified find one empty */
+ chanpos = pri_find_empty_chan(pri, 1);
+- else
++ } else {
+ chanpos = pri_find_principle(pri, e->ring.channel);
++ }
+ /* if no channel specified find one empty */
+ if (chanpos < 0) {
+- ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d/%d span %d\n",
+- PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
++ /* no channel specified and no free channel. this is a callwating SETUP */
++ if (e->ring.channel <= 0) {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Ignoring callwaiting SETUP on channel %d/%d span %d %d\n", PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span, e->ring.channel);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_USER_BUSY, -1);
++ break;
++ }
+ } else {
++ /* ok, we got a b channel for this call, lock it */
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->pvts[chanpos]->owner) {
+- if (pri->pvts[chanpos]->call == e->ring.call) {
+- ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n",
++ /* safety check, for messed up retransmissions? */
++ if (pri->pvts[chanpos]->call == e->ring.call) {
++ ast_log(LOG_WARNING, "Duplicate setup requested on channel %d/%d already in use on span %d\n",
+ PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+- break;
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ chanpos = -1;
++ break;
++ } else {
++ ast_log(LOG_WARNING, "Ring requested on channel %d/%d already in use on span %d. Hanging up owner.\n",
++ PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
++ if (pri->pvts[chanpos]->realcall) {
++ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ } else {
+- /* This is where we handle initial glare */
+- ast_log(LOG_DEBUG, "Ring requested on channel %d/%d already in use or previously requested on span %d. Attempting to renegotiating channel.\n",
+- PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), pri->span);
+- ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+- chanpos = -1;
++ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ /* XXX destroy the call here, so we can accept the retransmission as a new call */
++ pri_destroycall(pri->pri, e->ring.call);
+ }
+- }
+- if (chanpos > -1)
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ chanpos = -1;
++ break;
++ }
++ }
++ if (chanpos > -1) {
++ /* everything is ok with the b channel */
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ }
+ }
+- if ((chanpos < 0) && (e->ring.flexible))
+- chanpos = pri_find_empty_chan(pri, 1);
++ /* actually, we already got a valid channel by now */
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
++ /* dont detect dtmfs before the signalling is done */
++ disable_dtmf_detect(pri->pvts[chanpos]);
++ /* this channel is owned by this TEI */
++ pri->pvts[chanpos]->tei = e->ring.tei;
+ if (pri->switchtype == PRI_SWITCH_GR303_TMC) {
+ /* Should be safe to lock CRV AFAIK while bearer is still locked */
+ crv = pri_find_crv(pri, pri_get_crv(pri->pri, e->ring.call, NULL));
+@@ -8761,13 +8985,14 @@ static void *pri_dchannel(void *vpri)
+ ast_log(LOG_WARNING, "Call received for busy CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span);
+ } else
+ ast_log(LOG_NOTICE, "Call received for unconfigured CRV %d on span %d\n", pri_get_crv(pri->pri, e->ring.call, NULL), pri->span);
+- pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_INVALID_CALL_REFERENCE, -1);
+ if (crv)
+ ast_mutex_unlock(&crv->lock);
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ break;
+ }
+ }
++ /* assign call to b channel */
+ pri->pvts[chanpos]->call = e->ring.call;
+ apply_plan_to_number(plancallingnum, sizeof(plancallingnum), pri, e->ring.callingnum, e->ring.callingplan);
+ if (pri->pvts[chanpos]->use_callerid) {
+@@ -8792,34 +9017,82 @@ static void *pri_dchannel(void *vpri)
+ }
+ apply_plan_to_number(pri->pvts[chanpos]->rdnis, sizeof(pri->pvts[chanpos]->rdnis), pri,
+ e->ring.redirectingnum, e->ring.callingplanrdnis);
++ /* get callingpres */
++ pri->pvts[chanpos]->cid_pres = e->ring.callingpres;
++ switch (e->ring.callingpres) {
++ case PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
++ case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
++ case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
++ case PRES_PROHIB_NETWORK_NUMBER:
++ ast_copy_string(pri->pvts[chanpos]->cid_name, pri->withheldcid, sizeof(pri->pvts[chanpos]->cid_name));
++ break;
++ case PRES_NUMBER_NOT_AVAILABLE:
++ ast_copy_string(pri->pvts[chanpos]->cid_name, pri->nocid, sizeof(pri->pvts[chanpos]->cid_name));
++ break;
++ }
+ /* If immediate=yes go to s|1 */
+ if (pri->pvts[chanpos]->immediate) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of immediate=yes\n");
+ pri->pvts[chanpos]->exten[0] = 's';
+ pri->pvts[chanpos]->exten[1] = '\0';
+- }
+- /* Get called number */
+- else if (!ast_strlen_zero(e->ring.callednum)) {
+- ast_copy_string(pri->pvts[chanpos]->exten, e->ring.callednum, sizeof(pri->pvts[chanpos]->exten));
+- ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid));
+- } else if (pri->overlapdial)
+- pri->pvts[chanpos]->exten[0] = '\0';
+- else {
+- /* Some PRI circuits are set up to send _no_ digits. Handle them as 's'. */
+- pri->pvts[chanpos]->exten[0] = 's';
+- pri->pvts[chanpos]->exten[1] = '\0';
+- }
+- /* Set DNID on all incoming calls -- even immediate */
+- if (!ast_strlen_zero(e->ring.callednum))
+- ast_copy_string(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid));
+- /* No number yet, but received "sending complete"? */
+- if (e->ring.complete && (ast_strlen_zero(e->ring.callednum))) {
++ } else if (ast_strlen_zero(e->ring.callednum)) {
++ /* called party number is empty */
++ if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) {
++ if (!pri->overlapdial) {
++ // be able to set digittimeout for BRI phones
++ pri->pvts[chanpos]->exten[0] = 's';
++ pri->pvts[chanpos]->exten[1] = '\0';
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
++ } else {
++ pri->pvts[chanpos]->exten[0] = '\0';
++ }
++ } else {
++ if (pri->nodetype == BRI_CPE) {
++ /* fix for .at p2p bri lines */
++ pri->pvts[chanpos]->exten[0] = 's';
++ pri->pvts[chanpos]->exten[1] = '\0';
++ } else if (pri->overlapdial) {
++ pri->pvts[chanpos]->exten[0] = '\0';
++ } else {
++ /* Some PRI circuits are set up to send _no_ digits. Handle them as 's'. */
++ pri->pvts[chanpos]->exten[0] = 's';
++ pri->pvts[chanpos]->exten[1] = '\0';
++ }
++ }
++ /* No number yet, but received "sending complete"? */
++ if (e->ring.complete) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Going to extension s|1 because of Complete received\n");
+ pri->pvts[chanpos]->exten[0] = 's';
+ pri->pvts[chanpos]->exten[1] = '\0';
+- }
++ }
++ } else {
++ /* Get called number */
++ pri_make_callerid(pri, pri->pvts[chanpos]->dnid, sizeof(pri->pvts[chanpos]->dnid), e->ring.callednum, sizeof(e->ring.callednum), e->ring.calledplan, 0, pri->pvts[chanpos]->stripmsd);
++ pri_make_callerid(pri, pri->pvts[chanpos]->exten, sizeof(pri->pvts[chanpos]->exten), e->ring.callednum, sizeof(e->ring.callednum), e->ring.calledplan, 0, pri->pvts[chanpos]->stripmsd);
++ if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) {
++ /* if we get the next digit we should stop the dialtone */
++ if (!pri->overlapdial) {
++ // with overlapdial=no the exten is always prefixed by "s"
++ if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten + 1)) {
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1);
++ } else {
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
++ }
++ } else {
++ if (!ast_ignore_pattern(pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten)) {
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, -1);
++ } else {
++ tone_zone_play_tone(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_TONE_DIALTONE);
++ }
++ }
++ }
++ }
++ /* Part 3: create channel, setup audio... */
++ /* Set DNID on all incoming calls -- even immediate */
++ if (!ast_strlen_zero(e->ring.callednum))
++ strncpy(pri->pvts[chanpos]->dnid, e->ring.callednum, sizeof(pri->pvts[chanpos]->dnid) - 1);
+ /* Make sure extension exists (or in overlap dial mode, can exist) */
+ if ((pri->overlapdial && ast_canmatch_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) ||
+ ast_exists_extension(NULL, pri->pvts[chanpos]->context, pri->pvts[chanpos]->exten, 1, pri->pvts[chanpos]->cid_num)) {
+@@ -8838,19 +9111,36 @@ static void *pri_dchannel(void *vpri)
+ res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to set law on channel %d\n", pri->pvts[chanpos]->channel);
+- res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law);
++ if (IS_DIGITAL(e->ring.ctype)) {
++ res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[chanpos]->law);
++ } else {
++ res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law);
++ }
+ if (res < 0)
+ ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", pri->pvts[chanpos]->channel);
+- if (e->ring.complete || !pri->overlapdial) {
++ if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) {
++ if (e->ring.complete || !pri->overlapdial) {
+ /* Just announce proceeding */
+ pri->pvts[chanpos]->proceeding = 1;
+ pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0);
+- } else {
++ } else {
+ if (pri->switchtype != PRI_SWITCH_GR303_TMC)
+ pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
+ else
+ pri_answer(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
++ }
++ } else {
++ /* BRI_NETWORK | BRI_NETWORK_PTMP */
++ if (pri->overlapdial || (!strcasecmp(pri->pvts[chanpos]->exten, "s"))) {
++ /* send a SETUP_ACKNOWLEDGE */
++ pri_need_more_info(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
++ } else {
++ /* send an ALERTING ??? wtf */
++ // pri_acknowledge(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 1);
++ pri_proceeding(pri->pri, e->ring.call, PVT_TO_CHANNEL(pri->pvts[chanpos]), 0);
++ }
+ }
++ /* overlapdial = yes and the extension can be valid */
+ /* Get the use_callingpres state */
+ pri->pvts[chanpos]->callingpres = e->ring.callingpres;
+
+@@ -8862,10 +9152,17 @@ static void *pri_dchannel(void *vpri)
+ /* Set bearer and such */
+ pri_assign_bearer(crv, pri, pri->pvts[chanpos]);
+ c = zt_new(crv, AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype);
++ if (c && (e->ring.lowlayercompat[0] > 0)) {
++ memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat));
++ }
+ pri->pvts[chanpos]->owner = &inuse;
+ ast_log(LOG_DEBUG, "Started up crv %d:%d on bearer channel %d\n", pri->trunkgroup, crv->channel, crv->bearer->channel);
+ } else {
+ c = zt_new(pri->pvts[chanpos], AST_STATE_RESERVED, 0, SUB_REAL, law, e->ring.ctype);
++ if (c && (e->ring.lowlayercompat[0] > 0)) {
++ memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat));
++ }
++ zt_enable_ec(pri->pvts[chanpos]); /* XXX rethink */
+ }
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+@@ -8873,6 +9170,16 @@ static void *pri_dchannel(void *vpri)
+ if (!ast_strlen_zero(e->ring.callingsubaddr)) {
+ pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr);
+ }
++ if (!ast_strlen_zero(e->ring.callingnum)) {
++ char tmpstr[256];
++ pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingnum, sizeof(e->ring.callingnum), e->ring.callingplan, e->ring.callingpres, 0);
++ pbx_builtin_setvar_helper(c, "PRI_NETWORK_CID", tmpstr);
++ }
++ if (!ast_strlen_zero(e->ring.callingani)) {
++ char tmpstr[256];
++ pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingani, sizeof(e->ring.callingani), e->ring.callingplanuser, e->ring.callingpresuser, 0);
++ pbx_builtin_setvar_helper(c, "PRI_USER_CID", tmpstr);
++ }
+ if (e->ring.ani2 >= 0) {
+ snprintf(ani2str, 5, "%.2d", e->ring.ani2);
+ pbx_builtin_setvar_helper(c, "ANI2", ani2str);
+@@ -8896,8 +9203,8 @@ static void *pri_dchannel(void *vpri)
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (c && !ast_pthread_create(&threadid, &attr, ss_thread, c)) {
+ if (option_verbose > 2)
+- ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap call from '%s' to '%s' on channel %d/%d, span %d\n",
+- plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"),
++ ast_verbose(VERBOSE_PREFIX_3 "Accepting overlap %s call from '%s' to '%s' on channel %d/%d, span %d\n",
++ pri->pvts[chanpos]->digital ? "data" : "voice", plancallingnum, S_OR(pri->pvts[chanpos]->exten, "<unspecified>"),
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+ } else {
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
+@@ -8905,15 +9212,19 @@ static void *pri_dchannel(void *vpri)
+ if (c)
+ ast_hangup(c);
+ else {
+- pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION, -1);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ }
+ pthread_attr_destroy(&attr);
+ } else {
++ /* overlapdial = no */
+ ast_mutex_unlock(&pri->lock);
+ /* Release PRI lock while we create the channel */
+ c = zt_new(pri->pvts[chanpos], AST_STATE_RING, 1, SUB_REAL, law, e->ring.ctype);
++ if (c && (e->ring.lowlayercompat[0] > 0)) {
++ memcpy(c->lowlayercompat, e->ring.lowlayercompat, sizeof(c->lowlayercompat));
++ }
+ if (c) {
+ char calledtonstr[10];
+
+@@ -8940,26 +9251,43 @@ static void *pri_dchannel(void *vpri)
+ ast_mutex_lock(&pri->lock);
+
+ if (option_verbose > 2)
+- ast_verbose(VERBOSE_PREFIX_3 "Accepting call from '%s' to '%s' on channel %d/%d, span %d\n",
+- plancallingnum, pri->pvts[chanpos]->exten,
++ ast_verbose(VERBOSE_PREFIX_3 "Accepting %s call from '%s' to '%s' on channel %d/%d, span %d\n",
++ pri->pvts[chanpos]->digital ? "data" : "voice", e->ring.callingnum, pri->pvts[chanpos]->exten,
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+ zt_enable_ec(pri->pvts[chanpos]);
++ if(!ast_strlen_zero(e->ring.callingsubaddr)) {
++ pbx_builtin_setvar_helper(c, "CALLINGSUBADDR", e->ring.callingsubaddr);
++ }
++ if (!ast_strlen_zero(e->ring.callingnum)) {
++ char tmpstr[256];
++ pri_make_callerid(pri, tmpstr, sizeof(tmpstr), e->ring.callingnum, sizeof(e->ring.callingnum), e->ring.callingplan, e->ring.callingpres, 0);
++ pbx_builtin_setvar_helper(c, "PRI_NETWORK_CID", tmpstr);
++ }
++ if (!ast_strlen_zero(e->ring.callingani)) {
++ char tmpstr[256];
++ pri_make_callerid(pri, tmpstr,sizeof(tmpstr), e->ring.callingani, sizeof(e->ring.callingani), e->ring.callingplanuser, e->ring.callingpresuser, 0);
++ pbx_builtin_setvar_helper(c, "PRI_USER_CID", e->ring.callednum);
++ }
++ if (!ast_strlen_zero(e->ring.useruserinfo)) {
++ pbx_builtin_setvar_helper(c, "UUI", e->ring.useruserinfo);
++ }
+ } else {
+
+ ast_mutex_lock(&pri->lock);
+
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %d/%d, span %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span);
+- pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION, -1);
+ pri->pvts[chanpos]->call = NULL;
+ }
+ }
+ } else {
++ /* invalid extension */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Extension '%s' in context '%s' from '%s' does not exist. Rejecting call on channel %d/%d, span %d\n",
+ pri->pvts[chanpos]->exten, pri->pvts[chanpos]->context, pri->pvts[chanpos]->cid_num, pri->pvts[chanpos]->logicalspan,
+ pri->pvts[chanpos]->prioffset, pri->span);
+- pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED, -1);
+ pri->pvts[chanpos]->call = NULL;
+ pri->pvts[chanpos]->exten[0] = '\0';
+ }
+@@ -8968,9 +9296,9 @@ static void *pri_dchannel(void *vpri)
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else {
+ if (e->ring.flexible)
+- pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION, -1);
+ else
+- pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
++ pri_hangup(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, -1);
+ }
+ break;
+ case PRI_EVENT_RINGING:
+@@ -8986,7 +9314,7 @@ static void *pri_dchannel(void *vpri)
+ } else {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (ast_strlen_zero(pri->pvts[chanpos]->dop.dialstr)) {
+- zt_enable_ec(pri->pvts[chanpos]);
++ // XXX zt_enable_ec(pri->pvts[chanpos]);
+ pri->pvts[chanpos]->subs[SUB_REAL].needringing = 1;
+ pri->pvts[chanpos]->alerting = 1;
+ } else
+@@ -9018,9 +9346,16 @@ static void *pri_dchannel(void *vpri)
+ }
+ break;
+ case PRI_EVENT_PROGRESS:
+- /* Get chan value if e->e is not PRI_EVNT_RINGING */
++ /* Get chan value if e->e is not PRI_EVENT_RINGING */
+ chanpos = pri_find_principle(pri, e->proceeding.channel);
+ if (chanpos > -1) {
++ if ((pri->pvts[chanpos]->priindication_oob == 2) && (e->proceeding.cause == PRI_CAUSE_USER_BUSY)) {
++ /* received PROGRESS with cause BUSY, no inband callprogress wanted => hang up! */
++ if (pri->pvts[chanpos]->owner) {
++ pri->pvts[chanpos]->owner->hangupcause = AST_CAUSE_USER_BUSY;
++ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ }
++ } else {
+ #ifdef PRI_PROGRESS_MASK
+ if ((!pri->pvts[chanpos]->progress) || (e->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE)) {
+ #else
+@@ -9062,11 +9397,18 @@ static void *pri_dchannel(void *vpri)
+ pri->pvts[chanpos]->progress = 1;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
++ }
+ }
+ break;
+ case PRI_EVENT_PROCEEDING:
+ chanpos = pri_find_principle(pri, e->proceeding.channel);
+ if (chanpos > -1) {
++ chanpos = pri_fixup_principle(pri, chanpos, e->proceeding.call);
++ if (chanpos < 0) {
++ ast_log(LOG_WARNING, "Received PROCEEDING on channel %d/%d not in use on span %d\n",
++ PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), pri->span);
++ chanpos = -1;
++ } else {
+ if (!pri->pvts[chanpos]->proceeding) {
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_PROCEEDING, };
+
+@@ -9091,6 +9433,7 @@ static void *pri_dchannel(void *vpri)
+ pri->pvts[chanpos]->proceeding = 1;
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
++ }
+ }
+ break;
+ case PRI_EVENT_FACNAME:
+@@ -9114,6 +9457,163 @@ static void *pri_dchannel(void *vpri)
+ }
+ }
+ break;
++ case PRI_EVENT_SUSPEND_REQ:
++ if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) {
++ pri_suspend_reject(pri->pri, e->suspend_req.call, "");
++ break;
++ }
++ chanpos = pri_find_principle(pri, e->suspend_req.channel);
++ if (chanpos < 0) {
++ ast_log(LOG_WARNING, "Suspend requested on unconfigured channel %d span %d\n", chanpos, pri->span);
++ chanpos = -1;
++ }
++
++ if (chanpos > -1) {
++ ast_mutex_lock(&pri->pvts[chanpos]->lock);
++ if (pri->pvts[chanpos]->owner) {
++ if (ast_bridged_channel(pri->pvts[chanpos]->owner)) {
++ struct zt_suspended_call *zpc;
++ char tmpstr[256];
++ zpc = malloc(sizeof(struct zt_suspended_call));
++ if (!zpc) {
++ ast_log(LOG_ERROR, "unable to malloc zt_suspended_call\n");
++ break;
++ }
++ strncpy(zpc->msn, pri->pvts[chanpos]->cid_num, sizeof(zpc->msn));
++ strncpy(zpc->callid, e->suspend_req.callid, sizeof(zpc->callid));
++ ast_masq_park_call(ast_bridged_channel(pri->pvts[chanpos]->owner), NULL, 0, &zpc->parked_at);
++ zpc->next = pri->suspended_calls;
++ pri->suspended_calls = zpc;
++ snprintf(tmpstr, sizeof(tmpstr), "Parked at %d", zpc->parked_at);
++ pri_suspend_acknowledge(pri->pri, e->suspend_req.call,tmpstr);
++ pri->pvts[chanpos]->call = NULL;
++ pri->pvts[chanpos]->tei = -1;
++ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ } else {
++ pri_suspend_reject(pri->pri, e->suspend_req.call, "cant park a non-bridge");
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ break;
++ }
++ } else {
++ pri_suspend_reject(pri->pri, e->suspend_req.call, "");
++ }
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ }
++ break;
++ case PRI_EVENT_RESUME_REQ:
++ if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) {
++ break;
++ }
++ chanpos = pri_find_empty_chan(pri, 1);
++ if (chanpos < 0) {
++ pri_resume_reject(pri->pri, e->resume_req.call,"All channels busy");
++ ast_log(LOG_WARNING, "Resume requested on odd channel number %d span %d\n", chanpos, pri->span);
++ chanpos = -1;
++ } else if (!pri->pvts[chanpos]) {
++ pri_resume_reject(pri->pri, e->resume_req.call,"General protection fault in module 0x0BRI");
++ chanpos = -1;
++ }
++
++ if (chanpos > -1) {
++ ast_mutex_lock(&pri->pvts[chanpos]->lock);
++ if (!pri->pvts[chanpos]->owner) {
++ struct zt_suspended_call *zpc, *zpcl;
++ int unparked=0;
++ char extenstr[255], temp[255];
++ zpc = NULL;
++ zpcl = pri->suspended_calls;
++ while (zpcl) {
++ // ast_log(LOG_NOTICE, "zpc->parked_at %d zpcl->callid %s\n",zpcl->parked_at, zpcl->callid);
++ if (((strlen(zpcl->callid) == 0) && (strlen(e->resume_req.callid)==0)) || (!strcmp(zpcl->callid,e->resume_req.callid))) {
++ int law;
++ // found a parked call
++ snprintf(extenstr, sizeof(extenstr), "%d", zpcl->parked_at);
++ strncpy(pri->pvts[chanpos]->exten, extenstr, sizeof(pri->pvts[chanpos]->exten));
++ // strncpy(pri->pvts[chanpos]->context, ast_parking_con(), sizeof(pri->pvts[chanpos]->context));
++ pri->pvts[chanpos]->call = e->resume_req.call;
++ law = 1;
++ if (ioctl(pri->pvts[chanpos]->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1)
++ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]), law);
++ // uhh ohh...what shall we do without the bearer cap???
++ law = ZT_LAW_ALAW;
++ res = zt_setlaw(pri->pvts[chanpos]->subs[SUB_REAL].zfd, law);
++ if (res < 0)
++ ast_log(LOG_WARNING, "Unable to set law on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]));
++ if (!pri->pvts[chanpos]->digital) {
++ res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, pri->pvts[chanpos]->rxgain, pri->pvts[chanpos]->txgain, law);
++ } else {
++ res = set_actual_gain(pri->pvts[chanpos]->subs[SUB_REAL].zfd, 0, 0, 0, pri->pvts[chanpos]->law);
++ }
++ if (res < 0)
++ ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", PVT_TO_CHANNEL(pri->pvts[chanpos]));
++ /* Start PBX */
++ c = zt_new(pri->pvts[chanpos], AST_STATE_UP, 1, SUB_REAL, law, PRI_TRANS_CAP_SPEECH);
++ if (c) {
++ pri->pvts[chanpos]->owner = c;
++ pri->pvts[chanpos]->call = e->resume_req.call;
++ zt_enable_ec(pri->pvts[chanpos]);
++ zt_train_ec(pri->pvts[chanpos]);
++ } else {
++ ast_log(LOG_ERROR, "unable to start pbx\n");
++ }
++
++ if (zpc) {
++ zpc->next = zpcl->next;
++ free(zpcl);
++ zpcl = zpc->next;
++ } else {
++ // remove head
++ pri->suspended_calls = zpcl->next;
++ free(zpcl);
++ zpcl = pri->suspended_calls;
++ zpc = NULL;
++ }
++ unparked = 1;
++ snprintf(temp, sizeof(temp), "Unparked %s", extenstr);
++ pri_resume_acknowledge(pri->pri, e->resume_req.call, chanpos + 1, temp);
++ break;
++ }
++ zpc = zpcl;
++ if (zpcl) zpcl = zpcl->next;
++ }
++ if (!unparked)
++ pri_resume_reject(pri->pri, e->resume_req.call,"No suspended call to unpark!");
++ } else {
++ pri_resume_reject(pri->pri, e->resume_req.call,"No suspended call to unpark!");
++ }
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ }
++ break;
++ case PRI_EVENT_HOLD_REQ:
++ if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) {
++ pri_hold_reject(pri->pri, e->hold_req.call);
++ break;
++ }
++ /* holded calls are not implemented yet */
++ pri_hold_reject(pri->pri, e->hold_req.call);
++ break;
++ case PRI_EVENT_RETRIEVE_REQ:
++ if ((pri->nodetype != BRI_NETWORK_PTMP) && (pri->nodetype != BRI_NETWORK)) {
++ pri_retrieve_reject(pri->pri, e->retrieve_req.call);
++ break;
++ }
++ /* Holded calls are currently not supported */
++ pri_retrieve_reject(pri->pri, e->retrieve_req.call);
++ chanpos = -1;
++ break;
++ case PRI_EVENT_DISPLAY_RECEIVED:
++ ast_log(LOG_NOTICE, "DISPLAY IE: [ %s ] received\n",e->display.text);
++ chanpos = pri_find_principle(pri, e->display.channel);
++ if (chanpos < 0) {
++ ast_log(LOG_WARNING, "odd channel number %d span %d\n", chanpos, pri->span);
++ chanpos = -1;
++ }
++ if (chanpos > -1) {
++ if (pri->pvts[chanpos]->owner) {
++ // ast_sendtext(pri->pvt[chanpos]->owner, e->display.text);
++ }
++ }
++ break;
+ case PRI_EVENT_ANSWER:
+ chanpos = pri_find_principle(pri, e->answer.channel);
+ if (chanpos < 0) {
+@@ -9126,6 +9626,7 @@ static void *pri_dchannel(void *vpri)
+ PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), pri->span);
+ } else {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
++ pri->pvts[chanpos]->tei = e->answer.tei;
+ /* Now we can do call progress detection */
+
+ /* We changed this so it turns on the DSP no matter what... progress or no progress.
+@@ -9155,11 +9656,16 @@ static void *pri_dchannel(void *vpri)
+ ast_log(LOG_DEBUG, "Sent deferred digit string: %s\n", pri->pvts[chanpos]->dop.dialstr);
+ pri->pvts[chanpos]->dop.dialstr[0] = '\0';
+ } else if (pri->pvts[chanpos]->confirmanswer) {
+- ast_log(LOG_DEBUG, "Waiting on answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel);
++ ast_log(LOG_DEBUG, "Waiting for answer confirmation on channel %d!\n", pri->pvts[chanpos]->channel);
++ enable_dtmf_detect(pri->pvts[chanpos]);
+ } else {
++ pri->pvts[chanpos]->dialing = 0;
+ pri->pvts[chanpos]->subs[SUB_REAL].needanswer =1;
+ /* Enable echo cancellation if it's not on already */
+ zt_enable_ec(pri->pvts[chanpos]);
++ zt_train_ec(pri->pvts[chanpos]);
++ /* stop ignoring inband dtmf */
++ enable_dtmf_detect(pri->pvts[chanpos]);
+ }
+
+ #ifdef SUPPORT_USERUSER
+@@ -9216,20 +9722,29 @@ static void *pri_dchannel(void *vpri)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d got hangup, cause %d\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, e->hangup.cause);
+ } else {
+- pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
++ pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1);
+ pri->pvts[chanpos]->call = NULL;
++ pri->pvts[chanpos]->tei = -1;
+ }
+ if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) {
+- if (option_verbose > 2)
++ if ((pri->nodetype != BRI_CPE_PTMP) && (pri->nodetype != BRI_NETWORK_PTMP)) {
++ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d on span %d since channel reported in use\n",
+- PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+- pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
+- pri->pvts[chanpos]->resetting = 1;
+- }
+- if (e->hangup.aoc_units > -1)
++ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
++ pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
++ pri->pvts[chanpos]->resetting = 1;
++ }
++ }
++ if (e->hangup.aoc_units > -1) {
++ if (pri->pvts[chanpos]->owner) {
++ char tmpstr[256];
++ snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units);
++ pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr);
++ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
++ }
+
+ #ifdef SUPPORT_USERUSER
+ if (pri->pvts[chanpos]->owner && !ast_strlen_zero(e->hangup.useruserinfo)) {
+@@ -9242,8 +9757,9 @@ static void *pri_dchannel(void *vpri)
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else {
+- ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n",
+- PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
++ ast_log(LOG_NOTICE, "Hangup, did not find cref %d, tei %d\n",e->hangup.cref, e->hangup.tei);
++ ast_log(LOG_WARNING, "Hangup on bad channel %d/%d on span %d\n",
++ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+ }
+ }
+ break;
+@@ -9253,15 +9769,23 @@ static void *pri_dchannel(void *vpri)
+ case PRI_EVENT_HANGUP_REQ:
+ chanpos = pri_find_principle(pri, e->hangup.channel);
+ if (chanpos < 0) {
+- ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n",
+- PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+- } else {
++ if (pri->nodetype == BRI_NETWORK_PTMP) {
++ pri_hangup(pri->pri, e->hangup.call, e->hangup.cause, -1);
++ } else {
++ ast_log(LOG_WARNING, "Hangup REQ requested on unconfigured channel %d/%d span %d\n",
++ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
++ }
++ } else if ((pri->pvts[chanpos]->priindication_oob != 2) || (!e->hangup.inband_progress) || (!pri->pvts[chanpos]->outgoing)) {
++ /* dont hang up if we want to hear inband call progress */
+ chanpos = pri_fixup_principle(pri, chanpos, e->hangup.call);
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ if (pri->pvts[chanpos]->realcall)
+ pri_hangup_all(pri->pvts[chanpos]->realcall, pri);
+ else if (pri->pvts[chanpos]->owner) {
++ char tmpstr[256];
++ snprintf(tmpstr, sizeof(tmpstr), "%d", e->hangup.cause);
++ pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "PRI_CAUSE", tmpstr);
+ pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
+ if (pri->pvts[chanpos]->owner->_state == AST_STATE_UP)
+ pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
+@@ -9288,16 +9812,34 @@ static void *pri_dchannel(void *vpri)
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
+ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
++ if (e->hangup.aoc_units > -1) {
++ if (pri->pvts[chanpos]->owner) {
++ char tmpstr[256];
++ snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units);
++ pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr);
++ }
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
++ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
++ }
++ if (pri->nodetype == BRI_NETWORK_PTMP) {
++ pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1);
++ pri->pvts[chanpos]->call = NULL;
++ pri->pvts[chanpos]->tei = -1;
++ }
+ } else {
+- pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause);
++ pri_hangup(pri->pri, pri->pvts[chanpos]->call, e->hangup.cause, -1);
+ pri->pvts[chanpos]->call = NULL;
++ pri->pvts[chanpos]->tei = -1;
+ }
+ if (e->hangup.cause == PRI_CAUSE_REQUESTED_CHAN_UNAVAIL) {
+- if (option_verbose > 2)
++ if ((pri->nodetype != BRI_CPE_PTMP) && (pri->nodetype != BRI_NETWORK_PTMP)) {
++ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Forcing restart of channel %d/%d span %d since channel reported in use\n",
+ PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
+- pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
+- pri->pvts[chanpos]->resetting = 1;
++ pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[chanpos]));
++ pri->pvts[chanpos]->resetting = 1;
++ }
+ }
+
+ #ifdef SUPPORT_USERUSER
+@@ -9311,9 +9853,27 @@ static void *pri_dchannel(void *vpri)
+
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ } else {
+- ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
++ if (pri->nodetype != BRI_NETWORK_PTMP) {
++ ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
++ } else {
++ ast_log(LOG_WARNING, "Hangup REQ on bad channel %d/%d on span %d\n", PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), pri->span);
++ }
+ }
+ }
++ if ((chanpos > -1) && (pri->pvts[chanpos]->owner) && (pri->pvts[chanpos]->priindication_oob == 2) && (e->hangup.inband_progress) && (pri->pvts[chanpos]->outgoing)) {
++ ast_mutex_lock(&pri->pvts[chanpos]->lock);
++ if (e->hangup.aoc_units > -1) {
++ char tmpstr[256];
++ snprintf(tmpstr, sizeof(tmpstr), "%d", (int)e->hangup.aoc_units);
++ pbx_builtin_setvar_helper(pri->pvts[chanpos]->owner, "AOCEUNITS", tmpstr);
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %d/%d, span %d received AOC-E charging %d unit%s\n",
++ pri->pvts[chanpos]->logicalspan, pri->pvts[chanpos]->prioffset, pri->span, (int)e->hangup.aoc_units, (e->hangup.aoc_units == 1) ? "" : "s");
++ }
++ pri->pvts[chanpos]->owner->hangupcause = e->hangup.cause;
++ ast_channel_setwhentohangup(pri->pvts[chanpos]->owner, 5);
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ }
+ break;
+ case PRI_EVENT_HANGUP_ACK:
+ chanpos = pri_find_principle(pri, e->hangup.channel);
+@@ -9325,6 +9885,7 @@ static void *pri_dchannel(void *vpri)
+ if (chanpos > -1) {
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ pri->pvts[chanpos]->call = NULL;
++ pri->pvts[chanpos]->tei = -1;
+ pri->pvts[chanpos]->resetting = 0;
+ if (pri->pvts[chanpos]->owner) {
+ if (option_verbose > 2)
+@@ -9431,10 +9992,22 @@ static void *pri_dchannel(void *vpri)
+ ast_mutex_lock(&pri->pvts[chanpos]->lock);
+ switch (e->notify.info) {
+ case PRI_NOTIFY_REMOTE_HOLD:
++ if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) {
++ ast_log(LOG_DEBUG, "Received REMOTE_HOLD notification on NETWORK channel. Starting MoH\n");
++ ast_moh_start(ast_bridged_channel(pri->pvts[chanpos]->owner), NULL, pri->pvts[chanpos]->mohinterpret);
++ } else {
++ ast_log(LOG_DEBUG, "Received REMOTE_HOLD notification on CPE channel. Not Starting MoH\n");
++ }
+ f.subclass = AST_CONTROL_HOLD;
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ break;
+ case PRI_NOTIFY_REMOTE_RETRIEVAL:
++ if ((pri->nodetype == BRI_NETWORK_PTMP) || (pri->nodetype == BRI_NETWORK)) {
++ ast_log(LOG_DEBUG, "Received REMOTE_RETRIEVAL notification on NETWORK channel. Stopping MoH\n");
++ ast_moh_stop(ast_bridged_channel(pri->pvts[chanpos]->owner));
++ } else {
++ ast_log(LOG_DEBUG, "Received REMOTE_RETRIEVAL notification on CPE channel.\n");
++ }
+ f.subclass = AST_CONTROL_UNHOLD;
+ zap_queue_frame(pri->pvts[chanpos], &f, pri);
+ break;
+@@ -9442,6 +10015,23 @@ static void *pri_dchannel(void *vpri)
+ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
+ }
+ break;
++ case PRI_EVENT_FACILITY:
++ if (e->facility.operation == 0x0D) {
++ struct ast_channel *owner = pri->pvts[chanpos]->owner;
++
++ ast_log(LOG_NOTICE, "call deflection to %s requested.\n", e->facility.forwardnum);
++ ast_mutex_lock(&pri->pvts[chanpos]->lock);
++ /* transfer */
++ if (owner) {
++ ast_string_field_build(owner, call_forward,
++ "Local/%s@%s", e->facility.forwardnum,
++ owner->context);
++ }
++ ast_mutex_unlock(&pri->pvts[chanpos]->lock);
++ } else {
++ ast_log(LOG_WARNING, "Unknown facility operation %#x requested.\n", e->facility.operation);
++ }
++ break;
+ default:
+ ast_log(LOG_DEBUG, "Event: %d\n", e->e);
+ }
+@@ -9503,7 +10093,7 @@ static int start_pri(struct zt_pri *pri)
+ pri->fds[i] = -1;
+ return -1;
+ }
+- pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype);
++ pri->dchans[i] = pri_new(pri->fds[i], pri->nodetype, pri->switchtype, pri->span);
+ /* Force overlap dial if we're doing GR-303! */
+ if (pri->switchtype == PRI_SWITCH_GR303_TMC)
+ pri->overlapdial = 1;
+@@ -9571,39 +10161,77 @@ static char *complete_span_5(const char
+
+ static int handle_pri_set_debug_file(int fd, int argc, char **argv)
+ {
+- int myfd;
++ int myfd, x, d;
++ int span;
++
++ if (argc < 6)
++ return RESULT_SHOWUSAGE;
+
+ if (!strncasecmp(argv[1], "set", 3)) {
+- if (argc < 5)
++ if (argc < 7)
+ return RESULT_SHOWUSAGE;
+
+- if (ast_strlen_zero(argv[4]))
++ if (!argv[4] || ast_strlen_zero(argv[4]))
+ return RESULT_SHOWUSAGE;
+
++ if (!argv[5])
++ return RESULT_SHOWUSAGE;
++
++ if (!argv[6] || ast_strlen_zero(argv[6]))
++ return RESULT_SHOWUSAGE;
++
++ span = atoi(argv[6]);
++ if ((span < 1) && (span > NUM_SPANS)) {
++ return RESULT_SUCCESS;
++ }
++
++
+ myfd = open(argv[4], O_CREAT|O_WRONLY, 0600);
+ if (myfd < 0) {
+ ast_cli(fd, "Unable to open '%s' for writing\n", argv[4]);
+ return RESULT_SUCCESS;
+ }
++ for (x=0; x < NUM_SPANS; x++) {
++ ast_mutex_lock(&pris[x].lock);
+
+- ast_mutex_lock(&pridebugfdlock);
+-
+- if (pridebugfd >= 0)
+- close(pridebugfd);
+-
+- pridebugfd = myfd;
+- ast_copy_string(pridebugfilename,argv[4],sizeof(pridebugfilename));
+-
+- ast_mutex_unlock(&pridebugfdlock);
++ if (pris[x].span == span) {
++ if (pris[x].debugfd >= 0)
++ close(pris[x].debugfd);
++ pris[x].debugfd = myfd;
++ for (d=0; d < NUM_DCHANS; d++) {
++ if (pris[x].dchans[d])
++ pri_set_debug_fd(pris[x].dchans[d], myfd);
++ }
++ }
++ ast_mutex_unlock(&pris[x].lock);
++ }
+
+- ast_cli(fd, "PRI debug output will be sent to '%s'\n", argv[4]);
++ ast_cli(fd, "PRI debug output for span %d will be sent to '%s'\n", span, argv[4]);
+ } else {
++ if (!argv[5] || ast_strlen_zero(argv[5]))
++ return RESULT_SHOWUSAGE;
+ /* Assume it is unset */
+- ast_mutex_lock(&pridebugfdlock);
+- close(pridebugfd);
+- pridebugfd = -1;
+- ast_cli(fd, "PRI debug output to file disabled\n");
+- ast_mutex_unlock(&pridebugfdlock);
++ span = atoi(argv[5]);
++ if ((span < 1) && (span > NUM_SPANS)) {
++ return RESULT_SUCCESS;
++ }
++
++ for (x=0; x < NUM_SPANS; x++) {
++ ast_mutex_lock(&pris[x].lock);
++
++ if (pris[x].span == span) {
++ if (pris[x].debugfd >= 0)
++ close(pris[x].debugfd);
++ pris[x].debugfd = -1;
++ for (d=0; d < NUM_DCHANS; d++) {
++ if (pris[x].dchans[d])
++ pri_set_debug_fd(pris[x].dchans[d], -1);
++ }
++ }
++ ast_mutex_unlock(&pris[x].lock);
++ }
++
++ ast_cli(fd, "PRI debug output to file for span %d disabled\n", span);
+ }
+
+ return RESULT_SUCCESS;
+@@ -9644,6 +10272,7 @@ static int handle_pri_debug(int fd, int
+
+
+
++
+ static int handle_pri_no_debug(int fd, int argc, char *argv[])
+ {
+ int span;
+@@ -9793,10 +10422,6 @@ static int handle_pri_show_debug(int fd,
+ }
+
+ }
+- ast_mutex_lock(&pridebugfdlock);
+- if (pridebugfd >= 0)
+- ast_cli(fd, "Logging PRI debug to file %s\n", pridebugfilename);
+- ast_mutex_unlock(&pridebugfdlock);
+
+ if (!count)
+ ast_cli(fd, "No debug set or no PRI running\n");
+@@ -9823,6 +10448,18 @@ static const char pri_show_spans_help[]
+ "Usage: pri show spans\n"
+ " Displays PRI Information\n";
+
++static char bri_debug_help[] =
++ "Usage: bri debug span <span>\n"
++ " Enables debugging on a given BRI span\n";
++
++static char bri_no_debug_help[] =
++ "Usage: bri no debug span <span>\n"
++ " Disables debugging on a given BRI span\n";
++
++static char bri_really_debug_help[] =
++ "Usage: bri intensive debug span <span>\n"
++ " Enables debugging down to the Q.921 level\n";
++
+ static struct ast_cli_entry zap_pri_cli[] = {
+ { { "pri", "debug", "span", NULL },
+ handle_pri_debug, "Enables PRI debugging on a span",
+@@ -9847,6 +10484,15 @@ static struct ast_cli_entry zap_pri_cli[
+ { { "pri", "show", "debug", NULL },
+ handle_pri_show_debug, "Displays current PRI debug settings" },
+
++ { { "bri", "debug", "span", NULL }, handle_pri_debug,
++ "Enables BRI debugging on a span", bri_debug_help, complete_span_4 },
++
++ { { "bri", "no", "debug", "span", NULL }, handle_pri_no_debug,
++ "Disables BRI debugging on a span", bri_no_debug_help, complete_span_5 },
++
++ { { "bri", "intense", "debug", "span", NULL }, handle_pri_really_debug,
++ "Enables REALLY INTENSE BRI debugging", bri_really_debug_help, complete_span_5 },
++
+ { { "pri", "set", "debug", "file", NULL },
+ handle_pri_set_debug_file, "Sends PRI debug output to the specified file" },
+
+@@ -9859,8 +10505,76 @@ static struct ast_cli_entry zap_pri_cli[
+ #endif
+ };
+
++static char *zapCD_tdesc = "Call Deflection";
++static char *zapCD_app = "zapCD";
++static char *zapCD_synopsis = "Call Deflection";
++
++static int app_zapCD(struct ast_channel *chan, void *data)
++{
++ struct zt_pvt *p = chan->tech_pvt;
++
++ if((!p->pri) || (!p->pri->pri)) {
++ return -1;
++ }
++
++ if(!data) {
++ ast_log(LOG_WARNING, "zapCD wants a number to deflect to\n");
++ return -1;
++ }
++ return pri_deflect(p->pri->pri, p->call, data);
++}
++
++static char *zapInband_tdesc = "Inband Call Progress (pre-answer)";
++static char *zapInband_app = "zapInband";
++static char *zapInband_synopsis = "Inband Call Progress";
++
++static int app_zapInband(struct ast_channel *chan, void *data)
++{
++ struct zt_pvt *p = chan->tech_pvt;
++
++ return pri_acknowledge(p->pri->pri, p->call, PVT_TO_CHANNEL(p), 1);
++}
++
+ #endif /* HAVE_PRI */
+
++static int app_zapEC(struct ast_channel *chan, void *data)
++{
++ int res=-1;
++ struct zt_pvt *p = NULL;
++
++ if (!data) {
++ ast_log(LOG_WARNING, "zapEC requires one argument (on | off)\n");
++ }
++ if (chan && !strcasecmp("ZAP",chan->tech->type)) {
++ p = chan->tech_pvt;
++ if (!p) return res;
++ if (!strcasecmp("on",(char *)data)) {
++ zt_enable_ec(p);
++ res = 0;
++ if (option_verbose > 3) {
++ ast_verbose(VERBOSE_PREFIX_3 "Enabled echo cancelation on channel %s.\n", chan->name);
++ }
++ } else if (!strcasecmp("off",(char *)data)) {
++ zt_disable_ec(p);
++ res = 0;
++ if (option_verbose > 3) {
++ ast_verbose(VERBOSE_PREFIX_3 "Disabled echo cancelation on channel %s.\n", chan->name);
++ }
++ } else {
++ ast_log(LOG_WARNING, "Unknown argument %s to zapEC\n", (char *)data);
++ }
++ } else {
++ ast_log(LOG_WARNING, "zapNoEC only works on ZAP channels, check your extensions.conf!\n");
++ res = 0;
++ }
++
++ return res;
++}
++
++static char *zapEC_tdesc = "Enable/disable Echo cancelation";
++static char *zapEC_app = "zapEC";
++static char *zapEC_synopsis = "Enable/Disable Echo Cancelation on a Zap channel";
++
+ static int zap_destroy_channel(int fd, int argc, char **argv)
+ {
+ int channel;
+@@ -10441,8 +11155,11 @@ static int __unload_module(void)
+ }
+ ast_cli_unregister_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry));
+ ast_unregister_application(zap_send_keypad_facility_app);
++ ast_unregister_application(zapCD_app);
++ ast_unregister_application(zapInband_app);
+ #endif
+ ast_cli_unregister_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
++ ast_unregister_application(zapEC_app);
+ ast_manager_unregister( "ZapDialOffhook" );
+ ast_manager_unregister( "ZapHangup" );
+ ast_manager_unregister( "ZapTransfer" );
+@@ -10944,6 +11661,22 @@ static int process_zap(struct zt_chan_co
+ confp->chan.sig = SIG_GR303FXSKS;
+ confp->chan.radio = 0;
+ confp->pri.nodetype = PRI_CPE;
++ } else if (!strcasecmp(v->value, "bri_net_ptmp")) {
++ confp->chan.radio = 0;
++ confp->chan.sig = SIG_PRI;
++ confp->pri.nodetype = BRI_NETWORK_PTMP;
++ } else if (!strcasecmp(v->value, "bri_cpe_ptmp")) {
++ confp->chan.sig = SIG_PRI;
++ confp->chan.radio = 0;
++ confp->pri.nodetype = BRI_CPE_PTMP;
++ } else if (!strcasecmp(v->value, "bri_net")) {
++ confp->chan.radio = 0;
++ confp->chan.sig = SIG_PRI;
++ confp->pri.nodetype = BRI_NETWORK;
++ } else if (!strcasecmp(v->value, "bri_cpe")) {
++ confp->chan.sig = SIG_PRI;
++ confp->chan.radio = 0;
++ confp->pri.nodetype = BRI_CPE;
+ #endif
+ } else {
+ ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
+@@ -11056,9 +11789,21 @@ static int process_zap(struct zt_chan_co
+ confp->chan.priindication_oob = 1;
+ else if (!strcasecmp(v->value, "inband"))
+ confp->chan.priindication_oob = 0;
++ else if (!strcasecmp(v->value, "passthrough"))
++ confp->chan.priindication_oob = 2;
+ else
+- ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband' or 'outofband' at line %d\n",
++ ast_log(LOG_WARNING, "'%s' is not a valid pri indication value, should be 'inband', 'outofband' or 'passthrough' at line %d\n",
+ v->value, v->lineno);
++ } else if (!strcasecmp(v->name, "pritransfer")) {
++ if (!strcasecmp(v->value, "no"))
++ confp->chan.pritransfer = 0;
++ else if (!strcasecmp(v->value, "ect"))
++ confp->chan.pritransfer = 1;
++ else if (!strcasecmp(v->value, "hangup"))
++ confp->chan.pritransfer = 2;
++ else
++ ast_log(LOG_WARNING, "'%s' is not a valid pri transfer value, should be 'no' , 'ect' or 'hangup' at line %d\n",
++ v->value, v->lineno);
+ } else if (!strcasecmp(v->name, "priexclusive")) {
+ confp->chan.priexclusive = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "internationalprefix")) {
+@@ -11071,6 +11816,10 @@ static int process_zap(struct zt_chan_co
+ ast_copy_string(confp->pri.privateprefix, v->value, sizeof(confp->pri.privateprefix));
+ } else if (!strcasecmp(v->name, "unknownprefix")) {
+ ast_copy_string(confp->pri.unknownprefix, v->value, sizeof(confp->pri.unknownprefix));
++ } else if (!strcasecmp(v->name, "nocid")) {
++ ast_copy_string(confp->pri.nocid, v->value, sizeof(confp->pri.nocid));
++ } else if (!strcasecmp(v->name, "withheldcid")) {
++ ast_copy_string(confp->pri.withheldcid, v->value, sizeof(confp->pri.withheldcid));
+ } else if (!strcasecmp(v->name, "resetinterval")) {
+ if (!strcasecmp(v->value, "never"))
+ confp->pri.resetinterval = -1;
+@@ -11087,6 +11836,8 @@ static int process_zap(struct zt_chan_co
+ ast_copy_string(confp->pri.idleext, v->value, sizeof(confp->pri.idleext));
+ } else if (!strcasecmp(v->name, "idledial")) {
+ ast_copy_string(confp->pri.idledial, v->value, sizeof(confp->pri.idledial));
++ } else if (!strcasecmp(v->name, "pritrustusercid")) {
++ confp->pri.usercid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "overlapdial")) {
+ confp->pri.overlapdial = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "pritimer")) {
+@@ -11389,6 +12140,7 @@ static int setup_zap(int reload)
+ #ifdef HAVE_PRI
+ if (!reload) {
+ for (x = 0; x < NUM_SPANS; x++) {
++ pris[x].debugfd = -1;
+ if (pris[x].pvts[0]) {
+ if (start_pri(pris + x)) {
+ ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);
+@@ -11436,7 +12188,10 @@ static int load_module(void)
+ ast_string_field_init(&inuse, 16);
+ ast_string_field_set(&inuse, name, "GR-303InUse");
+ ast_cli_register_multiple(zap_pri_cli, sizeof(zap_pri_cli) / sizeof(struct ast_cli_entry));
++ ast_register_application(zapCD_app, app_zapCD, zapCD_synopsis, zapCD_tdesc);
++ ast_register_application(zapInband_app, app_zapInband, zapInband_synopsis, zapInband_tdesc);
+ #endif
++ ast_register_application(zapEC_app, app_zapEC, zapEC_synopsis, zapEC_tdesc);
+ ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
+
+ memset(round_robin, 0, sizeof(round_robin));
+@@ -11470,6 +12225,7 @@ static int zt_sendtext(struct ast_channe
+ float scont = 0.0;
+ int index;
+
++
+ index = zt_get_index(c, p, 0);
+ if (index < 0) {
+ ast_log(LOG_WARNING, "Huh? I don't exist?\n");
+--- a/configs/zapata.conf.sample
++++ b/configs/zapata.conf.sample
+@@ -123,9 +123,20 @@ switchtype=national
+ ;
+ ; outofband: Signal Busy/Congestion out of band with RELEASE/DISCONNECT
+ ; inband: Signal Busy/Congestion using in-band tones
++; passthrough: Listen to the telco
+ ;
+ ; priindication = outofband
+ ;
++; PRI/BRI transfers (HOLD -> SETUP -> ECT/Hangup)
++;
++; Configure how transfers are initiated. ECT should be preferred
++;
++; no: no transfers allowed (results in hangup)
++; ect: use ECT (facility)
++; hangup: transfer on hangup (if your phones dont support ECT)
++;
++; pritransfer = ect
++;
+ ; If you need to override the existing channels selection routine and force all
+ ; PRI channels to be marked as exclusively selected, set this to yes.
+ ; priexclusive = yes
+Change the ABI of ast_channel_tech to add a new function, send_message that is
+to be used by channels wanting to send text messages with a destination number
+(mainly GSM).
+
+--- a/include/asterisk/channel.h
++++ b/include/asterisk/channel.h
+@@ -247,6 +247,11 @@ struct ast_channel_tech {
+ /*! \brief Display or transmit text */
+ int (* const send_text)(struct ast_channel *chan, const char *text);
+
++#if 0 /* we (Debian) disable that addition because of ABI breakage */
++ /*! \brief send a message */
++ int (* const send_message)(struct ast_channel *chan, const char *dest, const char *text, int ispdu);
++#endif
++
+ /*! \brief Display or send an image */
+ int (* const send_image)(struct ast_channel *chan, struct ast_frame *frame);
+
+@@ -689,6 +694,16 @@ struct ast_channel *ast_request_and_dial
+
+ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname, struct outgoing_helper *oh);
+
++/*! \brief "Requests" a channel for sending a message
++ * \param type type of channel to request
++ * \param data data to pass to the channel requester
++ * \param status status
++ * Request a channel of a given type, with data as optional information used
++ * by the low level module
++ * \return Returns 0 on success, -1 on failure.
++ */
++int ast_send_message(const char *type, void *data, char *to, char *from, char *message, int ispdu);
++
+ /*!\brief Register a channel technology (a new channel driver)
+ * Called by a channel module to register the kind of channels it supports.
+ * \param tech Structure defining channel technology or "type"
+@@ -910,6 +925,16 @@ int ast_set_write_format(struct ast_chan
+ */
+ int ast_sendtext(struct ast_channel *chan, const char *text);
+
++/*! \brief Sends message to a channel
++ * Write text to a display on a channel
++ * \param chan channel to act upon
++ * \param dest destination number/user
++ * \param text string of text to send on the channel
++ * \param ispdu message is in PDU format
++ * \return Returns 0 on success, -1 on failure
++ */
++int ast_sendmessage(struct ast_channel *chan, const char *dest, const char *text, int ispdu);
++
+ /*! \brief Receives a text character from a channel
+ * \param chan channel to act upon
+ * \param timeout timeout in milliseconds (0 for infinite wait)
+--- a/main/channel.c
++++ b/main/channel.c
+@@ -2472,6 +2472,21 @@ int ast_sendtext(struct ast_channel *cha
+ return res;
+ }
+
++int ast_sendmessage(struct ast_channel *chan, const char *dest, const char *text, int ispdu)
++{
++ int res = 0;
++ /* Stop if we're a zombie or need a soft hangup */
++ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
++ return -1;
++ CHECK_BLOCKING(chan);
++#if 0 /* we (Debian) disable that addition because of ABI breakage */
++ if (chan->tech->send_message)
++ res = chan->tech->send_message(chan, dest, text, ispdu);
++#endif
++ ast_clear_flag(chan, AST_FLAG_BLOCKING);
++ return res;
++}
++
+ int ast_senddigit_begin(struct ast_channel *chan, char digit)
+ {
+ /* Device does not support DTMF tones, lets fake
+@@ -4515,6 +4530,25 @@ void ast_channel_stop_silence_generator(
+ }
+
+
++int ast_send_message(const char *type, void *data, char *to, char *from, char *message, int ispdu) {
++ struct ast_channel *chan = NULL;
++ int status;
++ int res = -1;
++
++ chan = ast_request(type, AST_FORMAT_SLINEAR, data, &status);
++ if (chan) {
++ if (from) {
++ ast_set_callerid(chan, from, from, from);
++ }
++ res = ast_sendmessage(chan, to, message, ispdu);
++ /* XXX what about message CDRs ??? XXX */
++ ast_hangup(chan);
++ return res;
++ }
++
++ return res;
++}
++
+ /*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */
+ const char *channelreloadreason2txt(enum channelreloadreason reason)
+ {
+--- a/main/manager.c
++++ b/main/manager.c
+@@ -11,6 +11,9 @@
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
++ * Copyright (C) 2003-2004, Junghanns.NET Gmbh
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+@@ -1427,6 +1430,49 @@ static int action_hangup(struct mansessi
+ return 0;
+ }
+
++static char mandescr_message[] =
++"Description: Send a message\n"
++"Variables: \n"
++" Channel: The destination channel(e.g. SIP/phone1)\n"
++" From: \n"
++" Message: The message to send\n";
++
++static int action_message(struct mansession *s, const struct message *m)
++{
++ const char *name = astman_get_header(m, "Channel");
++ const char *from = astman_get_header(m, "From");
++ const char *message = astman_get_header(m, "Message");
++ const char *pdu = astman_get_header(m, "PDU");
++ char tmp[256];
++ char *tech, *data;
++ int res;
++ if (ast_strlen_zero(name) || (ast_strlen_zero(message) && ast_strlen_zero(pdu))) {
++ astman_send_error(s, m, "No channel or message/PDU specified");
++ return 0;
++ }
++ ast_copy_string(tmp, name, sizeof(tmp));
++ tech = tmp;
++ data = strchr(tmp, '/');
++ if (!data) {
++ astman_send_error(s, m, "Invalid channel\n");
++ return 0;
++ }
++ *data = '\0';
++ data++;
++ if (ast_strlen_zero(pdu)) {
++ res = ast_send_message(tech, (char *)data, (char *)name, (char *)from, (char *)message, 0);
++ } else {
++ res = ast_send_message(tech, (char *)data, (char *)name, (char *)from, (char *)pdu, 1);
++ }
++
++ if (res) {
++ astman_send_error(s, m, "Error sending message");
++ return 0;
++ }
++ astman_send_ack(s, m, "Message sent");
++ return 0;
++}
++
+ static char mandescr_setvar[] =
+ "Description: Set a global or local channel variable.\n"
+ "Variables: (Names marked with * are required)\n"
+@@ -2853,6 +2899,7 @@ int init_manager(void)
+ ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
+ ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
+ ast_manager_register2("Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
++ ast_manager_register2("Message", EVENT_FLAG_CALL, action_message, "Send Message", mandescr_message);
+ ast_manager_register("Status", EVENT_FLAG_CALL, action_status, "Lists channel status" );
+ ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
+ ast_manager_register2("Getvar", EVENT_FLAG_CALL, action_getvar, "Gets a Channel Variable", mandescr_getvar );
+--- a/pbx/pbx_spool.c
++++ b/pbx/pbx_spool.c
+@@ -87,6 +87,10 @@ struct outgoing {
+ char app[256];
+ char data[256];
+
++ /* If SMS */
++ char message[256];
++ char pdu[256];
++
+ /* If extension/context/priority */
+ char exten[256];
+ char context[256];
+@@ -181,6 +185,10 @@ static int apply_outgoing(struct outgoin
+ ast_copy_string(o->app, c, sizeof(o->app));
+ } else if (!strcasecmp(buf, "data")) {
+ ast_copy_string(o->data, c, sizeof(o->data));
++ } else if (!strcasecmp(buf, "message")) {
++ strncpy(o->message, c, sizeof(o->message) - 1);
++ } else if (!strcasecmp(buf, "pdu")) {
++ strncpy(o->pdu, c, sizeof(o->pdu) - 1);
+ } else if (!strcasecmp(buf, "maxretries")) {
+ if (sscanf(c, "%d", &o->maxretries) != 1) {
+ ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn);
+@@ -241,8 +249,8 @@ static int apply_outgoing(struct outgoin
+ }
+ }
+ ast_copy_string(o->fn, fn, sizeof(o->fn));
+- if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
+- ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn);
++ if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || ((ast_strlen_zero(o->app) && ast_strlen_zero(o->exten) && ast_strlen_zero(o->message) && ast_strlen_zero(o->pdu)))) {
++ ast_log(LOG_WARNING, "At least one of app or extension (or keyword message/pdu)must be specified, along with tech and dest in file %s\n", fn);
+ return -1;
+ }
+ return 0;
+@@ -332,6 +340,14 @@ static void *attempt_thread(void *data)
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for application %s(%s) (Retry %d)\n", o->tech, o->dest, o->app, o->data, o->retries);
+ res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
++ } else if (!ast_strlen_zero(o->message)) {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Attempting to send message on %s/%s (Retry %d)\n", o->tech, o->dest, o->retries);
++ res = ast_send_message(o->tech, o->dest, o->dest, (ast_strlen_zero(o->cid_name) ? o->cid_num : o->cid_name), o->message, 0);
++ } else if (!ast_strlen_zero(o->pdu)) {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Attempting to send message in PDU format on %s/%s (Retry %d)\n", o->tech, o->dest, o->retries);
++ res = ast_send_message(o->tech, o->dest, o->dest, (ast_strlen_zero(o->cid_name) ? o->cid_num : o->cid_name), o->pdu, 1);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries);
+@@ -348,9 +364,14 @@ static void *attempt_thread(void *data)
+ safe_append(o, time(NULL), "EndRetry");
+ }
+ } else {
++ if (!ast_strlen_zero(o->message)) {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_2 "Message sent to %s/%s\n", o->tech, o->dest);
++ } else {
+ ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
+ ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest);
+- remove_from_queue(o, "Completed");
++ }
++ remove_from_queue(o, "Completed");
+ }
+ free_outgoing(o);
+ return NULL;
+--- a/include/asterisk/monitor.h
++++ b/include/asterisk/monitor.h
+@@ -38,6 +38,8 @@ struct ast_channel_monitor {
+ char write_filename[FILENAME_MAX];
+ char filename_base[FILENAME_MAX];
+ int filename_changed;
++ char target_url[FILENAME_MAX];
++ char target_script[FILENAME_MAX];
+ char *format;
+ int joinfiles;
+ enum AST_MONITORING_STATE state;
+@@ -46,7 +48,7 @@ struct ast_channel_monitor {
+
+ /* Start monitoring a channel */
+ int ast_monitor_start(struct ast_channel *chan, const char *format_spec,
+- const char *fname_base, int need_lock );
++ const char *fname_base, const char *target_url, const char *target_script, int need_lock );
+
+ /* Stop monitoring a channel */
+ int ast_monitor_stop(struct ast_channel *chan, int need_lock);
+--- a/res/res_monitor.c
++++ b/res/res_monitor.c
+@@ -130,7 +130,7 @@ static int ast_monitor_set_state(struct
+
+ /* Start monitoring a channel */
+ int ast_monitor_start( struct ast_channel *chan, const char *format_spec,
+- const char *fname_base, int need_lock)
++ const char *fname_base, const char *target_url, const char *target_script, int need_lock)
+ {
+ int res = 0;
+ char tmp[256];
+@@ -154,6 +154,11 @@ int ast_monitor_start( struct ast_channe
+ return -1;
+ }
+
++ if (target_url)
++ ast_copy_string(monitor->target_url, target_url, sizeof(monitor->target_url));
++ if (target_script)
++ ast_copy_string(monitor->target_script, target_script, sizeof(monitor->target_script));
++
+ /* Determine file names */
+ if (!ast_strlen_zero(fname_base)) {
+ int directory = strchr(fname_base, '/') ? 1 : 0;
+@@ -297,6 +302,8 @@ int ast_monitor_stop(struct ast_channel
+ if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
+ char tmp[1024];
+ char tmp2[1024];
++ char tmp3[1024];
++ int result;
+ const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
+ char *name = chan->monitor->filename_base;
+ int directory = strchr(name, '/') ? 1 : 0;
+@@ -325,8 +332,13 @@ int ast_monitor_stop(struct ast_channel
+ snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s%s%s-\"* ) &",tmp, dir, absolute, name); /* remove legs when done mixing */
+ ast_copy_string(tmp, tmp2, sizeof(tmp));
+ }
+- ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
+- if (ast_safe_system(tmp) == -1)
++ if (!ast_strlen_zero(chan->monitor->target_script) && !ast_strlen_zero(chan->monitor->target_url)) {
++ snprintf(tmp3,sizeof(tmp3), "( %s& nice -19 %s \"%s/%s.%s\" \"%s\" ) &",tmp, chan->monitor->target_script , dir, name, format, chan->monitor->target_url);
++ ast_copy_string(tmp, tmp3, sizeof(tmp));
++ }
++ ast_log(LOG_NOTICE,"monitor executing %s\n",tmp);
++ result = ast_safe_system(tmp);
++ if (result == -1)
+ ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
+ }
+
+@@ -467,7 +479,7 @@ static int start_monitor_exec(struct ast
+ return 0;
+ }
+
+- res = ast_monitor_start(chan, format, fname_base, 1);
++ res = ast_monitor_start(chan, format, fname_base, NULL, NULL, 1);
+ if (res < 0)
+ res = ast_monitor_change_fname(chan, fname_base, 1);
+ ast_monitor_setjoinfiles(chan, joinfiles);
+@@ -506,6 +518,8 @@ static int start_monitor_action(struct m
+ const char *fname = astman_get_header(m, "File");
+ const char *format = astman_get_header(m, "Format");
+ const char *mix = astman_get_header(m, "Mix");
++ const char *target_url = astman_get_header(m, "TargetURL");
++ const char *target_script = astman_get_header(m, "TargetScript");
+ char *d;
+
+ if (ast_strlen_zero(name)) {
+@@ -530,7 +544,7 @@ static int start_monitor_action(struct m
+ *d = '-';
+ }
+
+- if (ast_monitor_start(c, format, fname, 1)) {
++ if (ast_monitor_start(c, format, fname, target_url, target_script, 1)) {
+ if (ast_monitor_change_fname(c, fname, 1)) {
+ astman_send_error(s, m, "Could not start monitoring channel");
+ ast_channel_unlock(c);
+--- a/apps/app_queue.c
++++ b/apps/app_queue.c
+@@ -2891,13 +2891,13 @@ static int try_calling(struct queue_ent
+ else
+ which = peer;
+ if (monitorfilename)
+- ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
++ ast_monitor_start(which, qe->parent->monfmt, monitorfilename, NULL, NULL, 1 );
+ else if (qe->chan->cdr)
+- ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
++ ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, NULL, NULL, 1 );
+ else {
+ /* Last ditch effort -- no CDR, make up something */
+ snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
+- ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
++ ast_monitor_start(which, qe->parent->monfmt, tmpid, NULL, NULL, 1 );
+ }
+ if (qe->parent->monjoin)
+ ast_monitor_setjoinfiles(which, 1);
+--- a/channels/chan_agent.c
++++ b/channels/chan_agent.c
+@@ -419,7 +419,7 @@ static int __agent_start_monitoring(stru
+ if ((pointer = strchr(filename, '.')))
+ *pointer = '-';
+ snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
+- ast_monitor_start(ast, recordformat, tmp, needlock);
++ ast_monitor_start(ast, recordformat, tmp, NULL, NULL, needlock);
+ ast_monitor_setjoinfiles(ast, 1);
+ snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
+ #if 0
+--- a/include/asterisk/devicestate.h
++++ b/include/asterisk/devicestate.h
+@@ -47,7 +47,7 @@ extern "C" {
+ #define AST_DEVICE_ONHOLD 8
+
+ /*! \brief Devicestate watcher call back */
+-typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data);
++typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data, char *cid_num, char *cid_name);
+
+ /*! \brief Devicestate provider call back */
+ typedef int (*ast_devstate_prov_cb_type)(const char *data);
+@@ -92,7 +92,7 @@ int ast_device_state_changed(const char
+ * callbacks for the changed extensions
+ * Returns 0 on success, -1 on failure
+ */
+-int ast_device_state_changed_literal(const char *device);
++int ast_device_state_changed_literal(const char *device, const char *cid_num, const char *cid_name);
+
+ /*! \brief Registers a device state change callback
+ * \param callback Callback
+--- a/main/devicestate.c
++++ b/main/devicestate.c
+@@ -78,6 +78,8 @@ static AST_LIST_HEAD_STATIC(devstate_cbs
+
+ struct state_change {
+ AST_LIST_ENTRY(state_change) list;
++ char cid_num[AST_MAX_EXTENSION];
++ char cid_name[AST_MAX_EXTENSION];
+ char device[1];
+ };
+
+@@ -277,7 +279,7 @@ void ast_devstate_del(ast_devstate_cb_ty
+ /*! \brief Notify callback watchers of change, and notify PBX core for hint updates
+ Normally executed within a separate thread
+ */
+-static void do_state_change(const char *device)
++static void do_state_change(const char *device, char *cid_num, char *cid_name)
+ {
+ int state;
+ struct devstate_cb *devcb;
+@@ -288,13 +290,13 @@ static void do_state_change(const char *
+
+ AST_LIST_LOCK(&devstate_cbs);
+ AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
+- devcb->callback(device, state, devcb->data);
++ devcb->callback(device, state, devcb->data, cid_num, cid_name);
+ AST_LIST_UNLOCK(&devstate_cbs);
+
+- ast_hint_state_changed(device);
++ ast_hint_state_changed(device, cid_num, cid_name);
+ }
+
+-static int __ast_device_state_changed_literal(char *buf, int norecurse)
++static int __ast_device_state_changed_literal(char *buf, int norecurse, char *cid_num, char *cid_name)
+ {
+ char *device;
+ struct state_change *change;
+@@ -308,10 +310,16 @@ static int __ast_device_state_changed_li
+ if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
+ /* we could not allocate a change struct, or */
+ /* there is no background thread, so process the change now */
+- do_state_change(device);
++ do_state_change(device, cid_num, cid_name);
+ } else {
+ /* queue the change */
+ strcpy(change->device, device);
++ if (cid_num && (!ast_strlen_zero(cid_num))) {
++ strncpy(change->cid_num, cid_num, sizeof(change->cid_num) - 1);
++ }
++ if (cid_name && (!ast_strlen_zero(cid_name))) {
++ strncpy(change->cid_name, cid_name, sizeof(change->cid_name) - 1);
++ }
+ AST_LIST_LOCK(&state_changes);
+ AST_LIST_INSERT_TAIL(&state_changes, change, list);
+ if (AST_LIST_FIRST(&state_changes) == change)
+@@ -329,17 +337,23 @@ static int __ast_device_state_changed_li
+ */
+ if (!norecurse && (tmp = strrchr(device, '-'))) {
+ *tmp = '\0';
+- __ast_device_state_changed_literal(device, 1);
++ __ast_device_state_changed_literal(device, 1, cid_num, cid_name);
+ }
+
+ return 1;
+ }
+
+-int ast_device_state_changed_literal(const char *dev)
++int ast_device_state_changed_literal(const char *dev, const char *cid_num, const char *cid_name)
+ {
+ char *buf;
++ char *buf2 = NULL;
++ char *buf3 = NULL;
+ buf = ast_strdupa(dev);
+- return __ast_device_state_changed_literal(buf, 0);
++ if (cid_num)
++ buf2 = ast_strdupa(cid_num);
++ if (cid_name)
++ buf3 = ast_strdupa(cid_name);
++ return __ast_device_state_changed_literal(buf, 0, buf2, buf3);
+ }
+
+ /*! \brief Accept change notification, add it to change queue */
+@@ -351,7 +365,7 @@ int ast_device_state_changed(const char
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+- return __ast_device_state_changed_literal(buf, 0);
++ return __ast_device_state_changed_literal(buf, 0, NULL, NULL);
+ }
+
+ /*! \brief Go through the dev state change queue and update changes in the dev state thread */
+@@ -366,7 +380,7 @@ static void *do_devstate_changes(void *d
+ if (cur) {
+ /* we got an entry, so unlock the list while we process it */
+ AST_LIST_UNLOCK(&state_changes);
+- do_state_change(cur->device);
++ do_state_change(cur->device, cur->cid_num, cur->cid_name);
+ free(cur);
+ AST_LIST_LOCK(&state_changes);
+ } else {
+--- a/include/asterisk/channel.h
++++ b/include/asterisk/channel.h
+@@ -613,8 +613,13 @@ int ast_channel_datastore_remove(struct
+ /*! \brief Find a datastore on a channel */
+ struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid);
+
++extern ast_mutex_t uniquelock;
++
++/*! \brief Change the state of a channel and the callerid of the calling channel*/
++int ast_setstate_and_callerid(struct ast_channel *chan, enum ast_channel_state state, char *cid_num, char *cid_name);
++
+ /*! \brief Change the state of a channel */
+-int ast_setstate(struct ast_channel *chan, enum ast_channel_state);
++int ast_setstate(struct ast_channel *chan, enum ast_channel_state state);
+
+ /*! \brief Create a channel structure
+ \return Returns NULL on failure to allocate.
+--- a/main/channel.c
++++ b/main/channel.c
+@@ -1261,7 +1261,7 @@ void ast_channel_free(struct ast_channel
+ free(chan);
+ AST_LIST_UNLOCK(&channels);
+
+- ast_device_state_changed_literal(name);
++ ast_device_state_changed_literal(name, NULL, NULL);
+ }
+
+ struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid)
+@@ -3673,7 +3673,7 @@ void ast_set_callerid(struct ast_channel
+ ast_channel_unlock(chan);
+ }
+
+-int ast_setstate(struct ast_channel *chan, enum ast_channel_state state)
++int ast_setstate_and_callerid(struct ast_channel *chan, enum ast_channel_state state, char *cid_num, char *cid_name)
+ {
+ int oldstate = chan->_state;
+
+@@ -3681,7 +3681,7 @@ int ast_setstate(struct ast_channel *cha
+ return 0;
+
+ chan->_state = state;
+- ast_device_state_changed_literal(chan->name);
++ ast_device_state_changed_literal(chan->name, cid_num, cid_name);
+ /* setstate used to conditionally report Newchannel; this is no more */
+ manager_event(EVENT_FLAG_CALL,
+ "Newstate",
+@@ -3698,6 +3698,11 @@ int ast_setstate(struct ast_channel *cha
+ return 0;
+ }
+
++int ast_setstate(struct ast_channel *chan, enum ast_channel_state state)
++{
++ return ast_setstate_and_callerid(chan, state, NULL, NULL);
++}
++
+ /*! \brief Find bridged channel */
+ struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
+ {
+--- a/include/asterisk/pbx.h
++++ b/include/asterisk/pbx.h
+@@ -63,7 +63,7 @@ struct ast_ignorepat;
+ struct ast_sw;
+
+ /*! \brief Typedef for devicestate and hint callbacks */
+-typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
++typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data, char *cid_num, char *cid_name);
+
+ /*! \brief Data structure associated with a custom dialplan function */
+ struct ast_custom_function {
+@@ -875,7 +875,7 @@ int ast_func_read(struct ast_channel *ch
+ */
+ int ast_func_write(struct ast_channel *chan, char *function, const char *value);
+
+-void ast_hint_state_changed(const char *device);
++void ast_hint_state_changed(const char *device, char *cid_num, char *cid_name);
+
+ #if defined(__cplusplus) || defined(c_plusplus)
+ }
+--- a/main/pbx.c
++++ b/main/pbx.c
+@@ -2022,7 +2022,7 @@ int ast_extension_state(struct ast_chann
+ return ast_extension_state2(e); /* Check all devices in the hint */
+ }
+
+-void ast_hint_state_changed(const char *device)
++void ast_hint_state_changed(const char *device, char *cid_num, char *cid_name)
+ {
+ struct ast_hint *hint;
+
+@@ -2053,11 +2053,11 @@ void ast_hint_state_changed(const char *
+
+ /* For general callbacks */
+ for (cblist = statecbs; cblist; cblist = cblist->next)
+- cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
++ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data, cid_num, cid_name);
+
+ /* For extension callbacks */
+ for (cblist = hint->callbacks; cblist; cblist = cblist->next)
+- cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
++ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data, cid_num, cid_name);
+
+ hint->laststate = state; /* record we saw the change */
+ }
+@@ -2252,7 +2252,7 @@ static int ast_remove_hint(struct ast_ex
+ /* Notify with -1 and remove all callbacks */
+ cbprev = cblist;
+ cblist = cblist->next;
+- cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
++ cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data, NULL, NULL);
+ free(cbprev);
+ }
+ hint->callbacks = NULL;
+@@ -4021,7 +4021,7 @@ void ast_merge_contexts_and_delete(struc
+ while (thiscb) {
+ prevcb = thiscb;
+ thiscb = thiscb->next;
+- prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
++ prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data, NULL, NULL);
+ free(prevcb);
+ }
+ } else {
+--- a/channels/chan_sip.c
++++ b/channels/chan_sip.c
+@@ -1342,7 +1342,7 @@ static void ast_quiet_chan(struct ast_ch
+ static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
+
+ /*--- Device monitoring and Device/extension state handling */
+-static int cb_extensionstate(char *context, char* exten, int state, void *data);
++static int cb_extensionstate(char *context, char* exten, int state, void *data, char *cid_num, char *cid_name);
+ static int sip_devicestate(void *data);
+ static int sip_poke_noanswer(const void *data);
+ static int sip_poke_peer(struct sip_peer *peer);
+@@ -8593,7 +8593,7 @@ static void sip_peer_hold(struct sip_pvt
+ /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
+ \note If you add an "hint" priority to the extension in the dial plan,
+ you will get notifications on device state changes */
+-static int cb_extensionstate(char *context, char* exten, int state, void *data)
++static int cb_extensionstate(char *context, char* exten, int state, void *data, char *cid_num, char *cid_name)
+ {
+ struct sip_pvt *p = data;
+
+@@ -12783,7 +12783,7 @@ static void handle_response(struct sip_p
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) {
+ /* Ready to send the next state we have on queue */
+ ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
+- cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p);
++ cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p, NULL, NULL);
+ }
+ }
+ } else if (sipmethod == SIP_REGISTER)
+@@ -13036,7 +13036,7 @@ static void handle_response(struct sip_p
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE)) {
+ /* Ready to send the next state we have on queue */
+ ast_clear_flag(&p->flags[1], SIP_PAGE2_STATECHANGEQUEUE);
+- cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p);
++ cb_extensionstate((char *)p->context, (char *)p->exten, p->laststate, (void *) p, NULL, NULL);
+ }
+ }
+ } else if (sipmethod == SIP_BYE)
+--- a/apps/app_queue.c
++++ b/apps/app_queue.c
+@@ -721,7 +721,7 @@ static void *device_state_thread(void *d
+ return NULL;
+ }
+ /*! \brief Producer of the statechange queue */
+-static int statechange_queue(const char *dev, int state, void *ign)
++static int statechange_queue(const char *dev, int state, void *ign, char *cid_num, char *cid_name)
+ {
+ struct statechange *sc;
+
+--- a/include/asterisk/manager.h
++++ b/include/asterisk/manager.h
+@@ -55,6 +55,7 @@
+ #define EVENT_FLAG_AGENT (1 << 5) /* Ability to read/set agent info */
+ #define EVENT_FLAG_USER (1 << 6) /* Ability to read/set user info */
+ #define EVENT_FLAG_CONFIG (1 << 7) /* Ability to modify configurations */
++#define EVENT_FLAG_EXTENSIONSTATUS (1 << 8) /* ExtensionStatus events */
+
+ /* Export manager structures */
+ #define AST_MAX_MANHEADERS 128
+--- a/main/manager.c
++++ b/main/manager.c
+@@ -129,6 +129,7 @@ static struct permalias {
+ { EVENT_FLAG_AGENT, "agent" },
+ { EVENT_FLAG_USER, "user" },
+ { EVENT_FLAG_CONFIG, "config" },
++ { EVENT_FLAG_EXTENSIONSTATUS, "extensionstatus" },
+ { -1, "all" },
+ { 0, "none" },
+ };
+@@ -2551,10 +2552,12 @@ int ast_manager_unregister(char *action)
+ return 0;
+ }
+
+-static int manager_state_cb(char *context, char *exten, int state, void *data)
++static int manager_state_cb(char *context, char *exten, int state, void *data, char *cid_num, char *cid_name)
+ {
++ char hint[256] = "";
++ ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
+ /* Notify managers of change */
+- manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
++ manager_event(EVENT_FLAG_EXTENSIONSTATUS, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\nCallerID: \"%s\" <%s>\r\nHint: %s\r\n", exten, context, state, cid_num, cid_name, hint);
+ return 0;
+ }
+
+--- a/apps/app_devstate.c
++++ b/apps/app_devstate.c
+@@ -50,7 +50,7 @@ static struct ast_cli_entry cli_dev_sta
+ static int devstate_cli(int fd, int argc, char *argv[])
+ {
+ char devName[128];
+- if (argc != 3)
++ if ((argc != 3) && (argc != 4) && (argc != 5))
+ return RESULT_SHOWUSAGE;
+
+ if (ast_db_put("DEVSTATES", argv[1], argv[2]))
+@@ -58,7 +58,15 @@ static int devstate_cli(int fd, int argc
+ ast_log(LOG_DEBUG, "ast_db_put failed\n");
+ }
+ snprintf(devName, sizeof(devName), "DS/%s", argv[1]);
+- ast_device_state_changed_literal(devName);
++ if (argc == 4) {
++ ast_log(LOG_NOTICE, "devname %s cid %s\n", devName, argv[3]);
++ ast_device_state_changed_literal(devName, argv[3], NULL);
++ } else if (argc == 5) {
++ ast_log(LOG_NOTICE, "devname %s cid %s cidname %s\n", devName, argv[3], argv[4]);
++ ast_device_state_changed_literal(devName, argv[3], argv[4]);
++ } else {
++ ast_device_state_changed_literal(devName, NULL, NULL);
++ }
+ return RESULT_SUCCESS;
+ }
+
+@@ -93,7 +101,7 @@ static int devstate_exec(struct ast_chan
+ }
+
+ snprintf(devName, sizeof(devName), "DS/%s", device);
+- ast_device_state_changed_literal(devName);
++ ast_device_state_changed_literal(devName, NULL, NULL);
+
+ ast_module_user_remove(u);
+ return 0;
+@@ -150,6 +158,8 @@ static int action_devstate(struct manses
+ const char *devstate = astman_get_header(m, "Devstate");
+ const char *value = astman_get_header(m, "Value");
+ const char *id = astman_get_header(m,"ActionID");
++ const char *cid_num = astman_get_header(m, "CallerID");
++ const char *cid_name = astman_get_header(m, "CallerIDName");
+ char devName[128];
+ char idText[256] = "";
+
+@@ -166,7 +176,7 @@ static int action_devstate(struct manses
+
+ if (!ast_db_put("DEVSTATES", devstate, (char *)value)) {
+ snprintf(devName, sizeof(devName), "DS/%s", devstate);
+- ast_device_state_changed_literal(devName);
++ ast_device_state_changed_literal(devName, cid_num, cid_name);
+ astman_append(s, "Response: Success\r\n%s\r\n", idText);
+ } else {
+ ast_log(LOG_DEBUG, "ast_db_put failed\n");
+--- a/res/res_esel.c
++++ b/res/res_esel.c
+@@ -51,6 +51,8 @@ typedef struct esel_extension_state {
+ char context[AST_MAX_EXTENSION];
+ char exten[AST_MAX_EXTENSION];
+ int state;
++ char cid_num[AST_MAX_EXTENSION];
++ char cid_name[AST_MAX_EXTENSION];
+ char devstate[AST_MAX_EXTENSION];
+ struct esel_extension_state *next;
+ struct esel_extension_state *prev;
+@@ -93,7 +95,7 @@ typedef struct esel_pvt {
+
+ static struct esel_pvt *donkeys = NULL;
+
+-static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data) {
++static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) {
+ struct esel_extension_state *exstate = NULL;
+
+ exstate = malloc(sizeof(struct esel_extension_state));
+@@ -115,6 +117,8 @@ static int esel_queue_extension_state(st
+ }
+ ast_copy_string(exstate->exten, exten, sizeof(exstate->exten));
+ ast_copy_string(exstate->context, context, sizeof(exstate->context));
++ ast_copy_string(exstate->cid_num, cid_num, sizeof(exstate->cid_num));
++ ast_copy_string(exstate->cid_name, cid_name, sizeof(exstate->cid_name));
+ exstate->state = state;
+ if (!queue->head) {
+ /* Empty queue */
+@@ -161,7 +165,7 @@ static void esel_export_to_remote(struct
+ char msg[1024];
+ int sent = 0;
+ memset(msg, 0x0, sizeof(msg));
+- snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state));
++ snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\nCallerID: %s\r\nCallerIDName: %s\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state), exstate->cid_num, exstate->cid_name);
+ sent = send(esel->sockfd, msg, strlen(msg), 0);
+ if (sent == -1) {
+ esel->connected = 0;
+@@ -250,13 +254,13 @@ static void *do_esel_thread(void *data)
+ return NULL;
+ }
+
+-static int esel_state_cb(char *context, char *exten, int state, void *data) {
++static int esel_state_cb(char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) {
+ struct esel_pvt *esel;
+
+ esel = donkeys;
+ ast_mutex_lock(&listlock);
+ while (esel) {
+- esel_queue_extension_state(&esel->queue, context, exten, state, data);
++ esel_queue_extension_state(&esel->queue, context, exten, state, data, cid_num, cid_name);
+ esel = esel->next;
+ }
+ ast_mutex_unlock(&listlock);
+Add or convert channel operations so they can use the unique ID.
+
+--- a/include/asterisk/channel.h
++++ b/include/asterisk/channel.h
+@@ -682,6 +682,18 @@ void ast_channel_free(struct ast_channe
+ */
+ struct ast_channel *ast_request(const char *type, int format, void *data, int *status);
+
++/*! \brief Requests a channel
++ * \param type type of channel to request
++ * \param format requested channel format (codec)
++ * \param data data to pass to the channel requester
++ * \param status status
++ * \param uniqueid uniqueid
++ * Request a channel of a given type, with data as optional information used
++ * by the low level module. Sets the channels uniqueid to 'uniqueid'.
++ * \return Returns an ast_channel on success, NULL on failure.
++ */
++struct ast_channel *ast_request_with_uniqueid(const char *type, int format, void *data, int *status, char *uniqueid);
++
+ /*!
+ * \brief Request a channel of a given type, with data as optional information used
+ * by the low level module and attempt to place a call on it
+@@ -697,8 +709,12 @@ struct ast_channel *ast_request(const ch
+ */
+ struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname);
+
++struct ast_channel *ast_request_and_dial_uniqueid(const char *type, int format, void *data, int timeout, int *reason, int callingpres, const char *cidnum, const char *cidname, char *uniqueid);
++
+ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *reason, const char *cidnum, const char *cidname, struct outgoing_helper *oh);
+
++struct ast_channel *__ast_request_and_dial_uniqueid(const char *type, int format, void *data, int timeout, int *reason, int callingpres, const char *cidnum, const char *cidname, struct outgoing_helper *oh, char *uniqueid);
++
+ /*! \brief "Requests" a channel for sending a message
+ * \param type type of channel to request
+ * \param data data to pass to the channel requester
+@@ -990,6 +1006,8 @@ struct ast_channel *ast_get_channel_by_e
+ /*! \brief Get next channel by exten (and optionally context) and lock it */
+ struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
+ const char *context);
++/*! Get channel by uniqueid (locks channel) */
++struct ast_channel *ast_get_channel_by_uniqueid_locked(const char *uniqueid);
+
+ /*! ! \brief Waits for a digit
+ * \param c channel to wait for a digit on
+--- a/main/channel.c
++++ b/main/channel.c
+@@ -1017,7 +1017,7 @@ void ast_channel_undefer_dtmf(struct ast
+ */
+ static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
+ const char *name, const int namelen,
+- const char *context, const char *exten)
++ const char *context, const char *exten, const char *uniqueid)
+ {
+ const char *msg = prev ? "deadlock" : "initial deadlock";
+ int retries;
+@@ -1045,7 +1045,10 @@ static struct ast_channel *channel_find_
+ * XXX Need a better explanation for this ...
+ */
+ }
+- if (name) { /* want match by name */
++ if (uniqueid) {
++ if (!strcasecmp(c->uniqueid, uniqueid))
++ break;
++ } else if (name) { /* want match by name */
+ if ((!namelen && strcasecmp(c->name, name)) ||
+ (namelen && strncasecmp(c->name, name, namelen)))
+ continue; /* name match failed */
+@@ -1100,39 +1103,44 @@ static struct ast_channel *channel_find_
+ /*! \brief Browse channels in use */
+ struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
+ {
+- return channel_find_locked(prev, NULL, 0, NULL, NULL);
++ return channel_find_locked(prev, NULL, 0, NULL, NULL, NULL);
+ }
+
+ /*! \brief Get channel by name and lock it */
+ struct ast_channel *ast_get_channel_by_name_locked(const char *name)
+ {
+- return channel_find_locked(NULL, name, 0, NULL, NULL);
++ return channel_find_locked(NULL, name, 0, NULL, NULL, NULL);
+ }
+
+ /*! \brief Get channel by name prefix and lock it */
+ struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
+ {
+- return channel_find_locked(NULL, name, namelen, NULL, NULL);
++ return channel_find_locked(NULL, name, namelen, NULL, NULL, NULL);
+ }
+
+ /*! \brief Get next channel by name prefix and lock it */
+ struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name,
+ const int namelen)
+ {
+- return channel_find_locked(chan, name, namelen, NULL, NULL);
++ return channel_find_locked(chan, name, namelen, NULL, NULL, NULL);
+ }
+
+ /*! \brief Get channel by exten (and optionally context) and lock it */
+ struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context)
+ {
+- return channel_find_locked(NULL, NULL, 0, context, exten);
++ return channel_find_locked(NULL, NULL, 0, context, exten, NULL);
+ }
+
+ /*! \brief Get next channel by exten (and optionally context) and lock it */
+ struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
+ const char *context)
+ {
+- return channel_find_locked(chan, NULL, 0, context, exten);
++ return channel_find_locked(chan, NULL, 0, context, exten, NULL);
++}
++
++struct ast_channel *ast_get_channel_by_uniqueid_locked(const char *uniqueid)
++{
++ return channel_find_locked(NULL, NULL, 0, NULL, NULL, uniqueid);
+ }
+
+ /*! \brief Wait, look for hangups and condition arg */
+@@ -2862,6 +2870,12 @@ char *ast_channel_reason2str(int reason)
+
+ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
+ {
++ return __ast_request_and_dial_uniqueid(type, format, data,
++ timeout, outstate, 0, cid_num, cid_name, oh, NULL);
++}
++
++struct ast_channel *__ast_request_and_dial_uniqueid(const char *type, int format, void *data, int timeout, int *outstate, int callingpres, const char *cid_num, const char *cid_name, struct outgoing_helper *oh, char* uniqueid)
++{
+ int dummy_outstate;
+ int cause = 0;
+ struct ast_channel *chan;
+@@ -2873,7 +2887,7 @@ struct ast_channel *__ast_request_and_di
+ else
+ outstate = &dummy_outstate; /* make outstate always a valid pointer */
+
+- chan = ast_request(type, format, data, &cause);
++ chan = ast_request_with_uniqueid(type, format, data, &cause, uniqueid);
+ if (!chan) {
+ ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
+ /* compute error and return */
+@@ -2993,8 +3007,12 @@ struct ast_channel *ast_request_and_dial
+ {
+ return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
+ }
++struct ast_channel *ast_request_and_dial_uniqueid(const char *type, int format, void *data, int timeout, int *outstate, int callingpres, const char *cidnum, const char *cidname, char *uniqueid)
++{
++ return __ast_request_and_dial_uniqueid(type, format, data, timeout, outstate, 0, cidnum, cidname, NULL, uniqueid);
++}
+
+-struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
++struct ast_channel *ast_request_with_uniqueid(const char *type, int format, void *data, int *cause, char *uniqueid)
+ {
+ struct chanlist *chan;
+ struct ast_channel *c;
+@@ -3033,6 +3051,7 @@ struct ast_channel *ast_request(const ch
+ if (!(c = chan->tech->requester(type, capabilities | videoformat, data, cause)))
+ return NULL;
+
++ if (uniqueid) strncpy(c->uniqueid, uniqueid, sizeof(c->uniqueid));
+ /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
+ return c;
+ }
+@@ -3044,6 +3063,11 @@ struct ast_channel *ast_request(const ch
+ return NULL;
+ }
+
++struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
++{
++ return ast_request_with_uniqueid(type, format, data, cause, NULL);
++}
++
+ int ast_call(struct ast_channel *chan, char *addr, int timeout)
+ {
+ /* Place an outgoing call, but don't wait any longer than timeout ms before returning.
+--- a/include/asterisk/pbx.h
++++ b/include/asterisk/pbx.h
+@@ -717,9 +717,17 @@ int ast_async_goto_by_name(const char *c
+ int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel);
+
+ /*! Synchronously or asynchronously make an outbound call and send it to a
++ particular extension (extended version with callinpres and uniqueid) */
++int ast_pbx_outgoing_exten_uniqueid(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid);
++
++/*! Synchronously or asynchronously make an outbound call and send it to a
+ particular application with given extension */
+ int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel);
+
++/*! Synchronously or asynchronously make an outbound call and send it to a
++ particular application with given extension (extended version with callinpres and uniqueid) */
++int ast_pbx_outgoing_app_uniqueid(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid);
++
+ /*!
+ * \brief Evaluate a condition
+ *
+--- a/main/pbx.c
++++ b/main/pbx.c
+@@ -4992,7 +4992,7 @@ static int ast_pbx_outgoing_cdr_failed(v
+ return 0; /* success */
+ }
+
+-int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
++int ast_pbx_outgoing_exten_uniqueid(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, char *uniqueid)
+ {
+ struct ast_channel *chan;
+ struct async_stat *as;
+@@ -5002,7 +5002,7 @@ int ast_pbx_outgoing_exten(const char *t
+
+ if (sync) {
+ LOAD_OH(oh);
+- chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
++ chan = __ast_request_and_dial_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+@@ -5094,7 +5094,7 @@ int ast_pbx_outgoing_exten(const char *t
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+- chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
++ chan = ast_request_and_dial_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, uniqueid);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+@@ -5134,6 +5134,10 @@ outgoing_exten_cleanup:
+ return res;
+ }
+
++int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
++{
++ return ast_pbx_outgoing_exten_uniqueid(type, format, data, timeout, context, exten, priority, reason, sync, 0, cid_num, cid_name, vars, account, channel, NULL);
++}
+ struct app_tmp {
+ char app[256];
+ char data[256];
+@@ -5158,7 +5162,7 @@ static void *ast_pbx_run_app(void *data)
+ return NULL;
+ }
+
+-int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
++int ast_pbx_outgoing_app_uniqueid(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, int callingpres, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, char *uniqueid)
+ {
+ struct ast_channel *chan;
+ struct app_tmp *tmp;
+@@ -5177,7 +5181,7 @@ int ast_pbx_outgoing_app(const char *typ
+ goto outgoing_app_cleanup;
+ }
+ if (sync) {
+- chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
++ chan = __ast_request_and_dial_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid);
+ if (chan) {
+ if (!chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
+ chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
+@@ -5259,7 +5263,7 @@ int ast_pbx_outgoing_app(const char *typ
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+- chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
++ chan = __ast_request_and_dial_uniqueid(type, format, data, timeout, reason, callingpres, cid_num, cid_name, &oh, uniqueid);
+ if (!chan) {
+ free(as);
+ res = -1;
+@@ -5299,6 +5303,10 @@ outgoing_app_cleanup:
+ return res;
+ }
+
++int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
++{
++ return ast_pbx_outgoing_app_uniqueid(type, format, data, timeout, app, appdata, reason, sync, 0, cid_num, cid_name, vars, account, locked_channel, NULL);
++}
+ void __ast_context_destroy(struct ast_context *con, const char *registrar)
+ {
+ struct ast_context *tmp, *tmpl=NULL;
+--- a/res/res_monitor.c
++++ b/res/res_monitor.c
+@@ -340,6 +340,11 @@ int ast_monitor_stop(struct ast_channel
+ result = ast_safe_system(tmp);
+ if (result == -1)
+ ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
++ manager_event(EVENT_FLAG_CALL, "MonitorStopped",
++ "Channel: %s\r\n"
++ "Uniqueid: %s\r\n"
++ "Result: %d\r\n"
++ ,chan->name, chan->uniqueid, result);
+ }
+
+ free(chan->monitor->format);
+@@ -518,18 +523,28 @@ static int start_monitor_action(struct m
+ const char *fname = astman_get_header(m, "File");
+ const char *format = astman_get_header(m, "Format");
+ const char *mix = astman_get_header(m, "Mix");
++ const char *uniqueid = astman_get_header(m, "Uniqueid");
+ const char *target_url = astman_get_header(m, "TargetURL");
+ const char *target_script = astman_get_header(m, "TargetScript");
+ char *d;
+
+- if (ast_strlen_zero(name)) {
+- astman_send_error(s, m, "No channel specified");
++ if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) {
++ astman_send_error(s, m, "No channel/uniqueid specified");
++ return 0;
++ }
++
++ if (!ast_strlen_zero(uniqueid)) {
++ c = ast_get_channel_by_uniqueid_locked(uniqueid);
++ if (!c) {
++ astman_send_error(s, m, "No such uniqueid");
+ return 0;
+- }
+- c = ast_get_channel_by_name_locked(name);
+- if (!c) {
++ }
++ } else {
++ c = ast_get_channel_by_name_locked(name);
++ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
++ }
+ }
+
+ if (ast_strlen_zero(fname)) {
+@@ -570,16 +585,30 @@ static int stop_monitor_action(struct ma
+ {
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
++ const char *uniqueid = astman_get_header(m, "Uniqueid");
+ int res;
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+- c = ast_get_channel_by_name_locked(name);
+- if (!c) {
+- astman_send_error(s, m, "No such channel");
++ if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) {
++ astman_send_error(s, m, "No channel/uniqueid specified");
++ return 0;
++ }
++ if (!ast_strlen_zero(uniqueid)) {
++ c = ast_get_channel_by_uniqueid_locked(uniqueid);
++ if (!c) {
++ astman_send_error(s, m, "No such uniqueid");
+ return 0;
++ }
++ } else {
++ c = ast_get_channel_by_name_locked(name);
++ if (!c) {
++ astman_send_error(s, m, "No such channel");
++ return 0;
++ }
+ }
++
+ res = ast_monitor_stop(c, 1);
+ ast_channel_unlock(c);
+ if (res) {
+--- a/apps/app_chanspy.c
++++ b/apps/app_chanspy.c
+@@ -57,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+
+ static const char *tdesc = "Listen to a channel, and optionally whisper into it";
+ static const char *app_chan = "ChanSpy";
++static const char *app_chan_uniqueid = "ChanSpyChan";
+ static const char *desc_chan =
+ " ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
+ "audio from an Asterisk channel. This includes the audio coming in and\n"
+@@ -87,6 +88,27 @@ static const char *desc_chan =
+ " channel.\n"
+ ;
+
++static const char *desc_uniqueid =
++" ChanSpyChan(uniqueid[|options]): This application is used to listen to the\n"
++"audio from an Asterisk channel. This includes the audio coming in and\n"
++"out of the channel being spied on. The 'uniqueid' parameter has to be specified,\n"
++" While spying, the following actions may be performed:\n"
++" - Dialing # cycles the volume level.\n"
++" Options:\n"
++" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
++" selected channel name.\n"
++" r[(basename)] - Record the session to the monitor spool directory. An\n"
++" optional base for the filename may be specified. The\n"
++" default is 'chanspy'.\n"
++" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
++" negative value refers to a quieter setting.\n"
++" w - Enable 'whisper' mode, so the spying channel can talk to\n"
++" the spied-on channel.\n"
++" W - Enable 'private whisper' mode, so the spying channel can\n"
++" talk to the spied-on channel but cannot listen to that\n"
++" channel.\n"
++;
++
+ static const char *app_ext = "ExtenSpy";
+ static const char *desc_ext =
+ " ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
+@@ -456,7 +478,7 @@ static struct chanspy_ds *setup_chanspy_
+
+ static struct chanspy_ds *next_channel(struct ast_channel *chan,
+ const struct ast_channel *last, const char *spec,
+- const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
++ const char *exten, const char *context, struct chanspy_ds *chanspy_ds, const char *uniqueid)
+ {
+ struct ast_channel *this;
+
+@@ -465,6 +487,8 @@ redo:
+ this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
+ else if (exten)
+ this = ast_walk_channel_by_exten_locked(last, exten, context);
++ else if (uniqueid)
++ this = ast_get_channel_by_uniqueid_locked(uniqueid);
+ else
+ this = ast_channel_walk_locked(last);
+
+@@ -485,7 +509,7 @@ redo:
+
+ static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
+ int volfactor, const int fd, const char *mygroup, const char *spec,
+- const char *exten, const char *context)
++ const char *exten, const char *context, const char *uniqueid)
+ {
+ char nameprefix[AST_NAME_STRLEN];
+ char peer_name[AST_NAME_STRLEN + 5];
+@@ -530,11 +554,11 @@ static int common_exec(struct ast_channe
+ waitms = 100;
+ num_spyed_upon = 0;
+
+- for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
++ for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds, NULL);
+ peer_chanspy_ds;
+ chanspy_ds_free(peer_chanspy_ds), prev = peer,
+ peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
+- next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
++ next_channel(chan, prev, spec, exten, context, &chanspy_ds, NULL), next_chanspy_ds = NULL) {
+ const char *group;
+ int igrp = !mygroup;
+ char *groups[25];
+@@ -733,7 +757,7 @@ static int chanspy_exec(struct ast_chann
+ }
+ }
+
+- res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
++ res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL, NULL);
+
+ if (fd)
+ close(fd);
+@@ -818,7 +842,7 @@ static int extenspy_exec(struct ast_chan
+ }
+ }
+
+- res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
++ res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context, NULL);
+
+ if (fd)
+ close(fd);
+@@ -831,14 +855,100 @@ static int extenspy_exec(struct ast_chan
+ return res;
+ }
+
++static int chanspychan_exec(struct ast_channel *chan, void *data)
++{
++ struct ast_module_user *u;
++ char *options = NULL;
++ char *uniqueid = NULL;
++ char *argv[2];
++ char *mygroup = NULL;
++ char *recbase = NULL;
++ int fd = 0;
++ struct ast_flags flags;
++ int oldwf = 0;
++ int argc = 0;
++ int volfactor = 0;
++ int res;
++
++ data = ast_strdupa(data);
++
++ u = ast_module_user_add(chan);
++
++ if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
++ uniqueid = argv[0];
++ if (argc > 1)
++ options = argv[1];
++
++ if (ast_strlen_zero(uniqueid)) {
++ ast_log(LOG_ERROR, "no uniqueid specified.\n");
++ ast_module_user_remove(u);
++ return -1;
++ }
++ }
++
++ if (options) {
++ char *opts[OPT_ARG_ARRAY_SIZE];
++
++ ast_app_parse_options(spy_opts, &flags, opts, options);
++ if (ast_test_flag(&flags, OPTION_GROUP))
++ mygroup = opts[OPT_ARG_GROUP];
++
++ if (ast_test_flag(&flags, OPTION_RECORD) &&
++ !(recbase = opts[OPT_ARG_RECORD]))
++ recbase = "chanspy";
++
++ if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
++ int vol;
++
++ if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
++ ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
++ else
++ volfactor = vol;
++ }
++
++ if (ast_test_flag(&flags, OPTION_PRIVATE))
++ ast_set_flag(&flags, OPTION_WHISPER);
++ }
++
++ oldwf = chan->writeformat;
++ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
++ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
++ ast_module_user_remove(u);
++ return -1;
++ }
++
++ if (recbase) {
++ char filename[512];
++
++ snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
++ if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
++ ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
++ fd = 0;
++ }
++ }
++
++ res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, NULL, uniqueid);
++
++ if (fd)
++ close(fd);
++
++ if (oldwf && ast_set_write_format(chan, oldwf) < 0)
++ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
++
++ ast_module_user_remove(u);
++
++ return res;
++}
++
++
+ static int unload_module(void)
+ {
+ int res = 0;
+
+ res |= ast_unregister_application(app_chan);
++ res |= ast_unregister_application(app_chan_uniqueid);
+ res |= ast_unregister_application(app_ext);
+
+- ast_module_user_hangup_all();
+
+ return res;
+ }
+@@ -849,6 +959,7 @@ static int load_module(void)
+
+ res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
+ res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
++ res |= ast_register_application(app_chan_uniqueid, chanspychan_exec, tdesc, desc_uniqueid);
+
+ return res;
+ }
+--- a/main/manager.c
++++ b/main/manager.c
+@@ -87,6 +87,8 @@ struct fast_originate_helper {
+ char idtext[AST_MAX_EXTENSION];
+ char account[AST_MAX_ACCOUNT_CODE];
+ int priority;
++ int callingpres;
++ char uniqueid[64];
+ struct ast_variable *vars;
+ };
+
+@@ -1416,11 +1418,20 @@ static int action_hangup(struct mansessi
+ {
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+- if (ast_strlen_zero(name)) {
+- astman_send_error(s, m, "No channel specified");
++ const char *uniqueid = astman_get_header(m, "Uniqueid");
++
++ if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) {
++ astman_send_error(s, m, "No channel or uniqueid specified");
+ return 0;
+ }
+- c = ast_get_channel_by_name_locked(name);
++
++ if (!ast_strlen_zero(uniqueid)) {
++ c = ast_get_channel_by_uniqueid_locked(uniqueid);
++ } else {
++ if (!ast_strlen_zero(name))
++ c = ast_get_channel_by_name_locked(name);
++ }
++
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+@@ -1671,12 +1682,18 @@ static int action_redirect(struct manses
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ const char *priority = astman_get_header(m, "Priority");
++ const char *uniqueid = astman_get_header(m, "Uniqueid");
++ const char *uniqueid2 = astman_get_header(m, "ExtraUniqueid");
++ const char *exten2 = astman_get_header(m, "ExtraExten");
++ const char *context2 = astman_get_header(m, "ExtraContext");
++ const char *priority2 = astman_get_header(m, "ExtraPriority");
+ struct ast_channel *chan, *chan2 = NULL;
+ int pi = 0;
++ int pi2 = 0;
+ int res;
+
+- if (ast_strlen_zero(name)) {
+- astman_send_error(s, m, "Channel not specified");
++ if (ast_strlen_zero(name) && ast_strlen_zero(uniqueid)) {
++ astman_send_error(s, m, "Channel or Uniqueid not specified");
+ return 0;
+ }
+ if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
+@@ -1685,8 +1702,18 @@ static int action_redirect(struct manses
+ return 0;
+ }
+ }
++ if (!ast_strlen_zero(priority2) && (sscanf(priority2, "%d", &pi2) != 1)) {
++ if ((pi = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL)) < 1) {
++ astman_send_error(s, m, "Invalid extra priority\n");
++ return 0;
++ }
++ }
+ /* XXX watch out, possible deadlock!!! */
+- chan = ast_get_channel_by_name_locked(name);
++ if (!ast_strlen_zero(uniqueid)) {
++ chan = ast_get_channel_by_uniqueid_locked(uniqueid);
++ } else {
++ chan = ast_get_channel_by_name_locked(name);
++ }
+ if (!chan) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
+@@ -1698,8 +1725,11 @@ static int action_redirect(struct manses
+ ast_channel_unlock(chan);
+ return 0;
+ }
+- if (!ast_strlen_zero(name2))
++ if (!ast_strlen_zero(uniqueid2)) {
++ chan2 = ast_get_channel_by_uniqueid_locked(uniqueid2);
++ } else if (!ast_strlen_zero(name2)) {
+ chan2 = ast_get_channel_by_name_locked(name2);
++ }
+ if (chan2 && ast_check_hangup(chan2)) {
+ astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
+ ast_channel_unlock(chan);
+@@ -1708,9 +1738,9 @@ static int action_redirect(struct manses
+ }
+ res = ast_async_goto(chan, context, exten, pi);
+ if (!res) {
+- if (!ast_strlen_zero(name2)) {
++ if ((!ast_strlen_zero(name2)) || (!ast_strlen_zero(uniqueid2))){
+ if (chan2)
+- res = ast_async_goto(chan2, context, exten, pi);
++ res = ast_async_goto(chan2, context2, exten2, pi2);
+ else
+ res = -1;
+ if (!res)
+@@ -1789,15 +1819,15 @@ static void *fast_originate(void *data)
+ char requested_channel[AST_CHANNEL_NAME];
+
+ if (!ast_strlen_zero(in->app)) {
+- res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
++ res = ast_pbx_outgoing_app_uniqueid(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1, in->callingpres,
+ S_OR(in->cid_num, NULL),
+ S_OR(in->cid_name, NULL),
+- in->vars, in->account, &chan);
++ in->vars, in->account, &chan, in->uniqueid);
+ } else {
+- res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
++ res = ast_pbx_outgoing_exten_uniqueid(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1, in->callingpres,
+ S_OR(in->cid_num, NULL),
+ S_OR(in->cid_name, NULL),
+- in->vars, in->account, &chan);
++ in->vars, in->account, &chan, in->uniqueid);
+ }
+
+ if (!chan)
+@@ -1857,6 +1887,7 @@ static int action_originate(struct manse
+ const char *appdata = astman_get_header(m, "Data");
+ const char *async = astman_get_header(m, "Async");
+ const char *id = astman_get_header(m, "ActionID");
++ const char *callingpres = astman_get_header(m, "CallingPres");
+ struct ast_variable *vars = astman_get_variables(m);
+ char *tech, *data;
+ char *l = NULL, *n = NULL;
+@@ -1866,6 +1897,9 @@ static int action_originate(struct manse
+ int reason = 0;
+ char tmp[256];
+ char tmp2[256];
++ char *uniqueid;
++ int cpresi = 0;
++ char idText[256] = "";
+
+ pthread_t th;
+ pthread_attr_t attr;
+@@ -1883,6 +1917,10 @@ static int action_originate(struct manse
+ astman_send_error(s, m, "Invalid timeout\n");
+ return 0;
+ }
++ if (!ast_strlen_zero(callingpres) && (sscanf(callingpres, "%d", &cpresi) != 1)) {
++ astman_send_error(s, m, "Invalid CallingPres\n");
++ return 0;
++ }
+ ast_copy_string(tmp, name, sizeof(tmp));
+ tech = tmp;
+ data = strchr(tmp, '/');
+@@ -1902,6 +1940,7 @@ static int action_originate(struct manse
+ if (ast_strlen_zero(l))
+ l = NULL;
+ }
++ uniqueid = ast_alloc_uniqueid();
+ if (ast_true(async)) {
+ struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
+ if (!fast) {
+@@ -1921,8 +1960,10 @@ static int action_originate(struct manse
+ ast_copy_string(fast->context, context, sizeof(fast->context));
+ ast_copy_string(fast->exten, exten, sizeof(fast->exten));
+ ast_copy_string(fast->account, account, sizeof(fast->account));
++ ast_copy_string(fast->uniqueid, uniqueid, sizeof(fast->uniqueid));
+ fast->timeout = to;
+ fast->priority = pi;
++ fast->callingpres = cpresi;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ast_pthread_create(&th, &attr, fast_originate, fast)) {
+@@ -1933,19 +1974,28 @@ static int action_originate(struct manse
+ pthread_attr_destroy(&attr);
+ }
+ } else if (!ast_strlen_zero(app)) {
+- res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
++ res = ast_pbx_outgoing_app_uniqueid(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, cpresi, l, n, vars, account, NULL, uniqueid);
+ } else {
+ if (exten && context && pi)
+- res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
++ res = ast_pbx_outgoing_exten_uniqueid(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, cpresi, l, n, vars, account, NULL, uniqueid);
+ else {
+ astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
+ return 0;
+ }
+ }
+- if (!res)
+- astman_send_ack(s, m, "Originate successfully queued");
+- else
++ if (!res) {
++ if (id && !ast_strlen_zero(id)) {
++ snprintf(idText,256,"ActionID: %s\r\n",id);
++ }
++ ast_cli(s->fd, "Response: Success\r\n"
++ "%s"
++ "Message: Originate successfully queued\r\n"
++ "Uniqueid: %s\r\n"
++ "\r\n",
++ idText, uniqueid);
++ } else {
+ astman_send_error(s, m, "Originate failed");
++ }
+ return 0;
+ }
+
+--- a/include/asterisk/channel.h
++++ b/include/asterisk/channel.h
+@@ -89,6 +89,9 @@
+
+ #include "asterisk/abstract_jb.h"
+
++/* Max length of the uniqueid */
++#define AST_MAX_UNIQUEID 64
++
+ #include <unistd.h>
+ #ifdef POLLCOMPAT
+ #include "asterisk/poll-compat.h"
+@@ -1039,6 +1042,8 @@ int ast_waitfordigit_full(struct ast_cha
+ int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders);
+ int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders, int audiofd, int ctrlfd);
+
++char *ast_alloc_uniqueid(void);
++
+ /*! \brief Report DTMF on channel 0 */
+ #define AST_BRIDGE_DTMF_CHANNEL_0 (1 << 0)
+ /*! \brief Report DTMF on channel 1 */
+--- a/main/channel.c
++++ b/main/channel.c
+@@ -706,6 +706,15 @@ static const struct ast_channel_tech nul
+ .description = "Null channel (should not see this)",
+ };
+
++/*! \brief Create a uniqueid */
++char *ast_alloc_uniqueid(void) {
++ char *uniqueid;
++ uniqueid = malloc(64);
++ if (!uniqueid) return NULL;
++ snprintf(uniqueid, 63, "%s-%d-%li.%d", ast_config_AST_SYSTEM_NAME, ast_mainpid, (long)time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
++ return uniqueid;
++}
++
+ /*! \brief Create a new channel structure */
+ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...)
+ {
+--- a/include/asterisk/features.h
++++ b/include/asterisk/features.h
+@@ -47,6 +47,8 @@ struct ast_call_feature {
+ };
+
+
++extern int ast_autoanswer_login(struct ast_channel *chan, void *data);
++extern int ast_masq_autoanswer_login(struct ast_channel *rchan, void *data);
+
+ /*! \brief Park a call and read back parked location
+ * \param chan the channel to actually be parked
+--- a/res/res_features.c
++++ b/res/res_features.c
+@@ -11,6 +11,10 @@
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
++ * Copyright (C) 2004, Junghanns.NET GmbH
++ *
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+@@ -132,6 +136,20 @@ static char *descrip2 = "Park():"
+ "it already exists. In that case, execution will continue at next\n"
+ "priority.\n" ;
+
++static char *autoanswerlogin = "AutoanswerLogin";
++
++static char *synopsis3 = "Log in for autoanswer";
++
++static char *descrip3 = "AutoanswerLogin([context]|exten):"
++"Used to login to the autoanswer application for an extension.\n";
++
++static char *autoanswer = "Autoanswer";
++
++static char *synopsis4 = "Autoanswer a call";
++
++static char *descrip4 = "Autoanswer([context]|exten):"
++"Used to autoanswer a call for an extension.\n";
++
+ static struct ast_app *monitor_app = NULL;
+ static int monitor_ok = 1;
+
+@@ -150,6 +168,23 @@ struct parkeduser {
+ struct parkeduser *next;
+ };
+
++/* auto answer user */
++struct aauser {
++ struct ast_channel *chan;
++ struct timeval start;
++ /* waiting on this extension/context */
++ char exten[AST_MAX_EXTENSION];
++ char context[AST_MAX_EXTENSION];
++ int priority;
++ int notquiteyet;
++ struct aauser *next;
++};
++
++
++static struct aauser *aalot;
++AST_MUTEX_DEFINE_STATIC(autoanswer_lock);
++static pthread_t autoanswer_thread;
++
+ static struct parkeduser *parkinglot;
+
+ AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */
+@@ -405,11 +440,13 @@ static int park_call_full(struct ast_cha
+ "From: %s\r\n"
+ "Timeout: %ld\r\n"
+ "CallerID: %s\r\n"
+- "CallerIDName: %s\r\n",
++ "CallerIDName: %s\r\n"
++ "Uniqueid: %s\r\n",
+ pu->parkingexten, pu->chan->name, peer ? peer->name : "",
+ (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
+ S_OR(pu->chan->cid.cid_num, "<unknown>"),
+- S_OR(pu->chan->cid.cid_name, "<unknown>")
++ S_OR(pu->chan->cid.cid_name, "<unknown>"),
++ pu->chan->uniqueid
+ );
+
+ if (peer && adsipark && ast_adsi_available(peer)) {
+@@ -1656,11 +1693,13 @@ static void post_manager_event(const cha
+ "Exten: %s\r\n"
+ "Channel: %s\r\n"
+ "CallerID: %s\r\n"
+- "CallerIDName: %s\r\n\r\n",
++ "CallerIDName: %s\r\n"
++ "Uniqueid: %s\r\n\r\n",
+ parkingexten,
+ chan->name,
+ S_OR(chan->cid.cid_num, "<unknown>"),
+- S_OR(chan->cid.cid_name, "<unknown>")
++ S_OR(chan->cid.cid_name, "<unknown>"),
++ chan->uniqueid
+ );
+ }
+
+@@ -1928,10 +1967,12 @@ static int park_exec(struct ast_channel
+ "Channel: %s\r\n"
+ "From: %s\r\n"
+ "CallerID: %s\r\n"
+- "CallerIDName: %s\r\n",
++ "CallerIDName: %s\r\n"
++ "Uniqueid: %s\r\n",
+ pu->parkingexten, pu->chan->name, chan->name,
+ S_OR(pu->chan->cid.cid_num, "<unknown>"),
+- S_OR(pu->chan->cid.cid_name, "<unknown>")
++ S_OR(pu->chan->cid.cid_name, "<unknown>"),
++ pu->chan->uniqueid
+ );
+
+ free(pu);
+@@ -2085,15 +2126,10 @@ static struct ast_cli_entry cli_show_fea
+ handle_showfeatures, NULL,
+ NULL };
+
+-static struct ast_cli_entry cli_features[] = {
+- { { "feature", "show", NULL },
+- handle_showfeatures, "Lists configured features",
+- showfeatures_help, NULL, &cli_show_features_deprecated },
++static char showautoanswer_help[] =
++"Usage: show autoanswer\n"
++" Lists currently logged in autoanswer users.\n";
+
+- { { "show", "parkedcalls", NULL },
+- handle_parkedcalls, "Lists parked calls",
+- showparked_help },
+-};
+
+ /*! \brief Dump lot status */
+ static int manager_parking_status( struct mansession *s, const struct message *m)
+@@ -2117,12 +2153,13 @@ static int manager_parking_status( struc
+ "Timeout: %ld\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
++ "Uniqueid: %s\r\n\r\n"
+ "%s"
+ "\r\n",
+ cur->parkingnum, cur->chan->name, cur->peername,
+ (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
+ S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */
+- S_OR(cur->chan->cid.cid_name, ""),
++ S_OR(cur->chan->cid.cid_name, ""), cur->chan->uniqueid,
+ idText);
+ }
+
+@@ -2197,6 +2234,427 @@ static int manager_park(struct mansessio
+ return 0;
+ }
+
++static int handle_autoanswer(int fd, int argc, char *argv[])
++{
++ struct aauser *cur;
++
++ ast_cli(fd, "%25s %10s %15s \n", "Channel"
++ , "Extension", "Context");
++
++ ast_mutex_lock(&autoanswer_lock);
++
++ cur=aalot;
++ while(cur) {
++ ast_cli(fd, "%25s %10s %15s\n",cur->chan->name, cur->exten, cur->context);
++
++ cur = cur->next;
++ }
++
++ ast_mutex_unlock(&autoanswer_lock);
++
++ return RESULT_SUCCESS;
++}
++
++static struct ast_cli_entry cli_features[] = {
++ { { "feature", "list", NULL },
++ handle_showfeatures, "Lists configured features",
++ showfeatures_help, NULL, &cli_show_features_deprecated },
++
++ { { "show", "parkedcalls", NULL },
++ handle_parkedcalls, "Lists parked calls",
++ showparked_help },
++
++ { { "show", "autoanswer", NULL },
++ handle_autoanswer, "Lists autoanswer users",
++ showautoanswer_help },
++};
++int ast_masq_autoanswer_login(struct ast_channel *rchan, void *data)
++{
++ struct ast_channel *chan;
++ struct ast_frame *f;
++ /* Make a new, fake channel that we'll use to masquerade in the real one */
++ chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Autoanswer/%s", rchan->name);
++ if (chan) {
++ /* Let us keep track of the channel name */
++ ast_string_field_build(chan, name, "Autoanswer/%s",rchan->name);
++ /* Make formats okay */
++ chan->readformat = rchan->readformat;
++ chan->writeformat = rchan->writeformat;
++ ast_channel_masquerade(chan, rchan);
++ /* Setup the extensions and such */
++ strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
++ strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
++ chan->priority = rchan->priority;
++ /* might be dirty but we want trackable channels */
++ ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid);
++ /* Make the masq execute */
++ f = ast_read(chan);
++ if (f)
++ ast_frfree(f);
++ ast_autoanswer_login(chan, data);
++ } else {
++ ast_log(LOG_WARNING, "Unable to create aa channel\n");
++ return -1;
++ }
++ return 0;
++}
++
++static int autoanswer_login_exec(struct ast_channel *chan, void *data)
++{
++ int res=0;
++ struct ast_module_user *u;
++
++ u = ast_module_user_add(chan);
++ if (!data) {
++ ast_log(LOG_WARNING, "AutoanswerLogin requires an argument (extension number)\n");
++ return -1;
++ }
++ res = ast_masq_autoanswer_login(chan, data);
++ ast_module_user_remove(u);
++ return res;
++}
++
++int ast_autoanswer_login(struct ast_channel *chan, void *data)
++{
++ /* We put the user in the parking list, then wake up the parking thread to be sure it looks
++ after these channels too */
++ struct ast_context *con;
++ char exten[AST_MAX_EXTENSION];
++ struct aauser *pu,*pl = NULL;
++ char *s, *stringp, *aacontext, *aaexten = NULL;
++
++ s = ast_strdupa((void *) data);
++ stringp=s;
++ aacontext = strsep(&stringp, "|");
++ aaexten = strsep(&stringp, "|");
++ if (!aaexten) {
++ aaexten = aacontext;
++ aacontext = NULL;
++ }
++ if (!aaexten) {
++ ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n");
++ return -1;
++ } else {
++ if (!aacontext) {
++ aacontext = "default";
++ }
++ }
++
++ ast_mutex_lock(&autoanswer_lock);
++ pu = aalot;
++ while(pu) {
++ if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){
++ if (pl)
++ pl->next = pu->next;
++ else
++ aalot = pu->next;
++ break;
++ }
++ pl = pu;
++ pu = pu->next;
++ }
++ ast_mutex_unlock(&autoanswer_lock);
++ if (pu) {
++ ast_log(LOG_NOTICE, "Logout old Channel %s for %s@%s.\n",pu->chan->name, pu->exten, pu->context);
++ manager_event(EVENT_FLAG_CALL, "AutoanswerLogout",
++ "Channel: %s\r\n"
++ "Uniqueid: %s\r\n"
++ "Context: %s\r\n"
++ "Exten: %s\r\n"
++ ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten);
++ ast_hangup(pu->chan);
++ free(pu);
++ }
++ pu = malloc(sizeof(struct aauser));
++ if (pu) {
++ memset(pu, 0, sizeof(pu));
++ ast_mutex_lock(&autoanswer_lock);
++ chan->appl = "Autoanswer";
++ chan->data = NULL;
++
++ pu->chan = chan;
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
++
++ /* Start music on hold */
++ ast_moh_start(pu->chan, NULL, NULL);
++ gettimeofday(&pu->start, NULL);
++ strncpy(pu->exten, aaexten, sizeof(pu->exten)-1);
++ strncpy(pu->context, aacontext, sizeof(pu->exten)-1);
++ pu->next = aalot;
++ aalot = pu;
++ con = ast_context_find(aacontext);
++ if (!con) {
++ con = ast_context_create(NULL,aacontext, registrar);
++ if (!con) {
++ ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", aacontext);
++ }
++ }
++ if (con) {
++ snprintf(exten, sizeof(exten), "%s", aaexten);
++ ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)data), free, registrar);
++ }
++
++ ast_mutex_unlock(&autoanswer_lock);
++ /* Wake up the (presumably select()ing) thread */
++ pthread_kill(autoanswer_thread, SIGURG);
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "Autoanswer login from %s for %s@%s.\n", pu->chan->name, pu->exten, pu->context);
++ manager_event(EVENT_FLAG_CALL, "AutoanswerLogin",
++ "Channel: %s\r\n"
++ "Uniqueid: %s\r\n"
++ "Context: %s\r\n"
++ "Exten: %s\r\n"
++ ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten);
++
++ return 0;
++ } else {
++ ast_log(LOG_WARNING, "Out of memory\n");
++ return -1;
++ }
++ return 0;
++}
++
++static void autoanswer_reregister_extensions(void)
++{
++ struct aauser *cur;
++ struct ast_context *con;
++ char exten[AST_MAX_EXTENSION];
++ char args[AST_MAX_EXTENSION];
++
++ ast_mutex_lock(&autoanswer_lock);
++
++ cur=aalot;
++ while(cur) {
++ con = ast_context_find(cur->context);
++ if (!con) {
++ con = ast_context_create(NULL,cur->context, registrar);
++ if (!con) {
++ ast_log(LOG_ERROR, "Context '%s' does not exist and unable to create\n", cur->context);
++ }
++ }
++ if (con) {
++ snprintf(exten, sizeof(exten), "%s", cur->exten);
++ snprintf(args, sizeof(args), "%s|%s", cur->context, cur->exten);
++ ast_add_extension2(con, 1, exten, 1, NULL, NULL, autoanswer, strdup((char *)args), free, registrar);
++ }
++ cur = cur->next;
++ }
++
++ ast_mutex_unlock(&autoanswer_lock);
++}
++static void *do_autoanswer_thread(void *ignore)
++{
++ int ms, tms, max;
++ struct ast_context *con;
++ char exten[AST_MAX_EXTENSION];
++ struct aauser *pu, *pl, *pt = NULL;
++ struct timeval tv;
++ struct ast_frame *f;
++ int x;
++ fd_set rfds, efds;
++ fd_set nrfds, nefds;
++ FD_ZERO(&rfds);
++ FD_ZERO(&efds);
++ for (;;) {
++ ms = -1;
++ max = -1;
++ ast_mutex_lock(&autoanswer_lock);
++ pl = NULL;
++ pu = aalot;
++ gettimeofday(&tv, NULL);
++ FD_ZERO(&nrfds);
++ FD_ZERO(&nefds);
++ while(pu) {
++ tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
++ for (x=0;x<AST_MAX_FDS;x++) {
++ if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
++ if (FD_ISSET(pu->chan->fds[x], &efds))
++ ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
++ else
++ ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
++ pu->chan->fdno = x;
++ /* See if they need servicing */
++ f = ast_read(pu->chan);
++ if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
++ /* There's a problem, hang them up*/
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "%s logged out of autoanswer app\n", pu->chan->name);
++ manager_event(EVENT_FLAG_CALL, "AutoanswerLogout",
++ "Channel: %s\r\n"
++ "Uniqueid: %s\r\n"
++ "Context: %s\r\n"
++ "Exten: %s\r\n"
++ ,pu->chan->name, pu->chan->uniqueid, pu->context, pu->exten);
++ ast_hangup(pu->chan);
++ con = ast_context_find(pu->context);
++ if (con) {
++ snprintf(exten, sizeof(exten), "%s", pu->exten);
++ if (ast_context_remove_extension2(con, exten, 1, registrar))
++ ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
++ } else {
++ ast_log(LOG_WARNING, "Whoa, no %s context?\n", pu->exten);
++ }
++ /* And take them out of the parking lot */
++ if (pl)
++ pl->next = pu->next;
++ else
++ aalot = pu->next;
++ pt = pu;
++ pu = pu->next;
++ free(pt);
++ break;
++ } else {
++ /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
++ ast_frfree(f);
++ goto std; /* XXX Ick: jumping into an else statement??? XXX */
++ }
++ }
++ }
++ if (x >= AST_MAX_FDS) {
++std: for (x=0;x<AST_MAX_FDS;x++) {
++ /* Keep this one for next one */
++ if (pu->chan->fds[x] > -1) {
++ FD_SET(pu->chan->fds[x], &nrfds);
++ FD_SET(pu->chan->fds[x], &nefds);
++ if (pu->chan->fds[x] > max)
++ max = pu->chan->fds[x];
++ }
++ }
++ /* Keep track of our longest wait */
++ if ((tms < ms) || (ms < 0))
++ ms = tms;
++ pl = pu;
++ pu = pu->next;
++ }
++ }
++ ast_mutex_unlock(&autoanswer_lock);
++ rfds = nrfds;
++ efds = nefds;
++ tv.tv_sec = ms / 1000;
++ tv.tv_usec = (ms % 1000) * 1000;
++ /* Wait for something to happen */
++ ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
++ pthread_testcancel();
++ }
++ return NULL; /* Never reached */
++}
++
++static int autoanswer_exec(struct ast_channel *chan, void *data)
++{
++ int res=0;
++ struct ast_channel *peer=NULL;
++ struct aauser *pu, *pl=NULL;
++ struct ast_bridge_config config;
++ char *s, *stringp, *aacontext, *aaexten = NULL;
++ char datastring[80];
++ struct ast_module_user *u;
++
++
++ if (!data) {
++ ast_log(LOG_WARNING, "Autoanswer requires an argument (extension number)\n");
++ return -1;
++ }
++ s = ast_strdupa((void *) data);
++ stringp=s;
++ aacontext = strsep(&stringp, "|");
++ aaexten = strsep(&stringp, "|");
++ if (!aaexten) {
++ aaexten = aacontext;
++ aacontext = NULL;
++ }
++ if (!aaexten) {
++ ast_log(LOG_WARNING, "AutoanswerLogin requires at least an extension!\n");
++ return -1;
++ } else {
++ if (!aacontext) {
++ aacontext = "default";
++ }
++ }
++
++ u = ast_module_user_add(chan);
++ ast_mutex_lock(&autoanswer_lock);
++ pu = aalot;
++ while(pu) {
++ if ((!strncasecmp(pu->exten, aaexten, sizeof(pu->exten)-1)) && (!strncasecmp(pu->context, aacontext, sizeof(pu->context)-1))){
++ if (pl)
++ pl->next = pu->next;
++ else
++ aalot = pu->next;
++ break;
++ }
++ pl = pu;
++ pu = pu->next;
++ }
++ ast_mutex_unlock(&autoanswer_lock);
++ if (pu) {
++ peer = pu->chan;
++ free(pu);
++ pu = NULL;
++ }
++ /* JK02: it helps to answer the channel if not already up */
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
++
++ if (peer) {
++ ast_moh_stop(peer);
++ /* Play a courtesy beep in the callED channel to prefix the bridge connecting */
++ if (!ast_strlen_zero(courtesytone)) {
++ if (!ast_streamfile(peer, courtesytone, peer->language)) {
++ if (ast_waitstream(peer, "") < 0) {
++ ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
++ ast_hangup(peer);
++ return -1;
++ }
++ }
++ }
++
++ res = ast_channel_make_compatible(chan, peer);
++ if (res < 0) {
++ ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
++ ast_hangup(peer);
++ return -1;
++ }
++ /* This runs sorta backwards, since we give the incoming channel control, as if it
++ were the person called. */
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s autoanswered %s\n", peer->name, chan->name);
++ manager_event(EVENT_FLAG_CALL, "Autoanswer",
++ "Channel: %s\r\n"
++ "Uniqueid: %s\r\n"
++ "Channel2: %s\r\n"
++ "Uniqueid2: %s\r\n"
++ "Context: %s\r\n"
++ "Exten: %s\r\n"
++ ,chan->name, chan->uniqueid, peer->name, peer->uniqueid, aacontext, aaexten);
++
++
++ memset(&config,0,sizeof(struct ast_bridge_config));
++ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
++ ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
++ config.timelimit = 0;
++ config.play_warning = 0;
++ config.warning_freq = 0;
++ config.warning_sound=NULL;
++ res = ast_bridge_call(chan,peer,&config);
++
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "returning from bridge %s\n", peer->name);
++ /* relogin */
++ snprintf(datastring, sizeof(datastring) - 1, "%s|%s", aacontext, aaexten);
++ ast_autoanswer_login(peer, datastring);
++ return res;
++ } else {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Nobody logged in for autoanswer %s@%s\n", aaexten, aacontext);
++ res = -1;
++ }
++ ast_module_user_remove(u);
++ return res;
++}
++
+
+ int ast_pickup_call(struct ast_channel *chan)
+ {
+@@ -2460,6 +2918,7 @@ static int load_config(void)
+
+ static int reload(void)
+ {
++ autoanswer_reregister_extensions();
+ return load_config();
+ }
+
+@@ -2483,6 +2942,12 @@ static int load_module(void)
+ "Park a channel", mandescr_park);
+ }
+
++ ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL);
++ if (!res)
++ res |= ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3);
++ if (!res)
++ res |= ast_register_application(autoanswer, autoanswer_exec, synopsis4, descrip4);
++
+ res |= ast_devstate_prov_add("Park", metermaidstate);
+
+ return res;
+@@ -2497,6 +2962,8 @@ static int unload_module(void)
+ ast_manager_unregister("Park");
+ ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
+ ast_unregister_application(parkcall);
++ ast_unregister_application(autoanswer);
++ ast_unregister_application(autoanswerlogin);
+ ast_devstate_prov_del("Park");
+ return ast_unregister_application(parkedcall);
+ }
+--- a/include/asterisk/features.h
++++ b/include/asterisk/features.h
+@@ -72,6 +72,12 @@ int ast_park_call(struct ast_channel *ch
+ */
+ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *host, int timeout, int *extout);
+
++extern int ast_hold_call(struct ast_channel *chan, struct ast_channel *host);
++extern int ast_masq_hold_call(struct ast_channel *rchan, struct ast_channel *host);
++extern int ast_retrieve_call(struct ast_channel *chan, char *uniqueid);
++extern int ast_retrieve_call_to_death(char *uniqueid);
++extern struct ast_channel *ast_get_holded_call(char *uniqueid);
++
+ /*! \brief Determine system parking extension
+ * Returns the call parking extension for drivers that provide special
+ call parking help */
+--- a/res/res_features.c
++++ b/res/res_features.c
+@@ -66,6 +66,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ #include "asterisk/adsi.h"
+ #include "asterisk/devicestate.h"
+ #include "asterisk/monitor.h"
++#include "asterisk/indications.h"
+
+ #define DEFAULT_PARK_TIME 45000
+ #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
+@@ -84,6 +85,7 @@ enum {
+ };
+
+ static char *parkedcall = "ParkedCall";
++static char *holdedcall = "HoldedCall";
+
+ static int parkaddhints = 0; /*!< Add parking hints automatically */
+ static int parkingtime = DEFAULT_PARK_TIME; /*!< No more than 45 seconds parked before you do something with them */
+@@ -168,6 +170,22 @@ struct parkeduser {
+ struct parkeduser *next;
+ };
+
++struct holdeduser {
++ struct ast_channel *chan;
++ struct timeval start;
++ int parkingnum;
++ int cref;
++ int tei;
++ /* Where to go if our parking time expires */
++ char context[AST_MAX_EXTENSION];
++ char exten[AST_MAX_EXTENSION];
++ int priority;
++ int parkingtime;
++ char uniqueid[AST_MAX_UNIQUEID];
++ char uniqueidpeer[AST_MAX_UNIQUEID];
++ struct holdeduser *next;
++};
++
+ /* auto answer user */
+ struct aauser {
+ struct ast_channel *chan;
+@@ -187,10 +205,16 @@ static pthread_t autoanswer_thread;
+
+ static struct parkeduser *parkinglot;
+
++static struct holdeduser *holdlist;
++
+ AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */
+
++AST_MUTEX_DEFINE_STATIC(holding_lock);
++
+ static pthread_t parking_thread;
+
++static pthread_t holding_thread;
++
+ char *ast_parking_ext(void)
+ {
+ return parking_ext;
+@@ -2052,6 +2076,282 @@ static int park_exec(struct ast_channel
+ return res;
+ }
+
++int ast_hold_call(struct ast_channel *chan, struct ast_channel *peer)
++{
++ /* We put the user in the parking list, then wake up the parking thread to be sure it looks
++ after these channels too */
++ struct holdeduser *pu;
++ pu = malloc(sizeof(struct holdeduser));
++ if (pu) {
++ memset(pu, 0, sizeof(pu));
++ ast_mutex_lock(&holding_lock);
++ chan->appl = "Holded Call";
++ chan->data = NULL;
++
++ pu->chan = chan;
++ strncpy(pu->uniqueid, chan->uniqueid, sizeof(pu->uniqueid));
++ strncpy(pu->uniqueidpeer, peer->uniqueid, sizeof(pu->uniqueidpeer));
++ /* Start music on hold */
++ ast_moh_start(pu->chan, NULL, NULL);
++ gettimeofday(&pu->start, NULL);
++ pu->next = holdlist;
++ holdlist = pu;
++ ast_mutex_unlock(&holding_lock);
++ /* Wake up the (presumably select()ing) thread */
++ pthread_kill(holding_thread, SIGURG);
++
++ manager_event(EVENT_FLAG_CALL, "HoldedCall",
++ "Channel1: %s\r\n"
++ "Channel2: %s\r\n"
++ "Uniqueid1: %s\r\n"
++ "Uniqueid2: %s\r\n"
++ ,pu->chan->name, peer->name, pu->chan->uniqueid, peer->uniqueid);
++
++ } else {
++ ast_log(LOG_WARNING, "Out of memory\n");
++ return -1;
++ }
++ return 0;
++}
++
++int ast_masq_hold_call(struct ast_channel *rchan, struct ast_channel *peer)
++{
++ struct ast_channel *chan;
++ struct ast_frame *f;
++ /* Make a new, fake channel that we'll use to masquerade in the real one */
++ chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Onhold/%s",rchan->name);
++ if (chan) {
++ /* Let us keep track of the channel name */
++ ast_string_field_build(chan, name, "Onhold/%s",rchan->name);
++ /* Make formats okay */
++ chan->readformat = rchan->readformat;
++ chan->writeformat = rchan->writeformat;
++ ast_channel_masquerade(chan, rchan);
++ /* Setup the extensions and such */
++ strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
++ strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
++ chan->priority = rchan->priority;
++ /* this might be dirty, but we need to preserve the uniqueid */
++ ast_string_field_build(chan, uniqueid, "%s",rchan->uniqueid);
++ /* Make the masq execute */
++ f = ast_read(chan);
++ if (f)
++ ast_frfree(f);
++ ast_hold_call(chan, peer);
++ return -1;
++ } else {
++ ast_log(LOG_WARNING, "Unable to create holded channel\n");
++ return -1;
++ }
++ return 0;
++}
++
++int ast_retrieve_call(struct ast_channel *chan, char *uniqueid)
++{
++ int res=-1, dres=-1;
++ struct ast_channel *peer=NULL;
++ struct ast_bridge_config config;
++
++ peer = ast_get_holded_call(uniqueid);
++
++ /* JK02: it helps to answer the channel if not already up */
++ if (chan->_state != AST_STATE_UP) {
++ ast_answer(chan);
++ }
++
++ if (peer) {
++ ast_mutex_unlock(&peer->lock);
++ ast_moh_stop(peer);
++ res = ast_channel_make_compatible(chan, peer);
++ if (res < 0) {
++ ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
++ ast_hangup(peer);
++ return -1;
++ }
++ /* This runs sorta backwards, since we give the incoming channel control, as if it
++ were the person called. */
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to holded call %s\n", chan->name, peer->name);
++
++ memset(&config,0,sizeof(struct ast_bridge_config));
++ ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
++ ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
++ config.timelimit = 0;
++ config.play_warning = 0;
++ config.warning_freq = 0;
++ config.warning_sound=NULL;
++ res = ast_bridge_call(chan,peer,&config);
++
++ /* Simulate the PBX hanging up */
++ if (res != AST_PBX_NO_HANGUP_PEER)
++ ast_hangup(peer);
++ return res;
++ } else {
++ /* XXX Play a message XXX */
++ dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
++ if (!dres)
++ dres = ast_waitstream(chan, "");
++ else {
++ ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
++ dres = 0;
++ }
++ }
++ return res;
++}
++
++int ast_retrieve_call_to_death(char *uniqueid)
++{
++ int res=-1;
++ struct ast_channel *peer=NULL;
++
++ peer = ast_get_holded_call(uniqueid);
++
++ if (peer) {
++ res=0;
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name);
++ ast_mutex_unlock(&peer->lock);
++ ast_hangup(peer);
++ } else {
++ ast_log(LOG_WARNING, "Could not find channel with uniqueid %s to retrieve.\n", uniqueid);
++ }
++ return res;
++}
++
++struct ast_channel *ast_get_holded_call(char *uniqueid)
++{
++ int res=-1;
++ struct ast_channel *peer=NULL;
++ struct holdeduser *pu, *pl=NULL;
++
++ ast_mutex_lock(&holding_lock);
++ pu = holdlist;
++ while(pu) {
++ if (!strncmp(uniqueid,pu->uniqueid,sizeof(pu->uniqueid))) {
++ if (pl)
++ pl->next = pu->next;
++ else
++ holdlist = pu->next;
++ break;
++ }
++ pl = pu;
++ pu = pu->next;
++ }
++ ast_mutex_unlock(&holding_lock);
++ if (pu) {
++ peer = ast_get_channel_by_uniqueid_locked(pu->uniqueid);
++ free(pu);
++ if (peer) {
++ res=0;
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Channel %s removed from hold.\n", peer->name);
++ ast_moh_stop(peer);
++ return peer;
++ } else {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Could not find channel with uniqueid %s.\n", uniqueid);
++ return NULL;
++ }
++ } else {
++ ast_log(LOG_WARNING, "Could not find held channel with uniqueid %s to retrieve.\n", uniqueid);
++ }
++ return NULL;
++}
++
++/* this is our autmagically service thread that keeps channels onhold happy */
++static void *do_holding_thread(void *ignore)
++{
++ int ms, tms, max;
++ struct holdeduser *pu, *pl, *pt = NULL;
++ struct timeval tv;
++ struct ast_frame *f;
++ int x;
++ fd_set rfds, efds;
++ fd_set nrfds, nefds;
++ FD_ZERO(&rfds);
++ FD_ZERO(&efds);
++ for (;;) {
++ ms = -1;
++ max = -1;
++ ast_mutex_lock(&holding_lock);
++ pl = NULL;
++ pu = holdlist;
++ gettimeofday(&tv, NULL);
++ FD_ZERO(&nrfds);
++ FD_ZERO(&nefds);
++ while(pu) {
++ tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
++ for (x=0;x<AST_MAX_FDS;x++) {
++ if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
++ if (FD_ISSET(pu->chan->fds[x], &efds))
++ ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
++ else
++ ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
++ pu->chan->fdno = x;
++ /* See if they need servicing */
++ f = ast_read(pu->chan);
++ if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
++ /* There's a problem, hang them up*/
++ if (option_verbose > 1)
++ ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being onhold\n", pu->chan->name);
++ ast_hangup(pu->chan);
++ /* find the corresponding channel and hang them up too! */
++ /* but only if it is not bridged yet! */
++ /* And take them out of the parking lot */
++ if (pl)
++ pl->next = pu->next;
++ else
++ holdlist = pu->next;
++ pt = pu;
++ pu = pu->next;
++ free(pt);
++ break;
++ } else {
++ /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
++ ast_frfree(f);
++ goto std; /* XXX Ick: jumping into an else statement??? XXX */
++ }
++ }
++ }
++ if (x >= AST_MAX_FDS) {
++std: for (x=0;x<AST_MAX_FDS;x++) {
++ /* Keep this one for next one */
++ if (pu->chan->fds[x] > -1) {
++ FD_SET(pu->chan->fds[x], &nrfds);
++ FD_SET(pu->chan->fds[x], &nefds);
++ if (pu->chan->fds[x] > max)
++ max = pu->chan->fds[x];
++ }
++ }
++ /* Keep track of our longest wait */
++ if ((tms < ms) || (ms < 0))
++ ms = tms;
++ pl = pu;
++ pu = pu->next;
++ }
++ }
++ ast_mutex_unlock(&holding_lock);
++ rfds = nrfds;
++ efds = nefds;
++ tv.tv_sec = ms / 1000;
++ tv.tv_usec = (ms % 1000) * 1000;
++ /* Wait for something to happen */
++ ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
++ pthread_testcancel();
++ }
++ return NULL; /* Never reached */
++}
++
++static int retrieve_call_exec(struct ast_channel *chan, void *data) {
++ int res=0;
++ struct ast_module_user *u;
++ char *uniqueid = (char *)data;
++ u = ast_module_user_add(chan);
++ res = ast_retrieve_call(chan, uniqueid);
++ ast_module_user_remove(u);
++ return res;
++}
++
+ static int handle_showfeatures(int fd, int argc, char *argv[])
+ {
+ int i;
+@@ -2933,6 +3233,7 @@ static int load_module(void)
+ return res;
+ ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry));
+ ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
++ ast_pthread_create(&holding_thread, NULL, do_holding_thread, NULL);
+ res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
+ if (!res)
+ res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
+@@ -2942,6 +3243,7 @@ static int load_module(void)
+ "Park a channel", mandescr_park);
+ }
+
++ res |= ast_register_application(holdedcall, retrieve_call_exec, synopsis, descrip);
+ ast_pthread_create(&autoanswer_thread, NULL, do_autoanswer_thread, NULL);
+ if (!res)
+ res |= ast_register_application(autoanswerlogin, autoanswer_login_exec, synopsis3, descrip3);
+@@ -2964,6 +3266,7 @@ static int unload_module(void)
+ ast_unregister_application(parkcall);
+ ast_unregister_application(autoanswer);
+ ast_unregister_application(autoanswerlogin);
++ ast_unregister_application(holdedcall);
+ ast_devstate_prov_del("Park");
+ return ast_unregister_application(parkedcall);
+ }
+--- a/channels/chan_zap.c
++++ b/channels/chan_zap.c
+@@ -77,6 +77,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revisi
+ #ifdef HAVE_PRI
+ #include <bristuffed/libpri.h>
+ #endif
++#ifdef HAVE_GSMAT
++#include <libgsmat.h>
++#endif
+
+ #include "asterisk/lock.h"
+ #include "asterisk/channel.h"
+@@ -185,6 +188,7 @@ static const char config[] = "zapata.con
+ #define SIG_FXOGS ZT_SIG_FXOGS
+ #define SIG_FXOKS ZT_SIG_FXOKS
+ #define SIG_PRI ZT_SIG_CLEAR
++#define SIG_GSM (0x100000 | ZT_SIG_CLEAR)
+ #define SIG_SF ZT_SIG_SF
+ #define SIG_SFWINK (0x0100000 | ZT_SIG_SF)
+ #define SIG_SF_FEATD (0x0200000 | ZT_SIG_SF)
+@@ -234,6 +238,8 @@ static int matchdigittimeout = 3000;
+ /*! \brief Protect the interface list (of zt_pvt's) */
+ AST_MUTEX_DEFINE_STATIC(iflock);
+
++static char gsm_modem_pin[20];
++static char gsm_modem_exten[AST_MAX_EXTENSION];
+
+ static int ifcount = 0;
+
+@@ -251,6 +257,7 @@ static enum ast_bridge_result zt_bridge(
+
+ static int zt_sendtext(struct ast_channel *c, const char *text);
+
++static int zt_sendmessage(struct ast_channel *c, const char *dest, const char *text, int ispdu);
+
+ /*! \brief Avoid the silly zt_getevent which ignores a bunch of events */
+ static inline int zt_get_event(int fd)
+@@ -364,6 +371,19 @@ struct zt_pri {
+ int debugfd;
+ };
+
++#ifdef HAVE_GSMAT
++struct zt_gsm {
++ pthread_t master;
++ ast_mutex_t lock; /* Mutex */
++ int fd;
++ int span;
++ struct gsm_modul *modul;
++ char pin[256];
++ int available;
++ char exten[AST_MAX_EXTENSION]; /* Where to idle extra calls */
++ struct zt_pvt *pvt;
++};
++#endif
+
+ static struct zt_pri pris[NUM_SPANS];
+
+@@ -392,6 +412,7 @@ struct zt_pri;
+ #define POLARITY_REV 1
+
+
++
+ static struct zt_distRings drings;
+
+ struct distRingData {
+@@ -604,6 +625,9 @@ static struct zt_pvt {
+ int prioffset;
+ int logicalspan;
+ #endif
++#ifdef HAVE_GSMAT
++ struct zt_gsm gsm;
++#endif
+ int polarity;
+ int dsp_features;
+ char begindigit;
+@@ -710,7 +734,7 @@ static struct zt_chan_conf zt_chan_conf_
+ static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause);
+ static int zt_digit_begin(struct ast_channel *ast, char digit);
+ static int zt_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+-static int zt_sendtext(struct ast_channel *c, const char *text);
++static int zt_sendmessage(struct ast_channel *c, const char *dest, const char *text, int ispdu);
+ static int zt_call(struct ast_channel *ast, char *rdest, int timeout);
+ static int zt_hangup(struct ast_channel *ast);
+ static int zt_answer(struct ast_channel *ast);
+@@ -732,6 +756,9 @@ static const struct ast_channel_tech zap
+ .send_digit_begin = zt_digit_begin,
+ .send_digit_end = zt_digit_end,
+ .send_text = zt_sendtext,
++#if 0 /* we (Debian) disable that addition because of ABI breakage */
++ .send_message = zt_sendmessage,
++#endif
+ .call = zt_call,
+ .hangup = zt_hangup,
+ .answer = zt_answer,
+@@ -1262,6 +1289,8 @@ static char *zap_sig2str(int sig)
+ return "GR-303 with FXOKS";
+ case SIG_GR303FXSKS:
+ return "GR-303 with FXSKS";
++ case SIG_GSM:
++ return "GSM";
+ case 0:
+ return "Pseudo";
+ default:
+@@ -1683,7 +1712,7 @@ static inline int zt_confmute(struct zt_
+ {
+ int x, y, res;
+ x = muted;
+- if (p->sig == SIG_PRI) {
++ if ((p->sig == SIG_PRI) || (p->sig == SIG_GSM)) {
+ y = 1;
+ res = ioctl(p->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &y);
+ if (res)
+@@ -2098,6 +2127,25 @@ static int zt_call(struct ast_channel *a
+ p->dialdest[0] = '\0';
+ disable_dtmf_detect(p);
+ break;
++ case SIG_GSM:
++#ifdef HAVE_GSMAT
++ if (p->gsm.modul) {
++ c = strchr(dest, '/');
++ if (c)
++ c++;
++ else
++ c = dest;
++ ast_mutex_lock(&p->gsm.lock);
++ if (gsm_dial(p->gsm.modul, p->use_callingpres ? ast->cid.cid_pres : 0, c)) {
++ ast_log(LOG_WARNING, "dialing failed on channel %d\n", p->channel);
++ ast_mutex_unlock(&p->gsm.lock);
++ ast_mutex_unlock(&p->lock);
++ return -1;
++ }
++ ast_mutex_unlock(&p->gsm.lock);
++ }
++#endif
++ break;
+ default:
+ ast_log(LOG_DEBUG, "not yet implemented\n");
+ ast_mutex_unlock(&p->lock);
+@@ -2737,7 +2785,13 @@ static int zt_hangup(struct ast_channel
+ }
+ }
+ #endif
+- if (p->sig && (p->sig != SIG_PRI))
++#ifdef HAVE_GSMAT
++ if (p->gsm.modul) {
++ if (!p->alreadyhungup)
++ gsm_hangup(p->gsm.modul);
++ }
++#endif
++ if (p->sig && (p->sig != SIG_PRI) && (p->sig != SIG_GSM))
+ res = zt_set_hook(p->subs[SUB_REAL].zfd, ZT_ONHOOK);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+@@ -2914,6 +2968,13 @@ static int zt_answer(struct ast_channel
+ zt_train_ec(p);
+ break;
+ #endif
++#ifdef HAVE_GSMAT
++ case SIG_GSM:
++ if (p->gsm.modul) {
++ gsm_answer(p->gsm.modul);
++ }
++ break;
++#endif
+ case 0:
+ ast_mutex_unlock(&p->lock);
+ return 0;
+@@ -7302,6 +7363,10 @@ static int pri_create_spanmap(int span,
+
+ #endif
+
++#ifdef HAVE_GSMAT
++static void *gsm_dchannel(void *vgsm);
++#endif
++
+ static struct zt_pvt *mkintf(int channel, const struct zt_chan_conf *conf, struct zt_pri *pri, int reloading)
+ {
+ /* Make a zt_pvt structure for this interface (or CRV if "pri" is specified) */
+@@ -7530,6 +7595,37 @@ static struct zt_pvt *mkintf(int channel
+ tmp->prioffset = 0;
+ }
+ #endif
++#ifdef HAVE_GSMAT
++ if (conf->chan.sig == SIG_GSM) {
++ struct zt_bufferinfo bi;
++ ast_mutex_init(&tmp->gsm.lock);
++ strncpy(tmp->gsm.pin, gsm_modem_pin, sizeof(tmp->gsm.pin) - 1);
++ strncpy(tmp->gsm.exten, gsm_modem_exten, sizeof(tmp->gsm.exten) - 1);
++ tmp->gsm.available = 0;
++ snprintf(fn, sizeof(fn), "%d", channel + 1);
++ /* Open non-blocking */
++ tmp->gsm.fd = zt_open(fn);
++ bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
++ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
++ bi.numbufs = 16;
++ bi.bufsize = 1024;
++ if (ioctl(tmp->gsm.fd, ZT_SET_BUFINFO, &bi)) {
++ ast_log(LOG_ERROR, "Unable to set buffer info on channel '%s': %s\n", fn, strerror(errno));
++ return NULL;
++ }
++ tmp->gsm.pvt = tmp;
++ tmp->gsm.span = tmp->span;
++ tmp->gsm.modul = gsm_new(tmp->gsm.fd, 0, tmp->gsm.pin, tmp->span, tmp->channel);
++ if (ioctl(tmp->subs[SUB_REAL].zfd, ZT_AUDIOMODE, tmp->channel)) {
++ ast_log(LOG_ERROR, "Unable to set clear mode on clear channel %d: %s\n", tmp->channel, strerror(errno));
++ destroy_zt_pvt(&tmp);
++ return NULL;
++ }
++ if (ast_pthread_create(&tmp->gsm.master, NULL, gsm_dchannel, &tmp->gsm)) {
++ zt_close(tmp->gsm.fd);
++ }
++ }
++#endif
+ } else {
+ conf->chan.sig = tmp->sig;
+ conf->chan.radio = tmp->radio;
+@@ -7819,6 +7915,12 @@ static inline int available(struct zt_pv
+ return 1;
+ }
+ #endif
++#ifdef HAVE_GSMAT
++ if (p->gsm.modul) {
++ return gsm_available(p->gsm.modul);
++ }
++
++#endif
+ if (!(p->radio || (p->oprmode < 0)))
+ {
+ if (!p->sig || (p->sig == SIG_FXSLS))
+@@ -8176,6 +8278,235 @@ next:
+ return tmp;
+ }
+
++#ifdef HAVE_GSMAT
++static int zt_reset_span(int span, int sleep) {
++ int ctl;
++ int res;
++
++ ctl = open("/dev/zap/ctl", O_RDWR);
++ if (ctl < 0) {
++ ast_log(LOG_WARNING, "Unable to open /dev/zap/ctl: %s\n", strerror(errno));
++ return -1;
++ }
++ ast_verbose(VERBOSE_PREFIX_2 "Shutting down span %d. Please wait...\n", span);
++ res = ioctl(ctl, ZT_SHUTDOWN, &span);
++ if (res) {
++ ast_log(LOG_WARNING, "error shutting down span %d\n", span);
++ return -1;
++ }
++ usleep(sleep * 1000);
++ ast_verbose(VERBOSE_PREFIX_2 "Starting up span %d. Please wait...\n", span);
++ res = ioctl(ctl, ZT_STARTUP, &span);
++ if (res) {
++ ast_log(LOG_WARNING, "error starting up span %d\n", span);
++ return -1;
++ }
++ ast_verbose(VERBOSE_PREFIX_2 "Reset of span %d completed.\n", span);
++ return 0;
++}
++
++
++static void handle_gsm_event(struct zt_gsm *gsm, gsm_event *e)
++{
++ struct ast_channel *c = NULL;
++ int law = ZT_LAW_ALAW;
++ int res = 0;
++
++ switch(e->e) {
++ case GSM_EVENT_DCHAN_UP:
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "GSM Span %d registered to network!\n", gsm->span);
++ gsm->available = 1;
++ break;
++ case GSM_EVENT_DCHAN_DOWN:
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "GSM Span %d unregistered from network!\n", gsm->span);
++ gsm->available = 0;
++/* ast_mutex_lock(&gsm->pvt->lock);
++ gsm->pvt->alreadyhungup = 1;
++ if (gsm->pvt->owner) {
++ gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ }
++ ast_mutex_unlock(&gsm->pvt->lock); */
++ break;
++ case GSM_EVENT_RING:
++ ast_mutex_lock(&gsm->pvt->lock);
++ if (!ast_strlen_zero(e->ring.callingnum)) {
++ strncpy(gsm->pvt->cid_num, e->ring.callingnum, sizeof(gsm->pvt->cid_num) - 1);
++ } else {
++ strncpy(gsm->pvt->cid_name, "CID withheld", sizeof(gsm->pvt->cid_name));
++ }
++ if (!ast_strlen_zero(gsm->exten)) {
++ strncpy(gsm->pvt->exten, gsm->exten, sizeof(gsm->pvt->exten) - 1);
++ } else {
++ gsm->pvt->exten[0] = 's';
++ gsm->pvt->exten[1] = '\0';
++ }
++ c = zt_new(gsm->pvt, AST_STATE_RING, 1, SUB_REAL, ZT_LAW_ALAW, AST_TRANS_CAP_SPEECH);
++ if (c) {
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Ring on channel %d (from %s to %s)\n", e->ring.channel, e->ring.callingnum, gsm->exten);
++ gsm->pvt->owner = c;
++ if (ioctl(gsm->pvt->subs[SUB_REAL].zfd, ZT_AUDIOMODE, &law) == -1)
++ ast_log(LOG_WARNING, "Unable to set audio mode on channel %d to %d\n", gsm->pvt->channel, law);
++ res = zt_setlaw(gsm->pvt->subs[SUB_REAL].zfd, law);
++ res = set_actual_gain(gsm->pvt->subs[SUB_REAL].zfd, 0, gsm->pvt->rxgain, gsm->pvt->txgain, law);
++ if (res < 0) {
++ ast_log(LOG_WARNING, "Unable to set gains on channel %d\n", gsm->pvt->channel);
++// } else {
++// ast_log(LOG_NOTICE, "tx gain %f rx gain %f law %d pvt->law %d\n", gsm->pvt->txgain, gsm->pvt->rxgain, law, gsm->pvt->law);
++ }
++ }
++ ast_mutex_unlock(&gsm->pvt->lock);
++ break;
++ case GSM_EVENT_HANGUP:
++ ast_verbose(VERBOSE_PREFIX_3 "Got hang up on channel %d\n", e->hangup.channel);
++ ast_mutex_lock(&gsm->pvt->lock);
++ gsm->pvt->alreadyhungup = 1;
++ if (gsm->pvt->owner) {
++ gsm->pvt->owner->hangupcause = e->hangup.cause;
++ gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ }
++ ast_mutex_unlock(&gsm->pvt->lock);
++ break;
++ case GSM_EVENT_ERROR:
++ ast_log(LOG_WARNING, "Got error on channel\n");
++ ast_mutex_lock(&gsm->pvt->lock);
++ gsm->pvt->alreadyhungup = 1;
++ if (gsm->pvt->owner) {
++ gsm->pvt->owner->hangupcause = e->error.cause;
++ gsm->pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV;
++ }
++ ast_mutex_unlock(&gsm->pvt->lock);
++ if (e->error.hard) {
++// gsm_poweroff(gsm->modul);
++ zt_reset_span(gsm->span, 8000);
++// gsm_restart(gsm->modul, 10000);
++ } else {
++// gsm_poweroff(gsm->modul);
++ zt_reset_span(gsm->span, 8000);
++// gsm_restart(gsm->modul, 10000);
++ }
++ break;
++ case GSM_EVENT_ALERTING:
++ ast_mutex_lock(&gsm->pvt->lock);
++ gsm->pvt->subs[SUB_REAL].needringing =1;
++ ast_mutex_unlock(&gsm->pvt->lock);
++ break;
++ case GSM_EVENT_ANSWER:
++ ast_mutex_lock(&gsm->pvt->lock);
++ gsm->pvt->dialing = 0;
++ gsm->pvt->subs[SUB_REAL].needanswer =1;
++ gsm->pvt->ignoredtmf = 0;
++ ast_mutex_unlock(&gsm->pvt->lock);
++ break;
++ case GSM_EVENT_PIN_REQUIRED:
++ gsm_send_pin(gsm->modul, gsm->pin);
++ break;
++ case GSM_EVENT_SM_RECEIVED:
++ ast_verbose(VERBOSE_PREFIX_3 "SMS from %s received on span %d. (Text: %s) (PDU: %s)\n", e->sm_received.sender, gsm->span, e->sm_received.text, e->sm_received.pdu);
++ manager_event(EVENT_FLAG_CALL, "Message received",
++ "Span: %d\r\n"
++ "Sender: %s\r\n"
++ "SMSC: %s\r\n"
++ "Length: %d\r\n"
++ "Text: %s\r\n"
++ "PDU: %s\r\n",
++ gsm->span,
++ e->sm_received.sender,
++ e->sm_received.smsc,
++ e->sm_received.len,
++ e->sm_received.text,
++ e->sm_received.pdu);
++ break;
++ default:
++ ast_log(LOG_WARNING,"!! Unknown GSM event %d !!\n", e->e);
++ }
++}
++
++static void *gsm_dchannel(void *vgsm)
++{
++ struct zt_gsm *gsm = vgsm;
++ gsm_event *e;
++ struct timeval tv = {0,0}, *next;
++ fd_set rfds, efds;
++ int res,x;
++
++ if (!gsm) return NULL;
++
++ if (!gsm->modul) {
++ fprintf(stderr, "No gsm_mod\n");
++ return NULL;
++ }
++ gsm_set_debug(gsm->modul, GSM_DEBUG_NONE);
++ for (;;) {
++
++ /* Run the D-Channel */
++ FD_ZERO(&rfds);
++ FD_ZERO(&efds);
++ FD_SET(gsm->fd, &rfds);
++ FD_SET(gsm->fd, &efds);
++
++ if ((next = gsm_schedule_next(gsm->modul))) {
++ gettimeofday(&tv, NULL);
++ tv.tv_sec = next->tv_sec - tv.tv_sec;
++ tv.tv_usec = next->tv_usec - tv.tv_usec;
++ if (tv.tv_usec < 0) {
++ tv.tv_usec += 1000000;
++ tv.tv_sec -= 1;
++ }
++ if (tv.tv_sec < 0) {
++ tv.tv_sec = 0;
++ tv.tv_usec = 0;
++ }
++ }
++ res = select(gsm->fd + 1, &rfds, NULL, &efds, next ? &tv : NULL);
++ e = NULL;
++
++ ast_mutex_lock(&gsm->lock);
++ if (!res) {
++ e = gsm_schedule_run(gsm->modul);
++ } else if (res > 0) {
++ e = gsm_check_event(gsm->modul, 1);
++ } else if (errno == ELAST) {
++ res = ioctl(gsm->fd, ZT_GETEVENT, &x);
++ printf("Got Zaptel event: %d\n", x);
++ } else if (errno != EINTR)
++ fprintf(stderr, "Error (%d) on select: %s\n", ELAST, strerror(errno));
++
++ if (!e) {
++ e = gsm_check_event(gsm->modul, 0);
++ }
++
++ if (e) {
++ handle_gsm_event(gsm, e);
++ }
++ ast_mutex_unlock(&gsm->lock);
++
++ res = ioctl(gsm->fd, ZT_GETEVENT, &x);
++
++ if (!res && x) {
++ switch (x) {
++ case ZT_EVENT_NOALARM:
++ ast_log(LOG_NOTICE, "Alarm cleared on span %d\n", gsm->span);
++ usleep(1000);
++ gsm_restart(gsm->modul, 10000);
++ break;
++ case ZT_EVENT_ALARM:
++ ast_log(LOG_NOTICE, "Alarm detected on span %d\n", gsm->span);
++ break;
++ default:
++ fprintf(stderr, "Got event on GSM interface: %d\n", x);
++ }
++ }
++
++
++ }
++ return NULL;
++}
++
++#endif
++
+ #ifdef HAVE_PRI
+ static struct zt_pvt *pri_find_crv(struct zt_pri *pri, int crv)
+ {
+@@ -8450,6 +8781,18 @@ static void zt_pri_error(char *s, int sp
+ ast_log(LOG_WARNING, "%d %s", span, s);
+ }
+
++#ifdef HAVE_GSMAT
++static void zt_gsm_message(char *s, int channel)
++{
++ ast_verbose("GSM %d: %s", channel, s);
++}
++
++static void zt_gsm_error(char *s, int channel)
++{
++ ast_log(LOG_WARNING, "GSM %d: %s", channel, s);
++}
++#endif
++
+ static int pri_check_restart(struct zt_pri *pri)
+ {
+ if ((pri->nodetype != PRI_NETWORK) && (pri->nodetype != PRI_CPE)) {
+@@ -10868,6 +11211,243 @@ static int app_zapInband(struct ast_chan
+
+ #endif /* HAVE_PRI */
+
++#ifdef HAVE_GSMAT
++static int handle_zap_reset_span(int fd, int argc, char *argv[])
++{
++ int span;
++ int sleep = 5000;
++ if (argc < 4)
++ return RESULT_SHOWUSAGE;
++ span = atoi(argv[3]);
++ if ((span < 1) || (span > NUM_SPANS)) {
++ ast_cli(fd, "Invalid span '%s'. Should be a number from %d to %d\n", argv[3], 1, NUM_SPANS);
++ return RESULT_SUCCESS;
++ }
++ if (zt_reset_span(span, sleep)) {
++ return RESULT_FAILURE;
++ }
++ return RESULT_SUCCESS;
++}
++
++static int handle_gsm_debug_helper(int fd, int channel, int debug)
++{
++/* gsm debug channel <channel> */
++ struct zt_pvt *pvt = NULL;
++ if (channel < 1) {
++ ast_cli(fd, "Invalid channel %d. Should be a number.\n", channel);
++ return RESULT_SUCCESS;
++ }
++ pvt = iflist;
++ while (pvt) {
++ if (pvt->channel == channel) {
++ ast_mutex_lock(&pvt->lock);
++ gsm_set_debug(pvt->gsm.modul, debug);
++ ast_mutex_unlock(&pvt->lock);
++ ast_cli(fd, "%s debugging on channel %d\n", debug ? "Enabled":"Disabled", channel);
++ return RESULT_SUCCESS;
++ }
++ pvt = pvt->next;
++ }
++
++ ast_cli(fd, "No GSM running on channel %d\n", channel);
++ return RESULT_SUCCESS;
++}
++
++
++
++static int handle_gsm_debug(int fd, int argc, char *argv[])
++{
++/* gsm debug channel <channel> */
++ int channel;
++ if (argc < 4) {
++ return RESULT_SHOWUSAGE;
++ }
++ channel = atoi(argv[3]);
++ return handle_gsm_debug_helper(fd, channel, GSM_DEBUG_AT);
++}
++
++static int handle_gsm_no_debug(int fd, int argc, char *argv[])
++{
++/* gsm no debug channel <channel> */
++ int channel;
++ if (argc < 5) {
++ return RESULT_SHOWUSAGE;
++ }
++ channel = atoi(argv[4]);
++ return handle_gsm_debug_helper(fd, channel, GSM_DEBUG_NONE);
++}
++
++static char zap_reset_help[] =
++ "Usage: zap reset span <span>\n"
++ " Reset/Restart a zaptel span\n";
++
++static char gsm_debug_help[] =
++ "Usage: gsm debug channel <channel>\n"
++ " Enables debugging on a given GSM channel\n";
++
++static char gsm_no_debug_help[] =
++ "Usage: gsm no debug channel <channel>\n"
++ " Disables debugging on a given GSM channel\n";
++
++static struct ast_cli_entry zap_gsm_cli[] = {
++ { { "zap", "reset", "span", NULL }, handle_zap_reset_span,
++ "Restart a zaptel span", zap_reset_help, complete_span_4 },
++ { { "gsm", "debug", "channel", NULL }, handle_gsm_debug,
++ "Enables GSM debugging on a channel", gsm_debug_help },
++ { { "gsm", "no", "debug", "channel", NULL }, handle_gsm_no_debug,
++ "Disables GSM debugging on a channel", gsm_no_debug_help},
++};
++
++
++
++static char gsm_send_pdu_help[] =
++ "Usage: gsm send pdu <channel> <pdu>\n"
++ " Sends a PDU on a GSM channel\n";
++
++
++
++static int handle_gsm_send_pdu(int fd, int argc, char *argv[])
++{
++/* gsm send sms <channel> <destination> <message> */
++ int channel;
++ struct zt_pvt *pvt = NULL;
++ if (argc < 5) {
++ return RESULT_SHOWUSAGE;
++ }
++ channel = atoi(argv[3]);
++ if (channel < 1) {
++ ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]);
++ return RESULT_SUCCESS;
++ }
++ pvt = iflist;
++ while (pvt) {
++ if (pvt->channel == channel) {
++ if (pvt->owner) {
++ ast_cli(fd, "Channel in use.\n");
++ return RESULT_FAILURE;
++ } else {
++ ast_mutex_lock(&pvt->lock);
++ gsm_sms_send_pdu(pvt->gsm.modul, argv[4]);
++ ast_mutex_unlock(&pvt->lock);
++ return RESULT_SUCCESS;
++ }
++ }
++ pvt = pvt->next;
++ }
++
++ return RESULT_SUCCESS;
++}
++
++static struct ast_cli_entry gsm_send_pdu = {
++ { "gsm", "send", "pdu", NULL }, handle_gsm_send_pdu, "Sends a SM on a GSM channel", gsm_send_pdu_help, complete_span_4 };
++
++
++static char gsm_send_sms_help[] =
++ "Usage: gsm send sms <channel> <destination> <message>\n"
++ " Sends a SM on a GSM channel\n";
++
++
++static int handle_gsm_send_sms(int fd, int argc, char *argv[])
++{
++/* gsm send sms <channel> <destination> <message> */
++ int channel;
++ struct zt_pvt *pvt = NULL;
++ if (argc < 6) {
++ return RESULT_SHOWUSAGE;
++ }
++ channel = atoi(argv[3]);
++ if (channel < 1) {
++ ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]);
++ return RESULT_SUCCESS;
++ }
++ pvt = iflist;
++ while (pvt) {
++ if (pvt->channel == channel) {
++ if (pvt->owner) {
++ ast_cli(fd, "Channel in use.\n");
++ return RESULT_FAILURE;
++ } else {
++ ast_mutex_lock(&pvt->lock);
++ gsm_sms_send_text(pvt->gsm.modul, argv[4], argv[5]);
++ ast_mutex_unlock(&pvt->lock);
++ return RESULT_SUCCESS;
++ }
++ }
++ pvt = pvt->next;
++ }
++
++ return RESULT_SUCCESS;
++}
++
++static int zt_gsm_sendtext(struct ast_channel *chan, const char * dest, const char *text, int ispdu) {
++ struct zt_pvt *pvt = NULL;
++ char *c = NULL;
++ pvt = chan->tech_pvt;
++
++ if (!pvt) return -1;
++
++ /* parse dialstring */
++ c = strrchr(dest, '/');
++ if (c)
++ c++;
++ else
++ c = (char *)dest;
++
++ ast_mutex_lock(&pvt->lock);
++ if (ispdu) {
++ gsm_sms_send_pdu(pvt->gsm.modul, (char *)text);
++ } else {
++ gsm_sms_send_text(pvt->gsm.modul, c, (char *)text);
++ }
++ ast_mutex_unlock(&pvt->lock);
++ gsm_wait(pvt->gsm.modul);
++ return 0;
++}
++
++static struct ast_cli_entry gsm_send_sms = {
++ { "gsm", "send", "sms", NULL }, handle_gsm_send_sms, "Sends a SM on a GSM channel", gsm_send_sms_help, complete_span_4 };
++
++static char gsm_show_status_help[] =
++ "Usage: gsm show status <channel>>\n"
++ " Displays status information about the GSM channel.\n";
++
++
++static int handle_gsm_show_status(int fd, int argc, char *argv[])
++{
++ int channel;
++ struct zt_pvt *pvt = NULL;
++ if (argc < 4) {
++ return RESULT_SHOWUSAGE;
++ }
++ channel = atoi(argv[3]);
++ if (channel < 1) {
++ ast_cli(fd, "Invalid channel %s. Should be a number.\n", argv[3]);
++ return RESULT_SUCCESS;
++ }
++ pvt = iflist;
++ while (pvt) {
++ if (pvt->channel == channel) {
++ if (pvt->owner) {
++ ast_cli(fd, "Channel in use.\n");
++ return RESULT_FAILURE;
++ } else {
++ ast_mutex_lock(&pvt->lock);
++ gsm_request_status(pvt->gsm.modul);
++ ast_mutex_unlock(&pvt->lock);
++ return RESULT_SUCCESS;
++ }
++ }
++ pvt = pvt->next;
++ }
++
++ return RESULT_SUCCESS;
++}
++
++static struct ast_cli_entry gsm_show_status = {
++ { "gsm", "show", "status", NULL }, handle_gsm_show_status, "Displays status information about the GSM channel.", gsm_show_status_help, complete_span_4 };
++
++#endif /* HAVE_GSMAT */
++
+ static int app_zapEC(struct ast_channel *chan, void *data)
+ {
+ int res=-1;
+@@ -11489,6 +12069,12 @@ static int __unload_module(void)
+ ast_unregister_application(zapCD_app);
+ ast_unregister_application(zapInband_app);
+ #endif
++#ifdef HAVE_GSMAT
++ ast_cli_unregister_multiple(zap_gsm_cli, sizeof(zap_gsm_cli) / sizeof(zap_gsm_cli[0]));
++ ast_cli_unregister(&gsm_send_sms);
++ ast_cli_unregister(&gsm_send_pdu);
++ ast_cli_unregister(&gsm_show_status);
++#endif
+ ast_cli_unregister_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
+ ast_unregister_application(zapEC_app);
+ ast_manager_unregister( "ZapDialOffhook" );
+@@ -12009,6 +12595,11 @@ static int process_zap(struct zt_chan_co
+ confp->chan.radio = 0;
+ confp->pri.nodetype = BRI_CPE;
+ #endif
++#ifdef HAVE_GSMAT
++ } else if (!strcasecmp(v->value, "gsm")) {
++ confp->chan.sig = SIG_GSM;
++ confp->chan.radio = 0;
++#endif
+ } else {
+ ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
+ }
+@@ -12151,6 +12742,10 @@ static int process_zap(struct zt_chan_co
+ ast_copy_string(confp->pri.nocid, v->value, sizeof(confp->pri.nocid));
+ } else if (!strcasecmp(v->name, "withheldcid")) {
+ ast_copy_string(confp->pri.withheldcid, v->value, sizeof(confp->pri.withheldcid));
++ } else if (!strcasecmp(v->name, "pin")) {
++ ast_copy_string(gsm_modem_pin, v->value, sizeof(gsm_modem_pin) - 1);
++ } else if (!strcasecmp(v->name, "exten")) {
++ ast_copy_string(gsm_modem_exten, v->value, sizeof(gsm_modem_exten) - 1);
+ } else if (!strcasecmp(v->name, "resetinterval")) {
+ if (!strcasecmp(v->value, "never"))
+ confp->pri.resetinterval = -1;
+@@ -12506,6 +13101,10 @@ static int load_module(void)
+ ast_register_application(zap_send_keypad_facility_app, zap_send_keypad_facility_exec,
+ zap_send_keypad_facility_synopsis, zap_send_keypad_facility_descrip);
+ #endif
++#ifdef HAVE_GSMAT
++ gsm_set_error(zt_gsm_error);
++ gsm_set_message(zt_gsm_message);
++#endif
+ res = setup_zap(0);
+ /* Make sure we can register our Zap channel type */
+ if (res)
+@@ -12524,6 +13123,12 @@ static int load_module(void)
+ #endif
+ ast_register_application(zapEC_app, app_zapEC, zapEC_synopsis, zapEC_tdesc);
+ ast_cli_register_multiple(zap_cli, sizeof(zap_cli) / sizeof(struct ast_cli_entry));
++#ifdef HAVE_GSMAT
++ ast_cli_register(&gsm_send_sms);
++ ast_cli_register(&gsm_send_pdu);
++ ast_cli_register(&gsm_show_status);
++ ast_cli_register_multiple(zap_gsm_cli, sizeof(zap_gsm_cli) / sizeof(zap_gsm_cli[0]));
++#endif
+
+ memset(round_robin, 0, sizeof(round_robin));
+ ast_manager_register( "ZapTransfer", 0, action_transfer, "Transfer Zap Channel" );
+@@ -12537,7 +13142,66 @@ static int load_module(void)
+ return res;
+ }
+
+-static int zt_sendtext(struct ast_channel *c, const char *text)
++#ifdef HAVE_PRI
++static int zt_tdd_sendtext(struct ast_channel *c, const char *text);
++
++static int zt_pri_sendtext(struct ast_channel *c, const char *text) {
++ struct zt_pvt *p = c->tech_pvt;
++ if (!p) return -1;
++ if (!p->pri) return -1;
++ if (strlen(text)) {
++ if (p->pri) {
++ if (!pri_grab(p, p->pri)) {
++ // ast_log(LOG_NOTICE, "Sending Display IE '%s'\n", text);
++ pri_information_display(p->pri->pri,p->call,(char *)text);
++ pri_rel(p->pri);
++ } else ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
++ }
++ }
++ return 0;
++}
++#endif
++
++static int zt_sendtext(struct ast_channel *c, const char *text) {
++ struct zt_pvt *p = c->tech_pvt;
++ if (!p) return -1;
++ if (p->sig == SIG_PRI) {
++#ifdef HAVE_PRI
++ return zt_pri_sendtext(c, text);
++#endif
++ } else if (p->sig == SIG_GSM) {
++ } else {
++ return zt_tdd_sendtext(c, text);
++ }
++ return -1;
++}
++
++static int zt_sendmessage(struct ast_channel *c, const char *dest, const char *text, int ispdu) {
++struct zt_pvt *p = c->tech_pvt;
++ if (!p) return -1;
++ if (p->sig == SIG_PRI) {
++#ifdef HAVE_PRI
++ if (ispdu) {
++ ast_log(LOG_WARNING, "Dont know how to send PDU on ZAP ISDN channel\n");
++ return -1;
++ }
++ return zt_pri_sendtext(c, text);
++#endif
++ } else if (p->sig == SIG_GSM) {
++#ifdef HAVE_GSMAT
++ return zt_gsm_sendtext(c, dest, text, ispdu);
++#endif
++ } else {
++ if (ispdu) {
++ ast_log(LOG_WARNING, "Dont know how to send PDU on ZAP channel\n");
++ return -1;
++ }
++ return zt_tdd_sendtext(c, text);
++ }
++ return -1;
++}
++
++static int zt_tdd_sendtext(struct ast_channel *c, const char *text)
+ {
+ #define END_SILENCE_LEN 400
+ #define HEADER_MS 50
+--- a/configure.ac
++++ b/configure.ac
+@@ -178,6 +178,7 @@ AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capa
+ AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
+ AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls])
+ AST_EXT_LIB_SETUP([GSM], [GSM], [gsm], [, or 'internal'])
++AST_EXT_LIB_SETUP([GSMAT], [GSMAT], [GSM AT command signalling], [gsmat])
+ AST_EXT_LIB_SETUP([IKSEMEL], [Iksemel Jabber Library], [iksemel])
+ AST_EXT_LIB_SETUP([IMAP_TK], [UW IMAP Toolkit], [imap])
+ AST_EXT_LIB_SETUP([ISDNNET], [ISDN4Linux Library], [isdnnet])
+--- a/configure
++++ b/configure
+@@ -26600,6 +26600,188 @@ echo "$as_me: *** without explicitly spe
+ fi
+ fi
+
++{ echo "$as_me:$LINENO: checking for ${GSMAT_DIR}/include/libgsmat.h" >&5
++echo $ECHO_N "checking for ${GSMAT_DIR}/include/libgsmat.h... $ECHO_C" >&6; }
++if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
++ echo $ECHO_N "(cached) $ECHO_C" >&6
++else
++ eval "$as_ac_Header=\$ac_header_preproc"
++fi
++ac_res=`eval echo '${'$as_ac_Header'}'`
++ { echo "$as_me:$LINENO: result: $ac_res" >&5
++echo "${ECHO_T}$ac_res" >&6; }
++
++if test `eval echo '${'$as_ac_Header'}'` = yes; then
++ GSMAT_HEADER_FOUND=1
++else
++ GSMAT_HEADER_FOUND=0
++fi
++
++
++
++ if test "${GSMAT_HEADER_FOUND}" = "yes"; then
++ GSMAT_LIB="-lgsmat "
++ GSMAT_HEADER_FOUND="1"
++ if test "x${GSMAT_DIR}" != "x"; then
++ GSMAT_LIB="${pbxlibdir} ${GSMAT_LIB}"
++ GSMAT_INCLUDE="-I${GSMAT_DIR}/include"
++ fi
++ CPPFLAGS="${saved_cppflags}"
++ else
++ if test "xlibgsmat.h" != "x" ; then
++ if test "${ac_cv_header_libpri_h+set}" = set; then
++ { echo "$as_me:$LINENO: checking for libgsmat.h" >&5
++echo $ECHO_N "checking for libgsmat.h... $ECHO_C" >&6; }
++if test "${ac_cv_header_libgsmat_h+set}" = set; then
++ echo $ECHO_N "(cached) $ECHO_C" >&6
++fi
++{ echo "$as_me:$LINENO: result: $ac_cv_header_libgsmat_h" >&5
++echo "${ECHO_T}$ac_cv_header_libgsmat_h" >&6; }
++else
++ # Is the header compilable?
++{ echo "$as_me:$LINENO: checking libgsmat.h usability" >&5
++echo $ECHO_N "checking libgsmat.h usability... $ECHO_C" >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h. */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h. */
++$ac_includes_default
++#include <libgsmat.h>
++_ACEOF
++rm -f conftest.$ac_objext
++if { (ac_try="$ac_compile"
++case "(($ac_try" in
++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++ *) ac_try_echo=$ac_try;;
++esac
++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
++ (eval "$ac_compile") 2>conftest.er1
++ ac_status=$?
++ grep -v '^ *+' conftest.er1 >conftest.err
++ rm -f conftest.er1
++ cat conftest.err >&5
++ echo "$as_me:$LINENO: \$? = $ac_status" >&5
++ (exit $ac_status); } && {
++ test -z "$ac_c_werror_flag" ||
++ test ! -s conftest.err
++ } && test -s conftest.$ac_objext; then
++ ac_header_compiler=yes
++else
++ echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++ ac_header_compiler=no
++fi
++
++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
++{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
++echo "${ECHO_T}$ac_header_compiler" >&6; }
++
++# Is the header present?
++{ echo "$as_me:$LINENO: checking libgsmat.h presence" >&5
++echo $ECHO_N "checking libgsmat.h presence... $ECHO_C" >&6; }
++cat >conftest.$ac_ext <<_ACEOF
++/* confdefs.h. */
++_ACEOF
++cat confdefs.h >>conftest.$ac_ext
++cat >>conftest.$ac_ext <<_ACEOF
++/* end confdefs.h. */
++#include <libgsmat.h>
++_ACEOF
++if { (ac_try="$ac_cpp conftest.$ac_ext"
++case "(($ac_try" in
++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
++ *) ac_try_echo=$ac_try;;
++esac
++eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
++ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
++ ac_status=$?
++ grep -v '^ *+' conftest.er1 >conftest.err
++ rm -f conftest.er1
++ cat conftest.err >&5
++ echo "$as_me:$LINENO: \$? = $ac_status" >&5
++ (exit $ac_status); } >/dev/null && {
++ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
++ test ! -s conftest.err
++ }; then
++ ac_header_preproc=yes
++else
++ echo "$as_me: failed program was:" >&5
++sed 's/^/| /' conftest.$ac_ext >&5
++
++ ac_header_preproc=no
++fi
++
++rm -f conftest.err conftest.$ac_ext
++{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
++echo "${ECHO_T}$ac_header_preproc" >&6; }
++
++# So? What about this header?
++case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
++ yes:no: )
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: accepted by the compiler, rejected by the preprocessor!" >&5
++echo "$as_me: WARNING: libgsmat.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: proceeding with the compiler's result" >&5
++echo "$as_me: WARNING: libgsmat.h: proceeding with the compiler's result" >&2;}
++ ac_header_preproc=yes
++ ;;
++ no:yes:* )
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: present but cannot be compiled" >&5
++echo "$as_me: WARNING: libgsmat.h: present but cannot be compiled" >&2;}
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: check for missing prerequisite headers?" >&5
++echo "$as_me: WARNING: libgsmat.h: check for missing prerequisite headers?" >&2;}
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: see the Autoconf documentation" >&5
++echo "$as_me: WARNING: libgsmat.h: see the Autoconf documentation" >&2;}
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: section \"Present But Cannot Be Compiled\"" >&5
++echo "$as_me: WARNING: libgsmat.h: section \"Present But Cannot Be Compiled\"" >&2;}
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: proceeding with the preprocessor's result" >&5
++echo "$as_me: WARNING: libgsmat.h: proceeding with the preprocessor's result" >&2;}
++ { echo "$as_me:$LINENO: WARNING: libgsmat.h: in the future, the compiler will take precedence" >&5
++echo "$as_me: WARNING: libgsmat.h: in the future, the compiler will take precedence" >&2;}
++
++ ;;
++esac
++{ echo "$as_me:$LINENO: checking for libgsmat.h" >&5
++echo $ECHO_N "checking for libgsmat.h... $ECHO_C" >&6; }
++if test "${ac_cv_header_libgsmat_h+set}" = set; then
++ echo $ECHO_N "(cached) $ECHO_C" >&6
++else
++ ac_cv_header_libgsmat_h=$ac_header_preproc
++fi
++{ echo "$as_me:$LINENO: result: $ac_cv_header_libgsmat_h" >&5
++echo "${ECHO_T}$ac_cv_header_libgsmat_h" >&6; }
++
++fi
++
++
++ fi
++ fi
++ if test "x${GSMAT_HEADER_FOUND}" = "x0" ; then
++ if test -n "${GSMAT_MANDATORY}" ;
++ then
++ { echo "$as_me:$LINENO: ***" >&5
++echo "$as_me: ***" >&6;}
++ { echo "$as_me:$LINENO: *** It appears that you do not have the GSMAT development package installed." >&5
++echo "$as_me: *** It appears that you do not have the GSMAT development package installed." >&6;}
++ { echo "$as_me:$LINENO: *** Please install it to include ${GSMAT_DESCRIP} support, or re-run configure" >&5
++echo "$as_me: *** Please install it to include ${GSMAT_DESCRIP} support, or re-run configure" >&6;}
++ { echo "$as_me:$LINENO: *** without explicitly specifying --with-${GSMAT_OPTION}" >&5
++echo "$as_me: *** without explicitly specifying --with-${GSMAT_OPTION}" >&6;}
++ exit 1
++ fi
++ GSMAT_LIB=""
++ GSMAT_INCLUDE=""
++ PBX_GSMAT=0
++ else
++ PBX_GSMAT=1
++
++cat >>confdefs.h <<_ACEOF
++#define HAVE_GSMAT 1
++_ACEOF
++
++fi
+
+ if test "${USE_PWLIB}" != "no"; then
+ if test -n "${PWLIB_DIR}"; then
--- /dev/null
+diff -urN asterisk-1.4.0-beta3-o-o/build_tools/menuselect-deps.in asterisk-1.4.0-beta3/build_tools/menuselect-deps.in
+--- asterisk-1.4.0-beta3-o-o/build_tools/menuselect-deps.in 2006-09-19 11:07:22.000000000 -0600
++++ asterisk-1.4.0-beta3/build_tools/menuselect-deps.in 2006-11-06 12:45:04.000000000 -0700
+@@ -1,4 +1,5 @@
+ ASOUND=@PBX_ALSA@
++BLUETOOTH=@PBX_BLUETOOTH@
+ CURL=@PBX_CURL@
+ FREETDS=@PBX_FREETDS@
+ GSM=@PBX_GSM@
+diff -urN asterisk-1.4.0-beta3-o-o/channels/chan_bluetooth.c asterisk-1.4.0-beta3/channels/chan_bluetooth.c
+--- asterisk-1.4.0-beta3-o-o/channels/chan_bluetooth.c 1969-12-31 17:00:00.000000000 -0700
++++ asterisk-1.4.0-beta3/channels/chan_bluetooth.c 2006-11-06 12:44:39.000000000 -0700
+@@ -0,0 +1,3145 @@
++/*
++ * Asterisk -- A telephony toolkit for Linux.
++ *
++ * Asterisk Bluetooth Channel
++ *
++ * Author: Theo Zourzouvillys <theo@adaptive-it.co.uk>
++ *
++ * Adaptive Linux Solutions <http://www.adaptive-it.co.uk>
++ *
++ * Copyright (C) 2004 Adaptive Linux Solutions
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License
++ *
++ * ******************* NOTE NOTE NOTE NOTE NOTE *********************
++ *
++ * This code is not at all tested, and only been developed with a
++ * HBH-200 headset and a Nokia 6310i right now.
++ *
++ * Expect it to crash, dial random numbers, and steal all your money.
++ *
++ * PLEASE try any headsets and phones, and let me know the results,
++ * working or not, along with all debug output!
++ *
++ * ------------------------------------------------------------------
++ *
++ * Asterisk Bluetooth Support
++ *
++ * Well, here we go - Attempt to provide Handsfree profile support in
++ * both AG and HF modes, AG (AudioGateway) mode support for using
++ * headsets, and HF (Handsfree) mode for utilising mobile/cell phones
++ *
++ * It would be nice to also provide Headset support at some time in
++ * the future, however, a working Handsfree profile is nice for now,
++ * and as far as I can see, almost all new HS devices also support HF
++ *
++ * ------------------------------------------------------------------
++ * INSTRUCTIONS
++ *
++ * You need to have bluez's bluetooth stack, along with user space
++ * tools (>=v2.10), and running hcid and sdsp.
++ *
++ * See bluetooth.conf for configuration details.
++ *
++ * - Ensure bluetooth subsystem is up and running. 'hciconfig'
++ * should show interface as UP.
++ *
++ * - If you're trying to use a headset/HS, start up asterisk, and try
++ * to pair it as you normally would.
++ *
++ * - If you're trying to use a Phone/AG, just make sure bluetooth is
++ * enabled on your phone, and start up asterisk.
++ *
++ * - 'bluetooth show peers' will show all bluetooth devices states.
++ *
++ * - Send a call out by using Dial(BLT/DevName/0123456). Call a HS
++ * with Dial(BLT/DevName)
++ *
++ * ------------------------------------------------------------------
++ * BUGS
++ *
++ * - What should happen when an AG is paired with asterisk and
++ * someone uses the AG dalling a number manually? My test phone
++ * seems to try to open an SCO link. Perhaps an extension to
++ * route the call to, or maybe drop the RFCOM link all together?
++ *
++ * ------------------------------------------------------------------
++ * COMPATIBILITY
++ *
++ * PLEASE email <theo@adaptive-it.co.uk> with the results of ANY
++ * device not listed in here (working or not), or if the device is
++ * listed and it doesn't work! Please also email full debug output
++ * for any device not working correctly or generating errors in log.
++ *
++ * HandsFree Profile:
++ *
++ * HS (HeadSet):
++ * - Ericsson HBH-200
++ *
++ * AG (AudioGateway):
++ * - Nokia 6310i
++ *
++ * ------------------------------------------------------------------
++ *
++ * Questions, bugs, or (preferably) patches to:
++ *
++ * <theo@adaptive-it.co.uk>
++ *
++ * ------------------------------------------------------------------
++ */
++
++/*! \file
++ *
++ * \brief Channel driver for Bluetooth phones and headsets
++ *
++ * \author Theo Zourzouvillys <theo@adaptive-it.co.uk>
++ *
++ * \par See also
++ * \arg \ref Config_bluetooth
++ *
++ * \ingroup channel_drivers
++ */
++
++
++/*** MODULEINFO
++ <depend>bluetooth</depend>
++ ***/
++
++
++/* ---------------------------------- */
++
++#include <stdio.h>
++#include <string.h>
++#include <asterisk/lock.h>
++#include <asterisk/utils.h>
++#include <asterisk/channel.h>
++#include <asterisk/channel_pvt.h>
++#include <asterisk/config.h>
++#include <asterisk/logger.h>
++#include <asterisk/module.h>
++#include <asterisk/pbx.h>
++#include <asterisk/sched.h>
++#include <asterisk/options.h>
++#include <asterisk/cli.h>
++#include <asterisk/callerid.h>
++#include <sys/socket.h>
++#include <sys/signal.h>
++#include <sys/time.h>
++#include <errno.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <arpa/inet.h>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <ctype.h>
++
++#include <bluetooth/bluetooth.h>
++#include <bluetooth/hci.h>
++#include <bluetooth/hci_lib.h>
++#include <bluetooth/sco.h>
++#include <bluetooth/rfcomm.h>
++#include <bluetooth/sdp.h>
++#include <bluetooth/sdp_lib.h>
++
++/* --- Data types and definitions --- */
++
++#ifndef HANDSFREE_AUDIO_GW_SVCLASS_ID
++# define HANDSFREE_AUDIO_GW_SVCLASS_ID 0x111f
++#endif
++
++#define BLUETOOTH_FORMAT AST_FORMAT_SLINEAR
++#define BLT_CHAN_NAME "BLT"
++#define BLT_CONFIG_FILE "bluetooth.conf"
++#define BLT_RDBUFF_MAX 1024
++#define BLT_DEFAULT_HCI_DEV 0
++#define BLT_SVN_REVISION "$Rev: 38 $"
++
++/* ---------------------------------- */
++
++typedef enum {
++ BLT_ROLE_NONE = 0, // Unknown Device
++ BLT_ROLE_HS = 1, // Device is a Headset
++ BLT_ROLE_AG = 2 // Device is an Audio Gateway
++} blt_role_t;
++
++/* State when we're in HS mode */
++
++typedef enum {
++ BLT_STATE_WANT_R = 0,
++ BLT_STATE_WANT_N = 1,
++ BLT_STATE_WANT_CMD = 2,
++ BLT_STATE_WANT_N2 = 3,
++} blt_state_t;
++
++typedef enum {
++ BLT_STATUS_DOWN,
++ BLT_STATUS_CONNECTING,
++ BLT_STATUS_NEGOTIATING,
++ BLT_STATUS_READY,
++ BLT_STATUS_RINGING,
++ BLT_STATUS_IN_CALL,
++} blt_status_t;
++
++/* ---------------------------------- */
++
++/* Default config settings */
++
++#define BLT_DEFAULT_CHANNEL_AG 5
++#define BLT_DEFAULT_CHANNEL_HS 6
++#define BLT_DEFAULT_ROLE BLT_ROLE_HS
++#define BLT_OBUF_LEN (48 * 25)
++
++#define BUFLEN 4800
++
++/* ---------------------------------- */
++
++typedef struct blt_dev blt_dev_t;
++
++// XXX:T: Tidy this lot up.
++struct blt_dev {
++
++ blt_status_t status; /* Device Status */
++
++ struct ast_channel * owner; /* Channel we belong to, possibly NULL */
++ blt_dev_t * dev; /* The bluetooth device channel is for */
++ struct ast_frame fr; /* Recieved frame */
++
++ /* SCO Handler */
++ int sco_pipe[2]; /* SCO alert pipe */
++ int sco; /* SCO fd */
++ int sco_handle; /* SCO Handle */
++ int sco_mtu; /* SCO MTU */
++ int sco_running; /* 1 when sCO thread should be running */
++ pthread_t sco_thread; /* SCO thread */
++ ast_mutex_t sco_lock; /* SCO lock */
++ int sco_pos_in; /* Reader in position */
++ int sco_pos_out; /* Reader out position */
++ int sco_sending; /* Sending SCO packets */
++ char buf[1024]; /* Incoming data buffer */
++ char sco_buf_out[BUFLEN+1]; /* 24 chunks of 48 */
++ char sco_buf_in[BUFLEN+1]; /* 24 chunks of 48 */
++
++ char dnid[1024]; /* Outgoi gncall dialed number */
++ unsigned char * obuf[BLT_OBUF_LEN]; /* Outgoing data buffer */
++ int obuf_len; /* Output Buffer Position */
++ int obuf_wpos; /* Buffer Reader */
++
++ // device
++ int autoconnect; /* 1 for autoconnect */
++ int outgoing_id; /* Outgoing connection scheduler id */
++ char * name; /* Devices friendly name */
++ blt_role_t role; /* Device role (HS or AG) */
++ bdaddr_t bdaddr; /* remote address */
++ int channel; /* remote channel */
++ int rd; /* RFCOMM fd */
++ int tmp_rd; /* RFCOMM fd */
++ int call_cnt; /* Number of attempted calls */
++ ast_mutex_t lock; /* RFCOMM socket lock */
++ char rd_buff[BLT_RDBUFF_MAX]; /* RFCOMM input buffer */
++ int rd_buff_pos; /* RFCOMM input buffer position */
++ int ready; /* 1 When ready */
++
++ /* AG mode */
++ char last_ok_cmd[BLT_RDBUFF_MAX]; /* Runtime[AG]: Last AT command that was OK */
++ int cind; /* Runtime[AG]: Recieved +CIND */
++ int call_pos, service_pos, callsetup_pos; /* Runtime[AG]: Positions in CIND/CMER */
++ int call, service, callsetup; /* Runtime[AG]: Values */
++
++ /* HS mode */
++ blt_state_t state; /* Runtime: Device state (AG mode only) */
++ int ring_timer; /* Runtime:Ring Timer */
++ char last_err_cmd[BLT_RDBUFF_MAX]; /* Runtime: Last AT command that was OK */
++ void (*cb)(blt_dev_t * dev, char * str); /* Runtime: Callback when in HS mode */
++
++ int brsf; /* Runtime: Bluetooth Retrieve Supported Features */
++ int bvra; /* Runtime: Bluetooth Voice Recognised Activation */
++ int gain_speaker; /* Runtime: Gain Of Speaker */
++ int clip; /* Runtime: Supports CLID */
++ int colp; /* Runtime: Connected Line ID */
++ int elip; /* Runtime: (Ericsson) Supports CLID */
++ int eolp; /* Runtime: (Ericsson) Connected Line ID */
++ int ringing; /* Runtime: Device is ringing */
++
++ blt_dev_t * next; /* Next in linked list */
++
++};
++
++typedef struct blt_atcb {
++
++ /* The command */
++ char * str;
++
++ /* DTE callbacks: */
++ int (*set)(blt_dev_t * dev, const char * arg, int len);
++ int (*read)(blt_dev_t * dev);
++ int (*execute)(blt_dev_t * dev, const char * data);
++ int (*test)(blt_dev_t * dev);
++
++ /* DCE callbacks: */
++ int (*unsolicited)(blt_dev_t * dev, const char * value);
++
++} blt_atcb_t;
++
++/* ---------------------------------- */
++
++static void rd_close(blt_dev_t * dev, int reconnect, int err);
++static int send_atcmd(blt_dev_t * device, const char * fmt, ...);
++static int sco_connect(blt_dev_t * dev);
++
++/* ---------------------------------- */
++
++/* RFCOMM channel we listen on*/
++static int rfcomm_channel_ag = BLT_DEFAULT_CHANNEL_AG;
++static int rfcomm_channel_hs = BLT_DEFAULT_CHANNEL_HS;
++
++/* Address of local bluetooth interface */
++static int hcidev_id;
++static bdaddr_t local_bdaddr;
++
++/* All the current sockets */
++AST_MUTEX_DEFINE_STATIC(iface_lock);
++static blt_dev_t * iface_head;
++static int ifcount = 0;
++
++static int sdp_record_hs = -1;
++static int sdp_record_ag = -1;
++
++/* RFCOMM listen socket */
++static int rfcomm_sock_ag = -1;
++static int rfcomm_sock_hs = -1;
++static int sco_socket = -1;
++
++static int monitor_pid = -1;
++
++/* The socket monitoring thread */
++static pthread_t monitor_thread = AST_PTHREADT_NULL;
++AST_MUTEX_DEFINE_STATIC(monitor_lock);
++
++/* Cound how many times this module is currently in use */
++static int usecnt = 0;
++AST_MUTEX_DEFINE_STATIC(usecnt_lock);
++
++static struct sched_context * sched = NULL;
++
++/* ---------------------------------- */
++
++static const char *
++role2str(blt_role_t role)
++{
++ switch (role) {
++ case BLT_ROLE_HS:
++ return "HS";
++ case BLT_ROLE_AG:
++ return "AG";
++ case BLT_ROLE_NONE:
++ return "??";
++ }
++}
++
++static const char *
++status2str(blt_status_t status)
++{
++ switch (status) {
++ case BLT_STATUS_DOWN:
++ return "Down";
++ case BLT_STATUS_CONNECTING:
++ return "Connecting";
++ case BLT_STATUS_NEGOTIATING:
++ return "Negotiating";
++ case BLT_STATUS_READY:
++ return "Ready";
++ case BLT_STATUS_RINGING:
++ return "Ringing";
++ case BLT_STATUS_IN_CALL:
++ return "InCall";
++ };
++ return "Unknown";
++}
++
++int sock_err(int fd)
++{
++ int ret;
++ int len = sizeof(ret);
++ getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
++ return ret;
++}
++
++/* ---------------------------------- */
++
++static const char *
++parse_cind(const char * str, char * name, int name_len)
++{
++ int c = 0;
++
++ memset(name, 0, name_len);
++
++ while (*str) {
++ if (*str == '(') {
++ if (++c == 1 && *(str+1) == '"') {
++ const char * start = str + 2;
++ int len = 0;
++ str += 2;
++ while (*str && *str != '"') {
++ len++;
++ str++;
++ }
++ if (len == 0)
++ return NULL;
++ strncpy(name, start, (len > name_len) ? name_len : len);
++ }
++ } else if (*str == ')')
++ c--;
++ else if (c == 0 && *str == ',')
++ return str + 1;
++ str++;
++ }
++ return NULL;
++}
++
++static void
++set_cind(blt_dev_t * dev, int indicator, int val)
++{
++
++ ast_log(LOG_DEBUG, "CIND %d set to %d\n", indicator, val);
++
++ if (indicator == dev->callsetup_pos) {
++
++ // call progress
++
++ dev->callsetup = val;
++
++ switch (val) {
++ case 3:
++ // Outgoign ringing
++ if (dev->owner && dev->role == BLT_ROLE_AG)
++ ast_queue_control(dev->owner, AST_CONTROL_RINGING);
++ break;
++ case 2:
++ break;
++ case 1:
++ break;
++ case 0:
++ if (dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0)
++ ast_queue_control(dev->owner, AST_CONTROL_CONGESTION);
++ break;
++ }
++
++ } else if (indicator == dev->service_pos) {
++
++ // Signal
++
++ if (val == 0)
++ ast_log(LOG_NOTICE, "Audio Gateway %s lost signal\n", dev->name);
++ else if (dev->service == 0 && val > 0)
++ ast_log(LOG_NOTICE, "Audio Gateway %s got signal\n", dev->name);
++
++ dev->service = val;
++
++ } else if (indicator == dev->call_pos) {
++
++ // Call
++
++ dev->call = val;
++
++ if (dev->owner) {
++ if (val == 1) {
++ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
++ } else if (val == 0)
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ }
++
++ }
++
++
++}
++
++/* ---------------------------------- */
++
++int
++set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len)
++{
++ int start_pos = *(pos);
++ int done = 0;
++ int copy;
++
++ while (data_len) {
++ // Set can_do to the most we can do in this copy.
++
++ copy = MIN(circular_len - start_pos, data_len);
++ memcpy(ring + start_pos, data + done, copy);
++
++ done += copy;
++ start_pos += copy;
++ data_len -= copy;
++
++ if (start_pos == circular_len)
++ start_pos = 0;
++ }
++ *(pos) = start_pos;
++ return 0;
++}
++
++int
++get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy)
++{
++ int copy;
++
++ // |1|2|3|4|5|6|7|8|9|
++ // |-----|
++
++ while (to_copy) {
++
++ // Set can_do to the most we can do in this copy.
++ copy = MIN(ring_size - *head, to_copy);
++
++ // ast_log(LOG_DEBUG, "Getting: %d bytes, From pos %d\n", copy, *head);
++ memcpy(dst, ring + *head, copy);
++
++ dst += copy;
++ *head += copy;
++ to_copy -= copy;
++
++ if (*head == ring_size )
++ *head = 0;
++
++ }
++
++ return 0;
++}
++
++/* Handle SCO audio sync.
++ *
++ * If we are the MASTER, then we control the timing,
++ * in 48 byte chunks. If we're the SLAVE, we send
++ * as and when we recieve a packet.
++ *
++ * Because of packet/timing nessecity, we
++ * start up a thread when we're passing audio, so
++ * that things are timed exactly right.
++ *
++ * sco_thread() is the function that handles it.
++ *
++ */
++
++static void *
++sco_thread(void * data)
++{
++ blt_dev_t * dev = (blt_dev_t*)data;
++ int res;
++ struct pollfd pfd[2];
++ int in_pos = 0;
++ int out_pos = 0;
++ char c = 1;
++ int sending;
++ char buf[1024];
++ int len;
++
++ // Avoid deadlock in odd circumstances
++
++ ast_log(LOG_DEBUG, "SCO thread started on fd %d, pid %d\n", dev->sco, getpid());
++
++ // dev->status = BLT_STATUS_IN_CALL;
++ // ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
++ // Set buffer to silence, just incase.
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ memset(dev->sco_buf_in, 0x7f, BUFLEN);
++ memset(dev->sco_buf_out, 0x7f, BUFLEN);
++
++ dev->sco_pos_in = 0;
++ dev->sco_pos_out = 0;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ while (1) {
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ if (dev->sco_running != 1) {
++ ast_log(LOG_DEBUG, "SCO stopped.\n");
++ break;
++ }
++
++ pfd[0].fd = dev->sco;
++ pfd[0].events = POLLIN;
++
++ pfd[1].fd = dev->sco_pipe[1];
++ pfd[1].events = POLLIN;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ res = poll(pfd, 2, 50);
++
++ if (res == -1 && errno != EINTR) {
++ ast_log(LOG_DEBUG, "SCO poll() error\n");
++ break;
++ }
++
++ if (res == 0)
++ continue;
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ if (pfd[0].revents & POLLIN) {
++
++ len = read(dev->sco, buf, 48);
++
++ if (len) {
++ ast_mutex_lock(&(dev->lock));
++ set_buffer(dev->sco_buf_in, buf, BUFLEN, &in_pos, len);
++ get_buffer(buf, dev->sco_buf_out, BUFLEN, &out_pos, len);
++ write(dev->sco, buf, len);
++ if (dev->owner && dev->owner->_state == AST_STATE_UP)
++ write(dev->sco_pipe[1], &c, 1);
++ ast_mutex_unlock(&(dev->lock));
++ }
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ } else if (pfd[0].revents) {
++
++ int e = sock_err(pfd[0].fd);
++ ast_log(LOG_ERROR, "SCO connection error: %s (errno %d)\n", strerror(e), e);
++ break;
++
++ } else if (pfd[1].revents & POLLIN) {
++
++ int len;
++
++ len = read(pfd[1].fd, &c, 1);
++ sending = (sending) ? 0 : 1;
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ } else if (pfd[1].revents) {
++
++ int e = sock_err(pfd[1].fd);
++ ast_log(LOG_ERROR, "SCO pipe connection event %d on pipe[1]=%d: %s (errno %d)\n", pfd[1].revents, pfd[1].fd, strerror(e), e);
++ break;
++
++ } else {
++ ast_log(LOG_NOTICE, "Unhandled poll output\n");
++ ast_mutex_unlock(&(dev->sco_lock));
++ }
++
++ }
++
++ ast_mutex_lock(&(dev->lock));
++ close(dev->sco);
++ dev->sco = -1;
++ dev->sco_running = -1;
++ ast_mutex_unlock(&(dev->sco_lock));
++ if (dev->owner)
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ ast_mutex_unlock(&(dev->lock));
++ ast_log(LOG_DEBUG, "SCO thread stopped\n");
++ return NULL;
++}
++
++/* Start SCO thread. Must be called with dev->lock */
++
++static int
++sco_start(blt_dev_t * dev, int fd)
++{
++
++ if (dev->sco_pipe[1] <= 0) {
++ ast_log(LOG_ERROR, "SCO pipe[1] == %d\n", dev->sco_pipe[1]);
++ return -1;
++ }
++
++ ast_mutex_lock(&(dev->sco_lock));
++
++ if (dev->sco_running != -1) {
++ ast_log(LOG_ERROR, "Tried to start SCO thread while already running\n");
++ ast_mutex_unlock(&(dev->sco_lock));
++ return -1;
++ }
++
++ if (dev->sco == -1) {
++ if (fd > 0) {
++ dev->sco = fd;
++ } else if (sco_connect(dev) != 0) {
++ ast_log(LOG_ERROR, "SCO fd invalid\n");
++ ast_mutex_unlock(&(dev->sco_lock));
++ return -1;
++ }
++ }
++
++ dev->sco_running = 1;
++
++ if (ast_pthread_create(&(dev->sco_thread), NULL, sco_thread, dev) < 0) {
++ ast_log(LOG_ERROR, "Unable to start SCO thread.\n");
++ dev->sco_running = -1;
++ ast_mutex_unlock(&(dev->sco_lock));
++ return -1;
++ }
++
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ return 0;
++}
++
++/* Stop SCO thread. Must be called with dev->lock */
++
++static int
++sco_stop(blt_dev_t * dev)
++{
++ ast_mutex_lock(&(dev->sco_lock));
++ if (dev->sco_running == 1)
++ dev->sco_running = 0;
++ else
++ dev->sco_running = -1;
++ dev->sco_sending = 0;
++ ast_mutex_unlock(&(dev->sco_lock));
++ return 0;
++}
++
++/* ---------------------------------- */
++
++/* Answer the call. Call with lock held on device */
++
++static int
++answer(blt_dev_t * dev)
++{
++
++ if ( (!dev->owner) || (dev->ready != 1) || (dev->status != BLT_STATUS_READY && dev->status != BLT_STATUS_RINGING)) {
++ ast_log(LOG_ERROR, "Attempt to answer() in invalid state (owner=%p, ready=%d, status=%s)\n",
++ dev->owner, dev->ready, status2str(dev->status));
++ return -1;
++ }
++
++ // dev->sd = sco_connect(&local_bdaddr, &(dev->bdaddr), NULL, NULL, 0);
++ // dev->status = BLT_STATUS_IN_CALL;
++ // dev->owner->fds[0] = dev->sd;
++ // if we are answering (hitting button):
++ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
++ // if asterisk signals us to answer:
++ // ast_setstate(ast, AST_STATE_UP);
++
++ /* Start SCO link */
++ sco_start(dev, -1);
++ return 0;
++}
++
++/* ---------------------------------- */
++
++static int
++blt_write(struct ast_channel * ast, struct ast_frame * frame)
++{
++ blt_dev_t * dev = ast->pvt->pvt;
++
++ /* Write a frame of (presumably voice) data */
++
++ if (frame->frametype != AST_FRAME_VOICE) {
++ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
++ return 0;
++ }
++
++ if (!(frame->subclass & BLUETOOTH_FORMAT)) {
++ ast_log(LOG_WARNING, "Cannot handle frames in format %d\n", frame->subclass);
++ return 0;
++ }
++
++ if (ast->_state != AST_STATE_UP) {
++ return 0;
++ }
++
++ ast_mutex_lock(&(dev->sco_lock));
++ set_buffer(dev->sco_buf_out, frame->data, BUFLEN, &(dev->sco_pos_out), MIN(frame->datalen, BUFLEN));
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ return 0;
++
++}
++
++static struct ast_frame *
++blt_read(struct ast_channel * ast)
++{
++ blt_dev_t * dev = ast->pvt->pvt;
++ char c = 1;
++ int len;
++
++ /* Some nice norms */
++
++ dev->fr.datalen = 0;
++ dev->fr.samples = 0;
++ dev->fr.data = NULL;
++ dev->fr.src = BLT_CHAN_NAME;
++ dev->fr.offset = 0;
++ dev->fr.mallocd = 0;
++ dev->fr.delivery.tv_sec = 0;
++ dev->fr.delivery.tv_usec = 0;
++
++ ast_mutex_lock(&(dev->sco_lock));
++ dev->sco_sending = 1;
++ read(dev->sco_pipe[0], &c, 1);
++ len = get_buffer(dev->buf, dev->sco_buf_in, BUFLEN, &(dev->sco_pos_in), 48);
++ ast_mutex_unlock(&(dev->sco_lock));
++
++ dev->fr.data = dev->buf;
++ dev->fr.samples = len / 2;
++ dev->fr.datalen = len;
++ dev->fr.frametype = AST_FRAME_VOICE;
++ dev->fr.subclass = BLUETOOTH_FORMAT;
++ dev->fr.offset = 0;
++
++ return &dev->fr;
++}
++
++/* Escape Any '"' in str. Return malloc()ed string */
++static char *
++escape_str(char * str)
++{
++ char * ptr = str;
++ char * pret;
++ char * ret;
++ int len = 0;
++
++ while (*ptr) {
++ if (*ptr == '"')
++ len++;
++ len++;
++ ptr++;
++ }
++
++ ret = malloc(len + 1);
++ pret = memset(ret, 0, len + 1);
++
++ ptr = str;
++
++ while (*ptr) {
++ if (*ptr == '"')
++ *pret++ = '\\';
++ *pret++ = *ptr++;
++ }
++
++ return ret;
++}
++
++static int
++ring_hs(blt_dev_t * dev)
++{
++#if (ASTERISK_VERSION_NUM < 010100)
++ char tmp[AST_MAX_EXTENSION];
++ char *name, *num;
++#endif
++
++ ast_mutex_lock(&(dev->lock));
++
++ if (dev->owner == NULL) {
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ dev->ringing = 1;
++ dev->status = BLT_STATUS_RINGING;
++
++ send_atcmd(dev, "RING");
++
++ dev->owner->rings++;
++
++ // XXX:T: '"' needs to be escaped in ELIP.
++
++#if (ASTERISK_VERSION_NUM < 010100)
++
++ if (dev->owner->callerid) {
++
++ memset(tmp, 0, sizeof(tmp));
++ strncpy(tmp, dev->owner->callerid, sizeof(tmp)-1);
++
++ if (!ast_callerid_parse(tmp, &name, &num)) {
++
++ if (dev->clip && num)
++ send_atcmd(dev, "+CLIP: \"%s\",129", num);
++
++ if (dev->elip && name) {
++ char * esc = escape_str(name);
++ send_atcmd(dev, "*ELIP: \"%s\"", esc);
++ free(esc);
++ }
++ }
++ }
++
++
++#else
++
++ if (dev->clip && dev->owner->cid.cid_num)
++ send_atcmd(dev, "+CLIP: \"%s\",129", dev->owner->cid.cid_num);
++
++ if (dev->elip && dev->owner->cid.cid_name) {
++ char * esc = escape_str(dev->owner->cid.cid_name);
++ send_atcmd(dev, "*ELIP: \"%s\"", esc);
++ free(esc);
++ }
++
++#endif
++
++ ast_mutex_unlock(&(dev->lock));
++
++ return 1;
++}
++
++/*
++ * If the HS is already connected, then just send RING, otherwise, things get a
++ * little more sticky. We first have to find the channel for HS using SDP,
++ * then intiate the connection. Once we've done that, we can start the call.
++ */
++
++static int
++blt_call(struct ast_channel * ast, char * dest, int timeout)
++{
++ blt_dev_t * dev = ast->pvt->pvt;
++
++ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
++ ast_log(LOG_WARNING, "blt_call called on %s, neither down nor reserved\n", ast->name);
++ return -1;
++ }
++
++ ast_log(LOG_DEBUG, "Calling %s on %s [t: %d]\n", dest, ast->name, timeout);
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return -1;
++ }
++
++// ast_mutex_lock(&(dev->lock));
++
++ if (dev->ready == 0) {
++ ast_log(LOG_WARNING, "Tried to call a device not ready/connected.\n");
++ ast_setstate(ast, AST_CONTROL_CONGESTION);
++// ast_mutex_unlock(&(dev->lock));
++ ast_mutex_unlock(&iface_lock);
++ return 0;
++ }
++
++ if (dev->role == BLT_ROLE_HS) {
++
++ send_atcmd(dev, "+CIEV: 3,1");
++
++ dev->ring_timer = ast_sched_add(sched, 5000, AST_SCHED_CB(ring_hs), dev);
++
++ ring_hs(dev);
++
++ ast_setstate(ast, AST_STATE_RINGING);
++ ast_queue_control(ast, AST_CONTROL_RINGING);
++
++ } else if (dev->role == BLT_ROLE_AG) {
++
++ send_atcmd(dev, "ATD%s;", dev->dnid);
++
++ } else {
++
++ ast_setstate(ast, AST_CONTROL_CONGESTION);
++ ast_log(LOG_ERROR, "Unknown device role\n");
++
++ }
++
++// ast_mutex_unlock(&(dev->lock));
++ ast_mutex_unlock(&iface_lock);
++
++ return 0;
++}
++
++static int
++blt_hangup(struct ast_channel * ast)
++{
++ blt_dev_t * dev = ast->pvt->pvt;
++
++ ast_log(LOG_DEBUG, "blt_hangup(%s)\n", ast->name);
++
++ if (!ast->pvt->pvt) {
++ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
++ return 0;
++ }
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock\n");
++ return 0;
++ }
++
++ ast_mutex_lock(&(dev->lock));
++
++ sco_stop(dev);
++ dev->sco_sending = 0;
++
++ if (dev->role == BLT_ROLE_HS) {
++
++ if (dev->ringing == 0) {
++ // Actual call in progress
++ send_atcmd(dev, "+CIEV: 2,0");
++ } else {
++
++ // Just ringing still
++
++ if (dev->role == BLT_ROLE_HS)
++ send_atcmd(dev, "+CIEV: 3,0");
++
++ if (dev->ring_timer >= 0)
++ ast_sched_del(sched, dev->ring_timer);
++
++ dev->ring_timer = -1;
++ dev->ringing = 0;
++
++ }
++
++ } else if (dev->role == BLT_ROLE_AG) {
++
++ // Cancel call.
++ send_atcmd(dev, "AT+CHUP");
++
++ }
++
++ if (dev->status == BLT_STATUS_IN_CALL || dev->status == BLT_STATUS_RINGING)
++ dev->status = BLT_STATUS_READY;
++
++ ast->pvt->pvt = NULL;
++ dev->owner = NULL;
++ ast_mutex_unlock(&(dev->lock));
++ ast_setstate(ast, AST_STATE_DOWN);
++ ast_mutex_unlock(&(iface_lock));
++
++ return 0;
++}
++
++static int
++blt_indicate(struct ast_channel * c, int condition)
++{
++ ast_log(LOG_DEBUG, "blt_indicate (%d)\n", condition);
++
++ switch(condition) {
++ case AST_CONTROL_RINGING:
++ return -1;
++ default:
++ ast_log(LOG_WARNING, "Don't know how to condition %d\n", condition);
++ break;
++ }
++ return -1;
++}
++
++static int
++blt_answer(struct ast_channel * ast)
++{
++ blt_dev_t * dev = ast->pvt->pvt;
++
++ ast_mutex_lock(&dev->lock);
++
++ // if (dev->ring_timer >= 0)
++ // ast_sched_del(sched, dev->ring_timer);
++ // dev->ring_timer = -1;
++
++ ast_log(LOG_DEBUG, "Answering interface\n");
++
++ if (ast->_state != AST_STATE_UP) {
++ send_atcmd(dev, "+CIEV: 2,1");
++ send_atcmd(dev, "+CIEV: 3,0");
++ sco_start(dev, -1);
++ ast_setstate(ast, AST_STATE_UP);
++ }
++
++ ast_mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++static struct ast_channel *
++blt_new(blt_dev_t * dev, int state, const char * context, const char * number)
++{
++ struct ast_channel * ast;
++ char c = 0;
++
++ if ((ast = ast_channel_alloc(1)) == NULL) {
++ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
++ return NULL;
++ }
++
++ snprintf(ast->name, sizeof(ast->name), "BLT/%s", dev->name);
++
++ // ast->fds[0] = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
++
++ ast->nativeformats = BLUETOOTH_FORMAT;
++ ast->pvt->rawreadformat = BLUETOOTH_FORMAT;
++ ast->pvt->rawwriteformat = BLUETOOTH_FORMAT;
++ ast->writeformat = BLUETOOTH_FORMAT;
++ ast->readformat = BLUETOOTH_FORMAT;
++
++ ast_setstate(ast, state);
++
++ ast->type = BLT_CHAN_NAME;
++
++ ast->pvt->pvt = dev;
++
++ ast->pvt->call = blt_call;
++ ast->pvt->indicate = blt_indicate;
++ ast->pvt->hangup = blt_hangup;
++ ast->pvt->read = blt_read;
++ ast->pvt->write = blt_write;
++ ast->pvt->answer = blt_answer;
++
++ strncpy(ast->context, context, sizeof(ast->context)-1);
++ strncpy(ast->exten, number, sizeof(ast->exten) - 1);
++
++ ast->language[0] = '\0';
++
++ ast->fds[0] = dev->sco_pipe[0];
++ write(dev->sco_pipe[1], &c, 1);
++
++ dev->owner = ast;
++
++ ast_mutex_lock(&usecnt_lock);
++ usecnt++;
++ ast_mutex_unlock(&usecnt_lock);
++
++ ast_update_use_count();
++
++ if (state != AST_STATE_DOWN) {
++ if (ast_pbx_start(ast)) {
++ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast->name);
++ ast_hangup(ast);
++ }
++ }
++
++ return ast;
++}
++
++static struct ast_channel *
++#if (ASTERISK_VERSION_NUM < 010100)
++blt_request(char * type, int format, void * local_data)
++#else
++blt_request(const char * type, int format, void * local_data)
++#endif
++{
++ char * data = (char*)local_data;
++ int oldformat;
++ blt_dev_t * dev = NULL;
++ struct ast_channel * ast = NULL;
++ char * number = data, * dname;
++
++ dname = strsep(&number, "/");
++
++ oldformat = format;
++
++ format &= BLUETOOTH_FORMAT;
++
++ if (!format) {
++ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
++ return NULL;
++ }
++
++ ast_log(LOG_DEBUG, "Dialing '%s' via '%s'\n", number, dname);
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Unable to lock iface_list\n");
++ return NULL;
++ }
++
++ dev = iface_head;
++
++ while (dev) {
++ if (strcmp(dev->name, dname) == 0) {
++ ast_mutex_lock(&(dev->lock));
++ if (!dev->ready) {
++ ast_log(LOG_ERROR, "Device %s is not connected\n", dev->name);
++ ast_mutex_unlock(&(dev->lock));
++ ast_mutex_unlock(&iface_lock);
++ return NULL;
++ }
++ break;
++ }
++ dev = dev->next;
++ }
++
++ ast_mutex_unlock(&iface_lock);
++
++ if (!dev) {
++ ast_log(LOG_WARNING, "Failed to find device named '%s'\n", dname);
++ return NULL;
++ }
++
++ if (number && dev->role != BLT_ROLE_AG) {
++ ast_log(LOG_WARNING, "Tried to send a call out on non AG\n");
++ ast_mutex_unlock(&(dev->lock));
++ return NULL;
++ }
++
++ if (dev->role == BLT_ROLE_AG)
++ strncpy(dev->dnid, number, sizeof(dev->dnid) - 1);
++
++ ast = blt_new(dev, AST_STATE_DOWN, "bluetooth", "s");
++
++ ast_mutex_unlock(&(dev->lock));
++
++ return ast;
++}
++
++/* ---------------------------------- */
++
++
++/* ---- AT COMMAND SOCKET STUFF ---- */
++
++static int
++send_atcmd(blt_dev_t * dev, const char * fmt, ...)
++{
++ char buf[1024];
++ va_list ap;
++ int len;
++
++ va_start(ap, fmt);
++ len = vsnprintf(buf, 1023, fmt, ap);
++ va_end(ap);
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < %s\n", role2str(dev->role), 10, dev->name, buf);
++
++ write(dev->rd, "\r\n", 2);
++ len = write(dev->rd, buf, len);
++ write(dev->rd, "\r\n", 2);
++ return (len) ? 0 : -1;
++}
++
++
++static int
++send_atcmd_ok(blt_dev_t * dev, const char * cmd)
++{
++ int len;
++ strncpy(dev->last_ok_cmd, cmd, BLT_RDBUFF_MAX - 1);
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < OK\n", role2str(dev->role), 10, dev->name);
++ len = write(dev->rd, "\r\nOK\r\n", 6);
++ return (len) ? 0 : -1;
++}
++
++static int
++send_atcmd_error(blt_dev_t * dev)
++{
++ int len;
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < ERROR\n", role2str(dev->role), 10, dev->name);
++
++// write(dev->rd, "\r\n", 2);
++// len = write(dev->rd, dev->last_ok_cmd, 5);
++ write(dev->rd, "\r\n", 2);
++ len = write(dev->rd, "ERROR", 5);
++ write(dev->rd, "\r\n", 2);
++
++ return (len) ? 0 : -1;
++}
++
++
++/* ---------------------------------- */
++
++/* -- Handle negotiation when we're an AG -- */
++
++/* Bluetooth Support */
++
++static int
++atcmd_brsf_set(blt_dev_t * dev, const char * arg, int len)
++{
++ ast_log(LOG_DEBUG, "Device Supports: %s\n", arg);
++ dev->brsf = atoi(arg);
++ send_atcmd(dev, "+BRSF: %d", 23);
++ return 0;
++}
++
++/* Bluetooth Voice Recognition */
++
++static int
++atcmd_bvra_set(blt_dev_t * dev, const char * arg, int len)
++{
++ ast_log(LOG_WARNING, "+BVRA Not Yet Supported\n");
++ return -1;
++#if 0
++ // XXX:T: Fix voice recognition somehow!
++ int action = atoi(arg);
++ ast_log(LOG_DEBUG, "Voice Recognition: %s\n", (a) ? "ACTIVATED" : "DEACTIVATED");
++ if ((action == 0) & (dev->bvra == 1)) {
++ /* Disable it */
++ dev->bvra = 0;
++ // XXX:T: Shutdown any active bvra channel
++ ast_log(LOG_DEBUG, "Voice Recognition: DISABLED\n");
++ } else if ((action == 1) && (dev->bvra == 0)) {
++ /* Enable it */
++ dev->bvra = 1;
++ // XXX:T: Schedule connection to voice recognition extension/application
++ ast_log(LOG_DEBUG, "Voice Recognition: ENABLED\n");
++ } else {
++ ast_log(LOG_ERROR, "+BVRA out of sync (we think %d, but HS wants %d)\n", dev->bvra, action);
++ return -1;
++ }
++ return 0;
++#endif
++}
++
++/* Clock */
++
++static int
++atcmd_cclk_read(blt_dev_t * dev)
++{
++ struct tm t, *tp;
++ const time_t ti = time(0);
++ tp = localtime_r(&ti, &t);
++ send_atcmd(dev, "+CCLK: \"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"",
++ (tp->tm_year % 100), (tp->tm_mon + 1), (tp->tm_mday),
++ tp->tm_hour, tp->tm_min, tp->tm_sec, ((tp->tm_gmtoff / 60) / 15));
++ return 0;
++}
++
++/* CHUP - Hangup Call */
++
++static int
++atcmd_chup_execute(blt_dev_t * dev, const char * data)
++{
++ if (!dev->owner) {
++ ast_log(LOG_ERROR, "Request to hangup call when none in progress\n");
++ return -1;
++ }
++ ast_log(LOG_DEBUG, "Hangup Call\n");
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ return 0;
++}
++
++/* CIND - Call Indicator */
++
++static int
++atcmd_cind_read(blt_dev_t * dev)
++{
++ send_atcmd(dev, "+CIND: 1,0,0");
++ return 0;
++}
++
++static int
++atcmd_cind_test(blt_dev_t * dev)
++{
++ send_atcmd(dev, "+CIND: (\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-4))");
++ return 0;
++}
++
++/* Set Language */
++
++static int
++atcmd_clan_read(blt_dev_t * dev)
++{
++ send_atcmd(dev, "+CLAN: \"en\"");
++ return 0;
++}
++
++/* Caller Id Presentation */
++
++static int
++atcmd_clip_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->clip = atoi(arg);
++ return 0;
++}
++
++/* Conneced Line Identification Presentation */
++
++static int
++atcmd_colp_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->colp = atoi(arg);
++ return 0;
++}
++
++/* CMER - Mobile Equipment Event Reporting */
++
++static int
++atcmd_cmer_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->ready = 1;
++ dev->status = BLT_STATUS_READY;
++ return 0;
++}
++
++/* PhoneBook Types:
++ *
++ * - FD - SIM Fixed Dialing Phone Book
++ * - ME - ME Phone book
++ * - SM - SIM Phone Book
++ * - DC - ME dialled-calls list
++ * - RC - ME recieved-calls lisr
++ * - MC - ME missed-calls list
++ * - MV - ME Voice Activated Dialing List
++ * - HP - Hierachial Phone Book
++ * - BC - Own Business Card (PIN2 required)
++ *
++ */
++
++/* Read Phone Book Entry */
++
++static int
++atcmd_cpbr_set(blt_dev_t * dev, const char * arg, int len)
++{
++ // XXX:T: Fix the phone book!
++ // * Maybe add res_phonebook or something? */
++ send_atcmd(dev, "+CPBR: %d,\"%s\",128,\"%s\"", atoi(arg), arg, arg);
++ return 0;
++}
++
++/* Select Phone Book */
++
++static int
++atcmd_cpbs_set(blt_dev_t * dev, const char * arg, int len)
++{
++ // XXX:T: I guess we'll just accept any?
++ return 0;
++}
++
++static int
++atcmd_cscs_set(blt_dev_t * dev, const char * arg, int len)
++{
++ // XXX:T: Language
++ return 0;
++}
++
++static int
++atcmd_eips_set(blt_dev_t * dev, const char * arg, int len)
++{
++ ast_log(LOG_DEBUG, "Identify Presentation Set: %s=%s\n",
++ (*(arg) == 49) ? "ELIP" : "EOLP",
++ (*(arg+2) == 49) ? "ON" : "OFF");
++
++ if (*(arg) == 49)
++ dev->eolp = (*(arg+2) == 49) ? 1 : 0;
++ else
++ dev->elip = (*(arg+2) == 49) ? 1 : 0;
++
++ return 0;
++}
++
++/* VGS - Speaker Volume Gain */
++
++static int
++atcmd_vgs_set(blt_dev_t * dev, const char * arg, int len)
++{
++ dev->gain_speaker = atoi(arg);
++ return 0;
++}
++
++/* Dial */
++static int
++atcmd_dial_execute(blt_dev_t * dev, const char * data)
++{
++ char * number = NULL;
++
++ /* Make sure there is a ';' at the end of the line */
++ if (*(data + (strlen(data) - 1)) != ';') {
++ ast_log(LOG_WARNING, "Can't dial non-voice right now: %s\n", data);
++ return -1;
++ }
++
++ number = strndup(data, strlen(data) - 1);
++ ast_log(LOG_NOTICE, "Dial: [%s]\n", number);
++
++ send_atcmd(dev, "+CIEV: 2,1");
++ send_atcmd(dev, "+CIEV: 3,0");
++
++ sco_start(dev, -1);
++
++ if (blt_new(dev, AST_STATE_UP, "bluetooth", number) == NULL) {
++ sco_stop(dev);
++ }
++
++ free(number);
++
++ return 0;
++}
++
++/* Answer */
++
++static int
++atcmd_answer_execute(blt_dev_t * dev, const char * data)
++{
++
++ if (!dev->ringing || !dev->owner) {
++ ast_log(LOG_WARNING, "Can't answer non existant call\n");
++ return -1;
++ }
++
++ dev->ringing = 0;
++
++ if (dev->ring_timer >= 0)
++ ast_sched_del(sched, dev->ring_timer);
++
++ dev->ring_timer = -1;
++
++ send_atcmd(dev, "+CIEV: 2,1");
++ send_atcmd(dev, "+CIEV: 3,0");
++
++ return answer(dev);
++}
++
++static int
++ag_unsol_ciev(blt_dev_t * dev, const char * data)
++{
++ const char * orig = data;
++ int indicator;
++ int status;
++
++ while (*(data) && *(data) == ' ')
++ data++;
++
++ if (*(data) == 0) {
++ ast_log(LOG_WARNING, "Invalid value[1] for '+CIEV:%s'\n", orig);
++ return -1;
++ }
++
++ indicator = *(data++) - 48;
++
++ if (*(data++) != ',') {
++ ast_log(LOG_WARNING, "Invalid value[2] for '+CIEV:%s'\n", orig);
++ return -1;
++ }
++
++ if (*(data) == 0) {
++ ast_log(LOG_WARNING, "Invalid value[3] for '+CIEV:%s'\n", orig);
++ return -1;
++ }
++
++ status = *(data) - 48;
++
++ set_cind(dev, indicator, status);
++
++ return 0;
++}
++
++static int
++ag_unsol_cind(blt_dev_t * dev, const char * data)
++{
++
++ while (*(data) && *(data) == ' ')
++ data++;
++
++
++ if (dev->cind == 0)
++ {
++ int pos = 1;
++ char name[1024];
++
++ while ((data = parse_cind(data, name, 1023)) != NULL) {
++ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
++ if (strcmp(name, "call") == 0)
++ dev->call_pos = pos;
++ else if (strcmp(name, "service") == 0)
++ dev->service_pos = pos;
++ else if (strcmp(name, "call_setup") == 0 || strcmp(name, "callsetup") == 0)
++ dev->callsetup_pos = pos;
++ pos++;
++ }
++
++ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
++
++ } else {
++
++ int pos = 1, len = 0;
++ char val[128];
++ const char * start = data;
++
++ while (*data) {
++ if (*data == ',') {
++ memset(val, 0, 128);
++ strncpy(val, start, len);
++ set_cind(dev, pos, atoi(val));
++ pos++;
++ len = 0;
++ data++;
++ start = data;
++ continue;
++ }
++ len++;
++ data++;
++ }
++
++ memset(val, 0, 128);
++ strncpy(val, start, len);
++ ast_log(LOG_DEBUG, "CIND IND %d set to %d [%s]\n", pos, atoi(val), val);
++
++
++ }
++
++ return 0;
++}
++
++static blt_atcb_t
++atcmd_list[] =
++{
++ { "A", NULL, NULL, atcmd_answer_execute, NULL, NULL },
++ { "D", NULL, NULL, atcmd_dial_execute, NULL, NULL },
++ { "+BRSF", atcmd_brsf_set, NULL, NULL, NULL, NULL },
++ { "+BVRA", atcmd_bvra_set, NULL, NULL, NULL, NULL },
++ { "+CCLK", NULL, atcmd_cclk_read, NULL, NULL, NULL },
++ { "+CHUP", NULL, NULL, atcmd_chup_execute, NULL, NULL },
++ { "+CIEV", NULL, NULL, NULL, NULL, ag_unsol_ciev },
++ { "+CIND", NULL, atcmd_cind_read, NULL, atcmd_cind_test, ag_unsol_cind },
++ { "+CLAN", NULL, atcmd_clan_read, NULL, NULL, NULL },
++ { "+CLIP", atcmd_clip_set, NULL, NULL, NULL, NULL },
++ { "+COLP", atcmd_colp_set, NULL, NULL, NULL, NULL },
++ { "+CMER", atcmd_cmer_set, NULL, NULL, NULL, NULL },
++ { "+CPBR", atcmd_cpbr_set, NULL, NULL, NULL, NULL },
++ { "+CPBS", atcmd_cpbs_set, NULL, NULL, NULL, NULL },
++ { "+CSCS", atcmd_cscs_set, NULL, NULL, NULL, NULL },
++ { "*EIPS", atcmd_eips_set, NULL, NULL, NULL, NULL },
++ { "+VGS", atcmd_vgs_set, NULL, NULL, NULL, NULL },
++};
++
++#define ATCMD_LIST_LEN (sizeof(atcmd_list) / sizeof(blt_atcb_t))
++
++/* ---------------------------------- */
++
++/* -- Handle negotiation when we're a HS -- */
++
++void
++ag_unknown_response(blt_dev_t * dev, char * cmd)
++{
++ ast_log(LOG_DEBUG, "Got UNKN response: %s\n", cmd);
++
++ // DELAYED
++ // NO CARRIER
++
++}
++
++void
++ag_cgmi_response(blt_dev_t * dev, char * cmd)
++{
++ // CGMM - Phone Model
++ // CGMR - Phone Revision
++ // CGSN - IMEI
++ // AT*
++ // VTS - send tone
++ // CREG
++ // CBC - BATTERY
++ // CSQ - SIGANL
++ // CSMS - SMS STUFFS
++ // CMGL
++ // CMGR
++ // CMGS
++ // CSCA - sms CENTER NUMBER
++ // CNMI - SMS INDICATION
++ // ast_log(LOG_DEBUG, "Manufacturer: %s\n", cmd);
++ dev->cb = ag_unknown_response;
++}
++
++void
++ag_cgmi_valid_response(blt_dev_t * dev, char * cmd)
++{
++ // send_atcmd(dev, "AT+WS46?");
++ // send_atcmd(dev, "AT+CRC=1");
++ // send_atcmd(dev, "AT+CNUM");
++
++ if (strcmp(cmd, "OK") == 0) {
++ send_atcmd(dev, "AT+CGMI");
++ dev->cb = ag_cgmi_response;
++ } else {
++ dev->cb = ag_unknown_response;
++ }
++}
++
++void
++ag_clip_response(blt_dev_t * dev, char * cmd)
++{
++ send_atcmd(dev, "AT+CGMI=?");
++ dev->cb = ag_cgmi_valid_response;
++}
++
++void
++ag_cmer_response(blt_dev_t * dev, char * cmd)
++{
++ dev->cb = ag_clip_response;
++ dev->ready = 1;
++ dev->status = BLT_STATUS_READY;
++ send_atcmd(dev, "AT+CLIP=1");
++}
++
++void
++ag_cind_status_response(blt_dev_t * dev, char * cmd)
++{
++ // XXX:T: Handle response.
++ dev->cb = ag_cmer_response;
++ send_atcmd(dev, "AT+CMER=3,0,0,1");
++ // Initiase SCO link!
++}
++
++void
++ag_cind_response(blt_dev_t * dev, char * cmd)
++{
++ dev->cb = ag_cind_status_response;
++ dev->cind = 1;
++ send_atcmd(dev, "AT+CIND?");
++}
++
++void
++ag_brsf_response(blt_dev_t * dev, char * cmd)
++{
++ dev->cb = ag_cind_response;
++ ast_log(LOG_DEBUG, "Bluetooth features: %s\n", cmd);
++ dev->cind = 0;
++ send_atcmd(dev, "AT+CIND=?");
++}
++
++/* ---------------------------------- */
++
++static int
++sdp_register(sdp_session_t * session)
++{
++ // XXX:T: Fix this horrible function so it makes some sense and is extensible!
++ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
++ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
++ sdp_profile_desc_t profile;
++ sdp_list_t *aproto, *proto[2];
++ sdp_record_t record;
++ uint8_t u8 = rfcomm_channel_ag;
++ uint8_t u8_hs = rfcomm_channel_hs;
++ sdp_data_t *channel;
++ int ret = 0;
++
++ memset((void *)&record, 0, sizeof(sdp_record_t));
++ record.handle = 0xffffffff;
++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++ root = sdp_list_append(0, &root_uuid);
++ sdp_set_browse_groups(&record, root);
++
++ // Register as an AG
++
++ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
++ svclass_id = sdp_list_append(0, &svclass_uuid);
++ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++ sdp_set_service_classes(&record, svclass_id);
++ sdp_uuid16_create(&profile.uuid, 0x111f);
++ profile.version = 0x0100;
++ pfseq = sdp_list_append(0, &profile);
++
++ sdp_set_profile_descs(&record, pfseq);
++
++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++ proto[0] = sdp_list_append(0, &l2cap_uuid);
++ apseq = sdp_list_append(0, proto[0]);
++
++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++ proto[1] = sdp_list_append(0, &rfcomm_uuid);
++ channel = sdp_data_alloc(SDP_UINT8, &u8);
++ proto[1] = sdp_list_append(proto[1], channel);
++ apseq = sdp_list_append(apseq, proto[1]);
++
++ aproto = sdp_list_append(0, apseq);
++ sdp_set_access_protos(&record, aproto);
++
++ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
++
++ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++ ast_log(LOG_ERROR, "Service Record registration failed\n");
++ ret = -1;
++ goto end;
++ }
++
++ sdp_record_ag = record.handle;
++
++ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
++
++ sdp_data_free(channel);
++ sdp_list_free(proto[0], 0);
++ sdp_list_free(proto[1], 0);
++ sdp_list_free(apseq, 0);
++ sdp_list_free(aproto, 0);
++
++ // -------------
++
++ memset((void *)&record, 0, sizeof(sdp_record_t));
++ record.handle = 0xffffffff;
++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
++ root = sdp_list_append(0, &root_uuid);
++ sdp_set_browse_groups(&record, root);
++
++ // Register as an HS
++
++ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
++ svclass_id = sdp_list_append(0, &svclass_uuid);
++ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
++ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
++ sdp_set_service_classes(&record, svclass_id);
++ sdp_uuid16_create(&profile.uuid, 0x111e);
++ profile.version = 0x0100;
++ pfseq = sdp_list_append(0, &profile);
++ sdp_set_profile_descs(&record, pfseq);
++
++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
++ proto[0] = sdp_list_append(0, &l2cap_uuid);
++ apseq = sdp_list_append(0, proto[0]);
++
++ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
++ proto[1] = sdp_list_append(0, &rfcomm_uuid);
++ channel = sdp_data_alloc(SDP_UINT8, &u8_hs);
++ proto[1] = sdp_list_append(proto[1], channel);
++ apseq = sdp_list_append(apseq, proto[1]);
++
++ aproto = sdp_list_append(0, apseq);
++ sdp_set_access_protos(&record, aproto);
++ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
++
++ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
++ ast_log(LOG_ERROR, "Service Record registration failed\n");
++ ret = -1;
++ goto end;
++ }
++
++ sdp_record_hs = record.handle;
++
++ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
++
++end:
++ sdp_data_free(channel);
++ sdp_list_free(proto[0], 0);
++ sdp_list_free(proto[1], 0);
++ sdp_list_free(apseq, 0);
++ sdp_list_free(aproto, 0);
++
++ return ret;
++}
++
++static int
++rfcomm_listen(bdaddr_t * bdaddr, int channel)
++{
++
++ int sock = -1;
++ struct sockaddr_rc loc_addr;
++ int on = 1;
++
++ if ((sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
++ ast_log(LOG_ERROR, "Can't create socket: %s (errno: %d)\n", strerror(errno), errno);
++ return -1;
++ }
++
++ loc_addr.rc_family = AF_BLUETOOTH;
++
++ /* Local Interface Address */
++ bacpy(&loc_addr.rc_bdaddr, bdaddr);
++
++ /* Channel */
++ loc_addr.rc_channel = channel;
++
++ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
++ ast_log(LOG_ERROR, "Can't bind socket: %s (errno: %d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
++ ast_log(LOG_ERROR, "Set socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
++ close(sock);
++ return -1;
++ }
++
++ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
++
++ if (listen(sock, 10) < 0) {
++ ast_log(LOG_ERROR,"Can not listen on the socket. %s(%d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ ast_log(LOG_NOTICE, "Listening for RFCOMM channel %d connections on FD %d\n", channel, sock);
++
++ return sock;
++}
++
++
++static int
++sco_listen(bdaddr_t * bdaddr)
++{
++ int sock = -1;
++ int on = 1;
++ struct sockaddr_sco loc_addr;
++
++ memset(&loc_addr, 0, sizeof(loc_addr));
++
++ if ((sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
++ ast_log(LOG_ERROR, "Can't create SCO socket: %s (errno: %d)\n", strerror(errno), errno);
++ return -1;
++ }
++
++ loc_addr.sco_family = AF_BLUETOOTH;
++ bacpy(&loc_addr.sco_bdaddr, BDADDR_ANY);
++
++ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
++ ast_log(LOG_ERROR, "Can't bind SCO socket: %s (errno: %d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
++ ast_log(LOG_ERROR, "Set SCO socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
++ close(sock);
++ return -1;
++ }
++
++ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
++
++ if (listen(sock, 10) < 0) {
++ ast_log(LOG_ERROR,"Can not listen on SCO socket: %s(%d)\n", strerror(errno), errno);
++ close(sock);
++ return -1;
++ }
++
++ ast_log(LOG_NOTICE, "Listening for SCO connections on FD %d\n", sock);
++
++ return sock;
++}
++
++static int
++rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, int channel, int nbio)
++{
++ struct sockaddr_rc addr;
++ int s;
++
++ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.rc_family = AF_BLUETOOTH;
++ bacpy(&addr.rc_bdaddr, src);
++ addr.rc_channel = 0;
++
++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ close(s);
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.rc_family = AF_BLUETOOTH;
++ bacpy(&addr.rc_bdaddr, dst);
++ addr.rc_channel = channel;
++
++ if (nbio) {
++ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
++ }
++
++ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 && (nbio != 1 || (errno != EAGAIN))) {
++ close(s);
++ return -1;
++ }
++
++ return s;
++}
++
++/* Must be called with dev->lock held */
++
++static int
++sco_connect(blt_dev_t * dev)
++{
++ struct sockaddr_sco addr;
++ // struct sco_conninfo conn;
++ // struct sco_options opts;
++ // int size;
++ // bdaddr_t * src = &local_bdaddr;
++
++ int s;
++ bdaddr_t * dst = &(dev->bdaddr);
++
++ if (dev->sco != -1) {
++ ast_log(LOG_ERROR, "SCO fd already open.\n");
++ return -1;
++ }
++
++ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
++ ast_log(LOG_ERROR, "Can't create SCO socket(): %s\n", strerror(errno));
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++
++ addr.sco_family = AF_BLUETOOTH;
++ bacpy(&addr.sco_bdaddr, BDADDR_ANY);
++
++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ ast_log(LOG_ERROR, "Can't bind() SCO socket: %s\n", strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sco_family = AF_BLUETOOTH;
++ bacpy(&addr.sco_bdaddr, dst);
++
++ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
++
++ if ((connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) && (errno != EAGAIN)) {
++ ast_log(LOG_ERROR, "Can't connect() SCO socket: %s (errno %d)\n", strerror(errno), errno);
++ close(s);
++ return -1;
++ }
++
++ //size = sizeof(conn);
++
++
++/* XXX:T: HERE, fix getting SCO conninfo.
++
++ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ size = sizeof(opts);
++
++ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
++ close(s);
++ return -1;
++ }
++
++ dev->sco_handle = conn.hci_handle;
++ dev->sco_mtu = opts.mtu;
++
++*/
++
++ ast_log(LOG_DEBUG, "SCO: %d\n", s);
++
++ dev->sco = s;
++
++ return 0;
++}
++
++
++/* ---------------------------------- */
++
++/* Non blocking (async) outgoing bluetooth connection */
++
++static int
++try_connect(blt_dev_t * dev)
++{
++ int fd;
++ ast_mutex_lock(&(dev->lock));
++
++ if (dev->status != BLT_STATUS_CONNECTING && dev->status != BLT_STATUS_DOWN) {
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ if (dev->rd != -1) {
++
++ int ret;
++ struct pollfd pfd;
++
++ if (dev->status != BLT_STATUS_CONNECTING) {
++ ast_mutex_unlock(&(dev->lock));
++ dev->outgoing_id = -1;
++ return 0;
++ }
++
++ // ret = connect(dev->rd, (struct sockaddr *)&(dev->addr), sizeof(struct sockaddr_rc)); //
++
++ pfd.fd = dev->rd;
++ pfd.events = POLLIN | POLLOUT;
++
++ ret = poll(&pfd, 1, 0);
++
++ if (ret == -1) {
++ close(dev->rd);
++ dev->rd = -1;
++ dev->status = BLT_STATUS_DOWN;
++ dev->outgoing_id = ast_sched_add(sched, 10000, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ if (ret > 0) {
++
++ int len = sizeof(ret);
++ getsockopt(dev->rd, SOL_SOCKET, SO_ERROR, &ret, &len);
++
++ if (ret == 0) {
++
++ ast_log(LOG_NOTICE, "Initialised bluetooth link to device %s\n", dev->name);
++
++#if 0
++ {
++ struct hci_conn_info_req * cr;
++ int dd;
++ char name[248];
++
++ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
++ dd = hci_open_dev(hcidev_id);
++ cr->type = ACL_LINK;
++ bacpy(&cr->bdaddr, &(dev->bdaddr));
++
++ if (ioctl(dd, HCIGETCONNINFO, (unsigned long)cr) < 0) {
++ ast_log(LOG_ERROR, "Failed to get connection info: %s\n", strerror(errno));
++ } else {
++ ast_log(LOG_DEBUG, "HCI Handle: %d\n", cr->conn_info->handle);
++ }
++
++ if (hci_read_remote_name(dd, &(dev->bdaddr), sizeof(name), name, 25000) == 0)
++ ast_log(LOG_DEBUG, "Remote Name: %s\n", name);
++ free(cr);
++ }
++#endif
++
++ dev->status = BLT_STATUS_NEGOTIATING;
++
++ /* If this device is a AG, we initiate the negotiation. */
++
++ if (dev->role == BLT_ROLE_AG) {
++ dev->cb = ag_brsf_response;
++ send_atcmd(dev, "AT+BRSF=23");
++ }
++
++ dev->outgoing_id = -1;
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++
++ } else {
++
++ if (ret != EHOSTDOWN)
++ ast_log(LOG_NOTICE, "Connect to device %s failed: %s (errno %d)\n", dev->name, strerror(ret), ret);
++
++ close(dev->rd);
++ dev->rd = -1;
++ dev->status = BLT_STATUS_DOWN;
++ dev->outgoing_id = ast_sched_add(sched, (ret == EHOSTDOWN) ? 10000 : 2500, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++
++ }
++
++ }
++
++ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ fd = rfcomm_connect(&local_bdaddr, &(dev->bdaddr), dev->channel, 1);
++
++ if (fd == -1) {
++ ast_log(LOG_WARNING, "NBIO connect() to %s returned %d: %s\n", dev->name, errno, strerror(errno));
++ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++ }
++
++ dev->rd = fd;
++ dev->status = BLT_STATUS_CONNECTING;
++ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
++ ast_mutex_unlock(&(dev->lock));
++ return 0;
++}
++
++
++/* Called whenever a new command is recieved while we're the AG */
++
++
++static int
++process_rfcomm_cmd(blt_dev_t * dev, char * cmd)
++{
++ int i;
++ char * fullcmd = cmd;
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, cmd);
++
++ /* Read the 'AT' from the start of the string */
++ if (strncmp(cmd, "AT", 2)) {
++ ast_log(LOG_WARNING, "Unknown command without 'AT': %s\n", cmd);
++ send_atcmd_error(dev);
++ return 0;
++ }
++
++ cmd += 2;
++
++ // Don't forget 'AT' on it's own is OK.
++
++ if (strlen(cmd) == 0) {
++ send_atcmd_ok(dev, fullcmd);
++ return 0;
++ }
++
++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
++ if (strncmp(atcmd_list[i].str, cmd, strlen(atcmd_list[i].str)) == 0) {
++ char * pos = (cmd + strlen(atcmd_list[i].str));
++ if ((strncmp(pos, "=?", 2) == 0) && (strlen(pos) == 2)) {
++ /* TEST command */
++ if (atcmd_list[i].test) {
++ if (atcmd_list[i].test(dev) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ send_atcmd_ok(dev, fullcmd);
++ }
++ } else if ((strncmp(pos, "?", 1) == 0) && (strlen(pos) == 1)) {
++ /* READ command */
++ if (atcmd_list[i].read) {
++ if (atcmd_list[i].read(dev) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ ast_log(LOG_WARNING, "AT Command: '%s' missing READ function\n", fullcmd);
++ send_atcmd_error(dev);
++ }
++ } else if (strncmp(pos, "=", 1) == 0) {
++ /* SET command */
++ if (atcmd_list[i].set) {
++ if (atcmd_list[i].set(dev, (pos + 1), (*(pos + 1)) ? strlen(pos + 1) : 0) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ ast_log(LOG_WARNING, "AT Command: '%s' missing SET function\n", fullcmd);
++ send_atcmd_error(dev);
++ }
++ } else {
++ /* EXECUTE command */
++ if (atcmd_list[i].execute) {
++ if (atcmd_list[i].execute(dev, cmd + strlen(atcmd_list[i].str)) == 0)
++ send_atcmd_ok(dev, fullcmd);
++ else
++ send_atcmd_error(dev);
++ } else {
++ ast_log(LOG_WARNING, "AT Command: '%s' missing EXECUTE function\n", fullcmd);
++ send_atcmd_error(dev);
++ }
++ }
++ return 0;
++ }
++ }
++
++ ast_log(LOG_WARNING, "Unknown AT Command: '%s' (%s)\n", fullcmd, cmd);
++ send_atcmd_error(dev);
++
++ return 0;
++}
++
++/* Called when a socket is incoming */
++
++static void
++handle_incoming(int fd, blt_role_t role)
++{
++ blt_dev_t * dev;
++ struct sockaddr_rc addr;
++ int len = sizeof(addr);
++
++ // Got a new incoming socket.
++ ast_log(LOG_DEBUG, "Incoming RFCOMM socket\n");
++
++ ast_mutex_lock(&iface_lock);
++
++ fd = accept(fd, (struct sockaddr*)&addr, &len);
++
++ dev = iface_head;
++ while (dev) {
++ if (bacmp(&(dev->bdaddr), &addr.rc_bdaddr) == 0) {
++ ast_log(LOG_DEBUG, "Connect from %s\n", dev->name);
++ ast_mutex_lock(&(dev->lock));
++ /* Kill any outstanding connect attempt. */
++ if (dev->outgoing_id > -1) {
++ ast_sched_del(sched, dev->outgoing_id);
++ dev->outgoing_id = -1;
++ }
++
++ rd_close(dev, 0, 0);
++
++ dev->status = BLT_STATUS_NEGOTIATING;
++ dev->rd = fd;
++
++ if (dev->role == BLT_ROLE_AG) {
++ dev->cb = ag_brsf_response;
++ send_atcmd(dev, "AT+BRSF=23");
++ }
++
++ ast_mutex_unlock(&(dev->lock));
++ break;
++ }
++ dev = dev->next;
++ }
++
++ if (dev == NULL) {
++ ast_log(LOG_WARNING, "Connect from unknown device\n");
++ close(fd);
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ return;
++}
++
++static void
++handle_incoming_sco(int master)
++{
++
++ blt_dev_t * dev;
++ struct sockaddr_sco addr;
++ struct sco_conninfo conn;
++ struct sco_options opts;
++ int len = sizeof(addr);
++ int fd;
++
++ ast_log(LOG_DEBUG, "Incoming SCO socket\n");
++
++ fd = accept(master, (struct sockaddr*)&addr, &len);
++
++ if (fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK) != 0) {
++ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
++ close(fd);
++ return;
++ }
++
++ len = sizeof(conn);
++
++ if (getsockopt(fd, SOL_SCO, SCO_CONNINFO, &conn, &len) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
++ close(fd);
++ return;
++ }
++
++ len = sizeof(opts);
++
++ if (getsockopt(fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) {
++ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
++ close(fd);
++ return;
++ }
++
++ ast_mutex_lock(&iface_lock);
++ dev = iface_head;
++ while (dev) {
++ if (bacmp(&(dev->bdaddr), &addr.sco_bdaddr) == 0) {
++ ast_log(LOG_DEBUG, "SCO Connect from %s\n", dev->name);
++ ast_mutex_lock(&(dev->lock));
++ if (dev->sco_running != -1) {
++ ast_log(LOG_ERROR, "Incoming SCO socket, but SCO thread already running.\n");
++ } else {
++ sco_start(dev, fd);
++ }
++ ast_mutex_unlock(&(dev->lock));
++ break;
++ }
++ dev = dev->next;
++ }
++
++ ast_mutex_unlock(&iface_lock);
++
++ if (dev == NULL) {
++ ast_log(LOG_WARNING, "SCO Connect from unknown device\n");
++ close(fd);
++ } else {
++ // XXX:T: We need to handle the fact we might have an outgoing connection attempt in progress.
++ ast_log(LOG_DEBUG, "SCO: %d, HCIHandle=%d, MUT=%d\n", fd, conn.hci_handle, opts.mtu);
++ }
++
++
++
++ return;
++}
++
++/* Called when there is data waiting on a socket */
++
++static int
++handle_rd_data(blt_dev_t * dev)
++{
++ char c;
++ int ret;
++
++ while ((ret = read(dev->rd, &c, 1)) == 1) {
++
++ // log_buf[i++] = c;
++
++ if (dev->role == BLT_ROLE_HS) {
++
++ if (c == '\r') {
++ ret = process_rfcomm_cmd(dev, dev->rd_buff);
++ dev->rd_buff_pos = 0;
++ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
++ return ret;
++ }
++
++ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX)
++ return 0;
++
++ dev->rd_buff[dev->rd_buff_pos++] = c;
++
++ } else if (dev->role == BLT_ROLE_AG) {
++
++ switch (dev->state) {
++
++ case BLT_STATE_WANT_R:
++ if (c == '\r') {
++ dev->state = BLT_STATE_WANT_N;
++ } else if (c == '+') {
++ dev->state = BLT_STATE_WANT_CMD;
++ dev->rd_buff[dev->rd_buff_pos++] = '+';
++ } else {
++ ast_log(LOG_ERROR, "Device %s: Expected '\\r', got %d. state=BLT_STATE_WANT_R\n", dev->name, c);
++ return -1;
++ }
++ break;
++
++ case BLT_STATE_WANT_N:
++ if (c == '\n')
++ dev->state = BLT_STATE_WANT_CMD;
++ else {
++ ast_log(LOG_ERROR, "Device %s: Expected '\\n', got %d. state=BLT_STATE_WANT_N\n", dev->name, c);
++ return -1;
++ }
++ break;
++
++ case BLT_STATE_WANT_CMD:
++ if (c == '\r')
++ dev->state = BLT_STATE_WANT_N2;
++ else {
++ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) {
++ ast_log(LOG_ERROR, "Device %s: Buffer exceeded\n", dev->name);
++ return -1;
++ }
++ dev->rd_buff[dev->rd_buff_pos++] = c;
++ }
++ break;
++
++ case BLT_STATE_WANT_N2:
++ if (c == '\n') {
++
++ dev->state = BLT_STATE_WANT_R;
++
++ if (dev->rd_buff[0] == '+') {
++ int i;
++ // find unsolicited
++ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
++ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
++ if (atcmd_list[i].unsolicited)
++ atcmd_list[i].unsolicited(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
++ else
++ ast_log(LOG_WARNING, "Device %s: Unhandled Unsolicited: %s\n", dev->name, dev->rd_buff);
++ break;
++ }
++ }
++
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
++
++ if (i == ATCMD_LIST_LEN)
++ ast_log(LOG_DEBUG, "Device %s: Got unsolicited message: %s\n", dev->name, dev->rd_buff);
++
++ } else {
++
++ if (
++ strcmp(dev->rd_buff, "OK") != 0 &&
++ strcmp(dev->rd_buff, "CONNECT") != 0 &&
++ strcmp(dev->rd_buff, "RING") != 0 &&
++ strcmp(dev->rd_buff, "NO CARRIER") != 0 &&
++ strcmp(dev->rd_buff, "ERROR") != 0 &&
++ strcmp(dev->rd_buff, "NO DIALTONE") != 0 &&
++ strcmp(dev->rd_buff, "BUSY") != 0 &&
++ strcmp(dev->rd_buff, "NO ANSWER") != 0 &&
++ strcmp(dev->rd_buff, "DELAYED") != 0
++ ){
++ // It must be a multiline error
++ strncpy(dev->last_err_cmd, dev->rd_buff, 1023);
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
++ } else if (dev->cb) {
++ if (option_verbose)
++ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
++ dev->cb(dev, dev->rd_buff);
++ } else {
++ ast_log(LOG_ERROR, "Device %s: Data on socket in HS mode, but no callback\n", dev->name);
++ }
++
++ }
++
++ dev->rd_buff_pos = 0;
++ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
++
++ } else {
++
++ ast_log(LOG_ERROR, "Device %s: Expected '\\n' got %d. state = BLT_STATE_WANT_N2:\n", dev->name, c);
++ return -1;
++
++ }
++
++ break;
++
++ default:
++ ast_log(LOG_ERROR, "Device %s: Unknown device state %d\n", dev->name, dev->state);
++ return -1;
++
++ }
++
++ }
++
++ }
++
++ return 0;
++}
++
++/* Close the devices RFCOMM socket, and SCO if it exists. Must hold dev->lock */
++
++static void
++rd_close(blt_dev_t * dev, int reconnect, int e)
++{
++ dev->ready = 0;
++
++ if (dev->rd)
++ close(dev->rd);
++
++ dev->rd = -1;
++
++ dev->status = BLT_STATUS_DOWN;
++
++ sco_stop(dev);
++
++ if (dev->owner) {
++ ast_setstate(dev->owner, AST_STATE_DOWN);
++ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
++ }
++
++ /* Schedule a reconnect */
++ if (reconnect && dev->autoconnect) {
++ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
++
++ if (monitor_thread == pthread_self()) {
++ // Because we're not the monitor thread, we needd to inturrupt poll().
++ pthread_kill(monitor_thread, SIGURG);
++ }
++
++ if (e)
++ ast_log(LOG_NOTICE, "Device %s disconnected, scheduled reconnect in 5 seconds: %s (errno %d)\n", dev->name, strerror(e), e);
++ } else if (e) {
++ ast_log(LOG_NOTICE, "Device %s disconnected: %s (errno %d)\n", dev->name, strerror(e), e);
++ }
++
++ return;
++}
++
++/*
++ * Remember that we can only add to the scheduler from
++ * the do_monitor thread, as it calculates time to next one from
++ * this loop.
++ */
++
++static void *
++do_monitor(void * data)
++{
++#define SRV_SOCK_CNT 3
++
++ int res = 0;
++ blt_dev_t * dev;
++ struct pollfd * pfds = malloc(sizeof(struct pollfd) * (ifcount + SRV_SOCK_CNT));
++
++ /* -- We start off by trying to connect all of our devices (non blocking) -- */
++
++ monitor_pid = getpid();
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return NULL;
++ }
++
++ dev = iface_head;
++ while (dev) {
++
++ if (socketpair(PF_UNIX, SOCK_STREAM, 0, dev->sco_pipe) != 0) {
++ ast_log(LOG_ERROR, "Failed to create socket pair: %s (errno %d)\n", strerror(errno), errno);
++ ast_mutex_unlock(&iface_lock);
++ return NULL;
++ }
++
++ if (dev->autoconnect && dev->status == BLT_STATUS_DOWN)
++ dev->outgoing_id = ast_sched_add(sched, 1500, AST_SCHED_CB(try_connect), dev);
++ dev = dev->next;
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ /* -- Now, Scan all sockets, and service scheduler -- */
++
++ pfds[0].fd = rfcomm_sock_ag;
++ pfds[0].events = POLLIN;
++
++ pfds[1].fd = rfcomm_sock_hs;
++ pfds[1].events = POLLIN;
++
++ pfds[2].fd = sco_socket;
++ pfds[2].events = POLLIN;
++
++ while (1) {
++ int cnt = SRV_SOCK_CNT;
++ int i;
++
++ /* -- Build pfds -- */
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return NULL;
++ }
++ dev = iface_head;
++ while (dev) {
++ ast_mutex_lock(&(dev->lock));
++ if (dev->rd > 0 && ((dev->status != BLT_STATUS_DOWN) && (dev->status != BLT_STATUS_CONNECTING))) {
++ pfds[cnt].fd = dev->rd;
++ pfds[cnt].events = POLLIN;
++ cnt++;
++ }
++ ast_mutex_unlock(&(dev->lock));
++ dev = dev->next;
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ /* -- End Build pfds -- */
++
++ res = ast_sched_wait(sched);
++ res = poll(pfds, cnt, MAX(100, MIN(100, res)));
++
++ if (res == 0)
++ ast_sched_runq(sched);
++
++ if (pfds[0].revents) {
++ handle_incoming(rfcomm_sock_ag, BLT_ROLE_AG);
++ res--;
++ }
++
++ if (pfds[1].revents) {
++ handle_incoming(rfcomm_sock_hs, BLT_ROLE_HS);
++ res--;
++ }
++
++ if (pfds[2].revents) {
++ handle_incoming_sco(sco_socket);
++ res--;
++ }
++
++ if (res == 0)
++ continue;
++
++ for (i = SRV_SOCK_CNT ; i < cnt ; i++) {
++
++ /* Optimise a little bit */
++ if (res == 0)
++ break;
++ else if (pfds[i].revents == 0)
++ continue;
++
++ /* -- Find the socket that has activity -- */
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
++ return NULL;
++ }
++
++ dev = iface_head;
++
++ while (dev) {
++ if (pfds[i].fd == dev->rd) {
++ ast_mutex_lock(&(dev->lock));
++ if (pfds[i].revents & POLLIN) {
++ if (handle_rd_data(dev) == -1) {
++ rd_close(dev, 0, 0);
++ }
++ } else {
++ rd_close(dev, 1, sock_err(dev->rd));
++ }
++ ast_mutex_unlock(&(dev->lock));
++ res--;
++ break;
++ }
++ dev = dev->next;
++ }
++
++ if (dev == NULL) {
++ ast_log(LOG_ERROR, "Unhandled fd from poll()\n");
++ close(pfds[i].fd);
++ }
++
++ ast_mutex_unlock(&iface_lock);
++
++ /* -- End find socket with activity -- */
++
++ }
++
++ }
++
++ return NULL;
++}
++
++static int
++restart_monitor(void)
++{
++
++ if (monitor_thread == AST_PTHREADT_STOP)
++ return 0;
++
++ if (ast_mutex_lock(&monitor_lock)) {
++ ast_log(LOG_WARNING, "Unable to lock monitor\n");
++ return -1;
++ }
++
++ if (monitor_thread == pthread_self()) {
++ ast_mutex_unlock(&monitor_lock);
++ ast_log(LOG_WARNING, "Cannot kill myself\n");
++ return -1;
++ }
++
++ if (monitor_thread != AST_PTHREADT_NULL) {
++
++ /* Just signal it to be sure it wakes up */
++ pthread_cancel(monitor_thread);
++ pthread_kill(monitor_thread, SIGURG);
++ ast_log(LOG_DEBUG, "Waiting for monitor thread to join...\n");
++ pthread_join(monitor_thread, NULL);
++ ast_log(LOG_DEBUG, "joined\n");
++
++ } else {
++
++ /* Start a new monitor */
++ if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
++ ast_mutex_unlock(&monitor_lock);
++ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
++ return -1;
++ }
++
++ }
++
++ ast_mutex_unlock(&monitor_lock);
++ return 0;
++}
++
++static int
++blt_parse_config(void)
++{
++ struct ast_config * cfg;
++ struct ast_variable * v;
++ char * cat;
++
++ cfg = ast_load(BLT_CONFIG_FILE);
++
++ if (!cfg) {
++ ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE);
++ return -1;
++ }
++
++ v = ast_variable_browse(cfg, "general");
++
++ while (v) {
++ if (!strcasecmp(v->name, "rfchannel_ag")) {
++ rfcomm_channel_ag = atoi(v->value);
++ } else if (!strcasecmp(v->name, "rfchannel_hs")) {
++ rfcomm_channel_hs = atoi(v->value);
++ } else if (!strcasecmp(v->name, "interface")) {
++ hcidev_id = atoi(v->value);
++ } else {
++ ast_log(LOG_WARNING, "Unknown config key '%s' in section [general]\n", v->name);
++ }
++ v = v->next;
++ }
++ cat = ast_category_browse(cfg, NULL);
++
++ while(cat) {
++
++ char * str;
++
++ if (strcasecmp(cat, "general")) {
++ blt_dev_t * device = malloc(sizeof(blt_dev_t));
++ memset(device, 0, sizeof(blt_dev_t));
++ device->sco_running = -1;
++ device->sco = -1;
++ device->rd = -1;
++ device->outgoing_id = -1;
++ device->status = BLT_STATUS_DOWN;
++ str2ba(cat, &(device->bdaddr));
++ device->name = ast_variable_retrieve(cfg, cat, "name");
++
++ str = ast_variable_retrieve(cfg, cat, "type");
++
++ if (str == NULL) {
++ ast_log(LOG_ERROR, "Device [%s] has no role. Specify type=<HS/AG>\n", cat);
++ return -1;
++ } else if (strcasecmp(str, "HS") == 0)
++ device->role = BLT_ROLE_HS;
++ else if (strcasecmp(str, "AG") == 0) {
++ device->role = BLT_ROLE_AG;
++ } else {
++ ast_log(LOG_ERROR, "Device [%s] has invalid role '%s'\n", cat, str);
++ return -1;
++ }
++
++ /* XXX:T: Find channel to use using SDP.
++ * However, this needs to be non blocking, and I can't see
++ * anything in sdp_lib.h that will allow non blocking calls.
++ */
++
++ device->channel = 1;
++
++ if ((str = ast_variable_retrieve(cfg, cat, "channel")) != NULL)
++ device->channel = atoi(str);
++
++ if ((str = ast_variable_retrieve(cfg, cat, "autoconnect")) != NULL)
++ device->autoconnect = (strcasecmp(str, "yes") == 0 || strcmp(str, "1") == 0) ? 1 : 0;
++
++ device->next = iface_head;
++ iface_head = device;
++ ifcount++;
++ }
++
++ cat = ast_category_browse(cfg, cat);
++ }
++ return 0;
++}
++
++
++static int
++blt_show_peers(int fd, int argc, char *argv[])
++{
++ blt_dev_t * dev;
++
++ if (ast_mutex_lock(&iface_lock)) {
++ ast_log(LOG_ERROR, "Failed to get Iface lock\n");
++ ast_cli(fd, "Failed to get iface lock\n");
++ return RESULT_FAILURE;
++ }
++
++ dev = iface_head;
++
++ ast_cli(fd, "BDAddr Name Role Status A/C SCOCon/Fd/Th Sig\n");
++ ast_cli(fd, "----------------- ---------- ---- ----------- --- ------------ ---\n");
++
++ while (dev) {
++ char b1[18];
++ ba2str(&(dev->bdaddr), b1);
++ ast_cli(fd, "%s %-10s %-4s %-11s %-3s %2d/%02d/%-6ld %s\n",
++ b1, dev->name, (dev->role == BLT_ROLE_HS) ? "HS" : "AG", status2str(dev->status),
++ (dev->autoconnect) ? "Yes" : "No",
++ dev->sco_running,
++ dev->sco,
++ dev->sco_thread,
++ (dev->role == BLT_ROLE_AG) ? (dev->service) ? "Yes" : "No" : "N/A"
++ );
++ dev = dev->next;
++ }
++
++ ast_mutex_unlock(&iface_lock);
++ return RESULT_SUCCESS;
++}
++
++static int
++blt_show_information(int fd, int argc, char *argv[])
++{
++ char b1[18];
++ ba2str(&local_bdaddr, b1);
++ ast_cli(fd, "-------------------------------------------\n");
++ ast_cli(fd, " Version : %s\n", BLT_SVN_REVISION);
++ ast_cli(fd, " Monitor PID : %d\n", monitor_pid);
++ ast_cli(fd, " RFCOMM AG : Channel %d, FD %d\n", rfcomm_channel_ag, rfcomm_sock_ag);
++ ast_cli(fd, " RFCOMM HS : Channel %d, FD %d\n", rfcomm_channel_hs, rfcomm_sock_hs);
++ ast_cli(fd, " Device : hci%d, MAC Address %s\n", hcidev_id, b1);
++ ast_cli(fd, "-------------------------------------------\n");
++ return RESULT_SUCCESS;
++}
++
++static int
++blt_ag_sendcmd(int fd, int argc, char *argv[])
++{
++ blt_dev_t * dev;
++
++ if (argc != 4)
++ return RESULT_SHOWUSAGE;
++
++ ast_mutex_lock(&iface_lock);
++ dev = iface_head;
++ while (dev) {
++ if (!strcasecmp(argv[2], dev->name))
++ break;
++ dev = dev->next;
++ }
++ ast_mutex_unlock(&iface_lock);
++
++ if (!dev) {
++ ast_cli(fd, "Device '%s' does not exist\n", argv[2]);
++ return RESULT_FAILURE;
++ }
++
++ if (dev->role != BLT_ROLE_AG) {
++ ast_cli(fd, "Device '%s' is not an AudioGateway\n", argv[2]);
++ return RESULT_FAILURE;
++ }
++
++ if (dev->status == BLT_STATUS_DOWN || dev->status == BLT_STATUS_NEGOTIATING) {
++ ast_cli(fd, "Device '%s' is not connected\n", argv[2]);
++ return RESULT_FAILURE;
++ }
++
++ if (*(argv[3] + strlen(argv[3]) - 1) == '.')
++ *(argv[3] + strlen(argv[3]) - 1) = '?';
++
++ ast_cli(fd, "Sending AT command to %s: %s\n", dev->name, argv[3]);
++
++ ast_mutex_lock(&(dev->lock));
++ send_atcmd(dev, argv[3]);
++ ast_mutex_unlock(&(dev->lock));
++
++ return RESULT_SUCCESS;
++}
++
++static char *
++complete_device(char * line, char * word, int pos, int state, int rpos, blt_role_t role)
++{
++ blt_dev_t * dev;
++ int which = 0;
++ char *ret;
++
++ if (pos != rpos)
++ return NULL;
++
++ ast_mutex_lock(&iface_lock);
++
++ dev = iface_head;
++
++ while (dev) {
++
++ if ((dev->role == role) && (!strncasecmp(word, dev->name, strlen(word)))) {
++ if (++which > state)
++ break;
++ }
++
++ dev = dev->next;
++ }
++
++ if (dev)
++ ret = strdup(dev->name);
++ else
++ ret = NULL;
++
++ ast_mutex_unlock(&iface_lock);
++
++ return ret;
++}
++
++static char *
++complete_device_2_ag(char * line, char * word, int pos, int state)
++{
++ return complete_device(line, word, pos, state, 2, BLT_ROLE_AG);
++}
++
++static char show_peers_usage[] =
++"Usage: bluetooth show peers\n"
++" List all bluetooth peers and their status\n";
++
++static struct ast_cli_entry
++cli_show_peers =
++ { { "bluetooth", "show", "peers", NULL }, blt_show_peers, "List Bluetooth Peers", show_peers_usage };
++
++
++static char ag_sendcmd[] =
++"Usage: bluetooth ag <device> sendcmd <cmd>\n"
++" Sends a AT cmd over the RFCOMM link, and print result (AG only)\n";
++
++static struct ast_cli_entry
++cli_ag_sendcmd =
++ { { "bluetooth", "sendcmd", NULL }, blt_ag_sendcmd, "Send AG an AT command", ag_sendcmd, complete_device_2_ag };
++
++static char show_information[] =
++"Usage: bluetooth show information\n"
++" Lists information about the bluetooth subsystem\n";
++
++static struct ast_cli_entry
++cli_show_information =
++ { { "bluetooth", "show", "information", NULL }, blt_show_information, "List Bluetooth Info", show_information };
++
++void
++remove_sdp_records(void)
++{
++
++ sdp_session_t * sdp;
++ sdp_list_t * attr;
++ sdp_record_t * rec;
++ int res = -1;
++ uint32_t range = 0x0000ffff;
++
++ if (sdp_record_ag == -1 || sdp_record_hs == -1)
++ return;
++
++ ast_log(LOG_DEBUG, "Removing SDP records\n");
++
++ sdp = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
++
++ if (!sdp)
++ return;
++
++ attr = sdp_list_append(0, &range);
++ rec = sdp_service_attr_req(sdp, sdp_record_ag, SDP_ATTR_REQ_RANGE, attr);
++ sdp_list_free(attr, 0);
++
++ if (rec)
++ if (sdp_record_unregister(sdp, rec) == 0)
++ res = 0;
++
++ attr = sdp_list_append(0, &range);
++ rec = sdp_service_attr_req(sdp, sdp_record_hs, SDP_ATTR_REQ_RANGE, attr);
++ sdp_list_free(attr, 0);
++
++ if (rec)
++ if (sdp_record_unregister(sdp, rec) == 0)
++ res = 0;
++
++ sdp_close(sdp);
++
++ if (res == 0)
++ ast_log(LOG_NOTICE, "Removed SDP records\n");
++ else
++ ast_log(LOG_ERROR, "Failed to remove SDP records\n");
++
++}
++
++static int
++__unload_module(void)
++{
++
++ ast_channel_unregister(BLT_CHAN_NAME);
++
++ if (monitor_thread != AST_PTHREADT_NULL) {
++
++ if (ast_mutex_lock(&monitor_lock)) {
++
++ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
++ pthread_cancel(monitor_thread);
++ pthread_kill(monitor_thread, SIGURG);
++ fprintf(stderr, "Waiting for monitor thread to join...\n");
++ pthread_join(monitor_thread, NULL);
++ fprintf(stderr, "joined\n");
++ }
++ monitor_thread = AST_PTHREADT_STOP;
++ ast_mutex_unlock(&monitor_lock);
++
++ } else {
++
++ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
++ return -1;
++
++ }
++
++ }
++
++ ast_unregister_atexit(remove_sdp_records);
++ remove_sdp_records();
++ return 0;
++}
++
++int
++load_module()
++{
++ sdp_session_t * sess;
++ int dd;
++ uint16_t vs;
++
++ hcidev_id = BLT_DEFAULT_HCI_DEV;
++
++ if (blt_parse_config() != 0) {
++ ast_log(LOG_ERROR, "Bluetooth configuration error. Bluetooth Disabled\n");
++ return unload_module();
++ }
++
++ dd = hci_open_dev(hcidev_id);
++ if (dd == -1) {
++ ast_log(LOG_ERROR, "Unable to open interface hci%d: %s.\n", hcidev_id, strerror(errno));
++ return -1;
++ }
++
++ hci_read_voice_setting(dd, &vs, 1000);
++ vs = htobs(vs);
++ close(dd);
++
++ if (vs != 0x0060) {
++ ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060, not 0x%04x\n", vs);
++ unload_module();
++ return 0;
++ }
++
++ if ((sched = sched_context_create()) == NULL) {
++ ast_log(LOG_WARNING, "Unable to create schedule context\n");
++ return -1;
++ }
++
++ memset(&local_bdaddr, 0, sizeof(local_bdaddr));
++
++ hci_devba(hcidev_id, &local_bdaddr);
++
++ /* --- Add SDP record --- */
++
++ sess = sdp_connect(&local_bdaddr, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
++
++ if ((rfcomm_sock_ag = rfcomm_listen(&local_bdaddr, rfcomm_channel_ag)) < 0) {
++ return -1;
++ }
++
++ if ((rfcomm_sock_hs = rfcomm_listen(&local_bdaddr, rfcomm_channel_hs)) < 0)
++ return -1;
++
++ if ((sco_socket = sco_listen(&local_bdaddr)) < 0)
++ return -1;
++
++ if (!sess) {
++ ast_log(LOG_ERROR, "Failed to connect to SDP server: %s\n", strerror(errno));
++ return -1;
++ }
++
++ if (sdp_register(sess) != 0) {
++ ast_log(LOG_ERROR, "Failed to register HeadsetAudioGateway in SDP\n");
++ return -1;
++ }
++
++ sdp_close(sess);
++
++ if (restart_monitor() != 0)
++ return -1;
++
++ if (ast_channel_register(BLT_CHAN_NAME, "Bluetooth Driver", BLUETOOTH_FORMAT, blt_request)) {
++ ast_log(LOG_ERROR, "Unable to register channel class BTL\n");
++ __unload_module();
++ return -1;
++ }
++
++ ast_cli_register(&cli_show_information);
++ ast_cli_register(&cli_show_peers);
++ ast_cli_register(&cli_ag_sendcmd);
++
++ ast_register_atexit(remove_sdp_records);
++
++ ast_log(LOG_NOTICE, "Loaded Bluetooth support, %s\n", BLT_SVN_REVISION + 1);
++
++ return 0;
++}
++
++int
++unload_module(void)
++{
++ ast_cli_unregister(&cli_ag_sendcmd);
++ ast_cli_unregister(&cli_show_peers);
++ ast_cli_unregister(&cli_show_information);
++ return __unload_module();
++}
++
++int
++usecount()
++{
++ int res;
++ ast_mutex_lock(&usecnt_lock);
++ res = usecnt;
++ ast_mutex_unlock(&usecnt_lock);
++ return res;
++}
++
++char *description()
++{
++ return "Bluetooth Channel Driver";
++}
++
++char *
++key()
++{
++ return ASTERISK_GPL_KEY;
++}
++
++
+diff -urN asterisk-1.4.0-beta3-o-o/configs/bluetooth.conf.sample asterisk-1.4.0-beta3/configs/bluetooth.conf.sample
+--- asterisk-1.4.0-beta3-o-o/configs/bluetooth.conf.sample 1969-12-31 17:00:00.000000000 -0700
++++ asterisk-1.4.0-beta3/configs/bluetooth.conf.sample 2006-11-06 12:44:39.000000000 -0700
+@@ -0,0 +1,33 @@
++[general]
++; Channel we listen on as a HS (Headset)
++rfchannel_hs = 2
++; Channel we listen on as an AG (AudioGateway)
++rfchannel_ag = 3
++; hci interface to use (number - e.g '0')
++interface = 0
++
++;; A HBH-500 Handsfree Kit
++[00:0A:D9:A1:AA:D2]
++; Any name to use, this is what we use to send calls to (BLT/<name>).
++name = HBH-500
++; IS this a HS or AG?
++type = HS
++;
++;
++; RFCOMM channel to connect to. For a HandsSet:
++; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111E
++; or,for an AudioGateway (Phone):
++; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111F
++;
++; Find the 'channel' value under RFCOMM.
++;
++channel = 2
++; Automatically conenct?
++autoconnect = yes
++
++;; A Nokia 6310i
++[00:60:57:1C:00:99]
++name = Neil
++type = AG
++channel = 13
++autoconnect = yes
+diff -urN asterisk-1.4.0-beta3-o-o/configure.ac asterisk-1.4.0-beta3/configure.ac
+--- asterisk-1.4.0-beta3-o-o/configure.ac 2006-10-13 09:41:14.000000000 -0600
++++ asterisk-1.4.0-beta3/configure.ac 2006-11-06 12:44:39.000000000 -0700
+@@ -152,6 +152,7 @@
+ # by the --with option name, to make things easier for the users :-)
+
+ AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound])
++AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth], [bluetooth])
+ AST_EXT_LIB_SETUP([CURL], [cURL], [curl])
+ AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
+ AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls])
+@@ -314,6 +315,8 @@
+
+ AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl])
+
++AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [bt_malloc], [bluetooth/bluetooth.h])
++
+ AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
+
+ GSM_INTERNAL="yes"
+diff -urN asterisk-1.4.0-beta3-o-o/makeopts.in asterisk-1.4.0-beta3/makeopts.in
+--- asterisk-1.4.0-beta3-o-o/makeopts.in 2006-09-19 08:04:15.000000000 -0600
++++ asterisk-1.4.0-beta3/makeopts.in 2006-11-06 12:44:39.000000000 -0700
+@@ -157,3 +157,6 @@
+
+ SUPPSERV_INCLUDE=@SUPPSERV_INCLUDE@
+ SUPPSERV_LIB=@SUPPSERV_LIB@
++
++BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
++BLUETOOTH_LIB=@BLUETOOTH_LIB@