From: Andy Boyett Date: Mon, 29 Sep 2008 03:37:49 +0000 (+0000) Subject: Update asterisk-addons-1.4.x to 1.4.7 X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;hb=7c9399fb759ef8f45a6158f65e051e1113f63de2 Update asterisk-addons-1.4.x to 1.4.7 Requires a patch to pthread call. Signed-off-by: Michael Geddes SVN-Revision: 12795 --- diff --git a/net/asterisk-addons-1.4.x/Makefile b/net/asterisk-addons-1.4.x/Makefile index 205a1978fe..d630c9dd79 100644 --- a/net/asterisk-addons-1.4.x/Makefile +++ b/net/asterisk-addons-1.4.x/Makefile @@ -9,12 +9,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=asterisk-addons -PKG_VERSION:=1.4.2 +PKG_VERSION:=1.4.7 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=http://ftp.digium.com/pub/asterisk/old-releases/ -PKG_MD5SUM:=c080b02e6ddc81dab6a64691af890805 +PKG_SOURCE_URL:=http://downloads.digium.com/pub/asterisk/releases/ +PKG_MD5SUM:=3b14c147101c13ca6146c41870bad97a PKG_FIXUP = libtool @@ -91,6 +91,7 @@ ifneq ($(SDK)$(CONFIG_PACKAGE_asterisk14-mysql),) endif CONFIGURE_ARGS += \ + --with-asterisk="$(STAGING_DIR)/usr" \ --with-bluetooth="$(STAGING_DIR)/usr" \ --with-ncurses="$(STAGING_DIR)/usr" \ --with-mysqlclient="$(STAGING_DIR)/usr" \ @@ -110,12 +111,13 @@ endef MAKE_ARGS:= \ AM_CFLAGS="$(TARGET_CFLAGS)" \ - OPTIMIZE="$(TARGET_CFLAGS)" \ + OPTIMIZE="$(TARGET_CFLAGS) -DLOW_MEMORY" \ OPTIONS="" \ + NOISY_BUILD="yes" \ BLUETOOTH_LIB="$(TARGET_LDFLAGS) -lbluetooth" \ MAKE_VARS:= \ - CFLAGS="$(EXTRA_CFLAGS) -DLOW_MEMORY" \ + CFLAGS="$(EXTRA_CFLAGS)" \ LDFLAGS="$(EXTRA_LDFLAGS)" \ define Build/Compile diff --git a/net/asterisk-addons-1.4.x/patches/011-chan_mobile.patch b/net/asterisk-addons-1.4.x/patches/011-chan_mobile.patch index ed342e56c7..2c22aa8e63 100644 --- a/net/asterisk-addons-1.4.x/patches/011-chan_mobile.patch +++ b/net/asterisk-addons-1.4.x/patches/011-chan_mobile.patch @@ -1,1532 +1,1147 @@ -diff -Nru asterisk-addons-1.4.2/build_tools/menuselect-deps.in asterisk-addons-svn/build_tools/menuselect-deps.in ---- asterisk-addons-1.4.2/build_tools/menuselect-deps.in 2007-05-14 18:22:44.000000000 +0200 -+++ asterisk-addons-svn/build_tools/menuselect-deps.in 2007-06-04 19:10:59.000000000 +0200 +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 @@ -1,2 +1,3 @@ +BLUETOOTH=@PBX_BLUETOOTH@ MYSQLCLIENT=@PBX_MYSQLCLIENT@ ASTERISK=@PBX_ASTERISK@ -diff -Nru asterisk-addons-1.4.2/configs/mobile.conf.sample asterisk-addons-svn/configs/mobile.conf.sample ---- asterisk-addons-1.4.2/configs/mobile.conf.sample 1970-01-01 01:00:00.000000000 +0100 -+++ asterisk-addons-svn/configs/mobile.conf.sample 2007-06-04 19:11:00.000000000 +0200 -@@ -0,0 +1,30 @@ -+; -+; 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 out through a cell phone use Dial(Mobile/[device]/NNN.....) in your dialplan. -+; To call a headset use Dial(Mobile/[device]). +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 @@ ++/* ++ * Asterisk -- An open source telephony toolkit. ++ * ++ * Copyright (C) 1999 - 2006, Digium, Inc. ++ * ++ * Mark Spencer ++ * ++ * 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. ++ */ + -+;[dave] -+;address=00:12:56:90:6E:00 -+;port=4 -+;context=incoming-mobile ++/*! \file ++ * ++ * \brief Bluetooth Mobile Device channel driver ++ * ++ * \author Dave Bowerman ++ * ++ * \ingroup channel_drivers ++ */ + -+;[blackberry] -+;address=00:0F:86:0E:AE:42 -+;port=2 -+;context=incoming-mobile ++/*** MODULEINFO ++ bluetooth ++ ***/ + -+;[headset] -+;address=00:0B:9E:11:74:A5 -+;port=1 -+;type=headset -diff -Nru asterisk-addons-1.4.2/configure.ac asterisk-addons-svn/configure.ac ---- asterisk-addons-1.4.2/configure.ac 2007-05-14 18:22:44.000000000 +0200 -+++ asterisk-addons-svn/configure.ac 2007-06-04 19:11:00.000000000 +0200 -@@ -17,7 +17,7 @@ - AC_CONFIG_SRCDIR([res_config_mysql.c]) - - AC_COPYRIGHT("Asterisk-addons") --AC_REVISION($Revision: 382 $) -+AC_REVISION($Revision: 384 $) - - case "${host}" in - *freebsd*) -@@ -159,13 +159,14 @@ - # 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]) - - MYSQL_CONFIG=No -diff -Nru asterisk-addons-1.4.2/doc/chan_mobile.txt asterisk-addons-svn/doc/chan_mobile.txt ---- asterisk-addons-1.4.2/doc/chan_mobile.txt 1970-01-01 01:00:00.000000000 +0100 -+++ asterisk-addons-svn/doc/chan_mobile.txt 2007-06-04 19:11:00.000000000 +0200 -@@ -0,0 +1,262 @@ -+chan_mobile ++#include + -+Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices, and Headsets as FXS devices. ++ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416 $") + -+Features :- ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+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. ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+Using chan_mobile :- ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + -+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. ++#ifndef ast_debug ++#define ast_debug(level, ...) do { \ ++ if (option_debug >= (level)) { \ ++ ast_log(LOG_DEBUG, __VA_ARGS__); \ ++ } \ ++} while (0) ++#endif + -+Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles. ++#define AST_MODULE "chan_mobile" + -+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. ++#define MBL_CONFIG "mobile.conf" + -+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. ++static int prefformat = AST_FORMAT_SLINEAR; + -+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. ++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 sdp_session_t *sdp_session; + -+See www.bluez.org for other details about setting up Bluetooth under Linux. ++enum mbl_type { ++ MBL_TYPE_PHONE, ++ MBL_TYPE_HEADSET ++}; + -+Assuming you have bluetooth working ok:- ++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_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 ++}; + -+Load chan_mobile.so ++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 */ ++ 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 */ ++ 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]; ++ 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; ++}; + -+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. ++static AST_LIST_HEAD_STATIC(devices, mbl_pvt); + -+Headsets will generally have to be put into 'pairing' mode before they will show up here. ++/* The discovery thread */ ++static pthread_t discovery_thread = AST_PTHREADT_NULL; ++/* The sco listener thread */ ++static pthread_t sco_listener_thread = AST_PTHREADT_NULL; + -+This will return something like the following :- ++/* CLI stuff */ ++static const char show_usage[] = ++"Usage: mobile show devices\n" ++" Shows the state of Bluetooth Cell / Mobile devices.\n"; + -+*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 ++static const char search_usage[] = ++"Usage: mobile search\n" ++" Searches for Bluetooth Cell / Mobile devices in range.\n"; + -+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. ++static const char rfcomm_usage[] = ++"Usage: mobile rfcomm command\n" ++" Send command to the rfcomm port.\n"; + -+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. ++static int do_show_devices(int, int, char **); ++static int do_search_devices(int, int, char **); ++static int do_send_rfcomm(int, int, char **); + -+Assuming we want to use the devices above, mobile.conf needs to look like this :- ++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.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. ++/* 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"; + -+*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> ++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_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 struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num); + -+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. ++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 sdp_search(char *addr, int profile); + -+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. ++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 ++}; + -+As each phone is connected you will see a message on the Asterisk console :- ++static int do_show_devices(int fd, int argc, char **argv) ++{ + -+ Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver) -+ -- Bluetooth Device blackberry has connected. -+ -- Bluetooth Device dave has connected. ++ struct mbl_pvt *pvt; + -+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. ++ #define FORMAT "%-15.15s %-17.17s %-9.9s %-5.5s %-3.3s\n" + -+To make outbound calls, add something to you Dialplan like the following :- (modify to suit) ++ 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"); ++ } + -+; 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 ++ return RESULT_SUCCESS; + -+Pick up a SIP phone and dial 9 and the call vill go via the device 'dave' in -+mobile.conf. ++} + -+To incoming calls to a headset do something like this :- ++static int do_search_devices(int fd, int argc, char **argv) ++{ + -+[incoming-context] -+exten => s,1,Dial(Mobile/headset,30) -+exten => s,n,Hangup() ++ int hci_socket; ++ inquiry_info *ii = NULL; ++ int max_rsp, num_rsp; ++ int dev_id, len, flags; ++ int i, phport, hsport; ++ char addr[19] = {0}; ++ char name[31] = {0}; + -+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 :- ++ #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" + -+*CLI> originate Mobile/headset extension NNNNN@context ++ dev_id = hci_get_route(NULL); ++ hci_socket = hci_open_dev(dev_id); ++ len = 8; ++ max_rsp = 255; ++ flags = IREQ_CACHE_FLUSH; + -+This will call your headset, once you answer Asterisk will call NNNNN at context context ++ ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info)); ++ num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags); ++ if (num_rsp > 0) { ++ ast_cli(fd, FORMAT2, "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(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); ++ } ++ } else ++ ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n"); + -+Dialplan hints :- ++ free(ii); + -+chan_mobile supports 'device status' so you can do somthing like ++ hci_close_dev(hci_socket); + -+exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry ++ return RESULT_SUCCESS; + ++} + -+MobileStatus Application :- ++static int do_send_rfcomm(int fd, int argc, char **argv) ++{ + -+chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan -+to determine the 'state' of a device. ++ struct mbl_pvt *pvt; ++ char buf[128]; + -+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. ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, argv[2])) ++ break; ++ } + -+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 ++ if (!pvt || !pvt->connected) { ++ sprintf(buf, "Device %s not found.\n", argv[2]); ++ ast_cli(fd, buf); ++ return RESULT_SUCCESS; ++ } + -+MobileStatus sets the value of the given variable to :- ++ sprintf(buf, "%s\r", argv[3]); ++ rfcomm_write(pvt, buf); + -+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 ++ return RESULT_SUCCESS; + ++} + -+SMS Sending / Receiving ++static int mbl_status_exec(struct ast_channel *ast, void *data) ++{ + -+If Asterisk has detected your cell phone is capable of SMS via bluetooth, you will be able to send and -+receive SMS. ++ struct mbl_pvt *pvt; ++ char *args = NULL, *device = NULL, *variable = NULL; ++ int stat; ++ char status[2]; + -+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. ++ if (!data) ++ return -1; + -+So, to handle incoming SMS's, do something like the following in your dialplan ++ args = ast_strdupa((char *)data); ++ device = strsep(&args, "|"); ++ if (device && (device[0] != 0x00)) { ++ variable = args; ++ } else ++ return -1; + -+[incoming-mobile] -+exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT}) -+exten => sms,n,Hangup() ++ stat = 1; + -+The above will just print the message on the console. ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, device)) ++ break; ++ } + -+If you use res_jabber, you could do something like this :- ++ if (pvt) { ++ if (pvt->connected) ++ stat = 2; ++ if (pvt->owner) ++ stat = 3; ++ } + -+[incoming-mobile] -+exten => sms,1,JabberSend(transport,user@jabber.somewhere.com,SMS from ${SMSRC} ${SMSTXT}) -+exten => sms,2,Hangup() ++ sprintf(status, "%d", stat); ++ pbx_builtin_setvar_helper(ast, variable, status); + -+To send an SMS, use the application MobileSendSMS like the following :- ++ return 0; + -+exten => 99,1,MobileSendSMS(dave,0427123456,Hello World) ++} + -+This will send 'Hello World' via device 'dave' to '0427123456' ++static int mbl_sendsms_exec(struct ast_channel *ast, void *data) ++{ + ++ struct mbl_pvt *pvt; ++ char *args = NULL, *device = NULL, *dest = NULL, *message = NULL; + -+DTMF Debouncing :- ++ if (!data) ++ return -1; + -+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 ++ 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 { ++ ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n"); ++ return -1; ++ } ++ + -+[dave] -+address=00:12:56:90:6E:00 -+port=4 -+context=incoming-mobile -+dtmfskip=50 ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, device)) ++ break; ++ } + -+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. ++ if (!pvt) { ++ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n",device); ++ return -1; ++ } ++ ++ if (!pvt->connected) { ++ ast_log(LOG_ERROR,"bluetooth device %s wasn't connected -- SMS will not be sent.\n",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); ++ 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); ++ return -1; ++ } ++ ++ strcpy(pvt->dial_number, dest); ++ memset(pvt->sms_txt, 0x0, sizeof(pvt->sms_txt)); ++ strncpy(pvt->sms_txt, message, 160); ++ pvt->state = MBL_STATE_OUTSMS; + ++ return 0; + -+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. ++static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause) ++{ + -+The RIM Blackberry 7250 works extremely well. So does the LG TU500. ++ struct ast_channel *chn = NULL; ++ struct mbl_pvt *pvt; ++ char *dest_dev = NULL; ++ char *dest_num = NULL; ++ int oldformat; + -+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 (!data) { ++ ast_log(LOG_WARNING, "Channel requested with no data\n"); ++ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; ++ return NULL; ++ } + -+If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 1'. ++ 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; ++ } + -+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. ++ dest_dev = ast_strdupa((char *)data); + -+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... ++ dest_num = strchr(dest_dev, '/'); ++ if (dest_num) ++ *dest_num++ = 0x00; + -+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. ++ /* Find requested device and make sure its connected. */ ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->id, dest_dev)) { ++ break; ++ } ++ } ++ 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, "Cant determine destination number.\n"); ++ *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION; ++ return NULL; ++ } + -+Feedback, Support, Please can you make Cell Phone X work... etc :- ++ pvt->sco_out_ptr = pvt->sco_out_buf; ++ pvt->sco_out_len = 0; + -+email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc. -diff -Nru asterisk-addons-1.4.2/Makefile asterisk-addons-svn/Makefile ---- asterisk-addons-1.4.2/Makefile 2007-06-06 00:05:09.000000000 +0200 -+++ asterisk-addons-svn/Makefile 2007-07-28 15:12:17.000000000 +0200 -@@ -49,7 +49,7 @@ - endif - MODULES_DIR=$(ASTLIBDIR)/modules - --MODS:=app_addon_sql_mysql app_saycountpl cdr_addon_mysql chan_ooh323 format_mp3 res_config_mysql -+MODS:=app_addon_sql_mysql app_saycountpl cdr_addon_mysql chan_ooh323 format_mp3 res_config_mysql chan_mobile - - SELECTED_MODS:=$(patsubst %,%.so,$(filter-out $(MENUSELECT_ADDONS),$(MODS))) - -@@ -143,6 +143,9 @@ - app_addon_sql_mysql.so: app_addon_sql_mysql.o - $(CC) $(SOLINK) -o $@ $< $(MYSQLCLIENT_LIB) - -+chan_mobile.so: chan_mobile.o -+ $(CC) $(SOLINK) -o $@ $< $(BLUETOOTH_LIB) -+ - chan_ooh323.so: - @if [ ! -f asterisk-ooh323c/Makefile ] ; then \ - cd asterisk-ooh323c && ./configure ; \ -@@ -186,6 +189,8 @@ - menuselect.makeopts menuselect.makedeps: menuselect/menuselect menuselect-tree - @menuselect/menuselect --check-deps $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts - -+menuconfig: menuselect ++ 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; ++ } + - 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.2/makeopts.in asterisk-addons-svn/makeopts.in ---- asterisk-addons-1.4.2/makeopts.in 2007-05-14 18:22:44.000000000 +0200 -+++ asterisk-addons-svn/makeopts.in 2007-06-04 19:11:00.000000000 +0200 -@@ -33,6 +33,9 @@ - sharedstatedir = @sharedstatedir@ - sysconfdir = @sysconfdir@ - -+BLUETOOTH_LIB=@BLUETOOTH_LIB@ -+BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@ ++ return chn; + - CURSES_LIB=@CURSES_LIB@ - CURSES_INCLUDE=@CURSES_INCLUDE@ - -diff -Nru asterisk-addons-1.4.2/menuselect-tree asterisk-addons-svn/menuselect-tree ---- asterisk-addons-1.4.2/menuselect-tree 2007-05-14 18:22:44.000000000 +0200 -+++ asterisk-addons-svn/menuselect-tree 2007-06-04 19:11:00.000000000 +0200 -@@ -13,6 +13,10 @@ - mysqlclient - asterisk - -+ -+ bluetooth -+ asterisk -+ - - asterisk - ---- asterisk-addons-1.4.2/chan_mobile.c 1970-01-01 01:00:00.000000000 +0100 -+++ asterisk-addons-svn/chan_mobile.c 2007-07-29 13:30:43.000000000 +0200 -@@ -0,0 +1,1867 @@ -+/* -+ * Asterisk -- An open source telephony toolkit. -+ * -+ * Copyright (C) 1999 - 2006, Digium, Inc. -+ * -+ * Mark Spencer -+ * -+ * 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 -+ * -+ * \ingroup channel_drivers -+ */ ++static int mbl_call(struct ast_channel *ast, char *dest, int timeout) ++{ + -+/*** MODULEINFO -+ bluetooth -+ ***/ ++ struct mbl_pvt *pvt; ++ char *dest_dev = NULL; ++ char *dest_num = NULL; + -+#include ++ dest_dev = ast_strdupa((char *)dest); + -+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416 $") ++ pvt = ast->tech_pvt; + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ 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; ++ } + -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ 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; ++ } + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++ ast_debug(1, "Calling %s on %s\n", dest, ast->name); + -+#ifndef ast_debug -+#define ast_debug(level, ...) do { \ -+ if (option_debug >= (level)) { \ -+ ast_log(LOG_DEBUG, __VA_ARGS__); \ -+ } \ -+} while (0) -+#endif ++ 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; ++ } + -+#define AST_MODULE "chan_mobile" + -+#define MBL_CONFIG "mobile.conf" ++ return 0; + -+static int prefformat = AST_FORMAT_SLINEAR; ++} + -+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 sdp_session_t *sdp_session; ++static int mbl_hangup(struct ast_channel *ast) ++{ + -+enum mbl_type { -+ MBL_TYPE_PHONE, -+ MBL_TYPE_HEADSET -+}; ++ struct mbl_pvt *pvt; + -+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_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 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 */ -+ 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 */ -+ 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]; -+ 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; -+}; ++ if (!ast->tech_pvt) { ++ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); ++ return 0; ++ } ++ pvt = ast->tech_pvt; + -+static AST_LIST_HEAD_STATIC(devices, mbl_pvt); ++ ast_debug(1, "Hanging up device %s.\n", pvt->id); + -+/* The discovery thread */ -+static pthread_t discovery_thread = AST_PTHREADT_NULL; -+/* The sco listener thread */ -+static pthread_t sco_listener_thread = AST_PTHREADT_NULL; ++ ast_channel_lock(ast); ++ ast->fds[0] = -1; ++ ast_channel_unlock(ast); + -+/* CLI stuff */ -+static const char show_usage[] = -+"Usage: mobile show devices\n" -+" Shows the state of Bluetooth Cell / Mobile devices.\n"; ++ if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) { ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ } + -+static const char search_usage[] = -+"Usage: mobile search\n" -+" Searches for Bluetooth Cell / Mobile devices in range.\n"; ++ 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"); ++ pvt->state = MBL_STATE_HANGUP; ++ pvt->hangup_count = 0; ++ } else ++ pvt->state = MBL_STATE_IDLE; + -+static const char rfcomm_usage[] = -+"Usage: mobile rfcomm command\n" -+" Send command to the rfcomm port.\n"; ++ pvt->owner = NULL; ++ ast->tech_pvt = NULL; ++ ast_setstate(ast, AST_STATE_DOWN); + -+static int do_show_devices(int, int, char **); -+static int do_search_devices(int, int, char **); -+static int do_send_rfcomm(int, int, char **); ++ return 0; + -+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} -+}; ++} + -+/* 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 int mbl_answer(struct ast_channel *ast) ++{ + -+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"; ++ struct mbl_pvt *pvt; + -+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 struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num); ++ pvt = ast->tech_pvt; + -+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 sdp_search(char *addr, int profile); ++ rfcomm_write(pvt, "ATA\r"); + -+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 -+}; ++ ast_setstate(ast, AST_STATE_UP); + -+static int do_show_devices(int fd, int argc, char **argv) -+{ ++ pvt->sent_answer = 1; + -+ struct mbl_pvt *pvt; ++ return 0; + -+ #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"); -+ } ++static int mbl_digit_begin(struct ast_channel *chan, char digit) ++{ + -+ return RESULT_SUCCESS; ++ return 0; + +} + -+static int do_search_devices(int fd, int argc, char **argv) ++static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration) +{ + -+ int hci_socket; -+ inquiry_info *ii = NULL; -+ int max_rsp, num_rsp; -+ int dev_id, len, flags; -+ int i, phport, hsport; -+ char addr[19] = {0}; -+ char name[31] = {0}; ++ struct mbl_pvt *pvt; ++ char buf[11]; ++ ++ pvt = ast->tech_pvt; + -+ #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" ++ if (pvt->type == MBL_TYPE_HEADSET) ++ return 0; + -+ dev_id = hci_get_route(NULL); -+ hci_socket = hci_open_dev(dev_id); -+ len = 8; -+ max_rsp = 255; -+ flags = IREQ_CACHE_FLUSH; ++ ast_debug(1, "Dialed %c\n", digit); + -+ ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info)); -+ num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags); -+ if (num_rsp > 0) { -+ ast_cli(fd, FORMAT2, "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(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); -+ } -+ } else -+ ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n"); ++ switch(digit) { ++ case '0': ++ case '1': ++ case '2': ++ case '3': ++ case '4': ++ case '5': ++ case '6': ++ case '7': ++ case '8': ++ case '9': ++ case '*': ++ case '#': ++ sprintf(buf, "AT+VTS=%c\r", digit); ++ rfcomm_write(pvt, buf); ++ break; ++ default: ++ ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); ++ return -1; ++ } + -+ free(ii); ++ return 0; + -+ hci_close_dev(hci_socket); ++} + -+ return RESULT_SUCCESS; ++/* + -+} ++ 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 int do_send_rfcomm(int fd, int argc, char **argv) ++*/ ++ ++static struct ast_frame *mbl_read(struct ast_channel *ast) +{ + -+ struct mbl_pvt *pvt; -+ char buf[128]; ++ struct mbl_pvt *pvt = ast->tech_pvt; ++ int r; ++ struct ast_frame *f; + -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, argv[2])) -+ break; ++ if (!pvt->owner) { ++ return &ast_null_frame; + } + -+ if (!pvt || !pvt->connected) { -+ sprintf(buf, "Device %s not found.\n", argv[2]); -+ ast_cli(fd, buf); -+ return RESULT_SUCCESS; ++ if (pvt->state == MBL_STATE_HANGUP) { ++ return &ast_null_frame; + } + -+ sprintf(buf, "%s\r", argv[3]); -+ rfcomm_write(pvt, buf); ++ if (pvt->sco_socket == -1) { ++ return &ast_null_frame; ++ } + -+ return RESULT_SUCCESS; ++ 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; ++ } else { ++ pvt->skip_frames--; ++ } ++ } 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; + +} + -+static int mbl_status_exec(struct ast_channel *ast, void *data) -+{ ++/* + -+ struct mbl_pvt *pvt; -+ char *args = NULL, *device = NULL, *variable = NULL; -+ int stat; -+ char status[2]; ++ 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. + -+ if (!data) -+ return -1; ++*/ + -+ args = ast_strdupa((char *)data); -+ device = strsep(&args, "|"); -+ if (device && (device[0] != 0x00)) { -+ variable = args; -+ } else -+ return -1; ++static int mbl_write(struct ast_channel *ast, struct ast_frame *frame) ++{ + -+ stat = 1; ++ struct mbl_pvt *pvt = ast->tech_pvt; ++ int num_frames, i, r; ++ char *pfr; + -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, device)) -+ break; ++ if (frame->frametype != AST_FRAME_VOICE) { ++ return 0; ++ } ++ if (pvt->sco_socket == -1) { ++ return 0; + } + -+ if (pvt) { -+ if (pvt->connected) -+ stat = 2; -+ if (pvt->owner) -+ stat = 3; ++ if (pvt->state == MBL_STATE_HANGUP) { ++ return 0; + } + -+ sprintf(status, "%d", stat); -+ pbx_builtin_setvar_helper(ast, variable, status); ++ 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"); ++ } ++ ++ memmove(pvt->sco_out_ptr, frame->data, frame->datalen); ++ pvt->sco_out_len += frame->datalen; ++ num_frames = pvt->sco_out_len / 48; ++ ++ pfr = pvt->sco_out_buf; ++ for (i=0; isco_socket, pfr, 48)) == -1) { ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ } ++ pfr += 48; ++ } + ++ 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; + +} + -+static int mbl_sendsms_exec(struct ast_channel *ast, void *data) ++static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + -+ struct mbl_pvt *pvt; -+ char *args = NULL, *device = NULL, *dest = NULL, *message = NULL; -+ -+ if (!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 { -+ 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)) -+ break; -+ } ++ struct mbl_pvt *pvt = oldchan->tech_pvt; + -+ if (!pvt) { -+ ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n",device); -+ return -1; -+ } -+ -+ if (!pvt->connected) { -+ ast_log(LOG_ERROR,"bluetooth device %s wasn't connected -- SMS will not be sent.\n",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); -+ 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); -+ return -1; -+ } -+ -+ strcpy(pvt->dial_number, dest); -+ memset(pvt->sms_txt, 0x0, sizeof(pvt->sms_txt)); -+ strncpy(pvt->sms_txt, message, 160); -+ pvt->state = MBL_STATE_OUTSMS; ++ if (pvt && pvt->owner == oldchan) ++ pvt->owner = newchan; + + return 0; + +} + -+static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause) ++static int mbl_devicestate(void *data) +{ + -+ struct ast_channel *chn = NULL; ++ char *device; ++ int res = AST_DEVICE_INVALID; + struct mbl_pvt *pvt; -+ char *dest_dev = NULL; -+ char *dest_num = NULL; -+ int oldformat; -+ -+ 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); ++ device = ast_strdupa(S_OR(data, "")); + -+ dest_num = strchr(dest_dev, '/'); -+ if (dest_num) -+ *dest_num++ = 0x00; ++ ast_debug(1, "Checking device state for device %s\n", device); + -+ /* Find requested device and make sure its connected. */ + AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, dest_dev)) { ++ if (!strcmp(pvt->id, device)) + break; -+ } -+ } -+ 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, "Cant 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"); -+ *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL; -+ return NULL; ++ if (pvt) { ++ if (pvt->connected) { ++ if (pvt->owner) ++ res = AST_DEVICE_INUSE; ++ else ++ res = AST_DEVICE_NOT_INUSE; ++ } + } + -+ return chn; ++ return res; + +} + -+static int mbl_call(struct ast_channel *ast, char *dest, int timeout) ++static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num) +{ + -+ 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_debug(1, "Calling %s on %s\n", dest, ast->name); ++ struct ast_channel *chn; + -+ 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; -+ } ++ 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; + ++ } + -+ return 0; ++ return chn; + +} + -+static int mbl_hangup(struct ast_channel *ast) -+{ ++static int rfcomm_connect(char *bdaddr, int remote_channel) { + -+ struct mbl_pvt *pvt; ++ bdaddr_t dst; ++ struct sockaddr_rc addr; ++ int s; + -+ if (!ast->tech_pvt) { -+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n"); -+ return 0; ++ str2ba(bdaddr, &dst); ++ ++ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) { ++ ast_debug(1, "socket() failed (%d).\n", errno); ++ return -1; + } -+ pvt = ast->tech_pvt; + -+ ast_debug(1, "Hanging up device %s.\n", pvt->id); ++ 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_debug(1, "connect() failed (%d).\n", errno); ++ close(s); ++ return -1; ++ } + -+ ast_channel_lock(ast); -+ ast->fds[0] = -1; -+ ast_channel_unlock(ast); ++ return s; + -+ 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) { -+ rfcomm_write(pvt, "AT+CHUP\r"); -+ pvt->state = MBL_STATE_HANGUP; -+ pvt->hangup_count = 0; -+ } else -+ pvt->state = MBL_STATE_IDLE; ++static int rfcomm_write(struct mbl_pvt *pvt, char *buf) ++{ + -+ pvt->owner = NULL; -+ ast->tech_pvt = NULL; -+ ast_setstate(ast, AST_STATE_DOWN); ++ char *p; ++ ssize_t num_write; ++ int len; + -+ return 0; ++ ast_debug(1, "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); ++ return 0; ++ } ++ len -= num_write; ++ p += num_write; ++ } ++ ++ return 1; + +} + -+static int mbl_answer(struct ast_channel *ast) -+{ ++/* + -+ struct mbl_pvt *pvt; ++ 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. + -+ pvt = ast->tech_pvt; ++*/ + -+ rfcomm_write(pvt, "ATA\r"); ++static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout) ++{ + -+ ast_setstate(ast, AST_STATE_UP); ++ int sel, rlen, slen; ++ fd_set rfds; ++ struct timeval tv; ++ char *p; + -+ pvt->sent_answer = 1; ++ 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; ++ } + -+ return 0; ++ FD_ZERO(&rfds); ++ FD_SET(pvt->rfcomm_socket, &rfds); + -+} ++ tv.tv_sec = timeout; ++ tv.tv_usec = 0; + -+static int mbl_digit_begin(struct ast_channel *chan, char digit) -+{ ++ 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 0; ++ return 1; + +} + -+static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration) ++static int sco_connect(char *bdaddr) +{ + -+ struct mbl_pvt *pvt; -+ char buf[11]; -+ -+ pvt = ast->tech_pvt; ++ bdaddr_t dst; ++ struct sockaddr_sco addr; ++ int s; + -+ if (pvt->type == MBL_TYPE_HEADSET) -+ return 0; ++ str2ba(bdaddr, &dst); + -+ ast_debug(1, "Dialed %c\n", digit); ++ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) { ++ ast_debug(1, "socket() failed (%d).\n", errno); ++ return -1; ++ } + -+ switch(digit) { -+ case '0': -+ case '1': -+ case '2': -+ case '3': -+ case '4': -+ case '5': -+ case '6': -+ case '7': -+ case '8': -+ case '9': -+ case '*': -+ case '#': -+ sprintf(buf, "AT+VTS=%c\r", digit); -+ rfcomm_write(pvt, buf); -+ break; -+ default: -+ ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit); ++ 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_debug(1, "sco connect() failed (%d).\n", errno); ++ close(s); + return -1; + } + -+ return 0; ++ return s; + +} + +/* + -+ 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. ++ sdp_search() performs a service discovery on the given device to determine whether ++ or not it supports the Handsfree Profile. + +*/ + -+static struct ast_frame *mbl_read(struct ast_channel *ast) ++static int sdp_search(char *addr, int profile) +{ + -+ struct mbl_pvt *pvt = ast->tech_pvt; -+ int r; -+ struct ast_frame *f; -+ -+ if (!pvt->owner) { -+ return &ast_null_frame; -+ } -+ -+ if (pvt->state == MBL_STATE_HANGUP) { -+ return &ast_null_frame; -+ } ++ 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; + -+ if (pvt->sco_socket == -1) { -+ return &ast_null_frame; ++ str2ba(addr, &bdaddr); ++ 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); ++ return 0; + } + -+ 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; ++ 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); + } -+ return &pvt->fr; -+ } else { -+ pvt->skip_frames--; -+ } -+ } 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); -+ } ++ sdp_record_free(sdprec); ++ sdp_list_free(response_list, 0); ++ } else ++ ast_debug(1, "No responses returned for device %s.\n", addr); ++ } else ++ ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr); + -+ return &ast_null_frame; ++ ++ sdp_list_free(search_list, 0); ++ sdp_list_free(attrid_list, 0); ++ sdp_close(session); ++ ++ return port; + +} + +/* + -+ 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. ++ 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 int mbl_write(struct ast_channel *ast, struct ast_frame *frame) ++static sdp_session_t *sdp_register(void) +{ + -+ struct mbl_pvt *pvt = ast->tech_pvt; -+ int num_frames, i, r; -+ char *pfr; -+ -+ if (frame->frametype != AST_FRAME_VOICE) { -+ return 0; -+ } -+ if (pvt->sco_socket == -1) { -+ return 0; -+ } ++ 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"; + -+ if (pvt->state == MBL_STATE_HANGUP) { -+ return 0; -+ } ++ 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; + -+ 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"); -+ } ++ int err = 0; ++ sdp_session_t *session = 0; + -+ memmove(pvt->sco_out_ptr, frame->data, frame->datalen); -+ pvt->sco_out_len += frame->datalen; -+ num_frames = pvt->sco_out_len / 48; ++ sdp_record_t *record = sdp_record_alloc(); + -+ pfr = pvt->sco_out_buf; -+ for (i=0; isco_socket, pfr, 48)) == -1) { -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ } -+ pfr += 48; -+ } ++ sdp_uuid128_create(&svc_uuid, &service_uuid_int); ++ sdp_set_service_id(record, svc_uuid); + -+ 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; ++ 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); + -+static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -+{ ++ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); ++ root_list = sdp_list_append(0, &root_uuid); ++ sdp_set_browse_groups( record, root_list ); + -+ struct mbl_pvt *pvt = oldchan->tech_pvt; ++ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); ++ l2cap_list = sdp_list_append(0, &l2cap_uuid); ++ proto_list = sdp_list_append(0, l2cap_list); + -+ if (pvt && pvt->owner == oldchan) -+ pvt->owner = newchan; ++ 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); + -+ return 0; ++ 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); + -+static int mbl_devicestate(void *data) -+{ ++ 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); + -+ char *device; -+ int res = AST_DEVICE_INVALID; -+ struct mbl_pvt *pvt; ++ 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); + -+ device = ast_strdupa(S_OR(data, "")); ++ return session; + -+ ast_debug(1, "Checking device state for device %s\n", device); ++} + -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->id, device)) -+ break; -+ } ++/* + -+ if (pvt) { -+ if (pvt->connected) { -+ if (pvt->owner) -+ res = AST_DEVICE_INUSE; -+ else -+ res = AST_DEVICE_NOT_INUSE; -+ } -+ } ++ Phone Monitor Thread + -+ return res; ++ 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. + -+} ++*/ + -+static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num) ++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[161]; + -+ 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; -+ -+ } -+ -+ return chn; -+ -+} -+ -+static int rfcomm_connect(char *bdaddr, 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); -+ 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_debug(1, "connect() failed (%d).\n", errno); -+ close(s); -+ return -1; -+ } ++ brsf = nsmode = 0; + -+ return s; ++ if (!rfcomm_write(pvt, "AT+BRSF=4\r")) ++ monitor = 0; + -+} ++ while (monitor) { + -+static int rfcomm_write(struct mbl_pvt *pvt, char *buf) -+{ ++ 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; + -+ char *p; -+ ssize_t num_write; -+ int len; -+ -+ ast_debug(1, "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); -+ 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; -+ -+} -+ -+static int sco_connect(char *bdaddr) -+{ -+ -+ 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); -+ 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_debug(1, "sco connect() failed (%d).\n", errno); -+ close(s); -+ return -1; -+ } -+ -+ return s; -+ -+} -+ -+/* -+ -+ sdp_search() performs a service discovery on the given device to determine whether -+ or not it supports the Handsfree Profile. -+ -+*/ -+ -+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_debug(1, "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_debug(1, "No responses returned for device %s.\n", addr); -+ } else -+ ast_debug(1, "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; -+ -+} -+ -+/* -+ -+ 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) -+{ -+ -+ 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; -+ -+} -+ -+/* -+ -+ 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. -+ -+*/ -+ -+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[161]; -+ -+ 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); ++ 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); @@ -1762,502 +1377,846 @@ diff -Nru asterisk-addons-1.4.2/menuselect-tree asterisk-addons-svn/menuselect-t + 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) { -+ sprintf(buf, "AT+CMGR=%d\r", smsi); -+ rfcomm_write(pvt, buf); -+ pvt->state = MBL_STATE_INSMS; -+ } ++ } ++ /* Unsolicited responses */ ++ ++ if (strstr(buf, "+CMTI:")) { /* SMS Incoming... */ ++ if ((p = strchr(buf, ','))) { ++ p++; ++ smsi = atoi(p); ++ if (smsi > 0) { ++ sprintf(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) { ++ sprintf(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->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++; ++ } else if (pvt->state == MBL_STATE_OUTSMS) { ++ sprintf(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); ++ 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; ++ } ++ ++ } ++ ++ close(pvt->rfcomm_socket); ++ close(pvt->sco_socket); ++ pvt->sco_socket = -1; ++ pvt->connected = 0; ++ pvt->monitor_thread = AST_PTHREADT_NULL; ++ ++ return NULL; ++ ++} ++ ++/* ++ ++ Headset Monitor Thread ++ ++ 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. ++ ++*/ ++ ++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_debug(1, "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"); ++ 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; + } -+ -+ } 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) { ++ 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_DIAL) { -+ sprintf(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 if (pvt->state == MBL_STATE_RING) { ++ pvt->sco_socket = sco_connect(pvt->bdaddr); ++ 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; + } else { -+ pvt->state = MBL_STATE_DIAL1; ++ ast_queue_control(pvt->owner, AST_CONTROL_HANGUP); + } -+ } 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->hangup_count == 6) { -+ ast_debug(1, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id); -+ monitor = 0; ++ } 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; ++ } ++ ++ } ++ ++ 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; ++ } ++ } 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; ++ ++} ++ ++/* ++ ++ 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; ++ ++ 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); ++ } + } -+ rfcomm_write(pvt, "AT+CHUP\r"); -+ pvt->hangup_count++; -+ } else if (pvt->state == MBL_STATE_OUTSMS) { -+ sprintf(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); -+ 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; ++ } ++ } ++ /* 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; ++ struct sco_options so; ++ socklen_t len; ++ int opt = 1; ++ char saddr[18]; ++ socklen_t addrlen; ++ struct mbl_pvt *pvt; ++ ++ hci_devba(0, &local); ++ ++ if ((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) { ++ ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno); ++ close(sco_socket); ++ return NULL; ++ } ++ if (setsockopt(sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { ++ ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n"); ++ close(sco_socket); ++ return NULL; ++ } ++ if (listen(sco_socket, 5) < 0) { ++ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n"); ++ close(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); ++ ++ 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); ++ ++ pvt = NULL; ++ AST_LIST_TRAVERSE(&devices, pvt, entry) { ++ if (!strcmp(pvt->bdaddr, saddr)) ++ break; ++ } ++ 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"); ++ } ++ else ast_log(LOG_NOTICE, "Accept got a -1...."); ++ } ++ ++ return NULL; ++ ++} ++ ++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; ++ struct mbl_pvt *pvt; ++ ++ 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); ++ } ++ ++ cat = ast_category_browse(cfg, NULL); ++ while (cat) { ++ if (strcasecmp(cat, "general")) { ++ ast_debug(1, "Loading device %s.\n", cat); ++ address = ast_variable_retrieve(cfg, cat, "address"); ++ 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)))) { ++ if (type && !strcmp(type, "headset")) ++ pvt->type = MBL_TYPE_HEADSET; ++ else ++ pvt->type = MBL_TYPE_PHONE; ++ ast_copy_string(pvt->id, cat, sizeof(pvt->id)); ++ ast_copy_string(pvt->bdaddr, address, sizeof(pvt->bdaddr)); ++ ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context)); ++ pvt->connected = 0; ++ 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->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); + } -+ } 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 { ++ ast_log(LOG_ERROR, "Device %s has no address/port configured. It wont be enabled.\n", cat); + } -+ } 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; + } -+ ++ cat = ast_category_browse(cfg, cat); + } + -+ close(pvt->rfcomm_socket); -+ close(pvt->sco_socket); -+ pvt->sco_socket = -1; -+ pvt->connected = 0; -+ pvt->monitor_thread = AST_PTHREADT_NULL; ++ ast_config_destroy(cfg); + -+ return NULL; ++ return 1; + +} + -+/* ++static int reload_module(void) ++{ + -+ Headset Monitor Thread ++ return 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. ++} + -+*/ ++static int unload_module(void) ++{ + -+static void *do_monitor_headset(void *data) ++ struct mbl_pvt *pvt; ++ ++ /* 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); ++ } ++ /* 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))) { ++ if (pvt->monitor_thread != AST_PTHREADT_NULL) { ++ pthread_cancel(pvt->monitor_thread); ++ pthread_join(pvt->monitor_thread, NULL); ++ } ++ if (pvt->sco_socket > -1) { ++ close(pvt->sco_socket); ++ } ++ if (pvt->rfcomm_socket > -1) { ++ close(pvt->rfcomm_socket); ++ } ++ ast_dsp_free(pvt->dsp); ++ free(pvt); ++ } ++ ++ if (sdp_session) ++ sdp_close(sdp_session); ++ ++ return 0; ++ ++} ++ ++static int load_module(void) +{ + -+ struct mbl_pvt *pvt = (struct mbl_pvt *)data; -+ char monitor = 1; -+ char buf[256]; -+ int s, t; ++ int dev_id, s; ++ uint16_t vs; ++ ++ /* 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_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 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; ++ } ++ /* 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_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, ++ .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 @@ ++; ++; 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 out through a cell phone use Dial(Mobile/[device]/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 @@ + # 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]) + + 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:- + -+ pvt->state = MBL_STATE_PREIDLE; ++Load chan_mobile.so + -+ while (monitor) { ++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. + -+ if (pvt->state == MBL_STATE_RING2) -+ t = 2; -+ else -+ t = 1; -+ s = rfcomm_read(pvt, buf, 0, t); ++Headsets will generally have to be put into 'pairing' mode before they will show up here. + -+ if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) { -+ ast_debug(1, "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"); -+ 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->bdaddr); -+ 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; -+ } 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; -+ } ++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 + -+ return NULL; ++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. + -+static int start_monitor(struct mbl_pvt *pvt) -+{ ++Assuming we want to use the devices above, mobile.conf needs to look like this :- + -+ 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; -+ } -+ } else { -+ if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) { -+ pvt->monitor_thread = AST_PTHREADT_NULL; -+ return 0; -+ } -+ } ++=================================================================================== ++; ++; mobile.conf ++; + -+ return 1; ++[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 + -+ Device Discovery Thread. ++[headset] ++address=00:0B:9E:11:74:A5 ++port=1 ++type=headset ++=================================================================================== + -+ 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. ++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. + -+static void *do_discovery(void *data) -+{ ++Having done this, unload chan_mobile and load it again. + -+ struct mbl_pvt *pvt = data; ++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. + -+ 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); -+ } -+ } -+ } -+ } -+ /* Go to sleep */ -+ sleep(discovery_interval); -+ } ++*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> + -+ return NULL; -+} + -+/* ++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 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. ++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 :- + -+static void *do_sco_listen(void *data) -+{ ++ Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver) ++ -- Bluetooth Device blackberry has connected. ++ -- Bluetooth Device dave has connected. + -+ int ns; -+ bdaddr_t local; -+ struct sockaddr_sco addr; -+ struct sco_options so; -+ socklen_t len; -+ int opt = 1; -+ char saddr[18]; -+ socklen_t addrlen; -+ struct mbl_pvt *pvt; ++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. + -+ hci_devba(0, &local); ++To make outbound calls, add something to you Dialplan like the following :- (modify to suit) + -+ if ((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) { -+ ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno); -+ close(sco_socket); -+ return NULL; -+ } -+ if (setsockopt(sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) { -+ ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n"); -+ close(sco_socket); -+ return NULL; -+ } -+ if (listen(sco_socket, 5) < 0) { -+ ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n"); -+ close(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); ++; 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 + -+ len = sizeof(so); -+ getsockopt(ns, SOL_SCO, SCO_OPTIONS, &so, &len); ++Pick up a SIP phone and dial 9 and the call vill go via the device 'dave' in ++mobile.conf. + -+ ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu); ++To incoming calls to a headset do something like this :- + -+ pvt = NULL; -+ AST_LIST_TRAVERSE(&devices, pvt, entry) { -+ if (!strcmp(pvt->bdaddr, saddr)) -+ break; -+ } -+ 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"); -+ } -+ else ast_log(LOG_NOTICE, "Accept got a -1...."); -+ } ++[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 :- + -+ return NULL; ++*CLI> originate Mobile/headset extension NNNNN@context + -+} ++This will call your headset, once you answer Asterisk will call NNNNN at context context + -+static int mbl_load_config(void) -+{ ++Dialplan hints :- + -+ struct ast_config *cfg = NULL; -+ char *cat = NULL; -+ struct ast_variable *var; -+ const char *address, *port, *context, *type, *skip; -+ struct mbl_pvt *pvt; ++chan_mobile supports 'device status' so you can do somthing like + -+ cfg = ast_config_load(MBL_CONFIG); -+ if (!cfg) -+ return 0; ++exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry + -+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { -+ if (!strcasecmp(var->name, "interval")) -+ discovery_interval = atoi(var->value); -+ } + -+ cat = ast_category_browse(cfg, NULL); -+ while (cat) { -+ if (strcasecmp(cat, "general")) { -+ ast_debug(1, "Loading device %s.\n", cat); -+ address = ast_variable_retrieve(cfg, cat, "address"); -+ 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)))) { -+ if (type && !strcmp(type, "headset")) -+ pvt->type = MBL_TYPE_HEADSET; -+ else -+ pvt->type = MBL_TYPE_PHONE; -+ ast_copy_string(pvt->id, cat, sizeof(pvt->id)); -+ ast_copy_string(pvt->bdaddr, address, sizeof(pvt->bdaddr)); -+ ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context)); -+ pvt->connected = 0; -+ 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->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); -+ } -+ } else { -+ ast_log(LOG_ERROR, "Device %s has no address/port configured. It wont be enabled.\n", cat); -+ } -+ } -+ cat = ast_category_browse(cfg, cat); -+ } ++MobileStatus Application :- + -+ ast_config_destroy(cfg); ++chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan ++to determine the 'state' of a device. + -+ return 1; ++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 + -+static int reload_module(void) -+{ ++MobileStatus sets the value of the given variable to :- + -+ return 0; ++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 + -+} + -+static int unload_module(void) -+{ ++SMS Sending / Receiving + -+ struct mbl_pvt *pvt; ++If Asterisk has detected your cell phone is capable of SMS via bluetooth, you will be able to send and ++receive SMS. + -+ /* First, take us out of the channel loop */ -+ ast_channel_unregister(&mbl_tech); ++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. + -+ /* Kill the discovery thread */ -+ if (discovery_thread != AST_PTHREADT_NULL) { -+ 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); ++So, to handle incoming SMS's, do something like the following in your dialplan + -+ /* 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); ++[incoming-mobile] ++exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT}) ++exten => sms,n,Hangup() + -+ /* Destroy the device list */ -+ while ((pvt = AST_LIST_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_socket > -1) { -+ close(pvt->sco_socket); -+ } -+ if (pvt->rfcomm_socket > -1) { -+ close(pvt->rfcomm_socket); -+ } -+ ast_dsp_free(pvt->dsp); -+ free(pvt); -+ } ++The above will just print the message on the console. + -+ if (sdp_session) -+ sdp_close(sdp_session); ++If you use res_jabber, you could do something like this :- + -+ return 0; ++[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 :- + -+static int load_module(void) -+{ ++exten => 99,1,MobileSendSMS(dave,0427123456,Hello World) + -+ int dev_id, s; -+ uint16_t vs; ++This will send 'Hello World' via device 'dave' to '0427123456' + -+ /* 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_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; -+ } ++DTMF Debouncing :- + -+ hci_close_dev(s); ++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 + -+ 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; -+ } ++[dave] ++address=00:12:56:90:6E:00 ++port=4 ++context=incoming-mobile ++dtmfskip=50 + -+ sdp_session = sdp_register(); ++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. + -+ /* 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; -+ } -+ /* 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_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc); ++Debugging :- + -+ /* 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; -+ } ++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. + -+ return 0; -+} ++The RIM Blackberry 7250 works extremely well. So does the LG TU500. + -+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver", -+ .load = load_module, -+ .unload = unload_module, -+ .reload = reload_module, -+); ++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 +@@ -34,6 +34,9 @@ + sharedstatedir = @sharedstatedir@ + sysconfdir = @sysconfdir@ + ++BLUETOOTH_LIB=@BLUETOOTH_LIB@ ++BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@ ++ + CURSES_LIB=@CURSES_LIB@ + CURSES_INCLUDE=@CURSES_INCLUDE@ + diff --git a/net/asterisk-addons-1.4.x/patches/021-cross_configure.patch b/net/asterisk-addons-1.4.x/patches/021-cross_configure.patch index 42470c76ed..3d3fb112f0 100644 --- a/net/asterisk-addons-1.4.x/patches/021-cross_configure.patch +++ b/net/asterisk-addons-1.4.x/patches/021-cross_configure.patch @@ -1,12 +1,12 @@ -diff -ruN asterisk-addons-1.4.2-old/menuselect/Makefile asterisk-addons-1.4.2-new/menuselect/Makefile ---- asterisk-addons-1.4.2-old/menuselect/Makefile 2007-06-06 00:01:45.000000000 +0200 -+++ asterisk-addons-1.4.2-new/menuselect/Makefile 2007-09-19 18:00:27.000000000 +0200 -@@ -42,7 +42,7 @@ - @$(MAKE) menuselect +diff -Nru asterisk-addons-1.4.5.org/menuselect/Makefile asterisk-addons-1.4.5/menuselect/Makefile +--- asterisk-addons-1.4.5.org/menuselect/Makefile 2007-11-21 01:17:33.000000000 +0100 ++++ asterisk-addons-1.4.5/menuselect/Makefile 2007-12-20 22:47:17.000000000 +0100 +@@ -51,7 +51,7 @@ + $(OBJS) menuselect_gtk.o menuselect_curses.o menuselect_stub.o: autoconfig.h menuselect.h - autoconfig.h: -- @./configure $(CONFIGURE_SILENT) CC= LD= AR= -+ @./configure $(CONFIGURE_SILENT) CC= LD= AR= LDFLAGS= + makeopts autoconfig.h: autoconfig.h.in makeopts.in +- @./configure $(CONFIGURE_SILENT) CC= LD= AR= CFLAGS= ++ @./configure $(CONFIGURE_SILENT) CC= LD= AR= CFLAGS= LDFLAGS= + + menuselect gmenuselect: mxml/libmxml.a - _gmenuselect: autoconfig.h - @$(MAKE) gmenuselect diff --git a/net/asterisk-addons-1.4.x/patches/031-pthread_param.patch b/net/asterisk-addons-1.4.x/patches/031-pthread_param.patch new file mode 100644 index 0000000000..ed4e93f835 --- /dev/null +++ b/net/asterisk-addons-1.4.x/patches/031-pthread_param.patch @@ -0,0 +1,19 @@ +*** asterisk-addons-1.4.7/channels/ooh323c/src/ooCmdChannel.c.orig 2008-06-27 18:12:50.000000000 +0800 +--- asterisk-addons-1.4.7/channels/ooh323c/src/ooCmdChannel.c 2008-06-27 18:12:11.000000000 +0800 +*************** +*** 37,43 **** + if ((ret = pipe(thePipe)) == -1) { + return OO_FAILED; + } +! pthread_mutex_init(&gCmdChanLock); + + gH323ep.cmdSock = dup(thePipe[0]); + close(thePipe[0]); +--- 37,43 ---- + if ((ret = pipe(thePipe)) == -1) { + return OO_FAILED; + } +! pthread_mutex_init(&gCmdChanLock, NULL); + + gH323ep.cmdSock = dup(thePipe[0]); + close(thePipe[0]);