-diff -Nru asterisk-addons-1.4.6.org/build_tools/menuselect-deps.in asterisk-addons-1.4.6/build_tools/menuselect-deps.in
---- asterisk-addons-1.4.6.org/build_tools/menuselect-deps.in 2007-05-14 18:22:44.000000000 +0200
-+++ asterisk-addons-1.4.6/build_tools/menuselect-deps.in 2008-03-06 08:38:14.000000000 +0100
+diff -Nru asterisk-addons-1.4.7.org/build_tools/menuselect-deps.in asterisk-addons-1.4.7/build_tools/menuselect-deps.in
+--- asterisk-addons-1.4.7.org/build_tools/menuselect-deps.in 2007-05-14 18:22:44.000000000 +0200
++++ asterisk-addons-1.4.7/build_tools/menuselect-deps.in 2009-02-01 08:45:32.000000000 +0100
@@ -1,2 +1,3 @@
+BLUETOOTH=@PBX_BLUETOOTH@
MYSQLCLIENT=@PBX_MYSQLCLIENT@
ASTERISK=@PBX_ASTERISK@
-diff -Nru asterisk-addons-1.4.6.org/channels/chan_mobile.c asterisk-addons-1.4.6/channels/chan_mobile.c
---- asterisk-addons-1.4.6.org/channels/chan_mobile.c 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-addons-1.4.6/channels/chan_mobile.c 2008-03-06 08:38:57.000000000 +0100
-@@ -0,0 +1,1867 @@
+diff -Nru asterisk-addons-1.4.7.org/channels/chan_mobile.c asterisk-addons-1.4.7/channels/chan_mobile.c
+--- asterisk-addons-1.4.7.org/channels/chan_mobile.c 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-addons-1.4.7/channels/chan_mobile.c 2009-02-01 08:44:17.000000000 +0100
+@@ -0,0 +1,2150 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+/*! \file
+ *
+ * \brief Bluetooth Mobile Device channel driver
-+ *
++ *
+ * \author Dave Bowerman <david.bowerman@gmail.com>
+ *
+ * \ingroup channel_drivers
+
+#include <asterisk.h>
+
-+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416 $")
++ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <string.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
++#include <bluetooth/l2cap.h>
+
+#include <asterisk/lock.h>
+#include <asterisk/channel.h>
+#include <asterisk/devicestate.h>
+#include <asterisk/causes.h>
+#include <asterisk/dsp.h>
-+
-+#ifndef ast_debug
-+#define ast_debug(level, ...) do { \
-+ if (option_debug >= (level)) { \
-+ ast_log(LOG_DEBUG, __VA_ARGS__); \
-+ } \
-+} while (0)
-+#endif
-+
-+#define AST_MODULE "chan_mobile"
++#include <asterisk/app.h>
++#include <asterisk/manager.h>
+
+#define MBL_CONFIG "mobile.conf"
+
-+static int prefformat = AST_FORMAT_SLINEAR;
++#define DEVICE_FRAME_SIZE 48
++#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR
++#define CHANNEL_FRAME_SIZE 320
++
++static int prefformat = DEVICE_FRAME_FORMAT;
+
-+static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
-+static int sco_socket; /* This is global so it can be closed on module unload outside of the listener thread */
++static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
++static pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */
+static sdp_session_t *sdp_session;
+
+enum mbl_type {
+ MBL_STATE_INIT4,
+ MBL_STATE_INIT5,
+ MBL_STATE_INIT6,
++ MBL_STATE_INIT7,
+ MBL_STATE_PREIDLE,
+ MBL_STATE_IDLE,
+ MBL_STATE_DIAL,
+ MBL_STATE_OUTSMS2
+};
+
++struct adapter_pvt {
++ int dev_id; /* device id */
++ int hci_socket; /* device descriptor */
++ char id[31]; /* the 'name' from mobile.conf */
++ bdaddr_t addr; /* adddress of adapter */
++ unsigned int inuse:1; /* are we in use ? */
++ unsigned int alignment_detection:1; /* do alignment detection on this adpater? */
++ int sco_socket;
++ AST_LIST_ENTRY(adapter_pvt) entry;
++};
++
++static AST_RWLIST_HEAD_STATIC(adapters, adapter_pvt);
++
+struct mbl_pvt {
-+ struct ast_channel *owner; /* Channel we belong to, possibly NULL */
-+ struct ast_frame fr; /* "null" frame */
-+ enum mbl_type type; /* Phone or Headset */
-+ char id[31]; /* The id from mobile.conf */
-+ char bdaddr[18]; /* the bdaddr of the device */
-+ char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
-+ char connected; /* is it connected? */
-+ int rfcomm_port; /* rfcomm port number */
-+ int rfcomm_socket; /* rfcomm socket descriptor */
++ struct ast_channel *owner; /* Channel we belong to, possibly NULL */
++ struct ast_frame fr; /* "null" frame */
++ enum mbl_type type; /* Phone or Headset */
++ char id[31]; /* The id from mobile.conf */
++ int group; /* group number for group dialling */
++ bdaddr_t addr; /* address of device */
++ struct adapter_pvt *adapter; /* the adapter we use */
++ char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
++ char connected; /* is it connected? */
++ int rfcomm_port; /* rfcomm port number */
++ int rfcomm_socket; /* rfcomm socket descriptor */
+ char rfcomm_buf[256];
-+ int sco_socket; /* sco socket descriptor */
-+ enum mbl_state state; /* monitor thread current state */
-+ pthread_t monitor_thread; /* monitor thread handle */
-+ char sco_in_buf[48 + AST_FRIENDLY_OFFSET];
-+ char sco_out_buf[352];
-+ char *sco_out_ptr;
-+ int sco_out_len;
-+ char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */
++ char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET];
++ char io_save_buf[DEVICE_FRAME_SIZE];
++ int io_save_len;
++ int io_pipe[2];
++ int sco_socket; /* sco socket descriptor */
++ pthread_t sco_listener_thread; /* inbound sco listener for this device */
++ enum mbl_state state; /* monitor thread current state */
++ pthread_t monitor_thread; /* monitor thread handle */
++ char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */
+ int dial_timeout;
-+ char ciev_call_0[4]; /* dynamically build reponse strings */
-+ char ciev_call_1[4];
-+ char ciev_callsetup_0[4];
-+ char ciev_callsetup_1[4];
-+ char ciev_callsetup_2[4];
-+ char ciev_callsetup_3[4];
-+ char no_callsetup;
-+ char has_sms;
-+ char sms_txt[161];
++ char ciev_call_0[5]; /* dynamically built reponse strings */
++ char ciev_call_1[5];
++ char ciev_callsetup_0[5];
++ char ciev_callsetup_1[5];
++ char ciev_callsetup_2[5];
++ char ciev_callsetup_3[5];
++ unsigned int no_callsetup:1;
++ unsigned int has_sms:1;
++ unsigned int sent_answer:1;
++ unsigned int do_alignment_detection:1;
++ unsigned int alignment_detection_triggered:1;
++ unsigned int do_hangup:1;
++ unsigned int blackberry:1;
++ short alignment_samples[4];
++ int alignment_count;
++ char sms_txt[160];
+ struct ast_dsp *dsp;
+ struct ast_frame *dsp_fr;
+ int dtmf_skip;
+ int skip_frames;
-+ char sent_answer;
+ char hangup_count;
+ AST_LIST_ENTRY(mbl_pvt) entry;
+};
+
-+static AST_LIST_HEAD_STATIC(devices, mbl_pvt);
-+
-+/* The discovery thread */
-+static pthread_t discovery_thread = AST_PTHREADT_NULL;
-+/* The sco listener thread */
-+static pthread_t sco_listener_thread = AST_PTHREADT_NULL;
++static AST_RWLIST_HEAD_STATIC(devices, mbl_pvt);
+
+/* CLI stuff */
+static const char show_usage[] =
+"Usage: mobile rfcomm command\n"
+" Send command to the rfcomm port.\n";
+
-+static int do_show_devices(int, int, char **);
-+static int do_search_devices(int, int, char **);
-+static int do_send_rfcomm(int, int, char **);
++static int handle_cli_mobile_show_devices(int fd, int argc, char **argv);
++static int handle_cli_mobile_search(int fd, int argc, char **argv);
++static int handle_cli_mobile_rfcomm(int fd, int argc, char **argv);
+
+static struct ast_cli_entry mbl_cli[] = {
-+ {{"mobile", "show", "devices", NULL}, do_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage},
-+ {{"mobile", "search", NULL}, do_search_devices, "Search for Bluetooth Cell / Mobile devices", search_usage},
-+ {{"mobile", "rfcomm", NULL}, do_send_rfcomm, "Send commands to the rfcomm port for debugging", rfcomm_usage}
++ {{"mobile", "show", "devices", NULL}, handle_cli_mobile_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage},
++ {{"mobile", "search", NULL}, handle_cli_mobile_search, "Search for Bluetooth Cell / Mobile devices", search_usage},
++ {{"mobile", "rfcomm", NULL}, handle_cli_mobile_rfcomm, "Send commands to the rfcomm port for debugging", rfcomm_usage},
+};
+
+/* App stuff */
+" Dest - destination\n"
+" Message - text of the message\n";
+
++static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num);
+static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause);
+static int mbl_call(struct ast_channel *ast, char *dest, int timeout);
+static int mbl_hangup(struct ast_channel *ast);
+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame);
+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int mbl_devicestate(void *data);
-+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num);
+
++static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen);
++
++static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel);
+static int rfcomm_write(struct mbl_pvt *pvt, char *buf);
+static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout);
-+static int sco_connect(char *bdaddr);
++
++static int sco_connect(bdaddr_t src, bdaddr_t dst);
++static int sco_write(int s, char *buf, int len);
++static int sco_read(int s, char *buf, int len);
++
++static void *do_sco_listen(void *data);
+static int sdp_search(char *addr, int profile);
+
+static const struct ast_channel_tech mbl_tech = {
+ .devicestate = mbl_devicestate
+};
+
-+static int do_show_devices(int fd, int argc, char **argv)
-+{
++/* CLI Commands implementation */
+
++static int handle_cli_mobile_show_devices(int fd, int argc, char **argv)
++{
+ struct mbl_pvt *pvt;
++ char bdaddr[18];
++ char group[6];
++
++#define FORMAT1 "%-15.15s %-17.17s %-5.5s %-15.15s %-9.9s %-5.5s %-3.3s\n"
++
++ if (argc != 3)
++ return RESULT_SHOWUSAGE;
++
++ ast_cli(fd, FORMAT1, "ID", "Address", "Group", "Adapter", "Connected", "State", "SMS");
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
++ ba2str(&pvt->addr, bdaddr);
++ snprintf(group, 5, "%d", pvt->group);
++ ast_cli(fd, FORMAT1, pvt->id, bdaddr, group, pvt->adapter->id, pvt->connected ? "Yes" : "No",
++ (pvt->state == MBL_STATE_IDLE) ? "Free" : (pvt->state < MBL_STATE_IDLE) ? "Init" : "Busy",
++ (pvt->has_sms) ? "Yes" : "No");
++ }
++ AST_RWLIST_UNLOCK(&devices);
+
-+ #define FORMAT "%-15.15s %-17.17s %-9.9s %-5.5s %-3.3s\n"
-+
-+ ast_cli(fd, FORMAT, "ID", "Address", "Connected", "State", "SMS");
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
-+ ast_cli(fd, FORMAT, pvt->id, pvt->bdaddr, pvt->connected?"Yes":"No", (pvt->state == MBL_STATE_IDLE)?"Free":(pvt->state < MBL_STATE_IDLE)?"Init":"Busy", (pvt->has_sms)?"Yes":"No");
-+ }
++#undef FORMAT1
+
+ return RESULT_SUCCESS;
-+
+}
+
-+static int do_search_devices(int fd, int argc, char **argv)
++static int handle_cli_mobile_search(int fd, int argc, char **argv)
+{
-+
-+ int hci_socket;
++ struct adapter_pvt *adapter;
+ inquiry_info *ii = NULL;
+ int max_rsp, num_rsp;
-+ int dev_id, len, flags;
++ int len, flags;
+ int i, phport, hsport;
+ char addr[19] = {0};
+ char name[31] = {0};
+
-+ #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
-+ #define FORMAT3 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
++#define FORMAT1 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
++#define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
++
++ if (argc != 2)
++ return RESULT_SHOWUSAGE;
++
++ /* find a free adapter */
++ AST_RWLIST_RDLOCK(&adapters);
++ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
++ if (!adapter->inuse)
++ break;
++ }
++ AST_RWLIST_UNLOCK(&adapters);
++
++ if (!adapter) {
++ ast_cli(fd, "All Bluetooth adapters are in use at this time.\n");
++ return RESULT_SUCCESS;
++ }
+
-+ dev_id = hci_get_route(NULL);
-+ hci_socket = hci_open_dev(dev_id);
+ len = 8;
+ max_rsp = 255;
+ flags = IREQ_CACHE_FLUSH;
+
-+ ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
-+ num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
++ ii = alloca(max_rsp * sizeof(inquiry_info));
++ num_rsp = hci_inquiry(adapter->dev_id, len, max_rsp, NULL, &ii, flags);
+ if (num_rsp > 0) {
-+ ast_cli(fd, FORMAT2, "Address", "Name", "Usable", "Type", "Port");
++ ast_cli(fd, FORMAT1, "Address", "Name", "Usable", "Type", "Port");
+ for (i = 0; i < num_rsp; i++) {
-+ ba2str(&(ii+i)->bdaddr, addr);
++ ba2str(&(ii + i)->bdaddr, addr);
+ name[0] = 0x00;
-+ if (hci_read_remote_name(hci_socket, &(ii+i)->bdaddr, sizeof(name)-1, name, 0) < 0)
++ if (hci_read_remote_name(adapter->hci_socket, &(ii + i)->bdaddr, sizeof(name) - 1, name, 0) < 0)
+ strcpy(name, "[unknown]");
+ phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID);
+ if (!phport)
+ hsport = sdp_search(addr, HEADSET_PROFILE_ID);
+ else
+ hsport = 0;
-+ ast_cli(fd, FORMAT3, addr, name, (phport > 0 || hsport > 0)?"Yes":"No", (phport > 0)?"Phone":"Headset", (phport > 0)?phport:hsport);
++ ast_cli(fd, FORMAT2, addr, name, (phport > 0 || hsport > 0) ? "Yes" : "No",
++ (phport > 0) ? "Phone" : "Headset", (phport > 0) ? phport : hsport);
+ }
+ } else
+ ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n");
+
-+ free(ii);
-+
-+ hci_close_dev(hci_socket);
++#undef FORMAT1
++#undef FORMAT2
+
+ return RESULT_SUCCESS;
-+
+}
+
-+static int do_send_rfcomm(int fd, int argc, char **argv)
++static int handle_cli_mobile_rfcomm(int fd, int argc, char **argv)
+{
-+
-+ struct mbl_pvt *pvt;
+ char buf[128];
++ struct mbl_pvt *pvt = NULL;
+
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
++ if (argc != 4)
++ return RESULT_SHOWUSAGE;
++
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, argv[2]))
+ break;
+ }
++ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt || !pvt->connected) {
-+ sprintf(buf, "Device %s not found.\n", argv[2]);
-+ ast_cli(fd, buf);
++ ast_cli(fd, "Device %s not found.\n", argv[2]);
+ return RESULT_SUCCESS;
+ }
+
-+ sprintf(buf, "%s\r", argv[3]);
++ snprintf(buf, sizeof(buf), "%s\r", argv[3]);
+ rfcomm_write(pvt, buf);
+
+ return RESULT_SUCCESS;
-+
+}
+
++/*
++
++ Dialplan applications implementation
++
++*/
++
+static int mbl_status_exec(struct ast_channel *ast, void *data)
+{
+
+ struct mbl_pvt *pvt;
-+ char *args = NULL, *device = NULL, *variable = NULL;
++ char *parse;
+ int stat;
+ char status[2];
+
-+ if (!data)
++ AST_DECLARE_APP_ARGS(args,
++ AST_APP_ARG(device);
++ AST_APP_ARG(variable);
++ );
++
++ if (ast_strlen_zero(data))
+ return -1;
+
-+ args = ast_strdupa((char *)data);
-+ device = strsep(&args, "|");
-+ if (device && (device[0] != 0x00)) {
-+ variable = args;
-+ } else
++ parse = ast_strdupa(data);
++
++ AST_STANDARD_APP_ARGS(args, parse);
++
++ if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable))
+ return -1;
+
+ stat = 1;
+
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
-+ if (!strcmp(pvt->id, device))
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
++ if (!strcmp(pvt->id, args.device))
+ break;
+ }
++ AST_RWLIST_UNLOCK(&devices);
+
+ if (pvt) {
+ if (pvt->connected)
+ stat = 3;
+ }
+
-+ sprintf(status, "%d", stat);
-+ pbx_builtin_setvar_helper(ast, variable, status);
++ snprintf(status, sizeof(status), "%d", stat);
++ pbx_builtin_setvar_helper(ast, args.variable, status);
+
+ return 0;
+
+{
+
+ struct mbl_pvt *pvt;
-+ char *args = NULL, *device = NULL, *dest = NULL, *message = NULL;
++ char *parse;
+
-+ if (!data)
++ AST_DECLARE_APP_ARGS(args,
++ AST_APP_ARG(device);
++ AST_APP_ARG(dest);
++ AST_APP_ARG(message);
++ );
++
++ if (ast_strlen_zero(data))
+ return -1;
+
-+ args = ast_strdupa((char *)data);
-+ device = strsep(&args, "|");
-+ if (device && (device[0] != 0x00)) {
-+ dest = strsep(&args, "|");
-+ if (dest && (dest[0] != 0x00)) {
-+ message = args;
-+ if (!message || (message[0] == 0x00)) {
-+ ast_log(LOG_ERROR,"NULL Message to be sent-- SMS will not be sent.\n");
-+ return -1;
-+ }
-+ } else {
-+ ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
-+ return -1;
-+ }
-+
-+ } else {
++ parse = ast_strdupa(data);
++
++ AST_STANDARD_APP_ARGS(args, parse);
++
++ if (ast_strlen_zero(args.device)) {
+ ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
+ return -1;
+ }
-+
+
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
-+ if (!strcmp(pvt->id, device))
++ if (ast_strlen_zero(args.dest)) {
++ ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
++ return -1;
++ }
++
++ if (ast_strlen_zero(args.message)) {
++ ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n");
++ return -1;
++ }
++
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
++ if (!strcmp(pvt->id, args.device))
+ break;
+ }
++ AST_RWLIST_UNLOCK(&devices);
+
+ if (!pvt) {
-+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n",device);
++ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n", args.device);
+ return -1;
+ }
-+
++
+ if (!pvt->connected) {
-+ ast_log(LOG_ERROR,"bluetooth device %s wasn't connected -- SMS will not be sent.\n",device);
++ ast_log(LOG_ERROR,"Bluetooth device %s wasn't connected -- SMS will not be sent.\n", args.device);
+ return -1;
+ }
-+
++
+ if (!pvt->has_sms) {
-+ ast_log(LOG_ERROR,"bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n",device);
++ ast_log(LOG_ERROR,"Bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n", args.device);
+ return -1;
+ }
-+
++
+ if (pvt->state != MBL_STATE_IDLE) {
-+ ast_log(LOG_ERROR,"bluetooth device %s isn't IDLE -- SMS will not be sent.\n",device);
++ ast_log(LOG_ERROR,"Bluetooth device %s isn't IDLE -- SMS will not be sent.\n", args.device);
+ return -1;
+ }
-+
-+ strcpy(pvt->dial_number, dest);
-+ memset(pvt->sms_txt, 0x0, sizeof(pvt->sms_txt));
-+ strncpy(pvt->sms_txt, message, 160);
++
++ ast_copy_string(pvt->dial_number, args.dest, sizeof(pvt->dial_number));
++ ast_copy_string(pvt->sms_txt, args.message, sizeof(pvt->sms_txt));
+ pvt->state = MBL_STATE_OUTSMS;
+
+ return 0;
+
+}
+
++/*
++
++ Channel Driver callbacks
++
++*/
++
++static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num)
++{
++
++ struct ast_channel *chn;
++
++ if (pipe(pvt->io_pipe) == -1) {
++ ast_log(LOG_ERROR, "Failed to create io_pipe.\n");
++ return NULL;
++ }
++
++ if (pvt->sco_socket != -1)
++ close(pvt->sco_socket);
++ pvt->sco_socket = -1;
++ pvt->io_save_len = 0;
++ pvt->sent_answer = 0;
++ pvt->skip_frames = 0;
++ pvt->alignment_count = 0;
++ pvt->alignment_detection_triggered = 0;
++ if (pvt->adapter->alignment_detection)
++ pvt->do_alignment_detection = 1;
++ else
++ pvt->do_alignment_detection = 0;
++ pvt->do_hangup = 1;
++ chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
++ if (chn) {
++ chn->tech = &mbl_tech;
++ chn->nativeformats = prefformat;
++ chn->rawreadformat = prefformat;
++ chn->rawwriteformat = prefformat;
++ chn->writeformat = prefformat;
++ chn->readformat = prefformat;
++ chn->tech_pvt = pvt;
++ chn->fds[0] = pvt->io_pipe[0];
++ if (state == AST_STATE_RING)
++ chn->rings = 1;
++ ast_string_field_set(chn, language, "en");
++ pvt->owner = chn;
++ return chn;
++ }
++
++ return NULL;
++
++}
++
+static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause)
+{
+
+ struct mbl_pvt *pvt;
+ char *dest_dev = NULL;
+ char *dest_num = NULL;
-+ int oldformat;
++ int oldformat, group = -1;
+
+ if (!data) {
+ ast_log(LOG_WARNING, "Channel requested with no data\n");
+ if (dest_num)
+ *dest_num++ = 0x00;
+
-+ /* Find requested device and make sure its connected. */
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
-+ if (!strcmp(pvt->id, dest_dev)) {
++ if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) {
++ group = atoi(&dest_dev[1]);
++ }
++
++ /* Find requested device and make sure it's connected. */
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
++ if (group > -1 && pvt->group == group && pvt->connected && !pvt->owner) {
++ break;
++ } else if (!strcmp(pvt->id, dest_dev)) {
+ break;
+ }
+ }
++ AST_RWLIST_UNLOCK(&devices);
+ if (!pvt || !pvt->connected || pvt->owner) {
+ ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ }
+
+ if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
-+ ast_log(LOG_WARNING, "Cant determine destination number.\n");
++ ast_log(LOG_WARNING, "Can't determine destination number.\n");
+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+ return NULL;
+ }
+
-+ pvt->sco_out_ptr = pvt->sco_out_buf;
-+ pvt->sco_out_len = 0;
-+
+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
+ if (!chn) {
-+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
++ ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+ return NULL;
+ }
+ return -1;
+ }
+
-+ ast_debug(1, "Calling %s on %s\n", dest, ast->name);
++ ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name);
+
+ if (pvt->type == MBL_TYPE_PHONE) {
+ ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number));
+ pvt->state = MBL_STATE_RING;
+ }
+
-+
+ return 0;
+
+}
+ }
+ pvt = ast->tech_pvt;
+
-+ ast_debug(1, "Hanging up device %s.\n", pvt->id);
++ ast_log(LOG_DEBUG, "Hanging up device %s.\n", pvt->id);
+
-+ ast_channel_lock(ast);
+ ast->fds[0] = -1;
-+ ast_channel_unlock(ast);
++
++ close(pvt->io_pipe[0]);
++ close(pvt->io_pipe[1]);
+
+ if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) {
+ close(pvt->sco_socket);
+ }
+
+ if ((pvt->state == MBL_STATE_INCOMING || pvt->state == MBL_STATE_OUTGOING || pvt->state == MBL_STATE_DIAL1 || pvt->state == MBL_STATE_RING3) && pvt->type == MBL_TYPE_PHONE) {
-+ rfcomm_write(pvt, "AT+CHUP\r");
++ if (pvt->do_hangup) {
++ rfcomm_write(pvt, "AT+CHUP\r");
++ }
+ pvt->state = MBL_STATE_HANGUP;
+ pvt->hangup_count = 0;
+ } else
+
+ struct mbl_pvt *pvt;
+ char buf[11];
-+
++
+ pvt = ast->tech_pvt;
+
+ if (pvt->type == MBL_TYPE_HEADSET)
+ return 0;
+
-+ ast_debug(1, "Dialed %c\n", digit);
++ ast_log(LOG_DEBUG, "Dialed %c\n", digit);
+
+ switch(digit) {
+ case '0':
+ case '9':
+ case '*':
+ case '#':
-+ sprintf(buf, "AT+VTS=%c\r", digit);
++ snprintf(buf, sizeof(buf), "AT+VTS=%c\r", digit);
+ rfcomm_write(pvt, buf);
+ break;
+ default:
+
+}
+
-+/*
-+
-+ The SCO protocol basically delivers audio in 48 byte 'frames' in slin format.
-+ Here we just package these into an ast_frame and return them.
-+ The SCO connection from the device to Asterisk happens asynchronously, so it is feasible
-+ that Asterisk will call mbl_read() before the device has connected. In that case we just return
-+ a null frame.
-+
-+*/
-+
+static struct ast_frame *mbl_read(struct ast_channel *ast)
+{
+
+ struct mbl_pvt *pvt = ast->tech_pvt;
-+ int r;
+ struct ast_frame *f;
++ int r;
+
-+ if (!pvt->owner) {
-+ return &ast_null_frame;
-+ }
++ //ast_log(LOG_DEBUG, "*** mbl_read()\n");
+
-+ if (pvt->state == MBL_STATE_HANGUP) {
-+ return &ast_null_frame;
-+ }
-+
-+ if (pvt->sco_socket == -1) {
++ if (!pvt->owner) {
+ return &ast_null_frame;
+ }
-+
++ memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
+ pvt->fr.frametype = AST_FRAME_VOICE;
-+ pvt->fr.subclass = AST_FORMAT_SLINEAR;
-+ pvt->fr.data = pvt->sco_in_buf + AST_FRIENDLY_OFFSET;
-+
-+ if ((r = read(pvt->sco_socket, pvt->fr.data, 48)) == 48) {
-+ if (pvt->skip_frames == 0) {
-+ f = ast_dsp_process(0, pvt->dsp, &pvt->fr);
-+ if (f && (f->frametype == AST_FRAME_DTMF_END)) {
-+ pvt->fr.frametype = AST_FRAME_DTMF_END;
-+ pvt->fr.subclass = f->subclass;
-+ pvt->skip_frames = pvt->dtmf_skip;
-+ }
-+ return &pvt->fr;
++ pvt->fr.subclass = DEVICE_FRAME_FORMAT;
++ pvt->fr.datalen = CHANNEL_FRAME_SIZE;
++ pvt->fr.samples = CHANNEL_FRAME_SIZE / 2;
++ pvt->fr.src = "Mobile";
++ pvt->fr.offset = AST_FRIENDLY_OFFSET;
++ pvt->fr.mallocd = 0;
++ pvt->fr.delivery.tv_sec = 0;
++ pvt->fr.delivery.tv_usec = 0;
++ pvt->fr.data = pvt->io_buf + AST_FRIENDLY_OFFSET;
++
++ if ((r = read(pvt->io_pipe[0], pvt->fr.data, CHANNEL_FRAME_SIZE)) != CHANNEL_FRAME_SIZE) {
++ if (r == -1) {
++ ast_log(LOG_ERROR, "read error %d\n", errno);
++ return &ast_null_frame;
+ } else {
-+ pvt->skip_frames--;
++ pvt->fr.datalen = r;
++ pvt->fr.samples = r / 2;
+ }
-+ } else if (r == -1) {
-+ ast_debug(1, "mbl_read() read error %d.\n", errno);
-+ close(pvt->sco_socket);
-+ pvt->sco_socket = -1;
-+ ast_channel_lock(ast);
-+ ast->fds[0] = -1;
-+ ast_channel_unlock(ast);
-+ } else {
-+ ast_debug(1, "mbl_read() read short frame. (%d)\n", r);
+ }
+
-+ return &ast_null_frame;
-+
-+}
-+
-+/*
++ f = ast_dsp_process(0, pvt->dsp, &pvt->fr);
++ if (f && (f->frametype == AST_FRAME_DTMF_END)) {
++ pvt->fr.frametype = AST_FRAME_DTMF_END;
++ pvt->fr.subclass = f->subclass;
++ }
+
-+ We need to deliver 48 byte 'frames' of slin format audio to the device.
-+ mbl_write() handles this by buffering short frames until the next time we are called.
++ return &pvt->fr;
+
-+*/
++}
+
+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+
+ struct mbl_pvt *pvt = ast->tech_pvt;
-+ int num_frames, i, r;
-+ char *pfr;
++ int i, r, io_need, num_frames;
++ char *pfr, buf[DEVICE_FRAME_SIZE];
+
-+ if (frame->frametype != AST_FRAME_VOICE) {
-+ return 0;
-+ }
-+ if (pvt->sco_socket == -1) {
-+ return 0;
-+ }
++ //ast_log(LOG_DEBUG, "*** mbl_write\n");
+
-+ if (pvt->state == MBL_STATE_HANGUP) {
++ if (frame->frametype != AST_FRAME_VOICE) {
+ return 0;
+ }
+
-+ if (frame->datalen > sizeof(pvt->sco_out_buf) - pvt->sco_out_len) {
-+ frame->datalen = sizeof(pvt->sco_out_buf) - pvt->sco_out_len;
-+ ast_debug(1, "Overrun on sco_out_buf detected.\n");
++ io_need = 0;
++ if (pvt->io_save_len > 0) {
++ io_need = DEVICE_FRAME_SIZE - pvt->io_save_len;
++ memcpy(pvt->io_save_buf + pvt->io_save_len, frame->data, io_need);
++ sco_write(pvt->sco_socket, pvt->io_save_buf, DEVICE_FRAME_SIZE);
++ if ((r = sco_read(pvt->sco_socket, buf, DEVICE_FRAME_SIZE))) {
++ if (pvt->do_alignment_detection)
++ do_alignment_detection(pvt, buf, r);
++ if (ast->_state == AST_STATE_UP) /* Dont queue the audio in the pipe if the call is not up yet. just toss it. */
++ sco_write(pvt->io_pipe[1], buf, r);
++ }
+ }
+
-+ memmove(pvt->sco_out_ptr, frame->data, frame->datalen);
-+ pvt->sco_out_len += frame->datalen;
-+ num_frames = pvt->sco_out_len / 48;
++ num_frames = (frame->datalen - io_need) / DEVICE_FRAME_SIZE;
++ pfr = frame->data + io_need;
+
-+ pfr = pvt->sco_out_buf;
+ for (i=0; i<num_frames; i++) {
-+ if ((r = write(pvt->sco_socket, pfr, 48)) == -1) {
-+ close(pvt->sco_socket);
-+ pvt->sco_socket = -1;
++ sco_write(pvt->sco_socket, pfr, DEVICE_FRAME_SIZE);
++ if ((r = sco_read(pvt->sco_socket, buf, DEVICE_FRAME_SIZE))) {
++ if (pvt->do_alignment_detection)
++ do_alignment_detection(pvt, buf, r);
++ if (ast->_state == AST_STATE_UP)
++ sco_write(pvt->io_pipe[1], buf, r);
+ }
-+ pfr += 48;
++ pfr += DEVICE_FRAME_SIZE;
++ }
++
++ pvt->io_save_len = (frame->datalen - io_need) - (num_frames * DEVICE_FRAME_SIZE);
++ if (pvt->io_save_len > 0) {
++ memcpy(pvt->io_save_buf, pfr, pvt->io_save_len);
+ }
+
-+ pvt->sco_out_len = pvt->sco_out_len - (num_frames * 48);
-+ memmove(pvt->sco_out_buf, pfr, pvt->sco_out_len);
-+ pvt->sco_out_ptr = pvt->sco_out_buf + pvt->sco_out_len;
-+
+ return 0;
+
+}
+
+ device = ast_strdupa(S_OR(data, ""));
+
-+ ast_debug(1, "Checking device state for device %s\n", device);
++ ast_log(LOG_DEBUG, "Checking device state for device %s\n", device);
+
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+ if (!strcmp(pvt->id, device))
+ break;
+ }
++ AST_RWLIST_UNLOCK(&devices);
+
+ if (pvt) {
+ if (pvt->connected) {
+
+}
+
-+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num)
++/*
++
++ Callback helpers
++
++*/
++
++/*
++
++ do_alignment_detection()
++
++ This routine attempts to detect where we get misaligned sco audio data from the bluetooth adaptor.
++
++ Its enabled by alignmentdetect=yes under the adapter entry in mobile.conf
++
++ Some adapters suffer a problem where occasionally they will byte shift the audio stream one byte to the right.
++ The result is static or white noise on the inbound (from the adapter) leg of the call.
++ This is characterised by a sudden jump in magnitude of the value of the 16 bit samples.
++
++ Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame,
++ then average the sum of the averages of frames 1, 2, and 3.
++ Frame zero is usually zero.
++ If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes
++ for each subsequent frame during the call.
++
++ If the result is <= 100 then clear the flag so we dont come back in here...
++
++ This seems to work OK....
++
++*/
++
++static void do_alignment_detection(struct mbl_pvt *pvt, char *buf, int buflen)
+{
+
-+ struct ast_channel *chn;
++ int i;
++ short a, *s;
++ char *p;
+
-+ chn = ast_channel_alloc(1, state, 0, 0, 0, 0, 0, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
-+ if (chn) {
-+ chn->tech = &mbl_tech;
-+ chn->nativeformats = prefformat;
-+ chn->rawreadformat = prefformat;
-+ chn->rawwriteformat = prefformat;
-+ chn->writeformat = prefformat;
-+ chn->readformat = prefformat;
-+ chn->readq.first = NULL;
-+ pvt->fr.frametype = AST_FRAME_VOICE;
-+ pvt->fr.subclass = AST_FORMAT_SLINEAR;
-+ pvt->fr.datalen = 48;
-+ pvt->fr.samples = 24;
-+ pvt->fr.src = "Mobile";
-+ pvt->fr.offset = AST_FRIENDLY_OFFSET;
-+ pvt->fr.mallocd = 0;
-+ pvt->fr.delivery.tv_sec = 0;
-+ pvt->fr.delivery.tv_usec = 0;
-+ pvt->fr.data = pvt->sco_in_buf + AST_FRIENDLY_OFFSET;
-+ chn->tech_pvt = pvt;
-+ if (state == AST_STATE_RING)
-+ chn->rings = 1;
-+ ast_copy_string(chn->context, pvt->context, sizeof(chn->context));
-+ ast_copy_string(chn->exten, "s", sizeof(chn->exten));
-+ ast_string_field_set(chn, language, "en");
-+ if (cid_num)
-+ chn->cid.cid_num = ast_strdup(cid_num);
-+ chn->cid.cid_name = ast_strdup(pvt->id);
-+ pvt->owner = chn;
++ if (pvt->alignment_detection_triggered) {
++ for (i=buflen, p=buf+buflen-1; i>0; i--, p--)
++ *p = *(p-1);
++ *(p+1) = 0;
++ return;
++ }
+
++ if (pvt->alignment_count < 4) {
++ s = (short *)buf;
++ for (i=0, a=0; i<buflen/2; i++) {
++ a += *s++;
++ a /= i+1;
++ }
++ pvt->alignment_samples[pvt->alignment_count++] = a;
++ return;
+ }
+
-+ return chn;
++ ast_log(LOG_DEBUG, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]);
++
++ a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]);
++ a /= 3;
++ if (a > 100) {
++ pvt->alignment_detection_triggered = 1;
++ ast_log(LOG_DEBUG, "Alignment Detection Triggered.\n");
++ } else
++ pvt->do_alignment_detection = 0;
+
+}
+
-+static int rfcomm_connect(char *bdaddr, int remote_channel) {
++/*
++
++ rfcomm helpers
++
++*/
++
++static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel) {
+
-+ bdaddr_t dst;
+ struct sockaddr_rc addr;
+ int s;
+
-+ str2ba(bdaddr, &dst);
-+
+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
-+ ast_debug(1, "socket() failed (%d).\n", errno);
++ ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno);
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.rc_family = AF_BLUETOOTH;
++ bacpy(&addr.rc_bdaddr, &src);
++ addr.rc_channel = (uint8_t) 1;
++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ ast_log(LOG_DEBUG, "bind() failed (%d).\n", errno);
++ close(s);
+ return -1;
+ }
+
+ bacpy(&addr.rc_bdaddr, &dst);
+ addr.rc_channel = remote_channel;
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ ast_debug(1, "connect() failed (%d).\n", errno);
++ ast_log(LOG_DEBUG, "connect() failed (%d).\n", errno);
+ close(s);
+ return -1;
+ }
+ ssize_t num_write;
+ int len;
+
-+ ast_debug(1, "rfcomm_write() (%s) [%s]\n", pvt->id, buf);
++ ast_log(LOG_DEBUG, "rfcomm_write() (%s) [%s]\n", pvt->id, buf);
+ len = strlen(buf);
+ p = buf;
+ while (len > 0) {
+ if ((num_write = write(pvt->rfcomm_socket, p, len)) == -1) {
-+ ast_debug(1, "rfcomm_write() error [%d]\n", errno);
++ ast_log(LOG_DEBUG, "rfcomm_write() error [%d]\n", errno);
+ return 0;
+ }
+ len -= num_write;
+
+}
+
-+static int sco_connect(char *bdaddr)
++/*
++
++ sco helpers
++
++*/
++
++static int sco_connect(bdaddr_t src, bdaddr_t dst)
+{
+
-+ bdaddr_t dst;
+ struct sockaddr_sco addr;
+ int s;
+
-+ str2ba(bdaddr, &dst);
-+
+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
-+ ast_debug(1, "socket() failed (%d).\n", errno);
++ ast_log(LOG_DEBUG, "socket() failed (%d).\n", errno);
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sco_family = AF_BLUETOOTH;
++ bacpy(&addr.sco_bdaddr, &src);
++ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ ast_log(LOG_DEBUG, "bind() failed (%d).\n", errno);
++ close(s);
+ return -1;
+ }
+
+ bacpy(&addr.sco_bdaddr, &dst);
+
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ ast_debug(1, "sco connect() failed (%d).\n", errno);
++ ast_log(LOG_DEBUG, "sco connect() failed (%d).\n", errno);
+ close(s);
+ return -1;
+ }
+
+}
+
++static int sco_write(int s, char *buf, int len)
++{
++
++ int r;
++
++ if (s == -1) {
++ ast_log(LOG_DEBUG, "sco_write() not ready\n");
++ return 0;
++ }
++
++ ast_log(LOG_DEBUG, "sco_write()\n");
++
++ r = write(s, buf, len);
++ if (r == -1) {
++ ast_log(LOG_DEBUG, "sco write error %d\n", errno);
++ return 0;
++ }
++
++ return 1;
++
++}
++
++static int sco_read(int s, char *buf, int len)
++{
++
++ int r;
++
++ if (s == -1) {
++ ast_log(LOG_DEBUG, "sco_read() not ready\n");
++ return 0;
++ }
++
++ ast_log(LOG_DEBUG, "sco_read()\n");
++
++ r = read(s, buf, len);
++ if (r == -1) {
++ ast_log(LOG_DEBUG, "sco_read() error %d\n", errno);
++ return 0;
++ }
++
++ return r;
++
++}
++
+/*
+
-+ sdp_search() performs a service discovery on the given device to determine whether
-+ or not it supports the Handsfree Profile.
++ sdp helpers
+
+*/
+
+ port = 0;
+ session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
+ if (!session) {
-+ ast_debug(1, "sdp_connect() failed on device %s.\n", addr);
++ ast_log(LOG_DEBUG, "sdp_connect() failed on device %s.\n", addr);
+ return 0;
+ }
+
+ sdp_record_free(sdprec);
+ sdp_list_free(response_list, 0);
+ } else
-+ ast_debug(1, "No responses returned for device %s.\n", addr);
++ ast_log(LOG_DEBUG, "No responses returned for device %s.\n", addr);
+ } else
-+ ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr);
-+
++ ast_log(LOG_DEBUG, "sdp_service_search_attr_req() failed on device %s.\n", addr);
+
+ sdp_list_free(search_list, 0);
+ sdp_list_free(attrid_list, 0);
+
+}
+
-+/*
-+
-+ sdp_register()
-+
-+ Register GENERIC_AUDIO & HEADSET with the SDP daemon on the Asterisk Box.
-+ This assists connections to phones/pda's with dud bluetooth stacks like the IMate Jasjam
-+ and some Nokia's
-+
-+*/
-+
+static sdp_session_t *sdp_register(void)
+{
+
+
+/*
+
-+ Phone Monitor Thread
-+
-+ This thread is spun once a phone device is discovered and considered capable of being used, i.e. supports Handsfree Profile,
-+ and its configured in mobile.conf.
-+ The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls.
++ Thread routines
+
+*/
+
+ int callp = 0, callsetupp;
+ char brsf, nsmode, *p, *p1;
+ char sms_src[13];
-+ char sms_txt[161];
++ char sms_txt[160];
+
+ brsf = nsmode = 0;
+
+ s = rfcomm_read(pvt, buf, 0, t);
+
+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
-+ ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
++ ast_log(LOG_DEBUG, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
+ switch (pvt->state) {
+ case MBL_STATE_INIT:
+ if (strstr(buf, "+BRSF:")) {
+ brsf = 1;
-+ } else if (strstr(buf, "ERROR") && !nsmode) { /* Hmmm, Non-Standard Phone, just continue */
++ } else if (strstr(buf, "ERROR") && !nsmode) { /* Hmmm, Non-Standard Phone, just continue */
+ rfcomm_write(pvt, "AT+CIND=?\r");
+ pvt->state++;
+ nsmode = 1;
+ } else if (strstr(buf, "OK") && brsf) {
-+ rfcomm_write(pvt, "AT+CIND=?\r");
-+ pvt->state++;
++ if (pvt->blackberry) {
++ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
++ pvt->state = MBL_STATE_INIT3;
++ } else {
++ rfcomm_write(pvt, "AT+CIND=?\r");
++ pvt->state++;
++ }
+ }
+ break;
+ case MBL_STATE_INIT1:
+ group2++;
+ }
+ if (strstr(buf+i, "\"call\""))
-+ callp = group2;
++ callp = group2;
+ if (strstr(buf+i, "\"call_setup\""))
+ callsetupp = group2;
+ if (strstr(buf+i, "\"callsetup\""))
+ callsetupp = group2;
+ }
-+ sprintf(pvt->ciev_call_0, "%d,0", callp);
-+ sprintf(pvt->ciev_call_1, "%d,1", callp);
-+ sprintf(pvt->ciev_callsetup_0, "%d,0", callsetupp);
-+ sprintf(pvt->ciev_callsetup_1, "%d,1", callsetupp);
-+ sprintf(pvt->ciev_callsetup_2, "%d,2", callsetupp);
-+ sprintf(pvt->ciev_callsetup_3, "%d,3", callsetupp);
++ snprintf(pvt->ciev_call_0, sizeof(pvt->ciev_call_0), "%d,0", callp);
++ snprintf(pvt->ciev_call_1, sizeof(pvt->ciev_call_1), "%d,1", callp);
++ snprintf(pvt->ciev_callsetup_0, sizeof(pvt->ciev_callsetup_0), "%d,0", callsetupp);
++ snprintf(pvt->ciev_callsetup_1, sizeof(pvt->ciev_callsetup_1), "%d,1", callsetupp);
++ snprintf(pvt->ciev_callsetup_2, sizeof(pvt->ciev_callsetup_2), "%d,2", callsetupp);
++ snprintf(pvt->ciev_callsetup_3, sizeof(pvt->ciev_callsetup_3), "%d,3", callsetupp);
+ if (callsetupp == 0) /* This phone has no call setup indication!! ... */
+ pvt->no_callsetup = 1;
-+ ast_debug(1, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp);
++ ast_log(LOG_DEBUG, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp);
+ }
+ if (strstr(buf, "OK")) {
+ rfcomm_write(pvt, "AT+CIND?\r");
+ monitor = 0;
+ }
+ } else if (strstr(buf, "OK")) {
-+ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
-+ pvt->state++;
++ if (pvt->blackberry) {
++ rfcomm_write(pvt, "AT+CLIP=1\r");
++ pvt->state = MBL_STATE_INIT4;
++ } else {
++ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
++ pvt->state++;
++ }
+ }
+ break;
+ case MBL_STATE_INIT3:
+ if (strstr(buf, "OK")) {
-+ rfcomm_write(pvt, "AT+CLIP=1\r");
-+ pvt->state++;
++ if (pvt->blackberry) {
++ rfcomm_write(pvt, "AT+CIND=?\r");
++ pvt->state = MBL_STATE_INIT1;
++ } else {
++ rfcomm_write(pvt, "AT+CLIP=1\r");
++ pvt->state++;
++ }
+ }
+ break;
+ case MBL_STATE_INIT4:
+ if (strstr(buf, "OK")) {
-+ rfcomm_write(pvt, "AT+CMGF=1\r");
++ rfcomm_write(pvt, "AT+VGS=15\r");
+ pvt->state++;
+ }
+ break;
+ case MBL_STATE_INIT5:
-+ if (strstr(buf, "ERROR")) { /* No SMS Support ! */
++ if (strstr(buf, "OK")) {
++ rfcomm_write(pvt, "AT+CMGF=1\r");
++ pvt->state++;
++ }
++ break;
++ case MBL_STATE_INIT6:
++ if (strstr(buf, "ERROR")) { /* No SMS Support ! */
+ pvt->state = MBL_STATE_PREIDLE;
+ } else if (strstr(buf, "OK")) {
+ rfcomm_write(pvt, "AT+CNMI=2,1,0,1,0\r");
+ pvt->state++;
+ }
+ break;
-+ case MBL_STATE_INIT6:
-+ if (strstr(buf, "OK")) { /* We have SMS Support */
++ case MBL_STATE_INIT7:
++ if (strstr(buf, "OK")) { /* We have SMS Support */
+ pvt->has_sms = 1;
+ pvt->state = MBL_STATE_PREIDLE;
+ } else if (strstr(buf, "ERROR")) {
+ pvt->has_sms = 0;
-+ ast_log(LOG_NOTICE,"Device %s has no bluetooth SMS capability.\n", pvt->id);
+ pvt->state = MBL_STATE_PREIDLE;
+ }
+ break;
+ case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */
+ break;
+ case MBL_STATE_IDLE:
-+ ast_debug(1, "Device %s %s [%s]\n", pvt->bdaddr, pvt->id, buf);
++ ast_log(LOG_DEBUG, "Device %s [%s]\n", pvt->id, buf);
+ if (strstr(buf, "RING")) {
+ pvt->state = MBL_STATE_RING;
+ } else if (strstr(buf, "+CIEV:")) {
+ case MBL_STATE_OUTGOING:
+ if (strstr(buf, "+CIEV")) {
+ if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */
++ pvt->do_hangup = 0;
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ } else if (strstr(buf, pvt->ciev_callsetup_3)) { /* b-party ringing */
+ ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
+ cid_num[pcide - pcids - 1] = 0x00;
+ }
+ }
-+ pvt->sco_out_ptr = pvt->sco_out_buf;
-+ pvt->sco_out_len = 0;
-+ pvt->sent_answer = 0;
+ chn = mbl_new(AST_STATE_RING, pvt, cid_num);
+ if (chn) {
+ if (ast_pbx_start(chn)) {
+ }
+ break;
+ case MBL_STATE_RING2:
-+ pvt->sco_out_ptr = pvt->sco_out_buf;
-+ pvt->sco_out_len = 0;
-+ pvt->sent_answer = 0;
+ chn = mbl_new(AST_STATE_RING, pvt, cid_num);
+ if (chn) {
+ if (ast_pbx_start(chn)) {
+ pvt->state = MBL_STATE_INCOMING;
+ } else { /* User answered on handset!, disconnect */
+ pvt->state = MBL_STATE_IDLE;
-+ ast_log(LOG_NOTICE,"Closing the sco_socket in RING3 with CIEV\n");
+ if (pvt->sco_socket > -1)
+ close(pvt->sco_socket);
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ case MBL_STATE_INCOMING:
+ if (strstr(buf, "+CIEV")) {
+ if (strstr(buf, pvt->ciev_call_0)) {
++ pvt->do_hangup = 0;
+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
+ }
+ }
+ break;
+ case MBL_STATE_HANGUP:
+ if (strstr(buf, "OK") || strstr(buf, pvt->ciev_call_0)) {
++ close(pvt->sco_socket);
++ pvt->sco_socket = -1;
+ pvt->state = MBL_STATE_IDLE;
+ }
+ break;
+ ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n");
+ pvt->state = MBL_STATE_IDLE;
+ } else {
-+ memset(sms_txt, 0x00, sizeof(sms_txt));
-+ strncpy(sms_txt, buf, strlen(buf));
++ ast_copy_string(sms_txt, buf, sizeof(sms_txt));
+ }
+ break;
+ case MBL_STATE_OUTSMS:
+ p++;
+ smsi = atoi(p);
+ if (smsi > 0) {
-+ sprintf(buf, "AT+CMGR=%d\r", smsi);
++ snprintf(buf, sizeof(buf), "AT+CMGR=%d\r", smsi);
+ rfcomm_write(pvt, buf);
+ pvt->state = MBL_STATE_INSMS;
+ }
+ pvt->state++;
+ rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
+ } else if (pvt->state == MBL_STATE_INIT3) { /* Some devices dont respond to AT+CMER=3,0,0,1 properly. VK 2020 for example */
-+ pvt->state++;
-+ rfcomm_write(pvt, "AT+CLIP=1\r");
++ pvt->state++;
++ rfcomm_write(pvt, "AT+CLIP=1\r");
+ } else if (pvt->state == MBL_STATE_PREIDLE) {
+ pvt->connected = 1;
+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id);
+ pvt->state = MBL_STATE_IDLE;
+ } else if (pvt->state == MBL_STATE_DIAL) {
-+ sprintf(buf, "ATD%s;\r", pvt->dial_number);
++ snprintf(buf, sizeof(buf), "ATD%s;\r", pvt->dial_number);
+ if (!rfcomm_write(pvt, buf)) {
+ ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
+ ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
+ } else if (pvt->state == MBL_STATE_RING) { /* No CLIP?, bump it */
+ pvt->state = MBL_STATE_RING2;
+ } else if (pvt->state == MBL_STATE_HANGUP) {
-+ if (pvt->hangup_count == 6) {
-+ ast_debug(1, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id);
-+ monitor = 0;
-+ }
-+ rfcomm_write(pvt, "AT+CHUP\r");
-+ pvt->hangup_count++;
++ if (pvt->do_hangup) {
++ if (pvt->hangup_count == 6) {
++ ast_log(LOG_DEBUG, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id);
++ monitor = 0;
++ }
++ rfcomm_write(pvt, "AT+CHUP\r");
++ pvt->hangup_count++;
++ } else
++ pvt->state = MBL_STATE_IDLE;
+ } else if (pvt->state == MBL_STATE_OUTSMS) {
-+ sprintf(buf, "AT+CMGS=\"%s\"\r", pvt->dial_number);
++ snprintf(buf, sizeof(buf), "AT+CMGS=\"%s\"\r", pvt->dial_number);
+ rfcomm_write(pvt, buf);
+ pvt->state = MBL_STATE_OUTSMS1;
+ } else if (pvt->state == MBL_STATE_OUTSMS1) {
+ if (pvt->rfcomm_buf[0] == '>') {
-+ sprintf(buf, "%s%c", pvt->sms_txt, 0x1a);
++ snprintf(buf, sizeof(buf), "%s%c", pvt->sms_txt, 0x1a);
+ rfcomm_write(pvt, buf);
+ pvt->state = MBL_STATE_OUTSMS2;
+ } else {
+
+ }
+
-+ close(pvt->rfcomm_socket);
-+ close(pvt->sco_socket);
++ if (pvt->rfcomm_socket > -1)
++ close(pvt->rfcomm_socket);
++ if (pvt->sco_socket > -1)
++ close(pvt->sco_socket);
+ pvt->sco_socket = -1;
+ pvt->connected = 0;
+ pvt->monitor_thread = AST_PTHREADT_NULL;
+
-+ return NULL;
++ pthread_cancel(pvt->sco_listener_thread);
++ pthread_join(pvt->sco_listener_thread, NULL);
++ pvt->sco_listener_thread = AST_PTHREADT_NULL;
+
-+}
++ close(pvt->adapter->sco_socket);
+
-+/*
++ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
+
-+ Headset Monitor Thread
++ pvt->adapter->inuse = 0;
+
-+ This thread is spun once a headset device is discovered and considered capable of being used, i.e. supports Headset Profile,
-+ and its configured in mobile.conf.
-+ The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls.
++ return NULL;
+
-+*/
++}
+
+static void *do_monitor_headset(void *data)
+{
+ s = rfcomm_read(pvt, buf, 0, t);
+
+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
-+ ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
++ ast_log(LOG_DEBUG, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
+ switch (pvt->state) {
+ case MBL_STATE_RING2:
+ if (strstr(buf, "AT+CKPD=")) {
-+ ast_channel_lock(pvt->owner);
-+ pvt->owner->fds[0] = pvt->sco_socket;
-+ ast_log(LOG_NOTICE,"pvt-sco_socket used for fds in headphone code\n");
-+ ast_channel_unlock(pvt->owner);
+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
+ pvt->state = MBL_STATE_INCOMING;
+ rfcomm_write(pvt, "\r\n+VGS=13\r\n");
+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id);
+ pvt->state = MBL_STATE_IDLE;
+ } else if (pvt->state == MBL_STATE_RING) {
-+ pvt->sco_socket = sco_connect(pvt->bdaddr);
++ pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr);
+ if (pvt->sco_socket > -1) {
-+ ast_log(LOG_NOTICE,"sco_connect returned -1 in state RING\n");
+ ast_setstate(pvt->owner, AST_STATE_RINGING);
+ ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
+ pvt->state = MBL_STATE_RING2;
+
+ }
+
++ if (pvt->rfcomm_socket > -1)
++ close(pvt->rfcomm_socket);
++ if (pvt->sco_socket > -1)
++ close(pvt->sco_socket);
++ pvt->sco_socket = -1;
++ pvt->connected = 0;
++ pvt->monitor_thread = AST_PTHREADT_NULL;
++
++ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);
++
++ pvt->adapter->inuse = 0;
++
+ return NULL;
+
+}
+ pvt->monitor_thread = AST_PTHREADT_NULL;
+ return 0;
+ }
++ /* we are a phone, so spin the sco listener on the adapter as well */
++ if (ast_pthread_create_background(&pvt->sco_listener_thread, NULL, do_sco_listen, pvt->adapter) < 0) {
++ ast_log(LOG_ERROR, "Unable to create sco listener thread for device %s.\n", pvt->id);
++ }
++
+ } else {
+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) {
+ pvt->monitor_thread = AST_PTHREADT_NULL;
+
+}
+
-+/*
-+
-+ Device Discovery Thread.
-+
-+ This thread wakes every 'discovery_interval' seconds and trys to connect to
-+ those configured devices which are not connected. This saves the user from having
-+ to manually connect his/her cell phone to the asterisk box. Once a successful
-+ connection is made, a monitor thread is spun on the device which lives for the
-+ lifetime of the connection.
-+
-+*/
-+
+static void *do_discovery(void *data)
+{
+
-+ struct mbl_pvt *pvt = data;
++ struct adapter_pvt *adapter;
++ struct mbl_pvt *pvt;
+
+ for (;;) {
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
-+ if (!pvt->connected) {
-+ if ((pvt->rfcomm_socket = rfcomm_connect(pvt->bdaddr, pvt->rfcomm_port)) > -1) {
-+ pvt->state = 0;
-+ if (start_monitor(pvt)) {
-+ pvt->connected = 1;
-+ if (option_verbose > 2)
-+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id);
++ AST_RWLIST_RDLOCK(&adapters);
++ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
++ if (!adapter->inuse) {
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
++ if (!adapter->inuse && !pvt->connected && !strcmp(adapter->id, pvt->adapter->id)) {
++ if ((pvt->rfcomm_socket = rfcomm_connect(adapter->addr, pvt->addr, pvt->rfcomm_port)) > -1) {
++ pvt->state = 0;
++ if (start_monitor(pvt)) {
++ pvt->connected = 1;
++ adapter->inuse = 1;
++ manager_event(EVENT_FLAG_SYSTEM, "MobileStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id);
++ if (option_verbose > 2)
++ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id);
++ }
++ }
+ }
+ }
++ AST_RWLIST_UNLOCK(&devices);
+ }
+ }
++ AST_RWLIST_UNLOCK(&adapters);
+ /* Go to sleep */
+ sleep(discovery_interval);
+ }
+ return NULL;
+}
+
-+/*
-+
-+ This thread listens for incoming sco connections.
-+ Although the Bluetooth Handsfree Profile Specification says that either end may initiate the audio connection,
-+ in practice some devices (LG TU500) get upset unless they initiate the connection.
-+ We leave all sco initiation to the device.
-+ On an inbound sco connection, we need to find the appropriate device, and set the channel fd accordingly.
-+
-+*/
-+
+static void *do_sco_listen(void *data)
+{
+
+ int ns;
-+ bdaddr_t local;
+ struct sockaddr_sco addr;
++ char saddr[18];
+ struct sco_options so;
+ socklen_t len;
+ int opt = 1;
-+ char saddr[18];
+ socklen_t addrlen;
+ struct mbl_pvt *pvt;
++ struct adapter_pvt *adapter = (struct adapter_pvt *) data;
+
-+ hci_devba(0, &local);
-+
-+ if ((sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
++ if ((adapter->sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+ ast_log(LOG_ERROR, "Unable to create sco listener socket.\n");
+ return NULL;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
-+ bacpy(&addr.sco_bdaddr, &local);
-+ if (bind(sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
++ bacpy(&addr.sco_bdaddr, &adapter->addr);
++ if (bind(adapter->sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
-+ close(sco_socket);
++ close(adapter->sco_socket);
+ return NULL;
+ }
-+ if (setsockopt(sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
++ if (setsockopt(adapter->sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
+ ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
-+ close(sco_socket);
++ close(adapter->sco_socket);
+ return NULL;
+ }
-+ if (listen(sco_socket, 5) < 0) {
++ if (listen(adapter->sco_socket, 5) < 0) {
+ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
-+ close(sco_socket);
++ close(adapter->sco_socket);
+ return NULL;
+ }
+ while (1) {
-+ addrlen = sizeof(struct sockaddr);
-+ ast_log(LOG_NOTICE, "About to accept the sco_socket...\n");
-+ if ((ns = accept(sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) {
-+ ast_log(LOG_NOTICE, "sco_socket returns %d...\n",ns);
-+ ba2str(&addr.sco_bdaddr, saddr);
-+
++ ast_log(LOG_DEBUG, "About to accept() socket.\n");
++ addrlen = sizeof(struct sockaddr_sco);
++ if ((ns = accept(adapter->sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) {
++ ast_log(LOG_DEBUG, "accept()ed socket.\n");
+ len = sizeof(so);
+ getsockopt(ns, SOL_SCO, SCO_OPTIONS, &so, &len);
+
-+ ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
++ ba2str(&addr.sco_bdaddr, saddr);
++ ast_log(LOG_DEBUG, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
+
+ pvt = NULL;
-+ AST_LIST_TRAVERSE(&devices, pvt, entry) {
-+ if (!strcmp(pvt->bdaddr, saddr))
++ AST_RWLIST_RDLOCK(&devices);
++ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
++ if (!bacmp(&pvt->addr, &addr.sco_bdaddr))
+ break;
+ }
++ AST_RWLIST_UNLOCK(&devices);
+ if (pvt) {
-+ ast_log(LOG_NOTICE,"about to close the pvt-sco_socket and set it ns\n");
+ if (pvt->sco_socket != -1)
+ close(pvt->sco_socket);
+ pvt->sco_socket = ns;
-+ if (pvt->owner) {
-+ ast_channel_lock(pvt->owner);
-+ pvt->owner->fds[0] = ns;
-+ ast_channel_unlock(pvt->owner);
-+ }
+ } else
-+ ast_debug(1, "Could not find device for incoming Audio Connection.\n");
++ ast_log(LOG_DEBUG, "Could not find device for incoming Audio Connection.\n");
++ } else {
++ ast_log(LOG_ERROR, "accept() failed %d\n", errno);
+ }
-+ else ast_log(LOG_NOTICE, "Accept got a -1....");
+ }
+
+ return NULL;
+
+}
+
++/*
++
++ Module
++
++*/
++
+static int mbl_load_config(void)
+{
+
+ struct ast_config *cfg = NULL;
+ char *cat = NULL;
+ struct ast_variable *var;
-+ const char *address, *port, *context, *type, *skip;
++ const char *id, *address, *useadapter, *port, *context, *type, *skip, *group, *master, *nocallsetup, *aligndetect, *blackberry;
+ struct mbl_pvt *pvt;
++ struct adapter_pvt *adapter;
++ uint16_t vs;
++ struct hci_dev_req dr;
++ char nadapters = 0;
++ // struct ast_flags config_flags = { 0 };
+
+ cfg = ast_config_load(MBL_CONFIG);
+ if (!cfg)
+ discovery_interval = atoi(var->value);
+ }
+
++ /* load adapters first */
++ cat = ast_category_browse(cfg, NULL);
++ while (cat) {
++ if (!strcasecmp(cat, "adapter")) {
++ id = ast_variable_retrieve(cfg, cat, "id");
++ address = ast_variable_retrieve(cfg, cat, "address");
++ master = ast_variable_retrieve(cfg, cat, "forcemaster");
++ aligndetect = ast_variable_retrieve(cfg, cat, "alignmentdetection");
++ ast_log(LOG_DEBUG, "Loading adapter %s %s.\n", id, address);
++ if (!ast_strlen_zero(id) && !ast_strlen_zero(address)) {
++ if ((adapter = ast_calloc(1, sizeof(*adapter)))) {
++ ast_copy_string(adapter->id, id, sizeof(adapter->id));
++ str2ba(address, &adapter->addr);
++ if (!ast_strlen_zero(aligndetect)) {
++ if (*aligndetect == 'Y' || *aligndetect == 'y')
++ adapter->alignment_detection = 1;
++ }
++ adapter->dev_id = hci_devid(address);
++ adapter->hci_socket = hci_open_dev(adapter->dev_id);
++ if (adapter->dev_id < 0 || adapter->hci_socket < 0) {
++ ast_log(LOG_ERROR, "Unable to open adapter %s. It won't be enabled.\n", adapter->id);
++ ast_free(adapter);
++ } else {
++ if ((master) && (*master)) {
++ dr.dev_id = adapter->dev_id;
++ if (hci_strtolm("master", &dr.dev_opt)) {
++ if (ioctl(adapter->hci_socket, HCISETLINKMODE, (unsigned long) &dr) < 0) {
++ ast_log(LOG_WARNING, "Unable to set adapter %s link mode to MASTER.\n", adapter->id);
++ }
++ }
++ }
++ hci_read_voice_setting(adapter->hci_socket, &vs, 1000);
++ vs = htobs(vs);
++ if (vs != 0x0060) {
++ ast_log(LOG_ERROR, "Incorrect voice setting for adapter %s, it must be 0x0060 - see 'man hciconfig' for details.\n", adapter->id);
++ hci_close_dev(adapter->hci_socket);
++ ast_free(adapter);
++ } else {
++ AST_RWLIST_WRLOCK(&adapters);
++ AST_RWLIST_INSERT_HEAD(&adapters, adapter, entry);
++ AST_RWLIST_UNLOCK(&adapters);
++ nadapters++;
++ }
++ }
++ }
++ } else
++ ast_log(LOG_ERROR, "id/address missing for adapter %s. It won't be enabled.\n", cat);
++ }
++ cat = ast_category_browse(cfg, cat);
++ }
++
++ if (!nadapters) {
++ ast_log(LOG_WARNING, "***********************************************************************\n");
++ ast_log(LOG_WARNING, "No Adapters defined. Please review mobile.conf. See sample for details.\n");
++ ast_log(LOG_WARNING, "***********************************************************************\n");
++ }
++
++ /* now load devices */
+ cat = ast_category_browse(cfg, NULL);
+ while (cat) {
-+ if (strcasecmp(cat, "general")) {
-+ ast_debug(1, "Loading device %s.\n", cat);
++ if (strcasecmp(cat, "general") && strcasecmp(cat, "adapter")) {
++ ast_log(LOG_DEBUG, "Loading device %s.\n", cat);
+ address = ast_variable_retrieve(cfg, cat, "address");
++ useadapter = ast_variable_retrieve(cfg, cat, "adapter");
+ port = ast_variable_retrieve(cfg, cat, "port");
+ context = ast_variable_retrieve(cfg, cat, "context");
-+ ast_log(LOG_NOTICE, "context for non-general category %s was %s\n", cat, context);
+ type = ast_variable_retrieve(cfg, cat, "type");
+ skip = ast_variable_retrieve(cfg, cat, "dtmfskip");
-+ if (address && port) {
-+ if ((pvt = ast_malloc(sizeof(struct mbl_pvt)))) {
++ group = ast_variable_retrieve(cfg, cat, "group");
++ nocallsetup = ast_variable_retrieve(cfg, cat, "nocallsetup");
++ blackberry = ast_variable_retrieve(cfg, cat, "blackberry");
++ if (!ast_strlen_zero(address) && !ast_strlen_zero(port) && !ast_strlen_zero(useadapter)) {
++ /* find the adapter */
++ AST_RWLIST_RDLOCK(&adapters);
++ AST_RWLIST_TRAVERSE(&adapters, adapter, entry) {
++ if (!strcmp(adapter->id, useadapter))
++ break;
++ }
++ AST_RWLIST_UNLOCK(&adapters);
++ if (!adapter) {
++ ast_log(LOG_ERROR, "Device %s configured to use unknown adapter %s. It won't be enabled.\n", cat, useadapter);
++ break;
++ }
++ if ((pvt = ast_calloc(1, sizeof(*pvt)))) {
+ if (type && !strcmp(type, "headset"))
+ pvt->type = MBL_TYPE_HEADSET;
+ else
+ pvt->type = MBL_TYPE_PHONE;
++
++ if (blackberry)
++ pvt->blackberry = ast_true(blackberry);
++
+ ast_copy_string(pvt->id, cat, sizeof(pvt->id));
-+ ast_copy_string(pvt->bdaddr, address, sizeof(pvt->bdaddr));
++ str2ba(address, &pvt->addr);
+ ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context));
-+ pvt->connected = 0;
++ if (group)
++ pvt->group = atoi(group); /* group 0 if invalid */
+ pvt->state = MBL_STATE_INIT;
+ pvt->rfcomm_socket = -1;
+ pvt->rfcomm_port = atoi(port);
-+ pvt->rfcomm_buf[0] = 0x00;
+ pvt->sco_socket = -1;
+ pvt->monitor_thread = AST_PTHREADT_NULL;
-+ pvt->owner = NULL;
-+ pvt->no_callsetup = 0;
-+ pvt->has_sms = 0;
++ pvt->sco_listener_thread = AST_PTHREADT_NULL;
++ if (!ast_strlen_zero(nocallsetup)) {
++ if ((*nocallsetup == 'y') || (*nocallsetup == 'Y')) {
++ pvt->no_callsetup = 1;
++ ast_log(LOG_DEBUG, "Setting nocallsetup mode for device %s.\n", pvt->id);
++ }
++ }
+ pvt->dsp = ast_dsp_new();
+ if (skip) {
+ if ((pvt->dtmf_skip = atoi(skip)) == 0)
+ pvt->dtmf_skip = 200;
+ } else
+ pvt->dtmf_skip = 200;
-+ pvt->skip_frames = 0;
+ ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT);
+ ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
-+ AST_LIST_INSERT_HEAD(&devices, pvt, entry);
++ pvt->adapter = adapter;
++ AST_RWLIST_WRLOCK(&devices);
++ AST_RWLIST_INSERT_HEAD(&devices, pvt, entry);
++ AST_RWLIST_UNLOCK(&devices);
+ }
+ } else {
-+ ast_log(LOG_ERROR, "Device %s has no address/port configured. It wont be enabled.\n", cat);
++ ast_log(LOG_ERROR, "Device %s has no address/port/adapter configured. It won't be enabled.\n", cat);
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+
+}
+
-+static int reload_module(void)
-+{
-+
-+ return 0;
-+
-+}
-+
+static int unload_module(void)
+{
+
+ struct mbl_pvt *pvt;
++ struct adapter_pvt *adapter;
+
+ /* First, take us out of the channel loop */
+ ast_channel_unregister(&mbl_tech);
+ pthread_cancel(discovery_thread);
+ pthread_join(discovery_thread, NULL);
+ }
-+ /* Kill the sco listener thread */
-+ if (sco_listener_thread != AST_PTHREADT_NULL) {
-+ pthread_cancel(sco_listener_thread);
-+ pthread_join(sco_listener_thread, NULL);
-+ }
-+ if ((close(sco_socket) == -1))
-+ ast_log(LOG_ERROR, "Unable to close sco_socket %d.\n", errno);
-+
-+ /* Unregister the CLI & APP */
-+ ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
-+ ast_unregister_application(app_mblstatus);
-+ ast_unregister_application(app_mblsendsms);
+
+ /* Destroy the device list */
-+ while ((pvt = AST_LIST_REMOVE_HEAD(&devices, entry))) {
++ AST_RWLIST_WRLOCK(&devices);
++ while ((pvt = AST_RWLIST_REMOVE_HEAD(&devices, entry))) {
+ if (pvt->monitor_thread != AST_PTHREADT_NULL) {
+ pthread_cancel(pvt->monitor_thread);
+ pthread_join(pvt->monitor_thread, NULL);
+ }
++ if (pvt->sco_listener_thread != AST_PTHREADT_NULL) {
++ pthread_cancel(pvt->sco_listener_thread);
++ pthread_join(pvt->sco_listener_thread, NULL);
++ }
+ if (pvt->sco_socket > -1) {
+ close(pvt->sco_socket);
+ }
++ if (pvt->adapter->sco_socket > -1) {
++ close(pvt->adapter->sco_socket);
++ }
+ if (pvt->rfcomm_socket > -1) {
+ close(pvt->rfcomm_socket);
+ }
+ ast_dsp_free(pvt->dsp);
-+ free(pvt);
++ ast_free(pvt);
++ }
++ AST_RWLIST_UNLOCK(&devices);
++
++ /* Destroy the adapter list */
++ AST_RWLIST_WRLOCK(&adapters);
++ while ((adapter = AST_RWLIST_REMOVE_HEAD(&adapters, entry))) {
++ hci_close_dev(adapter->hci_socket);
++ ast_free(adapter);
+ }
++ AST_RWLIST_UNLOCK(&adapters);
+
+ if (sdp_session)
+ sdp_close(sdp_session);
+
++ /* Unregister the CLI & APP */
++ ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
++ ast_unregister_application(app_mblstatus);
++ ast_unregister_application(app_mblsendsms);
++
+ return 0;
+
+}
+{
+
+ int dev_id, s;
-+ uint16_t vs;
+
+ /* Check if we have Bluetooth, no point loading otherwise... */
+ dev_id = hci_get_route(NULL);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
-+ hci_read_voice_setting(s, &vs, 1000);
-+ vs = htobs(vs);
-+ if (vs != 0x0060) {
-+ ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060 - see hciconfig hci0 voice.\n");
-+ hci_close_dev(s);
-+ return AST_MODULE_LOAD_DECLINE;
-+ }
-+
+ hci_close_dev(s);
+
+ if (!mbl_load_config()) {
+ ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
+ return AST_MODULE_LOAD_DECLINE;
+ }
-+ /* Spin the sco listener thread */
-+ if (ast_pthread_create_background(&sco_listener_thread, NULL, do_sco_listen, NULL) < 0) {
-+ ast_log(LOG_ERROR, "Unable to create sco listener thread.\n");
-+ pthread_cancel(discovery_thread);
-+ pthread_join(discovery_thread, NULL);
-+ return AST_MODULE_LOAD_DECLINE;
-+ }
+
+ ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
+ ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc);
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver",
+ .load = load_module,
+ .unload = unload_module,
-+ .reload = reload_module,
+);
-diff -Nru asterisk-addons-1.4.6.org/configs/mobile.conf.sample asterisk-addons-1.4.6/configs/mobile.conf.sample
---- asterisk-addons-1.4.6.org/configs/mobile.conf.sample 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-addons-1.4.6/configs/mobile.conf.sample 2008-03-06 08:38:14.000000000 +0100
-@@ -0,0 +1,30 @@
+diff -Nru asterisk-addons-1.4.7.org/configs/mobile.conf.sample asterisk-addons-1.4.7/configs/mobile.conf.sample
+--- asterisk-addons-1.4.7.org/configs/mobile.conf.sample 1970-01-01 01:00:00.000000000 +0100
++++ asterisk-addons-1.4.7/configs/mobile.conf.sample 2009-02-01 08:46:24.000000000 +0100
+@@ -0,0 +1,68 @@
+;
+; mobile.conf
++; configuration file for chan_mobile
+;
+
+[general]
-+interval=60 ; Number of seconds between trying to connect to devices.
++interval=30 ; Number of seconds between trying to connect to devices.
++
++; The following is a list of adapters we use.
++; id must be unique and address is the bdaddr of the adapter from hciconfig.
++; Each adapter may only have one device (headset or phone) connected at a time.
++; Add an [adapter] entry for each adapter you have.
++
++[adapter]
++id=blue
++address=00:09:DD:60:01:A3
++;forcemaster=yes ; attempt to force adapter into master mode. default is no.
++;alignmentdetection=yes ; enable this if you sometimes get 'white noise' on asterisk side of the call
++ ; its a bug in the bluetooth adapter firmware, enabling this will compensate for it.
++ ; default is no.
++
++[adapter]
++id=dlink
++address=00:80:C8:35:52:78
+
+; The following is a list of the devices we deal with.
+; Every device listed below will be available for calls in and out of Asterisk.
-+; Discovered devices not in this list are not available.
++; Each device needs an adapter=xxxx entry which determines which bluetooth adapter is used.
+; Use the CLI command 'mobile search' to discover devices.
+; Use the CLI command 'mobile show devices' to see device status.
+;
-+; To place out through a cell phone use Dial(Mobile/[device]/NNN.....) in your dialplan.
++; To place a call out through a mobile phone use Dial(Mobile/[device]/NNN.....) or Dial(Mobile/gn/NNN......) in your dialplan.
+; To call a headset use Dial(Mobile/[device]).
+
-+;[dave]
-+;address=00:12:56:90:6E:00
-+;port=4
-+;context=incoming-mobile
-+
-+;[blackberry]
-+;address=00:0F:86:0E:AE:42
-+;port=2
-+;context=incoming-mobile
-+
-+;[headset]
-+;address=00:0B:9E:11:74:A5
-+;port=1
-+;type=headset
-diff -Nru asterisk-addons-1.4.6.org/configure.ac asterisk-addons-1.4.6/configure.ac
---- asterisk-addons-1.4.6.org/configure.ac 2008-02-13 23:58:11.000000000 +0100
-+++ asterisk-addons-1.4.6/configure.ac 2008-03-06 08:38:14.000000000 +0100
-@@ -161,13 +161,14 @@
++[LGTU550]
++address=00:E0:91:7F:46:44 ; the address of the phone
++port=4 ; the rfcomm port number (from mobile search)
++context=incoming-mobile ; dialplan context for incoming calls
++adapter=dlink ; adapter to use
++group=1 ; this phone is in channel group 1
++;nocallsetup=yes ; set this only if your phone reports that it supports call progress notification, but does not do it. Motorola L6 for example.
++
++[blackberry]
++address=00:60:57:32:7E:B2
++port=2
++context=incoming-mobile
++adapter=dlink
++group=1
++;blackberry=yes ; set this if you are using a blackberry device
++
++[6310i]
++address=00:60:57:32:7E:B1
++port=13
++context=incoming-mobile
++adapter=dlink
++group=1 ; this phone is in channel group 1 also.
++
++[headset]
++address=00:0B:9E:11:AE:C6
++port=1
++type=headset ; This is a headset, not a Phone !
++adapter=blue
++
++[headset1]
++address=00:0B:9E:11:74:A5
++port=1
++type=headset
++adapter=dlink
+diff -Nru asterisk-addons-1.4.7.org/configure.ac asterisk-addons-1.4.7/configure.ac
+--- asterisk-addons-1.4.7.org/configure.ac 2008-02-13 23:58:11.000000000 +0100
++++ asterisk-addons-1.4.7/configure.ac 2009-02-01 08:48:08.000000000 +0100
+@@ -161,11 +161,13 @@
# from here on down, library checking should be done in alphabetical order
# by the --with option name, to make things easier for the users :-)
+AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h])
AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
--
- AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h])
-
- MYSQL_CONFIG=No
-diff -Nru asterisk-addons-1.4.6.org/doc/chan_mobile.txt asterisk-addons-1.4.6/doc/chan_mobile.txt
---- asterisk-addons-1.4.6.org/doc/chan_mobile.txt 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-addons-1.4.6/doc/chan_mobile.txt 2008-03-06 08:38:14.000000000 +0100
-@@ -0,0 +1,262 @@
-+chan_mobile
-+
-+Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices, and Headsets as FXS devices.
-+
-+Features :-
-+
-+Multiple cell phones can be connected.
-+Multiple headsets can be connected.
-+Asterisk automatically connects to each configured cell phone / headset when it comes in range.
-+CLI command to discover bluetooth devices.
-+Inbound calls on the cell network to the cell phones are handled by Asterisk, just like inbound calls on a Zap channel.
-+CLI passed through on inbound calls.
-+Dial outbound on a cell phone using Dial(Mobile/device/nnnnnnn) in the dialplan.
-+Dial a headset using Dial(Mobile/device) in the dialplan.
-+Application MobileStatus can be used in the dialplan to see if a cell phone / headset is connected.
-+Supports devicestate for dialplan hinting.
-+Supports Inbound and Outbound SMS.
-+
-+Using chan_mobile :-
-+
-+In order to use chan_mobile, you must have a working bluetooth subsystem on your Asterisk box.
-+This means a working bluetooth adapter, and the BlueZ packages.
-+
-+Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles.
-+
-+The BlueZ package you need is bluez-utils. If you are using a GUI then you might want to install bluez-pin also.
-+You also need libbluetooth, and libbluetooth-dev if you are compiling Asterisk from source.
-+
-+You need to get bluetooth working with your phone before attempting to use chan_mobile.
-+This means 'pairing' your phone with your Asterisk box. I dont describe how to do this here as the process
-+differs from distro to distro. You only need to pair once.
-+
-+However, the easist way to pair, is to use you cell phone to search for bluetooth devices, select your Asterisk box
-+and enter the requested PIN.
-+
-+See www.bluez.org for other details about setting up Bluetooth under Linux.
-+
-+Assuming you have bluetooth working ok:-
-+
-+Load chan_mobile.so
-+
-+Search for your bluetooth devices using the CLI command 'mobile search'. Be patient with this command as
-+it will take 8 - 10 seconds to do the discovery.
-+
-+Headsets will generally have to be put into 'pairing' mode before they will show up here.
-+
-+This will return something like the following :-
-+
-+*CLI> mobile search
-+Address Name Usable Type Port
-+00:12:56:90:6E:00 LG TU500 Yes Phone 4
-+00:80:C8:35:52:78 Toaster No Headset 0
-+00:0B:9E:11:74:A5 Hello II Plus Yes Headset 1
-+00:0F:86:0E:AE:42 Daves Blackberry Yes Phone 7
-+
-+This is a list of all bluetooth devices seen and whether or not they are usable with chan_cellphone.
-+The Address field contains the 'bd address' of the device. This is like an ethernet mac address.
-+The Name field is whatever is configured into the device as its name.
-+The Usable field tells you whether or not the device supports the Bluetooth Handsfree Profile or Headset profile.
-+The Type field tells you whether the device is usable as a Phone line (FXO) or a headset (FXS)
-+The Port field is the number to put in the configuration file.
-+
-+Choose which device(s) you want to use and edit /etc/asterisk/mobile.conf. There is a sample included
-+with the Asterisk source under configs/mobile.conf.sample.
-+
-+Assuming we want to use the devices above, mobile.conf needs to look like this :-
-+
-+===================================================================================
-+;
-+; mobile.conf
-+;
-+
-+[general]
-+interval=60 ; Number of seconds between trying to connect to devices.
-+
-+; The following is a list of the devices we deal with.
-+; Every device listed below will be available for calls in and out of Asterisk.
-+; Discovered devices not in this list are not available.
-+; Use the CLI command 'mobile search' to discover devices.
-+; Use the CLI command 'mobile show devices' to see device status.
-+;
-+; To place a call use Dial(Mobile/[device]/NNN.....) in your dialplan.
-+
-+[dave]
-+address=00:12:56:90:6E:00
-+port=4
-+context=incoming-mobile
-+
-+[headset]
-+address=00:0B:9E:11:74:A5
-+port=1
-+type=headset
-+===================================================================================
-+
-+Be sure to configure the right bd address and port number from the search. If you want inbound
-+calls on a device to go to a specific context, add a context= line, otherwise the default will
-+be used. The 'id' of the device [bitinbrackets] can be anything you like, just make the unique.
-+
-+If your are configuring a Headset be sure to include the type=headset line, if left out it defaults
-+to phone.
-+
-+Having done this, unload chan_mobile and load it again.
-+
-+The CLI command 'mobile show devices' can be used at any time to show the status of configured devices,
-+and whether or not the device is capable of sending / receiving SMS via bluetooth.
-+
-+*CLI> mobile show devices
-+ID Address Connected State SMS
-+blackberry 00:0F:86:0E:AE:42 Yes Free Yes
-+dave 00:12:56:90:6E:00 Yes Free No
-+headset 00:0B:9E:11:74:A5 Yes Free No
-+*CLI>
-+
-+
-+All being well Asterisk will now try and establish a connection to each configured device. If it cant
-+it will retry after 'interval' seconds, infinately.
-+
-+This means that as your cell phone comes into range and goes out of range, Asterisk will automatically
-+connect and disconnect from it. You dont need to worry about it.
-+
-+As each phone is connected you will see a message on the Asterisk console :-
-+
-+ Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver)
-+ -- Bluetooth Device blackberry has connected.
-+ -- Bluetooth Device dave has connected.
-+
-+If someone calls your cell phone now, Asterisk will handle the call and it will be sent into the
-+context you specified, or the default context. Mostly likely this means some SIP phone somewhere will
-+ring, pick it up and take the call.
-+
-+To make outbound calls, add something to you Dialplan like the following :- (modify to suit)
-+
-+; Calls via TU500
-+exten => _9X.,1,Dial(Mobile/dave/${EXTEN:1},45)
-+exten => _9X.,n,Hangup
-+; Calls via Blackberry
-+exten => _8X.,1,Dial(Mobile/blackberry/${EXTEN:1},45)
-+exten => _8X.,n,Hangup
-+
-+Pick up a SIP phone and dial 9<number of pizza shop> and the call vill go via the device 'dave' in
-+mobile.conf.
-+
-+To incoming calls to a headset do something like this :-
-+
-+[incoming-context]
-+exten => s,1,Dial(Mobile/headset,30)
-+exten => s,n,Hangup()
-+
-+To dial out on a headset, you need to use some other mechanism, because the headset is not likely
-+to have all the needed buttons on it. res_clioriginate is good for this :-
-+
-+*CLI> originate Mobile/headset extension NNNNN@context
-+
-+This will call your headset, once you answer Asterisk will call NNNNN at context context
-+
-+Dialplan hints :-
-+
-+chan_mobile supports 'device status' so you can do somthing like
-+
-+exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry
-+
-+
-+MobileStatus Application :-
-+
-+chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan
-+to determine the 'state' of a device.
-+
-+For example, suppose you wanted to call dave's extension, but only if he was in the office. You could
-+test to see if his cell phone was attached to Asterisk, if it is dial his extension, otherwise dial his
-+cell phone.
-+
-+exten => 40,1,MobileStatus(dave,DAVECELL)
-+exten => 40,2,GotoIf($["${DAVECELL}" = "1"]?3:5)
-+exten => 40,3,Dial(ZAP/g1/0427466412,45,tT)
-+exten => 40,4,Hangup
-+exten => 40,5,Dial(SIP/40,45,tT)
-+exten => 40,6,Hangup
-+
-+MobileStatus sets the value of the given variable to :-
-+
-+1 = Disconnected. i.e. Device not in range of Asterisk, or turned off etc etc
-+2 = Connected and Not on a call. i.e. Free
-+3 = Connected and on a call. i.e. Busy
-+
-+
-+SMS Sending / Receiving
-+
-+If Asterisk has detected your cell phone is capable of SMS via bluetooth, you will be able to send and
-+receive SMS.
-+
-+Incoming SMS's cause Asterisk to create an inbound call to the context you defined in mobile.conf or the default
-+context if you did not define one. The call will start at extension 'sms'. Two channel variables will be available,
-+SMSSRC = the number of the originator of the SMS and SMSTXT which is the text of the SMS.
-+This is not a voice call, so grab the values of the variables and hang the call up.
-+
-+So, to handle incoming SMS's, do something like the following in your dialplan
-+
-+[incoming-mobile]
-+exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT})
-+exten => sms,n,Hangup()
-+
-+The above will just print the message on the console.
-+
-+If you use res_jabber, you could do something like this :-
-+
-+[incoming-mobile]
-+exten => sms,1,JabberSend(transport,user@jabber.somewhere.com,SMS from ${SMSRC} ${SMSTXT})
-+exten => sms,2,Hangup()
-+
-+To send an SMS, use the application MobileSendSMS like the following :-
-+
-+exten => 99,1,MobileSendSMS(dave,0427123456,Hello World)
-+
-+This will send 'Hello World' via device 'dave' to '0427123456'
-+
-+
-+DTMF Debouncing :-
-+
-+DTMF detection varies from phone to phone. There is a configuration variable that allows you to tune
-+this to your needs. e.g. in mobile.conf
-+
-+[dave]
-+address=00:12:56:90:6E:00
-+port=4
-+context=incoming-mobile
-+dtmfskip=50
-+
-+change dtmfskip to suit your phone. The default is 200. The larger the number, the more chance of missed DTMF.
-+The smaller the number the more chance of multiple digits being detected.
-+
-+
-+Debugging :-
-+
-+Different phone manufacturers have different interpretations of the Bluetooth Handsfree Profile Spec.
-+This means that not all phones work the same way, particularly in the connection setup / initialisation
-+sequence. I've tried to make chan_cellphone as general as possible, but it may need modification to
-+support some phone i've never tested.
-+
-+The RIM Blackberry 7250 works extremely well. So does the LG TU500.
-+
-+Some phones, most notably Sony Ericsson 'T' series, dont quite conform to the Bluetooth HFP spec.
-+chan_mobile will detect these and adapt accordingly. The T-610 and T-630 have been tested and
-+work fine.
-+
-+If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 1'.
-+
-+This will log a bunch of debug messages indicating what the phone is doing, importantly the rfcomm
-+conversation between Asterisk and the phone. This can be used to sort out what your phone is doing
-+and make chan_mobile support it.
-+
-+Be aware also, that just about all cell phones behave differently. For example my LG TU500 wont dial unless
-+the phone is a the 'idle' screen. i.e. if the phone is showing a 'menu' on the display, when you dial via
-+Asterisk, the call will not work. chan_mobile handles this, but there may be other phones that do
-+other things too...
-+
-+Important: Watch what your cell phone is doing the first few times. Asterisk wont make random calls but
-+if chan_mobile fails to hangup for some reason and you get a huge bill from your telco, dont blame me.
-+
-+
-+Feedback, Support, Please can you make Cell Phone X work... etc :-
-+
-+email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc.
-diff -Nru asterisk-addons-1.4.6.org/Makefile asterisk-addons-1.4.6/Makefile
---- asterisk-addons-1.4.6.org/Makefile 2008-02-13 23:58:11.000000000 +0100
-+++ asterisk-addons-1.4.6/Makefile 2008-03-06 08:38:14.000000000 +0100
-@@ -215,6 +215,8 @@
-
- gmenuconfig: gmenuselect
-+menuconfig: menuselect
-+
- menuselect: menuselect/menuselect menuselect-tree
- -@menuselect/menuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && echo "menuselect changes saved!" || echo "menuselect changes NOT saved!"
-
-diff -Nru asterisk-addons-1.4.6.org/makeopts.in asterisk-addons-1.4.6/makeopts.in
---- asterisk-addons-1.4.6.org/makeopts.in 2008-02-13 23:58:11.000000000 +0100
-+++ asterisk-addons-1.4.6/makeopts.in 2008-03-06 08:38:14.000000000 +0100
+ AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h])
+diff -Nru asterisk-addons-1.4.7.org/makeopts.in asterisk-addons-1.4.7/makeopts.in
+--- asterisk-addons-1.4.7.org/makeopts.in 2008-02-13 23:58:11.000000000 +0100
++++ asterisk-addons-1.4.7/makeopts.in 2009-02-01 08:49:46.000000000 +0100
@@ -34,6 +34,9 @@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@