+++ /dev/null
-diff -Nru asterisk-addons-1.4.8.org/build_tools/menuselect-deps.in asterisk-addons-1.4.8/build_tools/menuselect-deps.in
---- asterisk-addons-1.4.8.org/build_tools/menuselect-deps.in 2007-05-14 18:22:44.000000000 +0200
-+++ asterisk-addons-1.4.8/build_tools/menuselect-deps.in 2009-06-04 22:20:03.000000000 +0200
-@@ -1,2 +1,3 @@
-+BLUETOOTH=@PBX_BLUETOOTH@
- MYSQLCLIENT=@PBX_MYSQLCLIENT@
- ASTERISK=@PBX_ASTERISK@
-diff -Nru asterisk-addons-1.4.8.org/channels/chan_mobile.c asterisk-addons-1.4.8/channels/chan_mobile.c
---- asterisk-addons-1.4.8.org/channels/chan_mobile.c 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-addons-1.4.8/channels/chan_mobile.c 2009-06-04 22:20:03.000000000 +0200
-@@ -0,0 +1,2150 @@
-+/*
-+ * Asterisk -- An open source telephony toolkit.
-+ *
-+ * Copyright (C) 1999 - 2006, Digium, Inc.
-+ *
-+ * Mark Spencer <markster@digium.com>
-+ *
-+ * See http://www.asterisk.org for more information about
-+ * the Asterisk project. Please do not directly contact
-+ * any of the maintainers of this project for assistance;
-+ * the project provides a web site, mailing lists and IRC
-+ * channels for your use.
-+ *
-+ * 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.
-+ */
-+
-+/*! \file
-+ *
-+ * \brief Bluetooth Mobile Device channel driver
-+ *
-+ * \author Dave Bowerman <david.bowerman@gmail.com>
-+ *
-+ * \ingroup channel_drivers
-+ */
-+
-+/*** MODULEINFO
-+ <depend>bluetooth</depend>
-+ ***/
-+
-+#include <asterisk.h>
-+
-+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-+
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/socket.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 <signal.h>
-+
-+#include <bluetooth/bluetooth.h>
-+#include <bluetooth/hci.h>
-+#include <bluetooth/hci_lib.h>
-+#include <bluetooth/sdp.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/config.h>
-+#include <asterisk/logger.h>
-+#include <asterisk/module.h>
-+#include <asterisk/pbx.h>
-+#include <asterisk/options.h>
-+#include <asterisk/utils.h>
-+#include <asterisk/linkedlists.h>
-+#include <asterisk/cli.h>
-+#include <asterisk/devicestate.h>
-+#include <asterisk/causes.h>
-+#include <asterisk/dsp.h>
-+#include <asterisk/app.h>
-+#include <asterisk/manager.h>
-+
-+#define MBL_CONFIG "mobile.conf"
-+
-+#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 pthread_t discovery_thread = AST_PTHREADT_NULL; /* The discovery thread */
-+static sdp_session_t *sdp_session;
-+
-+enum mbl_type {
-+ MBL_TYPE_PHONE,
-+ MBL_TYPE_HEADSET
-+};
-+
-+enum mbl_state {
-+ MBL_STATE_INIT = 0,
-+ MBL_STATE_INIT1,
-+ MBL_STATE_INIT2,
-+ MBL_STATE_INIT3,
-+ MBL_STATE_INIT4,
-+ MBL_STATE_INIT5,
-+ MBL_STATE_INIT6,
-+ MBL_STATE_INIT7,
-+ MBL_STATE_PREIDLE,
-+ MBL_STATE_IDLE,
-+ MBL_STATE_DIAL,
-+ MBL_STATE_DIAL1,
-+ MBL_STATE_OUTGOING,
-+ MBL_STATE_RING,
-+ MBL_STATE_RING2,
-+ MBL_STATE_RING3,
-+ MBL_STATE_INCOMING,
-+ MBL_STATE_HANGUP,
-+ MBL_STATE_INSMS,
-+ MBL_STATE_OUTSMS,
-+ MBL_STATE_OUTSMS1,
-+ 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 */
-+ 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];
-+ 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[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 hangup_count;
-+ AST_LIST_ENTRY(mbl_pvt) entry;
-+};
-+
-+static AST_RWLIST_HEAD_STATIC(devices, mbl_pvt);
-+
-+/* CLI stuff */
-+static const char show_usage[] =
-+"Usage: mobile show devices\n"
-+" Shows the state of Bluetooth Cell / Mobile devices.\n";
-+
-+static const char search_usage[] =
-+"Usage: mobile search\n"
-+" Searches for Bluetooth Cell / Mobile devices in range.\n";
-+
-+static const char rfcomm_usage[] =
-+"Usage: mobile rfcomm command\n"
-+" Send command to the rfcomm port.\n";
-+
-+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}, 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 */
-+static char *app_mblstatus = "MobileStatus";
-+static char *mblstatus_synopsis = "MobileStatus(Device,Variable)";
-+static char *mblstatus_desc =
-+"MobileStatus(Device,Variable)\n"
-+" Device - Id of mobile device from mobile.conf\n"
-+" Variable - Variable to store status in will be 1-3.\n"
-+" In order, Disconnected, Connected & Free, Connected & Busy.\n";
-+
-+static char *app_mblsendsms = "MobileSendSMS";
-+static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)";
-+static char *mblsendsms_desc =
-+"MobileSendSms(Device,Dest,Message)\n"
-+" Device - Id of device from mobile.conf\n"
-+" 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_answer(struct ast_channel *ast);
-+static int mbl_digit_begin(struct ast_channel *ast, char digit);
-+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
-+static struct ast_frame *mbl_read(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 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(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 = {
-+ .type = "Mobile",
-+ .description = "Bluetooth Mobile Device Channel Driver",
-+ .capabilities = AST_FORMAT_SLINEAR,
-+ .requester = mbl_request,
-+ .call = mbl_call,
-+ .hangup = mbl_hangup,
-+ .answer = mbl_answer,
-+ .send_digit_begin = mbl_digit_begin,
-+ .send_digit_end = mbl_digit_end,
-+ .read = mbl_read,
-+ .write = mbl_write,
-+ .fixup = mbl_fixup,
-+ .devicestate = mbl_devicestate
-+};
-+
-+/* 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);
-+
-+#undef FORMAT1
-+
-+ return RESULT_SUCCESS;
-+}
-+
-+static int handle_cli_mobile_search(int fd, int argc, char **argv)
-+{
-+ struct adapter_pvt *adapter;
-+ inquiry_info *ii = NULL;
-+ int max_rsp, num_rsp;
-+ int len, flags;
-+ int i, phport, hsport;
-+ char addr[19] = {0};
-+ char name[31] = {0};
-+
-+#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;
-+ }
-+
-+ len = 8;
-+ max_rsp = 255;
-+ flags = IREQ_CACHE_FLUSH;
-+
-+ 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, FORMAT1, "Address", "Name", "Usable", "Type", "Port");
-+ for (i = 0; i < num_rsp; i++) {
-+ ba2str(&(ii + i)->bdaddr, addr);
-+ name[0] = 0x00;
-+ 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, 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");
-+
-+#undef FORMAT1
-+#undef FORMAT2
-+
-+ return RESULT_SUCCESS;
-+}
-+
-+static int handle_cli_mobile_rfcomm(int fd, int argc, char **argv)
-+{
-+ char buf[128];
-+ struct mbl_pvt *pvt = NULL;
-+
-+ 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) {
-+ ast_cli(fd, "Device %s not found.\n", argv[2]);
-+ return RESULT_SUCCESS;
-+ }
-+
-+ 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 *parse;
-+ int stat;
-+ char status[2];
-+
-+ AST_DECLARE_APP_ARGS(args,
-+ AST_APP_ARG(device);
-+ AST_APP_ARG(variable);
-+ );
-+
-+ if (ast_strlen_zero(data))
-+ return -1;
-+
-+ 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_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 = 2;
-+ if (pvt->owner)
-+ stat = 3;
-+ }
-+
-+ snprintf(status, sizeof(status), "%d", stat);
-+ pbx_builtin_setvar_helper(ast, args.variable, status);
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_sendsms_exec(struct ast_channel *ast, void *data)
-+{
-+
-+ struct mbl_pvt *pvt;
-+ char *parse;
-+
-+ AST_DECLARE_APP_ARGS(args,
-+ AST_APP_ARG(device);
-+ AST_APP_ARG(dest);
-+ AST_APP_ARG(message);
-+ );
-+
-+ if (ast_strlen_zero(data))
-+ return -1;
-+
-+ 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;
-+ }
-+
-+ 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", args.device);
-+ return -1;
-+ }
-+
-+ if (!pvt->connected) {
-+ 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", 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", args.device);
-+ return -1;
-+ }
-+
-+ 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 ast_channel *chn = NULL;
-+ struct mbl_pvt *pvt;
-+ char *dest_dev = NULL;
-+ char *dest_num = NULL;
-+ int oldformat, group = -1;
-+
-+ if (!data) {
-+ ast_log(LOG_WARNING, "Channel requested with no data\n");
-+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
-+ return NULL;
-+ }
-+
-+ oldformat = format;
-+ format &= (AST_FORMAT_SLINEAR);
-+ if (!format) {
-+ ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat);
-+ *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
-+ return NULL;
-+ }
-+
-+ dest_dev = ast_strdupa((char *)data);
-+
-+ dest_num = strchr(dest_dev, '/');
-+ if (dest_num)
-+ *dest_num++ = 0x00;
-+
-+ 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;
-+ return NULL;
-+ }
-+
-+ if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
-+ ast_log(LOG_WARNING, "Can't determine destination number.\n");
-+ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
-+ return NULL;
-+ }
-+
-+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
-+ if (!chn) {
-+ ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
-+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
-+ return NULL;
-+ }
-+
-+ return chn;
-+
-+}
-+
-+static int mbl_call(struct ast_channel *ast, char *dest, int timeout)
-+{
-+
-+ struct mbl_pvt *pvt;
-+ char *dest_dev = NULL;
-+ char *dest_num = NULL;
-+
-+ dest_dev = ast_strdupa((char *)dest);
-+
-+ pvt = ast->tech_pvt;
-+
-+ if (pvt->type == MBL_TYPE_PHONE) {
-+ dest_num = strchr(dest_dev, '/');
-+ if (!dest_num) {
-+ ast_log(LOG_WARNING, "Cant determine destination number.\n");
-+ return -1;
-+ }
-+ *dest_num++ = 0x00;
-+ }
-+
-+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
-+ ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name);
-+ return -1;
-+ }
-+
-+ 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_DIAL;
-+ pvt->dial_timeout = (timeout == 0) ? 30 : timeout;
-+ } else {
-+ pvt->state = MBL_STATE_RING;
-+ }
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_hangup(struct ast_channel *ast)
-+{
-+
-+ struct mbl_pvt *pvt;
-+
-+ if (!ast->tech_pvt) {
-+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
-+ return 0;
-+ }
-+ pvt = ast->tech_pvt;
-+
-+ ast_log(LOG_DEBUG, "Hanging up device %s.\n", pvt->id);
-+
-+ ast->fds[0] = -1;
-+
-+ close(pvt->io_pipe[0]);
-+ close(pvt->io_pipe[1]);
-+
-+ if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) {
-+ close(pvt->sco_socket);
-+ pvt->sco_socket = -1;
-+ }
-+
-+ 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) {
-+ if (pvt->do_hangup) {
-+ rfcomm_write(pvt, "AT+CHUP\r");
-+ }
-+ pvt->state = MBL_STATE_HANGUP;
-+ pvt->hangup_count = 0;
-+ } else
-+ pvt->state = MBL_STATE_IDLE;
-+
-+ pvt->owner = NULL;
-+ ast->tech_pvt = NULL;
-+ ast_setstate(ast, AST_STATE_DOWN);
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_answer(struct ast_channel *ast)
-+{
-+
-+ struct mbl_pvt *pvt;
-+
-+ pvt = ast->tech_pvt;
-+
-+ rfcomm_write(pvt, "ATA\r");
-+
-+ ast_setstate(ast, AST_STATE_UP);
-+
-+ pvt->sent_answer = 1;
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_digit_begin(struct ast_channel *chan, char digit)
-+{
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
-+{
-+
-+ struct mbl_pvt *pvt;
-+ char buf[11];
-+
-+ pvt = ast->tech_pvt;
-+
-+ if (pvt->type == MBL_TYPE_HEADSET)
-+ return 0;
-+
-+ ast_log(LOG_DEBUG, "Dialed %c\n", digit);
-+
-+ switch(digit) {
-+ case '0':
-+ case '1':
-+ case '2':
-+ case '3':
-+ case '4':
-+ case '5':
-+ case '6':
-+ case '7':
-+ case '8':
-+ case '9':
-+ case '*':
-+ case '#':
-+ snprintf(buf, sizeof(buf), "AT+VTS=%c\r", digit);
-+ rfcomm_write(pvt, buf);
-+ break;
-+ default:
-+ ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
-+ return -1;
-+ }
-+
-+ return 0;
-+
-+}
-+
-+static struct ast_frame *mbl_read(struct ast_channel *ast)
-+{
-+
-+ struct mbl_pvt *pvt = ast->tech_pvt;
-+ struct ast_frame *f;
-+ int r;
-+
-+ //ast_log(LOG_DEBUG, "*** mbl_read()\n");
-+
-+ if (!pvt->owner) {
-+ return &ast_null_frame;
-+ }
-+ memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
-+ pvt->fr.frametype = AST_FRAME_VOICE;
-+ 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->fr.datalen = r;
-+ pvt->fr.samples = r / 2;
-+ }
-+ }
-+
-+ 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;
-+ }
-+
-+ return &pvt->fr;
-+
-+}
-+
-+static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
-+{
-+
-+ struct mbl_pvt *pvt = ast->tech_pvt;
-+ int i, r, io_need, num_frames;
-+ char *pfr, buf[DEVICE_FRAME_SIZE];
-+
-+ //ast_log(LOG_DEBUG, "*** mbl_write\n");
-+
-+ if (frame->frametype != AST_FRAME_VOICE) {
-+ return 0;
-+ }
-+
-+ 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);
-+ }
-+ }
-+
-+ num_frames = (frame->datalen - io_need) / DEVICE_FRAME_SIZE;
-+ pfr = frame->data + io_need;
-+
-+ for (i=0; i<num_frames; i++) {
-+ 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 += 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);
-+ }
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
-+{
-+
-+ struct mbl_pvt *pvt = oldchan->tech_pvt;
-+
-+ if (pvt && pvt->owner == oldchan)
-+ pvt->owner = newchan;
-+
-+ return 0;
-+
-+}
-+
-+static int mbl_devicestate(void *data)
-+{
-+
-+ char *device;
-+ int res = AST_DEVICE_INVALID;
-+ struct mbl_pvt *pvt;
-+
-+ device = ast_strdupa(S_OR(data, ""));
-+
-+ ast_log(LOG_DEBUG, "Checking device state for device %s\n", device);
-+
-+ 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) {
-+ if (pvt->owner)
-+ res = AST_DEVICE_INUSE;
-+ else
-+ res = AST_DEVICE_NOT_INUSE;
-+ }
-+ }
-+
-+ return res;
-+
-+}
-+
-+/*
-+
-+ 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)
-+{
-+
-+ int i;
-+ short a, *s;
-+ char *p;
-+
-+ 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;
-+ }
-+
-+ 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;
-+
-+}
-+
-+/*
-+
-+ rfcomm helpers
-+
-+*/
-+
-+static int rfcomm_connect(bdaddr_t src, bdaddr_t dst, int remote_channel) {
-+
-+ struct sockaddr_rc addr;
-+ int s;
-+
-+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
-+ 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;
-+ }
-+
-+ memset(&addr, 0, sizeof(addr));
-+ addr.rc_family = AF_BLUETOOTH;
-+ bacpy(&addr.rc_bdaddr, &dst);
-+ addr.rc_channel = remote_channel;
-+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ ast_log(LOG_DEBUG, "connect() failed (%d).\n", errno);
-+ close(s);
-+ return -1;
-+ }
-+
-+ return s;
-+
-+}
-+
-+static int rfcomm_write(struct mbl_pvt *pvt, char *buf)
-+{
-+
-+ char *p;
-+ ssize_t num_write;
-+ int len;
-+
-+ 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_log(LOG_DEBUG, "rfcomm_write() error [%d]\n", errno);
-+ return 0;
-+ }
-+ len -= num_write;
-+ p += num_write;
-+ }
-+
-+ return 1;
-+
-+}
-+
-+/*
-+
-+ Here we need to return complete '\r' terminated single responses to the devices monitor thread, or
-+ a timeout if nothing is available.
-+ The rfcomm connection to the device is asynchronous, so there is no guarantee that responses will
-+ be returned in a single read() call. We handle this by buffering the input and returning one response
-+ per call, or a timeout if nothing is available.
-+
-+*/
-+
-+static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout)
-+{
-+
-+ int sel, rlen, slen;
-+ fd_set rfds;
-+ struct timeval tv;
-+ char *p;
-+
-+ if (!flush) {
-+ if ((p = strchr(pvt->rfcomm_buf, '\r'))) {
-+ *p++ = 0x00;
-+ if (*p == '\n')
-+ p++;
-+ memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf));
-+ *(buf + strlen(pvt->rfcomm_buf)) = 0x00;
-+ memmove(pvt->rfcomm_buf, p, strlen(p));
-+ *(pvt->rfcomm_buf+strlen(p)) = 0x00;
-+ return 1;
-+ }
-+ } else {
-+ pvt->rfcomm_buf[0] = 0x00;
-+ }
-+
-+ FD_ZERO(&rfds);
-+ FD_SET(pvt->rfcomm_socket, &rfds);
-+
-+ tv.tv_sec = timeout;
-+ tv.tv_usec = 0;
-+
-+ if ((sel = select(pvt->rfcomm_socket + 1, &rfds, NULL, NULL, &tv)) > 0) {
-+ if (FD_ISSET(pvt->rfcomm_socket, &rfds)) {
-+ slen = strlen(pvt->rfcomm_buf);
-+ rlen = read(pvt->rfcomm_socket, pvt->rfcomm_buf + slen, sizeof(pvt->rfcomm_buf) - slen - 1);
-+ if (rlen > 0) {
-+ pvt->rfcomm_buf[slen+rlen] = 0x00;
-+ if ((p = strchr(pvt->rfcomm_buf, '\r'))) {
-+ *p++ = 0x00;
-+ if (*p == '\n')
-+ p++;
-+ memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf));
-+ *(buf + strlen(pvt->rfcomm_buf)) = 0x00;
-+ memmove(pvt->rfcomm_buf, p, strlen(p));
-+ *(pvt->rfcomm_buf+strlen(p)) = 0x00;
-+ return 1;
-+ }
-+ } else
-+ return rlen;
-+ }
-+ } else if (sel == 0) { /* timeout */
-+ return 0;
-+ }
-+
-+ return 1;
-+
-+}
-+
-+/*
-+
-+ sco helpers
-+
-+*/
-+
-+static int sco_connect(bdaddr_t src, bdaddr_t dst)
-+{
-+
-+ struct sockaddr_sco addr;
-+ int s;
-+
-+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
-+ 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;
-+ }
-+
-+ memset(&addr, 0, sizeof(addr));
-+ addr.sco_family = AF_BLUETOOTH;
-+ bacpy(&addr.sco_bdaddr, &dst);
-+
-+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-+ ast_log(LOG_DEBUG, "sco connect() failed (%d).\n", errno);
-+ close(s);
-+ return -1;
-+ }
-+
-+ return s;
-+
-+}
-+
-+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 helpers
-+
-+*/
-+
-+static int sdp_search(char *addr, int profile)
-+{
-+
-+ sdp_session_t *session = 0;
-+ bdaddr_t bdaddr;
-+ uuid_t svc_uuid;
-+ uint32_t range = 0x0000ffff;
-+ sdp_list_t *response_list, *search_list, *attrid_list;
-+ int status, port;
-+ sdp_list_t *proto_list;
-+ sdp_record_t *sdprec;
-+
-+ str2ba(addr, &bdaddr);
-+ port = 0;
-+ session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
-+ if (!session) {
-+ ast_log(LOG_DEBUG, "sdp_connect() failed on device %s.\n", addr);
-+ return 0;
-+ }
-+
-+ sdp_uuid32_create(&svc_uuid, profile);
-+ search_list = sdp_list_append(0, &svc_uuid);
-+ attrid_list = sdp_list_append(0, &range);
-+ response_list = 0x00;
-+ status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
-+ if (status == 0) {
-+ if (response_list) {
-+ sdprec = (sdp_record_t *) response_list->data;
-+ proto_list = 0x00;
-+ if (sdp_get_access_protos(sdprec, &proto_list) == 0) {
-+ port = sdp_get_proto_port(proto_list, RFCOMM_UUID);
-+ sdp_list_free(proto_list, 0);
-+ }
-+ sdp_record_free(sdprec);
-+ sdp_list_free(response_list, 0);
-+ } else
-+ ast_log(LOG_DEBUG, "No responses returned for device %s.\n", addr);
-+ } else
-+ 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_close(session);
-+
-+ return port;
-+
-+}
-+
-+static sdp_session_t *sdp_register(void)
-+{
-+
-+ uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID};
-+ uint8_t rfcomm_channel = 1;
-+ const char *service_name = "Asterisk PABX";
-+ const char *service_dsc = "Asterisk PABX";
-+ const char *service_prov = "Asterisk";
-+
-+ uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid;
-+ sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0;
-+ sdp_data_t *channel = 0;
-+
-+ int err = 0;
-+ sdp_session_t *session = 0;
-+
-+ sdp_record_t *record = sdp_record_alloc();
-+
-+ sdp_uuid128_create(&svc_uuid, &service_uuid_int);
-+ sdp_set_service_id(record, svc_uuid);
-+
-+ sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID);
-+ sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID);
-+
-+ svc_uuid_list = sdp_list_append(0, &svc_class1_uuid);
-+ svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid);
-+ sdp_set_service_classes(record, svc_uuid_list);
-+
-+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-+ root_list = sdp_list_append(0, &root_uuid);
-+ sdp_set_browse_groups( record, root_list );
-+
-+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
-+ l2cap_list = sdp_list_append(0, &l2cap_uuid);
-+ proto_list = sdp_list_append(0, l2cap_list);
-+
-+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
-+ channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
-+ rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
-+ sdp_list_append(rfcomm_list, channel);
-+ sdp_list_append(proto_list, rfcomm_list);
-+
-+ access_proto_list = sdp_list_append(0, proto_list);
-+ sdp_set_access_protos(record, access_proto_list);
-+
-+ sdp_set_info_attr(record, service_name, service_prov, service_dsc);
-+
-+ if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY)))
-+ ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n");
-+ else
-+ err = sdp_record_register(session, record, 0);
-+
-+ sdp_data_free(channel);
-+ sdp_list_free(rfcomm_list, 0);
-+ sdp_list_free(root_list, 0);
-+ sdp_list_free(access_proto_list, 0);
-+ sdp_list_free(svc_uuid_list, 0);
-+
-+ return session;
-+
-+}
-+
-+/*
-+
-+ Thread routines
-+
-+*/
-+
-+static void *do_monitor_phone(void *data)
-+{
-+
-+ struct mbl_pvt *pvt = (struct mbl_pvt *)data;
-+ struct ast_channel *chn;
-+ char monitor = 1;
-+ char buf[256];
-+ char cid_num[AST_MAX_EXTENSION], *pcids, *pcide;
-+ int s, t, i, smsi;
-+ int group, group2;
-+ int callp = 0, callsetupp;
-+ char brsf, nsmode, *p, *p1;
-+ char sms_src[13];
-+ char sms_txt[160];
-+
-+ brsf = nsmode = 0;
-+
-+ if (!rfcomm_write(pvt, "AT+BRSF=4\r"))
-+ monitor = 0;
-+
-+ while (monitor) {
-+
-+ if (pvt->state == MBL_STATE_DIAL1)
-+ t = pvt->dial_timeout;
-+ else if (pvt->state == MBL_STATE_HANGUP)
-+ t = 2;
-+ else if (pvt->state == MBL_STATE_OUTSMS1)
-+ t = 2;
-+ else if (pvt->state == MBL_STATE_OUTSMS2)
-+ t = 10;
-+ else
-+ t = 1;
-+
-+ s = rfcomm_read(pvt, buf, 0, t);
-+
-+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
-+ 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 */
-+ rfcomm_write(pvt, "AT+CIND=?\r");
-+ pvt->state++;
-+ nsmode = 1;
-+ } else if (strstr(buf, "OK") && brsf) {
-+ 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:
-+ if (strstr(buf, "+CIND:")) {
-+ group = callp = callsetupp = 0;
-+ group2 = 1;
-+ for (i=0; i<strlen(buf); i++) {
-+ if (buf[i] == '(')
-+ group++;
-+ if (buf[i] == ')') {
-+ group--;
-+ if (group == 0)
-+ group2++;
-+ }
-+ if (strstr(buf+i, "\"call\""))
-+ callp = group2;
-+ if (strstr(buf+i, "\"call_setup\""))
-+ callsetupp = group2;
-+ if (strstr(buf+i, "\"callsetup\""))
-+ callsetupp = group2;
-+ }
-+ 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_log(LOG_DEBUG, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp);
-+ }
-+ if (strstr(buf, "OK")) {
-+ rfcomm_write(pvt, "AT+CIND?\r");
-+ pvt->state++;
-+ }
-+ break;
-+ case MBL_STATE_INIT2:
-+ if ((p = strstr(buf, "+CIND:"))) {
-+ p += 5;
-+ if (*(p+(callp*2)) == '1') {
-+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id);
-+ monitor = 0;
-+ }
-+ } else if (strstr(buf, "OK")) {
-+ 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")) {
-+ 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+VGS=15\r");
-+ pvt->state++;
-+ }
-+ break;
-+ case MBL_STATE_INIT5:
-+ 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_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;
-+ 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_log(LOG_DEBUG, "Device %s [%s]\n", pvt->id, buf);
-+ if (strstr(buf, "RING")) {
-+ pvt->state = MBL_STATE_RING;
-+ } else if (strstr(buf, "+CIEV:")) {
-+ if (strstr(buf, pvt->ciev_callsetup_3)) { /* User has dialed out on handset */
-+ monitor = 0; /* We disconnect now, as he is */
-+ } /* probably leaving BT range... */
-+ }
-+ break;
-+ case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */
-+ break;
-+ case MBL_STATE_DIAL1:
-+ if (strstr(buf, "OK")) {
-+ if (pvt->no_callsetup) {
-+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
-+ } else {
-+ ast_setstate(pvt->owner, AST_STATE_RINGING);
-+ }
-+ pvt->state = MBL_STATE_OUTGOING;
-+ }
-+ break;
-+ 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);
-+ } else if (strstr(buf, pvt->ciev_call_1) && !pvt->no_callsetup) { /* b-party answer */
-+ ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
-+ }
-+ }
-+ break;
-+ case MBL_STATE_RING:
-+ cid_num[0] = 0x00;
-+ if ((pcids = strstr(buf, "+CLIP:"))) {
-+ if ((pcids = strchr(pcids, '"'))) {
-+ if ((pcide = strchr(pcids+1, '"'))) {
-+ strncpy(cid_num, pcids+1, pcide - pcids - 1);
-+ cid_num[pcide - pcids - 1] = 0x00;
-+ }
-+ }
-+ chn = mbl_new(AST_STATE_RING, pvt, cid_num);
-+ if (chn) {
-+ if (ast_pbx_start(chn)) {
-+ ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
-+ ast_hangup(chn);
-+ } else
-+ pvt->state = MBL_STATE_RING3;
-+ } else {
-+ ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
-+ rfcomm_write(pvt, "AT+CHUP\r");
-+ pvt->state = MBL_STATE_IDLE;
-+ }
-+ }
-+ break;
-+ case MBL_STATE_RING2:
-+ chn = mbl_new(AST_STATE_RING, pvt, cid_num);
-+ if (chn) {
-+ if (ast_pbx_start(chn)) {
-+ ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
-+ ast_hangup(chn);
-+ } else
-+ pvt->state = MBL_STATE_RING3;
-+ } else {
-+ ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
-+ rfcomm_write(pvt, "AT+CHUP\r");
-+ pvt->state = MBL_STATE_IDLE;
-+ }
-+ break;
-+ case MBL_STATE_RING3:
-+ if (strstr(buf, "+CIEV")) {
-+ if (strstr(buf, pvt->ciev_call_1)) {
-+ if (pvt->sent_answer) { /* We answered */
-+ pvt->state = MBL_STATE_INCOMING;
-+ } else { /* User answered on handset!, disconnect */
-+ pvt->state = MBL_STATE_IDLE;
-+ if (pvt->sco_socket > -1)
-+ close(pvt->sco_socket);
-+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
-+ }
-+ }
-+ if ((strstr(buf, pvt->ciev_callsetup_0) || strstr(buf, pvt->ciev_call_0))) { /* Caller disconnected */
-+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
-+ }
-+ }
-+ break;
-+ 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;
-+ case MBL_STATE_INSMS:
-+ if (strstr(buf, "+CMGR:")) {
-+ memset(sms_src, 0x00, sizeof(sms_src));
-+ if ((p = strchr(buf, ','))) {
-+ if (*(++p) == '"')
-+ p++;
-+ if ((p1 = strchr(p, ','))) {
-+ if (*(--p1) == '"')
-+ p1--;
-+ memset(sms_src, 0x00, sizeof(sms_src));
-+ strncpy(sms_src, p, p1 - p + 1);
-+ }
-+ }
-+ } else if (strstr(buf, "OK")) {
-+ chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
-+ strcpy(chn->exten, "sms");
-+ pbx_builtin_setvar_helper(chn, "SMSSRC", sms_src);
-+ pbx_builtin_setvar_helper(chn, "SMSTXT", sms_txt);
-+ if (ast_pbx_start(chn))
-+ ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n");
-+ pvt->state = MBL_STATE_IDLE;
-+ } else {
-+ ast_copy_string(sms_txt, buf, sizeof(sms_txt));
-+ }
-+ break;
-+ case MBL_STATE_OUTSMS:
-+ break;
-+ case MBL_STATE_OUTSMS1:
-+ break;
-+ case MBL_STATE_OUTSMS2:
-+ if (strstr(buf, "OK")) {
-+ pvt->state = MBL_STATE_IDLE;
-+ }
-+ break;
-+ }
-+ /* Unsolicited responses */
-+
-+ if (strstr(buf, "+CMTI:")) { /* SMS Incoming... */
-+ if ((p = strchr(buf, ','))) {
-+ p++;
-+ smsi = atoi(p);
-+ if (smsi > 0) {
-+ snprintf(buf, sizeof(buf), "AT+CMGR=%d\r", smsi);
-+ rfcomm_write(pvt, buf);
-+ pvt->state = MBL_STATE_INSMS;
-+ }
-+ }
-+ }
-+
-+ } else if (s == 0) { /* Timeouts */
-+ if (pvt->state == MBL_STATE_INIT2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */
-+ 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");
-+ } 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) {
-+ 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);
-+ pvt->state = MBL_STATE_IDLE;
-+ } else {
-+ pvt->state = MBL_STATE_DIAL1;
-+ }
-+ } else if (pvt->state == MBL_STATE_DIAL1) {
-+ ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
-+ ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
-+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
-+ pvt->state = MBL_STATE_IDLE;
-+ } 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->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) {
-+ 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] == '>') {
-+ snprintf(buf, sizeof(buf), "%s%c", pvt->sms_txt, 0x1a);
-+ rfcomm_write(pvt, buf);
-+ pvt->state = MBL_STATE_OUTSMS2;
-+ } else {
-+ ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
-+ pvt->state = MBL_STATE_IDLE;
-+ }
-+ } else if (pvt->state == MBL_STATE_OUTSMS2) {
-+ ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
-+ pvt->state = MBL_STATE_IDLE;
-+ }
-+ } else if (s == -1) {
-+ if (option_verbose > 2)
-+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
-+ monitor = 0;
-+ }
-+
-+ }
-+
-+ 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;
-+
-+ 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);
-+
-+ pvt->adapter->inuse = 0;
-+
-+ return NULL;
-+
-+}
-+
-+static void *do_monitor_headset(void *data)
-+{
-+
-+ struct mbl_pvt *pvt = (struct mbl_pvt *)data;
-+ char monitor = 1;
-+ char buf[256];
-+ int s, t;
-+
-+ pvt->state = MBL_STATE_PREIDLE;
-+
-+ while (monitor) {
-+
-+ if (pvt->state == MBL_STATE_RING2)
-+ t = 2;
-+ else
-+ t = 1;
-+ s = rfcomm_read(pvt, buf, 0, t);
-+
-+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
-+ 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_queue_control(pvt->owner, AST_CONTROL_ANSWER);
-+ pvt->state = MBL_STATE_INCOMING;
-+ rfcomm_write(pvt, "\r\n+VGS=13\r\n");
-+ rfcomm_write(pvt, "\r\n+VGM=13\r\n");
-+ }
-+ break;
-+ case MBL_STATE_INCOMING:
-+ if (strstr(buf, "AT+CKPD=")) {
-+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
-+ }
-+ break;
-+ default:
-+ break;
-+ }
-+ if (strstr(buf, "AT+VGS=")) {
-+ rfcomm_write(pvt, "\r\nOK\r\n");
-+ } else if (strstr(buf, "AT+VGM=")) {
-+ rfcomm_write(pvt, "\r\nOK\r\n");
-+ }
-+ } else if (s == 0) { /* Timeouts */
-+ 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_RING) {
-+ pvt->sco_socket = sco_connect(pvt->adapter->addr, pvt->addr);
-+ if (pvt->sco_socket > -1) {
-+ ast_setstate(pvt->owner, AST_STATE_RINGING);
-+ ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
-+ pvt->state = MBL_STATE_RING2;
-+ } else {
-+ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
-+ }
-+ } else if (pvt->state == MBL_STATE_RING2) {
-+ rfcomm_write(pvt, "\r\nRING\r\n");
-+ }
-+ } else if (s == -1) {
-+ if (option_verbose > 2)
-+ ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
-+ monitor = 0;
-+ }
-+
-+ }
-+
-+ 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;
-+
-+}
-+
-+static int start_monitor(struct mbl_pvt *pvt)
-+{
-+
-+ if (pvt->type == MBL_TYPE_PHONE) {
-+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
-+ 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;
-+ return 0;
-+ }
-+ }
-+
-+ return 1;
-+
-+}
-+
-+static void *do_discovery(void *data)
-+{
-+
-+ struct adapter_pvt *adapter;
-+ struct mbl_pvt *pvt;
-+
-+ for (;;) {
-+ 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;
-+}
-+
-+static void *do_sco_listen(void *data)
-+{
-+
-+ int ns;
-+ struct sockaddr_sco addr;
-+ char saddr[18];
-+ struct sco_options so;
-+ socklen_t len;
-+ int opt = 1;
-+ socklen_t addrlen;
-+ struct mbl_pvt *pvt;
-+ struct adapter_pvt *adapter = (struct adapter_pvt *) data;
-+
-+ 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, &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(adapter->sco_socket);
-+ return NULL;
-+ }
-+ 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(adapter->sco_socket);
-+ return NULL;
-+ }
-+ if (listen(adapter->sco_socket, 5) < 0) {
-+ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
-+ close(adapter->sco_socket);
-+ return NULL;
-+ }
-+ while (1) {
-+ 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);
-+
-+ 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_RWLIST_RDLOCK(&devices);
-+ AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
-+ if (!bacmp(&pvt->addr, &addr.sco_bdaddr))
-+ break;
-+ }
-+ AST_RWLIST_UNLOCK(&devices);
-+ if (pvt) {
-+ if (pvt->sco_socket != -1)
-+ close(pvt->sco_socket);
-+ pvt->sco_socket = ns;
-+ } else
-+ ast_log(LOG_DEBUG, "Could not find device for incoming Audio Connection.\n");
-+ } else {
-+ ast_log(LOG_ERROR, "accept() failed %d\n", errno);
-+ }
-+ }
-+
-+ return NULL;
-+
-+}
-+
-+/*
-+
-+ Module
-+
-+*/
-+
-+static int mbl_load_config(void)
-+{
-+
-+ struct ast_config *cfg = NULL;
-+ char *cat = NULL;
-+ struct ast_variable *var;
-+ 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)
-+ return 0;
-+
-+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
-+ if (!strcasecmp(var->name, "interval"))
-+ 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") && 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");
-+ type = ast_variable_retrieve(cfg, cat, "type");
-+ skip = ast_variable_retrieve(cfg, cat, "dtmfskip");
-+ 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));
-+ str2ba(address, &pvt->addr);
-+ ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context));
-+ 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->sco_socket = -1;
-+ pvt->monitor_thread = AST_PTHREADT_NULL;
-+ 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;
-+ ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT);
-+ ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
-+ 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/adapter configured. It won't be enabled.\n", cat);
-+ }
-+ }
-+ cat = ast_category_browse(cfg, cat);
-+ }
-+
-+ ast_config_destroy(cfg);
-+
-+ return 1;
-+
-+}
-+
-+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);
-+
-+ /* Kill the discovery thread */
-+ if (discovery_thread != AST_PTHREADT_NULL) {
-+ pthread_cancel(discovery_thread);
-+ pthread_join(discovery_thread, NULL);
-+ }
-+
-+ /* Destroy the device list */
-+ 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);
-+ 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;
-+
-+}
-+
-+static int load_module(void)
-+{
-+
-+ int dev_id, s;
-+
-+ /* Check if we have Bluetooth, no point loading otherwise... */
-+ dev_id = hci_get_route(NULL);
-+ s = hci_open_dev(dev_id);
-+ if (dev_id < 0 || s < 0) {
-+ ast_log(LOG_ERROR, "No Bluetooth device found. Not loading module.\n");
-+ return AST_MODULE_LOAD_DECLINE;
-+ }
-+
-+ hci_close_dev(s);
-+
-+ if (!mbl_load_config()) {
-+ ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", MBL_CONFIG);
-+ return AST_MODULE_LOAD_DECLINE;
-+ }
-+
-+ sdp_session = sdp_register();
-+
-+ /* Spin the discovery thread */
-+ if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) {
-+ ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
-+ 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_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc);
-+
-+ /* Make sure we can register our channel type */
-+ if (ast_channel_register(&mbl_tech)) {
-+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile");
-+ return AST_MODULE_LOAD_FAILURE;
-+ }
-+
-+ return 0;
-+}
-+
-+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver",
-+ .load = load_module,
-+ .unload = unload_module,
-+);
-diff -Nru asterisk-addons-1.4.8.org/configs/mobile.conf.sample asterisk-addons-1.4.8/configs/mobile.conf.sample
---- asterisk-addons-1.4.8.org/configs/mobile.conf.sample 1970-01-01 01:00:00.000000000 +0100
-+++ asterisk-addons-1.4.8/configs/mobile.conf.sample 2009-06-04 22:20:03.000000000 +0200
-@@ -0,0 +1,68 @@
-+;
-+; mobile.conf
-+; configuration file for chan_mobile
-+;
-+
-+[general]
-+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.
-+; 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 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]).
-+
-+[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.8.org/configure.ac asterisk-addons-1.4.8/configure.ac
---- asterisk-addons-1.4.8.org/configure.ac 2008-02-13 23:58:11.000000000 +0100
-+++ asterisk-addons-1.4.8/configure.ac 2009-06-04 22:20:03.000000000 +0200
-@@ -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_SETUP([BLUETOOTH], [Bluetooth Support], [bluetooth])
- AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
- AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses])
- AST_EXT_LIB_SETUP([MYSQLCLIENT], [mysqlclient], [mysqlclient])
- AST_EXT_LIB_SETUP([ASTERISK], [asterisk], [asterisk])
-
-+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])
-diff -Nru asterisk-addons-1.4.8.org/makeopts.in asterisk-addons-1.4.8/makeopts.in
---- asterisk-addons-1.4.8.org/makeopts.in 2008-02-13 23:58:11.000000000 +0100
-+++ asterisk-addons-1.4.8/makeopts.in 2009-06-04 22:20:03.000000000 +0200
-@@ -34,6 +34,9 @@
- sharedstatedir = @sharedstatedir@
- sysconfdir = @sysconfdir@
-
-+BLUETOOTH_LIB=@BLUETOOTH_LIB@
-+BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
-+
- CURSES_LIB=@CURSES_LIB@
- CURSES_INCLUDE=@CURSES_INCLUDE@
-