1 diff -Nru asterisk-addons-1.4.2/build_tools/menuselect-deps.in asterisk-addons-svn/build_tools/menuselect-deps.in
2 --- asterisk-addons-1.4.2/build_tools/menuselect-deps.in 2007-05-14 18:22:44.000000000 +0200
3 +++ asterisk-addons-svn/build_tools/menuselect-deps.in 2007-06-04 19:10:59.000000000 +0200
5 +BLUETOOTH=@PBX_BLUETOOTH@
6 MYSQLCLIENT=@PBX_MYSQLCLIENT@
7 ASTERISK=@PBX_ASTERISK@
8 diff -Nru asterisk-addons-1.4.2/configs/mobile.conf.sample asterisk-addons-svn/configs/mobile.conf.sample
9 --- asterisk-addons-1.4.2/configs/mobile.conf.sample 1970-01-01 01:00:00.000000000 +0100
10 +++ asterisk-addons-svn/configs/mobile.conf.sample 2007-06-04 19:11:00.000000000 +0200
17 +interval=60 ; Number of seconds between trying to connect to devices.
19 +; The following is a list of the devices we deal with.
20 +; Every device listed below will be available for calls in and out of Asterisk.
21 +; Discovered devices not in this list are not available.
22 +; Use the CLI command 'mobile search' to discover devices.
23 +; Use the CLI command 'mobile show devices' to see device status.
25 +; To place out through a cell phone use Dial(Mobile/[device]/NNN.....) in your dialplan.
26 +; To call a headset use Dial(Mobile/[device]).
29 +;address=00:12:56:90:6E:00
31 +;context=incoming-mobile
34 +;address=00:0F:86:0E:AE:42
36 +;context=incoming-mobile
39 +;address=00:0B:9E:11:74:A5
42 diff -Nru asterisk-addons-1.4.2/configure.ac asterisk-addons-svn/configure.ac
43 --- asterisk-addons-1.4.2/configure.ac 2007-05-14 18:22:44.000000000 +0200
44 +++ asterisk-addons-svn/configure.ac 2007-06-04 19:11:00.000000000 +0200
46 AC_CONFIG_SRCDIR([res_config_mysql.c])
48 AC_COPYRIGHT("Asterisk-addons")
49 -AC_REVISION($Revision: 382 $)
50 +AC_REVISION($Revision: 384 $)
55 # from here on down, library checking should be done in alphabetical order
56 # by the --with option name, to make things easier for the users :-)
58 +AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth Support], [bluetooth])
59 AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
60 AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses])
61 AST_EXT_LIB_SETUP([MYSQLCLIENT], [mysqlclient], [mysqlclient])
62 AST_EXT_LIB_SETUP([ASTERISK], [asterisk], [asterisk])
64 +AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h])
65 AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
67 AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h])
70 diff -Nru asterisk-addons-1.4.2/doc/chan_mobile.txt asterisk-addons-svn/doc/chan_mobile.txt
71 --- asterisk-addons-1.4.2/doc/chan_mobile.txt 1970-01-01 01:00:00.000000000 +0100
72 +++ asterisk-addons-svn/doc/chan_mobile.txt 2007-06-04 19:11:00.000000000 +0200
76 +Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices, and Headsets as FXS devices.
80 +Multiple cell phones can be connected.
81 +Multiple headsets can be connected.
82 +Asterisk automatically connects to each configured cell phone / headset when it comes in range.
83 +CLI command to discover bluetooth devices.
84 +Inbound calls on the cell network to the cell phones are handled by Asterisk, just like inbound calls on a Zap channel.
85 +CLI passed through on inbound calls.
86 +Dial outbound on a cell phone using Dial(Mobile/device/nnnnnnn) in the dialplan.
87 +Dial a headset using Dial(Mobile/device) in the dialplan.
88 +Application MobileStatus can be used in the dialplan to see if a cell phone / headset is connected.
89 +Supports devicestate for dialplan hinting.
90 +Supports Inbound and Outbound SMS.
94 +In order to use chan_mobile, you must have a working bluetooth subsystem on your Asterisk box.
95 +This means a working bluetooth adapter, and the BlueZ packages.
97 +Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles.
99 +The BlueZ package you need is bluez-utils. If you are using a GUI then you might want to install bluez-pin also.
100 +You also need libbluetooth, and libbluetooth-dev if you are compiling Asterisk from source.
102 +You need to get bluetooth working with your phone before attempting to use chan_mobile.
103 +This means 'pairing' your phone with your Asterisk box. I dont describe how to do this here as the process
104 +differs from distro to distro. You only need to pair once.
106 +However, the easist way to pair, is to use you cell phone to search for bluetooth devices, select your Asterisk box
107 +and enter the requested PIN.
109 +See www.bluez.org for other details about setting up Bluetooth under Linux.
111 +Assuming you have bluetooth working ok:-
115 +Search for your bluetooth devices using the CLI command 'mobile search'. Be patient with this command as
116 +it will take 8 - 10 seconds to do the discovery.
118 +Headsets will generally have to be put into 'pairing' mode before they will show up here.
120 +This will return something like the following :-
123 +Address Name Usable Type Port
124 +00:12:56:90:6E:00 LG TU500 Yes Phone 4
125 +00:80:C8:35:52:78 Toaster No Headset 0
126 +00:0B:9E:11:74:A5 Hello II Plus Yes Headset 1
127 +00:0F:86:0E:AE:42 Daves Blackberry Yes Phone 7
129 +This is a list of all bluetooth devices seen and whether or not they are usable with chan_cellphone.
130 +The Address field contains the 'bd address' of the device. This is like an ethernet mac address.
131 +The Name field is whatever is configured into the device as its name.
132 +The Usable field tells you whether or not the device supports the Bluetooth Handsfree Profile or Headset profile.
133 +The Type field tells you whether the device is usable as a Phone line (FXO) or a headset (FXS)
134 +The Port field is the number to put in the configuration file.
136 +Choose which device(s) you want to use and edit /etc/asterisk/mobile.conf. There is a sample included
137 +with the Asterisk source under configs/mobile.conf.sample.
139 +Assuming we want to use the devices above, mobile.conf needs to look like this :-
141 +===================================================================================
147 +interval=60 ; Number of seconds between trying to connect to devices.
149 +; The following is a list of the devices we deal with.
150 +; Every device listed below will be available for calls in and out of Asterisk.
151 +; Discovered devices not in this list are not available.
152 +; Use the CLI command 'mobile search' to discover devices.
153 +; Use the CLI command 'mobile show devices' to see device status.
155 +; To place a call use Dial(Mobile/[device]/NNN.....) in your dialplan.
158 +address=00:12:56:90:6E:00
160 +context=incoming-mobile
163 +address=00:0B:9E:11:74:A5
166 +===================================================================================
168 +Be sure to configure the right bd address and port number from the search. If you want inbound
169 +calls on a device to go to a specific context, add a context= line, otherwise the default will
170 +be used. The 'id' of the device [bitinbrackets] can be anything you like, just make the unique.
172 +If your are configuring a Headset be sure to include the type=headset line, if left out it defaults
175 +Having done this, unload chan_mobile and load it again.
177 +The CLI command 'mobile show devices' can be used at any time to show the status of configured devices,
178 +and whether or not the device is capable of sending / receiving SMS via bluetooth.
180 +*CLI> mobile show devices
181 +ID Address Connected State SMS
182 +blackberry 00:0F:86:0E:AE:42 Yes Free Yes
183 +dave 00:12:56:90:6E:00 Yes Free No
184 +headset 00:0B:9E:11:74:A5 Yes Free No
188 +All being well Asterisk will now try and establish a connection to each configured device. If it cant
189 +it will retry after 'interval' seconds, infinately.
191 +This means that as your cell phone comes into range and goes out of range, Asterisk will automatically
192 +connect and disconnect from it. You dont need to worry about it.
194 +As each phone is connected you will see a message on the Asterisk console :-
196 + Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver)
197 + -- Bluetooth Device blackberry has connected.
198 + -- Bluetooth Device dave has connected.
200 +If someone calls your cell phone now, Asterisk will handle the call and it will be sent into the
201 +context you specified, or the default context. Mostly likely this means some SIP phone somewhere will
202 +ring, pick it up and take the call.
204 +To make outbound calls, add something to you Dialplan like the following :- (modify to suit)
207 +exten => _9X.,1,Dial(Mobile/dave/${EXTEN:1},45)
208 +exten => _9X.,n,Hangup
209 +; Calls via Blackberry
210 +exten => _8X.,1,Dial(Mobile/blackberry/${EXTEN:1},45)
211 +exten => _8X.,n,Hangup
213 +Pick up a SIP phone and dial 9<number of pizza shop> and the call vill go via the device 'dave' in
216 +To incoming calls to a headset do something like this :-
219 +exten => s,1,Dial(Mobile/headset,30)
220 +exten => s,n,Hangup()
222 +To dial out on a headset, you need to use some other mechanism, because the headset is not likely
223 +to have all the needed buttons on it. res_clioriginate is good for this :-
225 +*CLI> originate Mobile/headset extension NNNNN@context
227 +This will call your headset, once you answer Asterisk will call NNNNN at context context
231 +chan_mobile supports 'device status' so you can do somthing like
233 +exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry
236 +MobileStatus Application :-
238 +chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan
239 +to determine the 'state' of a device.
241 +For example, suppose you wanted to call dave's extension, but only if he was in the office. You could
242 +test to see if his cell phone was attached to Asterisk, if it is dial his extension, otherwise dial his
245 +exten => 40,1,MobileStatus(dave,DAVECELL)
246 +exten => 40,2,GotoIf($["${DAVECELL}" = "1"]?3:5)
247 +exten => 40,3,Dial(ZAP/g1/0427466412,45,tT)
248 +exten => 40,4,Hangup
249 +exten => 40,5,Dial(SIP/40,45,tT)
250 +exten => 40,6,Hangup
252 +MobileStatus sets the value of the given variable to :-
254 +1 = Disconnected. i.e. Device not in range of Asterisk, or turned off etc etc
255 +2 = Connected and Not on a call. i.e. Free
256 +3 = Connected and on a call. i.e. Busy
259 +SMS Sending / Receiving
261 +If Asterisk has detected your cell phone is capable of SMS via bluetooth, you will be able to send and
264 +Incoming SMS's cause Asterisk to create an inbound call to the context you defined in mobile.conf or the default
265 +context if you did not define one. The call will start at extension 'sms'. Two channel variables will be available,
266 +SMSSRC = the number of the originator of the SMS and SMSTXT which is the text of the SMS.
267 +This is not a voice call, so grab the values of the variables and hang the call up.
269 +So, to handle incoming SMS's, do something like the following in your dialplan
272 +exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT})
273 +exten => sms,n,Hangup()
275 +The above will just print the message on the console.
277 +If you use res_jabber, you could do something like this :-
280 +exten => sms,1,JabberSend(transport,user@jabber.somewhere.com,SMS from ${SMSRC} ${SMSTXT})
281 +exten => sms,2,Hangup()
283 +To send an SMS, use the application MobileSendSMS like the following :-
285 +exten => 99,1,MobileSendSMS(dave,0427123456,Hello World)
287 +This will send 'Hello World' via device 'dave' to '0427123456'
292 +DTMF detection varies from phone to phone. There is a configuration variable that allows you to tune
293 +this to your needs. e.g. in mobile.conf
296 +address=00:12:56:90:6E:00
298 +context=incoming-mobile
301 +change dtmfskip to suit your phone. The default is 200. The larger the number, the more chance of missed DTMF.
302 +The smaller the number the more chance of multiple digits being detected.
307 +Different phone manufacturers have different interpretations of the Bluetooth Handsfree Profile Spec.
308 +This means that not all phones work the same way, particularly in the connection setup / initialisation
309 +sequence. I've tried to make chan_cellphone as general as possible, but it may need modification to
310 +support some phone i've never tested.
312 +The RIM Blackberry 7250 works extremely well. So does the LG TU500.
314 +Some phones, most notably Sony Ericsson 'T' series, dont quite conform to the Bluetooth HFP spec.
315 +chan_mobile will detect these and adapt accordingly. The T-610 and T-630 have been tested and
318 +If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 1'.
320 +This will log a bunch of debug messages indicating what the phone is doing, importantly the rfcomm
321 +conversation between Asterisk and the phone. This can be used to sort out what your phone is doing
322 +and make chan_mobile support it.
324 +Be aware also, that just about all cell phones behave differently. For example my LG TU500 wont dial unless
325 +the phone is a the 'idle' screen. i.e. if the phone is showing a 'menu' on the display, when you dial via
326 +Asterisk, the call will not work. chan_mobile handles this, but there may be other phones that do
329 +Important: Watch what your cell phone is doing the first few times. Asterisk wont make random calls but
330 +if chan_mobile fails to hangup for some reason and you get a huge bill from your telco, dont blame me.
333 +Feedback, Support, Please can you make Cell Phone X work... etc :-
335 +email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc.
336 diff -Nru asterisk-addons-1.4.2/Makefile asterisk-addons-svn/Makefile
337 --- asterisk-addons-1.4.2/Makefile 2007-06-06 00:05:09.000000000 +0200
338 +++ asterisk-addons-svn/Makefile 2007-07-28 15:12:17.000000000 +0200
341 MODULES_DIR=$(ASTLIBDIR)/modules
343 -MODS:=app_addon_sql_mysql app_saycountpl cdr_addon_mysql chan_ooh323 format_mp3 res_config_mysql
344 +MODS:=app_addon_sql_mysql app_saycountpl cdr_addon_mysql chan_ooh323 format_mp3 res_config_mysql chan_mobile
346 SELECTED_MODS:=$(patsubst %,%.so,$(filter-out $(MENUSELECT_ADDONS),$(MODS)))
349 app_addon_sql_mysql.so: app_addon_sql_mysql.o
350 $(CC) $(SOLINK) -o $@ $< $(MYSQLCLIENT_LIB)
352 +chan_mobile.so: chan_mobile.o
353 + $(CC) $(SOLINK) -o $@ $< $(BLUETOOTH_LIB)
356 @if [ ! -f asterisk-ooh323c/Makefile ] ; then \
357 cd asterisk-ooh323c && ./configure ; \
359 menuselect.makeopts menuselect.makedeps: menuselect/menuselect menuselect-tree
360 @menuselect/menuselect --check-deps $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts
362 +menuconfig: menuselect
364 menuselect: menuselect/menuselect menuselect-tree
365 -@menuselect/menuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && echo "menuselect changes saved!" || echo "menuselect changes NOT saved!"
367 diff -Nru asterisk-addons-1.4.2/makeopts.in asterisk-addons-svn/makeopts.in
368 --- asterisk-addons-1.4.2/makeopts.in 2007-05-14 18:22:44.000000000 +0200
369 +++ asterisk-addons-svn/makeopts.in 2007-06-04 19:11:00.000000000 +0200
371 sharedstatedir = @sharedstatedir@
372 sysconfdir = @sysconfdir@
374 +BLUETOOTH_LIB=@BLUETOOTH_LIB@
375 +BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
377 CURSES_LIB=@CURSES_LIB@
378 CURSES_INCLUDE=@CURSES_INCLUDE@
380 diff -Nru asterisk-addons-1.4.2/menuselect-tree asterisk-addons-svn/menuselect-tree
381 --- asterisk-addons-1.4.2/menuselect-tree 2007-05-14 18:22:44.000000000 +0200
382 +++ asterisk-addons-svn/menuselect-tree 2007-06-04 19:11:00.000000000 +0200
384 <depend>mysqlclient</depend>
385 <depend>asterisk</depend>
387 + <member name="chan_mobile" remove_on_change="chan_mobile.so chan_mobile.o" displayname="Bluetooth Mobile Device channel driver">
388 + <depend>bluetooth</depend>
389 + <depend>asterisk</depend>
391 <member name="chan_ooh323" displayname="Objective Systems H.323 Channel Driver">
392 <depend>asterisk</depend>
394 --- asterisk-addons-1.4.2/chan_mobile.c 1970-01-01 01:00:00.000000000 +0100
395 +++ asterisk-addons-svn/chan_mobile.c 2007-07-29 13:30:43.000000000 +0200
398 + * Asterisk -- An open source telephony toolkit.
400 + * Copyright (C) 1999 - 2006, Digium, Inc.
402 + * Mark Spencer <markster@digium.com>
404 + * See http://www.asterisk.org for more information about
405 + * the Asterisk project. Please do not directly contact
406 + * any of the maintainers of this project for assistance;
407 + * the project provides a web site, mailing lists and IRC
408 + * channels for your use.
410 + * This program is free software, distributed under the terms of
411 + * the GNU General Public License Version 2. See the LICENSE file
412 + * at the top of the source tree.
417 + * \brief Bluetooth Mobile Device channel driver
419 + * \author Dave Bowerman <david.bowerman@gmail.com>
421 + * \ingroup channel_drivers
425 + <depend>bluetooth</depend>
428 +#include <asterisk.h>
430 +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416 $")
434 +#include <sys/socket.h>
435 +#include <sys/time.h>
439 +#include <arpa/inet.h>
441 +#include <sys/ioctl.h>
444 +#include <bluetooth/bluetooth.h>
445 +#include <bluetooth/hci.h>
446 +#include <bluetooth/hci_lib.h>
447 +#include <bluetooth/sdp.h>
448 +#include <bluetooth/sdp_lib.h>
449 +#include <bluetooth/rfcomm.h>
450 +#include <bluetooth/sco.h>
452 +#include <asterisk/lock.h>
453 +#include <asterisk/channel.h>
454 +#include <asterisk/config.h>
455 +#include <asterisk/logger.h>
456 +#include <asterisk/module.h>
457 +#include <asterisk/pbx.h>
458 +#include <asterisk/options.h>
459 +#include <asterisk/utils.h>
460 +#include <asterisk/linkedlists.h>
461 +#include <asterisk/cli.h>
462 +#include <asterisk/devicestate.h>
463 +#include <asterisk/causes.h>
464 +#include <asterisk/dsp.h>
467 +#define ast_debug(level, ...) do { \
468 + if (option_debug >= (level)) { \
469 + ast_log(LOG_DEBUG, __VA_ARGS__); \
474 +#define AST_MODULE "chan_mobile"
476 +#define MBL_CONFIG "mobile.conf"
478 +static int prefformat = AST_FORMAT_SLINEAR;
480 +static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
481 +static int sco_socket; /* This is global so it can be closed on module unload outside of the listener thread */
482 +static sdp_session_t *sdp_session;
490 + MBL_STATE_INIT = 0,
501 + MBL_STATE_OUTGOING,
505 + MBL_STATE_INCOMING,
514 + struct ast_channel *owner; /* Channel we belong to, possibly NULL */
515 + struct ast_frame fr; /* "null" frame */
516 + enum mbl_type type; /* Phone or Headset */
517 + char id[31]; /* The id from mobile.conf */
518 + char bdaddr[18]; /* the bdaddr of the device */
519 + char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
520 + char connected; /* is it connected? */
521 + int rfcomm_port; /* rfcomm port number */
522 + int rfcomm_socket; /* rfcomm socket descriptor */
523 + char rfcomm_buf[256];
524 + int sco_socket; /* sco socket descriptor */
525 + enum mbl_state state; /* monitor thread current state */
526 + pthread_t monitor_thread; /* monitor thread handle */
527 + char sco_in_buf[48 + AST_FRIENDLY_OFFSET];
528 + char sco_out_buf[352];
531 + char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */
533 + char ciev_call_0[4]; /* dynamically build reponse strings */
534 + char ciev_call_1[4];
535 + char ciev_callsetup_0[4];
536 + char ciev_callsetup_1[4];
537 + char ciev_callsetup_2[4];
538 + char ciev_callsetup_3[4];
542 + struct ast_dsp *dsp;
543 + struct ast_frame *dsp_fr;
548 + AST_LIST_ENTRY(mbl_pvt) entry;
551 +static AST_LIST_HEAD_STATIC(devices, mbl_pvt);
553 +/* The discovery thread */
554 +static pthread_t discovery_thread = AST_PTHREADT_NULL;
555 +/* The sco listener thread */
556 +static pthread_t sco_listener_thread = AST_PTHREADT_NULL;
559 +static const char show_usage[] =
560 +"Usage: mobile show devices\n"
561 +" Shows the state of Bluetooth Cell / Mobile devices.\n";
563 +static const char search_usage[] =
564 +"Usage: mobile search\n"
565 +" Searches for Bluetooth Cell / Mobile devices in range.\n";
567 +static const char rfcomm_usage[] =
568 +"Usage: mobile rfcomm command\n"
569 +" Send command to the rfcomm port.\n";
571 +static int do_show_devices(int, int, char **);
572 +static int do_search_devices(int, int, char **);
573 +static int do_send_rfcomm(int, int, char **);
575 +static struct ast_cli_entry mbl_cli[] = {
576 + {{"mobile", "show", "devices", NULL}, do_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage},
577 + {{"mobile", "search", NULL}, do_search_devices, "Search for Bluetooth Cell / Mobile devices", search_usage},
578 + {{"mobile", "rfcomm", NULL}, do_send_rfcomm, "Send commands to the rfcomm port for debugging", rfcomm_usage}
582 +static char *app_mblstatus = "MobileStatus";
583 +static char *mblstatus_synopsis = "MobileStatus(Device,Variable)";
584 +static char *mblstatus_desc =
585 +"MobileStatus(Device,Variable)\n"
586 +" Device - Id of mobile device from mobile.conf\n"
587 +" Variable - Variable to store status in will be 1-3.\n"
588 +" In order, Disconnected, Connected & Free, Connected & Busy.\n";
590 +static char *app_mblsendsms = "MobileSendSMS";
591 +static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)";
592 +static char *mblsendsms_desc =
593 +"MobileSendSms(Device,Dest,Message)\n"
594 +" Device - Id of device from mobile.conf\n"
595 +" Dest - destination\n"
596 +" Message - text of the message\n";
598 +static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause);
599 +static int mbl_call(struct ast_channel *ast, char *dest, int timeout);
600 +static int mbl_hangup(struct ast_channel *ast);
601 +static int mbl_answer(struct ast_channel *ast);
602 +static int mbl_digit_begin(struct ast_channel *ast, char digit);
603 +static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
604 +static struct ast_frame *mbl_read(struct ast_channel *ast);
605 +static int mbl_write(struct ast_channel *ast, struct ast_frame *frame);
606 +static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
607 +static int mbl_devicestate(void *data);
608 +static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num);
610 +static int rfcomm_write(struct mbl_pvt *pvt, char *buf);
611 +static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout);
612 +static int sco_connect(char *bdaddr);
613 +static int sdp_search(char *addr, int profile);
615 +static const struct ast_channel_tech mbl_tech = {
617 + .description = "Bluetooth Mobile Device Channel Driver",
618 + .capabilities = AST_FORMAT_SLINEAR,
619 + .requester = mbl_request,
621 + .hangup = mbl_hangup,
622 + .answer = mbl_answer,
623 + .send_digit_begin = mbl_digit_begin,
624 + .send_digit_end = mbl_digit_end,
626 + .write = mbl_write,
627 + .fixup = mbl_fixup,
628 + .devicestate = mbl_devicestate
631 +static int do_show_devices(int fd, int argc, char **argv)
634 + struct mbl_pvt *pvt;
636 + #define FORMAT "%-15.15s %-17.17s %-9.9s %-5.5s %-3.3s\n"
638 + ast_cli(fd, FORMAT, "ID", "Address", "Connected", "State", "SMS");
639 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
640 + 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");
643 + return RESULT_SUCCESS;
647 +static int do_search_devices(int fd, int argc, char **argv)
651 + inquiry_info *ii = NULL;
652 + int max_rsp, num_rsp;
653 + int dev_id, len, flags;
654 + int i, phport, hsport;
655 + char addr[19] = {0};
656 + char name[31] = {0};
658 + #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
659 + #define FORMAT3 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
661 + dev_id = hci_get_route(NULL);
662 + hci_socket = hci_open_dev(dev_id);
665 + flags = IREQ_CACHE_FLUSH;
667 + ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
668 + num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
670 + ast_cli(fd, FORMAT2, "Address", "Name", "Usable", "Type", "Port");
671 + for (i = 0; i < num_rsp; i++) {
672 + ba2str(&(ii+i)->bdaddr, addr);
674 + if (hci_read_remote_name(hci_socket, &(ii+i)->bdaddr, sizeof(name)-1, name, 0) < 0)
675 + strcpy(name, "[unknown]");
676 + phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID);
678 + hsport = sdp_search(addr, HEADSET_PROFILE_ID);
681 + ast_cli(fd, FORMAT3, addr, name, (phport > 0 || hsport > 0)?"Yes":"No", (phport > 0)?"Phone":"Headset", (phport > 0)?phport:hsport);
684 + ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n");
688 + hci_close_dev(hci_socket);
690 + return RESULT_SUCCESS;
694 +static int do_send_rfcomm(int fd, int argc, char **argv)
697 + struct mbl_pvt *pvt;
700 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
701 + if (!strcmp(pvt->id, argv[2]))
705 + if (!pvt || !pvt->connected) {
706 + sprintf(buf, "Device %s not found.\n", argv[2]);
708 + return RESULT_SUCCESS;
711 + sprintf(buf, "%s\r", argv[3]);
712 + rfcomm_write(pvt, buf);
714 + return RESULT_SUCCESS;
718 +static int mbl_status_exec(struct ast_channel *ast, void *data)
721 + struct mbl_pvt *pvt;
722 + char *args = NULL, *device = NULL, *variable = NULL;
729 + args = ast_strdupa((char *)data);
730 + device = strsep(&args, "|");
731 + if (device && (device[0] != 0x00)) {
738 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
739 + if (!strcmp(pvt->id, device))
744 + if (pvt->connected)
750 + sprintf(status, "%d", stat);
751 + pbx_builtin_setvar_helper(ast, variable, status);
757 +static int mbl_sendsms_exec(struct ast_channel *ast, void *data)
760 + struct mbl_pvt *pvt;
761 + char *args = NULL, *device = NULL, *dest = NULL, *message = NULL;
766 + args = ast_strdupa((char *)data);
767 + device = strsep(&args, "|");
768 + if (device && (device[0] != 0x00)) {
769 + dest = strsep(&args, "|");
770 + if (dest && (dest[0] != 0x00)) {
772 + if (!message || (message[0] == 0x00)) {
773 + ast_log(LOG_ERROR,"NULL Message to be sent-- SMS will not be sent.\n");
777 + ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
782 + ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
787 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
788 + if (!strcmp(pvt->id, device))
793 + ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n",device);
797 + if (!pvt->connected) {
798 + ast_log(LOG_ERROR,"bluetooth device %s wasn't connected -- SMS will not be sent.\n",device);
802 + if (!pvt->has_sms) {
803 + ast_log(LOG_ERROR,"bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n",device);
807 + if (pvt->state != MBL_STATE_IDLE) {
808 + ast_log(LOG_ERROR,"bluetooth device %s isn't IDLE -- SMS will not be sent.\n",device);
812 + strcpy(pvt->dial_number, dest);
813 + memset(pvt->sms_txt, 0x0, sizeof(pvt->sms_txt));
814 + strncpy(pvt->sms_txt, message, 160);
815 + pvt->state = MBL_STATE_OUTSMS;
821 +static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause)
824 + struct ast_channel *chn = NULL;
825 + struct mbl_pvt *pvt;
826 + char *dest_dev = NULL;
827 + char *dest_num = NULL;
831 + ast_log(LOG_WARNING, "Channel requested with no data\n");
832 + *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
836 + oldformat = format;
837 + format &= (AST_FORMAT_SLINEAR);
839 + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat);
840 + *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
844 + dest_dev = ast_strdupa((char *)data);
846 + dest_num = strchr(dest_dev, '/');
848 + *dest_num++ = 0x00;
850 + /* Find requested device and make sure its connected. */
851 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
852 + if (!strcmp(pvt->id, dest_dev)) {
856 + if (!pvt || !pvt->connected || pvt->owner) {
857 + ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
858 + *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
862 + if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
863 + ast_log(LOG_WARNING, "Cant determine destination number.\n");
864 + *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
868 + pvt->sco_out_ptr = pvt->sco_out_buf;
869 + pvt->sco_out_len = 0;
871 + chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
873 + ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
874 + *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
882 +static int mbl_call(struct ast_channel *ast, char *dest, int timeout)
885 + struct mbl_pvt *pvt;
886 + char *dest_dev = NULL;
887 + char *dest_num = NULL;
889 + dest_dev = ast_strdupa((char *)dest);
891 + pvt = ast->tech_pvt;
893 + if (pvt->type == MBL_TYPE_PHONE) {
894 + dest_num = strchr(dest_dev, '/');
896 + ast_log(LOG_WARNING, "Cant determine destination number.\n");
899 + *dest_num++ = 0x00;
902 + if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
903 + ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name);
907 + ast_debug(1, "Calling %s on %s\n", dest, ast->name);
909 + if (pvt->type == MBL_TYPE_PHONE) {
910 + ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number));
911 + pvt->state = MBL_STATE_DIAL;
912 + pvt->dial_timeout = (timeout == 0) ? 30 : timeout;
914 + pvt->state = MBL_STATE_RING;
922 +static int mbl_hangup(struct ast_channel *ast)
925 + struct mbl_pvt *pvt;
927 + if (!ast->tech_pvt) {
928 + ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
931 + pvt = ast->tech_pvt;
933 + ast_debug(1, "Hanging up device %s.\n", pvt->id);
935 + ast_channel_lock(ast);
937 + ast_channel_unlock(ast);
939 + if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) {
940 + close(pvt->sco_socket);
941 + pvt->sco_socket = -1;
944 + 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) {
945 + rfcomm_write(pvt, "AT+CHUP\r");
946 + pvt->state = MBL_STATE_HANGUP;
947 + pvt->hangup_count = 0;
949 + pvt->state = MBL_STATE_IDLE;
952 + ast->tech_pvt = NULL;
953 + ast_setstate(ast, AST_STATE_DOWN);
959 +static int mbl_answer(struct ast_channel *ast)
962 + struct mbl_pvt *pvt;
964 + pvt = ast->tech_pvt;
966 + rfcomm_write(pvt, "ATA\r");
968 + ast_setstate(ast, AST_STATE_UP);
970 + pvt->sent_answer = 1;
976 +static int mbl_digit_begin(struct ast_channel *chan, char digit)
983 +static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
986 + struct mbl_pvt *pvt;
989 + pvt = ast->tech_pvt;
991 + if (pvt->type == MBL_TYPE_HEADSET)
994 + ast_debug(1, "Dialed %c\n", digit);
1009 + sprintf(buf, "AT+VTS=%c\r", digit);
1010 + rfcomm_write(pvt, buf);
1013 + ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
1023 + The SCO protocol basically delivers audio in 48 byte 'frames' in slin format.
1024 + Here we just package these into an ast_frame and return them.
1025 + The SCO connection from the device to Asterisk happens asynchronously, so it is feasible
1026 + that Asterisk will call mbl_read() before the device has connected. In that case we just return
1031 +static struct ast_frame *mbl_read(struct ast_channel *ast)
1034 + struct mbl_pvt *pvt = ast->tech_pvt;
1036 + struct ast_frame *f;
1038 + if (!pvt->owner) {
1039 + return &ast_null_frame;
1042 + if (pvt->state == MBL_STATE_HANGUP) {
1043 + return &ast_null_frame;
1046 + if (pvt->sco_socket == -1) {
1047 + return &ast_null_frame;
1050 + pvt->fr.frametype = AST_FRAME_VOICE;
1051 + pvt->fr.subclass = AST_FORMAT_SLINEAR;
1052 + pvt->fr.data = pvt->sco_in_buf + AST_FRIENDLY_OFFSET;
1054 + if ((r = read(pvt->sco_socket, pvt->fr.data, 48)) == 48) {
1055 + if (pvt->skip_frames == 0) {
1056 + f = ast_dsp_process(0, pvt->dsp, &pvt->fr);
1057 + if (f && (f->frametype == AST_FRAME_DTMF_END)) {
1058 + pvt->fr.frametype = AST_FRAME_DTMF_END;
1059 + pvt->fr.subclass = f->subclass;
1060 + pvt->skip_frames = pvt->dtmf_skip;
1064 + pvt->skip_frames--;
1066 + } else if (r == -1) {
1067 + ast_debug(1, "mbl_read() read error %d.\n", errno);
1068 + close(pvt->sco_socket);
1069 + pvt->sco_socket = -1;
1070 + ast_channel_lock(ast);
1072 + ast_channel_unlock(ast);
1074 + ast_debug(1, "mbl_read() read short frame. (%d)\n", r);
1077 + return &ast_null_frame;
1083 + We need to deliver 48 byte 'frames' of slin format audio to the device.
1084 + mbl_write() handles this by buffering short frames until the next time we are called.
1088 +static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
1091 + struct mbl_pvt *pvt = ast->tech_pvt;
1092 + int num_frames, i, r;
1095 + if (frame->frametype != AST_FRAME_VOICE) {
1098 + if (pvt->sco_socket == -1) {
1102 + if (pvt->state == MBL_STATE_HANGUP) {
1106 + if (frame->datalen > sizeof(pvt->sco_out_buf) - pvt->sco_out_len) {
1107 + frame->datalen = sizeof(pvt->sco_out_buf) - pvt->sco_out_len;
1108 + ast_debug(1, "Overrun on sco_out_buf detected.\n");
1111 + memmove(pvt->sco_out_ptr, frame->data, frame->datalen);
1112 + pvt->sco_out_len += frame->datalen;
1113 + num_frames = pvt->sco_out_len / 48;
1115 + pfr = pvt->sco_out_buf;
1116 + for (i=0; i<num_frames; i++) {
1117 + if ((r = write(pvt->sco_socket, pfr, 48)) == -1) {
1118 + close(pvt->sco_socket);
1119 + pvt->sco_socket = -1;
1124 + pvt->sco_out_len = pvt->sco_out_len - (num_frames * 48);
1125 + memmove(pvt->sco_out_buf, pfr, pvt->sco_out_len);
1126 + pvt->sco_out_ptr = pvt->sco_out_buf + pvt->sco_out_len;
1132 +static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
1135 + struct mbl_pvt *pvt = oldchan->tech_pvt;
1137 + if (pvt && pvt->owner == oldchan)
1138 + pvt->owner = newchan;
1144 +static int mbl_devicestate(void *data)
1148 + int res = AST_DEVICE_INVALID;
1149 + struct mbl_pvt *pvt;
1151 + device = ast_strdupa(S_OR(data, ""));
1153 + ast_debug(1, "Checking device state for device %s\n", device);
1155 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
1156 + if (!strcmp(pvt->id, device))
1161 + if (pvt->connected) {
1163 + res = AST_DEVICE_INUSE;
1165 + res = AST_DEVICE_NOT_INUSE;
1173 +static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num)
1176 + struct ast_channel *chn;
1178 + chn = ast_channel_alloc(1, state, 0, 0, 0, 0, 0, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
1180 + chn->tech = &mbl_tech;
1181 + chn->nativeformats = prefformat;
1182 + chn->rawreadformat = prefformat;
1183 + chn->rawwriteformat = prefformat;
1184 + chn->writeformat = prefformat;
1185 + chn->readformat = prefformat;
1186 + chn->readq.first = NULL;
1187 + pvt->fr.frametype = AST_FRAME_VOICE;
1188 + pvt->fr.subclass = AST_FORMAT_SLINEAR;
1189 + pvt->fr.datalen = 48;
1190 + pvt->fr.samples = 24;
1191 + pvt->fr.src = "Mobile";
1192 + pvt->fr.offset = AST_FRIENDLY_OFFSET;
1193 + pvt->fr.mallocd = 0;
1194 + pvt->fr.delivery.tv_sec = 0;
1195 + pvt->fr.delivery.tv_usec = 0;
1196 + pvt->fr.data = pvt->sco_in_buf + AST_FRIENDLY_OFFSET;
1197 + chn->tech_pvt = pvt;
1198 + if (state == AST_STATE_RING)
1200 + ast_copy_string(chn->context, pvt->context, sizeof(chn->context));
1201 + ast_copy_string(chn->exten, "s", sizeof(chn->exten));
1202 + ast_string_field_set(chn, language, "en");
1204 + chn->cid.cid_num = ast_strdup(cid_num);
1205 + chn->cid.cid_name = ast_strdup(pvt->id);
1214 +static int rfcomm_connect(char *bdaddr, int remote_channel) {
1217 + struct sockaddr_rc addr;
1220 + str2ba(bdaddr, &dst);
1222 + if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
1223 + ast_debug(1, "socket() failed (%d).\n", errno);
1227 + memset(&addr, 0, sizeof(addr));
1228 + addr.rc_family = AF_BLUETOOTH;
1229 + bacpy(&addr.rc_bdaddr, &dst);
1230 + addr.rc_channel = remote_channel;
1231 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1232 + ast_debug(1, "connect() failed (%d).\n", errno);
1241 +static int rfcomm_write(struct mbl_pvt *pvt, char *buf)
1245 + ssize_t num_write;
1248 + ast_debug(1, "rfcomm_write() (%s) [%s]\n", pvt->id, buf);
1249 + len = strlen(buf);
1252 + if ((num_write = write(pvt->rfcomm_socket, p, len)) == -1) {
1253 + ast_debug(1, "rfcomm_write() error [%d]\n", errno);
1266 + Here we need to return complete '\r' terminated single responses to the devices monitor thread, or
1267 + a timeout if nothing is available.
1268 + The rfcomm connection to the device is asynchronous, so there is no guarantee that responses will
1269 + be returned in a single read() call. We handle this by buffering the input and returning one response
1270 + per call, or a timeout if nothing is available.
1274 +static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout)
1277 + int sel, rlen, slen;
1279 + struct timeval tv;
1283 + if ((p = strchr(pvt->rfcomm_buf, '\r'))) {
1287 + memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf));
1288 + *(buf + strlen(pvt->rfcomm_buf)) = 0x00;
1289 + memmove(pvt->rfcomm_buf, p, strlen(p));
1290 + *(pvt->rfcomm_buf+strlen(p)) = 0x00;
1294 + pvt->rfcomm_buf[0] = 0x00;
1298 + FD_SET(pvt->rfcomm_socket, &rfds);
1300 + tv.tv_sec = timeout;
1303 + if ((sel = select(pvt->rfcomm_socket + 1, &rfds, NULL, NULL, &tv)) > 0) {
1304 + if (FD_ISSET(pvt->rfcomm_socket, &rfds)) {
1305 + slen = strlen(pvt->rfcomm_buf);
1306 + rlen = read(pvt->rfcomm_socket, pvt->rfcomm_buf + slen, sizeof(pvt->rfcomm_buf) - slen - 1);
1308 + pvt->rfcomm_buf[slen+rlen] = 0x00;
1309 + if ((p = strchr(pvt->rfcomm_buf, '\r'))) {
1313 + memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf));
1314 + *(buf + strlen(pvt->rfcomm_buf)) = 0x00;
1315 + memmove(pvt->rfcomm_buf, p, strlen(p));
1316 + *(pvt->rfcomm_buf+strlen(p)) = 0x00;
1322 + } else if (sel == 0) { /* timeout */
1330 +static int sco_connect(char *bdaddr)
1334 + struct sockaddr_sco addr;
1337 + str2ba(bdaddr, &dst);
1339 + if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
1340 + ast_debug(1, "socket() failed (%d).\n", errno);
1344 + memset(&addr, 0, sizeof(addr));
1345 + addr.sco_family = AF_BLUETOOTH;
1346 + bacpy(&addr.sco_bdaddr, &dst);
1348 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1349 + ast_debug(1, "sco connect() failed (%d).\n", errno);
1360 + sdp_search() performs a service discovery on the given device to determine whether
1361 + or not it supports the Handsfree Profile.
1365 +static int sdp_search(char *addr, int profile)
1368 + sdp_session_t *session = 0;
1371 + uint32_t range = 0x0000ffff;
1372 + sdp_list_t *response_list, *search_list, *attrid_list;
1374 + sdp_list_t *proto_list;
1375 + sdp_record_t *sdprec;
1377 + str2ba(addr, &bdaddr);
1379 + session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
1381 + ast_debug(1, "sdp_connect() failed on device %s.\n", addr);
1385 + sdp_uuid32_create(&svc_uuid, profile);
1386 + search_list = sdp_list_append(0, &svc_uuid);
1387 + attrid_list = sdp_list_append(0, &range);
1388 + response_list = 0x00;
1389 + status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
1390 + if (status == 0) {
1391 + if (response_list) {
1392 + sdprec = (sdp_record_t *) response_list->data;
1393 + proto_list = 0x00;
1394 + if (sdp_get_access_protos(sdprec, &proto_list) == 0) {
1395 + port = sdp_get_proto_port(proto_list, RFCOMM_UUID);
1396 + sdp_list_free(proto_list, 0);
1398 + sdp_record_free(sdprec);
1399 + sdp_list_free(response_list, 0);
1401 + ast_debug(1, "No responses returned for device %s.\n", addr);
1403 + ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr);
1406 + sdp_list_free(search_list, 0);
1407 + sdp_list_free(attrid_list, 0);
1408 + sdp_close(session);
1418 + Register GENERIC_AUDIO & HEADSET with the SDP daemon on the Asterisk Box.
1419 + This assists connections to phones/pda's with dud bluetooth stacks like the IMate Jasjam
1424 +static sdp_session_t *sdp_register(void)
1427 + uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID};
1428 + uint8_t rfcomm_channel = 1;
1429 + const char *service_name = "Asterisk PABX";
1430 + const char *service_dsc = "Asterisk PABX";
1431 + const char *service_prov = "Asterisk";
1433 + uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid;
1434 + sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0;
1435 + sdp_data_t *channel = 0;
1438 + sdp_session_t *session = 0;
1440 + sdp_record_t *record = sdp_record_alloc();
1442 + sdp_uuid128_create(&svc_uuid, &service_uuid_int);
1443 + sdp_set_service_id(record, svc_uuid);
1445 + sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID);
1446 + sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID);
1448 + svc_uuid_list = sdp_list_append(0, &svc_class1_uuid);
1449 + svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid);
1450 + sdp_set_service_classes(record, svc_uuid_list);
1452 + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
1453 + root_list = sdp_list_append(0, &root_uuid);
1454 + sdp_set_browse_groups( record, root_list );
1456 + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
1457 + l2cap_list = sdp_list_append(0, &l2cap_uuid);
1458 + proto_list = sdp_list_append(0, l2cap_list);
1460 + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
1461 + channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
1462 + rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
1463 + sdp_list_append(rfcomm_list, channel);
1464 + sdp_list_append(proto_list, rfcomm_list);
1466 + access_proto_list = sdp_list_append(0, proto_list);
1467 + sdp_set_access_protos(record, access_proto_list);
1469 + sdp_set_info_attr(record, service_name, service_prov, service_dsc);
1471 + if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY)))
1472 + ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n");
1474 + err = sdp_record_register(session, record, 0);
1476 + sdp_data_free(channel);
1477 + sdp_list_free(rfcomm_list, 0);
1478 + sdp_list_free(root_list, 0);
1479 + sdp_list_free(access_proto_list, 0);
1480 + sdp_list_free(svc_uuid_list, 0);
1488 + Phone Monitor Thread
1490 + This thread is spun once a phone device is discovered and considered capable of being used, i.e. supports Handsfree Profile,
1491 + and its configured in mobile.conf.
1492 + The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls.
1496 +static void *do_monitor_phone(void *data)
1499 + struct mbl_pvt *pvt = (struct mbl_pvt *)data;
1500 + struct ast_channel *chn;
1503 + char cid_num[AST_MAX_EXTENSION], *pcids, *pcide;
1504 + int s, t, i, smsi;
1505 + int group, group2;
1506 + int callp = 0, callsetupp;
1507 + char brsf, nsmode, *p, *p1;
1509 + char sms_txt[161];
1511 + brsf = nsmode = 0;
1513 + if (!rfcomm_write(pvt, "AT+BRSF=4\r"))
1518 + if (pvt->state == MBL_STATE_DIAL1)
1519 + t = pvt->dial_timeout;
1520 + else if (pvt->state == MBL_STATE_HANGUP)
1522 + else if (pvt->state == MBL_STATE_OUTSMS1)
1524 + else if (pvt->state == MBL_STATE_OUTSMS2)
1529 + s = rfcomm_read(pvt, buf, 0, t);
1531 + if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
1532 + ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
1533 + switch (pvt->state) {
1534 + case MBL_STATE_INIT:
1535 + if (strstr(buf, "+BRSF:")) {
1537 + } else if (strstr(buf, "ERROR") && !nsmode) { /* Hmmm, Non-Standard Phone, just continue */
1538 + rfcomm_write(pvt, "AT+CIND=?\r");
1541 + } else if (strstr(buf, "OK") && brsf) {
1542 + rfcomm_write(pvt, "AT+CIND=?\r");
1546 + case MBL_STATE_INIT1:
1547 + if (strstr(buf, "+CIND:")) {
1548 + group = callp = callsetupp = 0;
1550 + for (i=0; i<strlen(buf); i++) {
1551 + if (buf[i] == '(')
1553 + if (buf[i] == ')') {
1558 + if (strstr(buf+i, "\"call\""))
1560 + if (strstr(buf+i, "\"call_setup\""))
1561 + callsetupp = group2;
1562 + if (strstr(buf+i, "\"callsetup\""))
1563 + callsetupp = group2;
1565 + sprintf(pvt->ciev_call_0, "%d,0", callp);
1566 + sprintf(pvt->ciev_call_1, "%d,1", callp);
1567 + sprintf(pvt->ciev_callsetup_0, "%d,0", callsetupp);
1568 + sprintf(pvt->ciev_callsetup_1, "%d,1", callsetupp);
1569 + sprintf(pvt->ciev_callsetup_2, "%d,2", callsetupp);
1570 + sprintf(pvt->ciev_callsetup_3, "%d,3", callsetupp);
1571 + if (callsetupp == 0) /* This phone has no call setup indication!! ... */
1572 + pvt->no_callsetup = 1;
1573 + ast_debug(1, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp);
1575 + if (strstr(buf, "OK")) {
1576 + rfcomm_write(pvt, "AT+CIND?\r");
1580 + case MBL_STATE_INIT2:
1581 + if ((p = strstr(buf, "+CIND:"))) {
1583 + if (*(p+(callp*2)) == '1') {
1584 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id);
1587 + } else if (strstr(buf, "OK")) {
1588 + rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
1592 + case MBL_STATE_INIT3:
1593 + if (strstr(buf, "OK")) {
1594 + rfcomm_write(pvt, "AT+CLIP=1\r");
1598 + case MBL_STATE_INIT4:
1599 + if (strstr(buf, "OK")) {
1600 + rfcomm_write(pvt, "AT+CMGF=1\r");
1604 + case MBL_STATE_INIT5:
1605 + if (strstr(buf, "ERROR")) { /* No SMS Support ! */
1606 + pvt->state = MBL_STATE_PREIDLE;
1607 + } else if (strstr(buf, "OK")) {
1608 + rfcomm_write(pvt, "AT+CNMI=2,1,0,1,0\r");
1612 + case MBL_STATE_INIT6:
1613 + if (strstr(buf, "OK")) { /* We have SMS Support */
1615 + pvt->state = MBL_STATE_PREIDLE;
1616 + } else if (strstr(buf, "ERROR")) {
1618 + ast_log(LOG_NOTICE,"Device %s has no bluetooth SMS capability.\n", pvt->id);
1619 + pvt->state = MBL_STATE_PREIDLE;
1622 + case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */
1624 + case MBL_STATE_IDLE:
1625 + ast_debug(1, "Device %s %s [%s]\n", pvt->bdaddr, pvt->id, buf);
1626 + if (strstr(buf, "RING")) {
1627 + pvt->state = MBL_STATE_RING;
1628 + } else if (strstr(buf, "+CIEV:")) {
1629 + if (strstr(buf, pvt->ciev_callsetup_3)) { /* User has dialed out on handset */
1630 + monitor = 0; /* We disconnect now, as he is */
1631 + } /* probably leaving BT range... */
1634 + case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */
1636 + case MBL_STATE_DIAL1:
1637 + if (strstr(buf, "OK")) {
1638 + if (pvt->no_callsetup) {
1639 + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
1641 + ast_setstate(pvt->owner, AST_STATE_RINGING);
1643 + pvt->state = MBL_STATE_OUTGOING;
1646 + case MBL_STATE_OUTGOING:
1647 + if (strstr(buf, "+CIEV")) {
1648 + if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */
1649 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1650 + } else if (strstr(buf, pvt->ciev_callsetup_3)) { /* b-party ringing */
1651 + ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
1652 + } else if (strstr(buf, pvt->ciev_call_1) && !pvt->no_callsetup) { /* b-party answer */
1653 + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
1657 + case MBL_STATE_RING:
1658 + cid_num[0] = 0x00;
1659 + if ((pcids = strstr(buf, "+CLIP:"))) {
1660 + if ((pcids = strchr(pcids, '"'))) {
1661 + if ((pcide = strchr(pcids+1, '"'))) {
1662 + strncpy(cid_num, pcids+1, pcide - pcids - 1);
1663 + cid_num[pcide - pcids - 1] = 0x00;
1666 + pvt->sco_out_ptr = pvt->sco_out_buf;
1667 + pvt->sco_out_len = 0;
1668 + pvt->sent_answer = 0;
1669 + chn = mbl_new(AST_STATE_RING, pvt, cid_num);
1671 + if (ast_pbx_start(chn)) {
1672 + ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
1675 + pvt->state = MBL_STATE_RING3;
1677 + ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
1678 + rfcomm_write(pvt, "AT+CHUP\r");
1679 + pvt->state = MBL_STATE_IDLE;
1683 + case MBL_STATE_RING2:
1684 + pvt->sco_out_ptr = pvt->sco_out_buf;
1685 + pvt->sco_out_len = 0;
1686 + pvt->sent_answer = 0;
1687 + chn = mbl_new(AST_STATE_RING, pvt, cid_num);
1689 + if (ast_pbx_start(chn)) {
1690 + ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
1693 + pvt->state = MBL_STATE_RING3;
1695 + ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
1696 + rfcomm_write(pvt, "AT+CHUP\r");
1697 + pvt->state = MBL_STATE_IDLE;
1700 + case MBL_STATE_RING3:
1701 + if (strstr(buf, "+CIEV")) {
1702 + if (strstr(buf, pvt->ciev_call_1)) {
1703 + if (pvt->sent_answer) { /* We answered */
1704 + pvt->state = MBL_STATE_INCOMING;
1705 + } else { /* User answered on handset!, disconnect */
1706 + pvt->state = MBL_STATE_IDLE;
1707 + ast_log(LOG_NOTICE,"Closing the sco_socket in RING3 with CIEV\n");
1708 + if (pvt->sco_socket > -1)
1709 + close(pvt->sco_socket);
1710 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1713 + if ((strstr(buf, pvt->ciev_callsetup_0) || strstr(buf, pvt->ciev_call_0))) { /* Caller disconnected */
1714 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1718 + case MBL_STATE_INCOMING:
1719 + if (strstr(buf, "+CIEV")) {
1720 + if (strstr(buf, pvt->ciev_call_0)) {
1721 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1725 + case MBL_STATE_HANGUP:
1726 + if (strstr(buf, "OK") || strstr(buf, pvt->ciev_call_0)) {
1727 + pvt->state = MBL_STATE_IDLE;
1730 + case MBL_STATE_INSMS:
1731 + if (strstr(buf, "+CMGR:")) {
1732 + memset(sms_src, 0x00, sizeof(sms_src));
1733 + if ((p = strchr(buf, ','))) {
1734 + if (*(++p) == '"')
1736 + if ((p1 = strchr(p, ','))) {
1737 + if (*(--p1) == '"')
1739 + memset(sms_src, 0x00, sizeof(sms_src));
1740 + strncpy(sms_src, p, p1 - p + 1);
1743 + } else if (strstr(buf, "OK")) {
1744 + chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
1745 + strcpy(chn->exten, "sms");
1746 + pbx_builtin_setvar_helper(chn, "SMSSRC", sms_src);
1747 + pbx_builtin_setvar_helper(chn, "SMSTXT", sms_txt);
1748 + if (ast_pbx_start(chn))
1749 + ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n");
1750 + pvt->state = MBL_STATE_IDLE;
1752 + memset(sms_txt, 0x00, sizeof(sms_txt));
1753 + strncpy(sms_txt, buf, strlen(buf));
1756 + case MBL_STATE_OUTSMS:
1758 + case MBL_STATE_OUTSMS1:
1760 + case MBL_STATE_OUTSMS2:
1761 + if (strstr(buf, "OK")) {
1762 + pvt->state = MBL_STATE_IDLE;
1766 + /* Unsolicited responses */
1768 + if (strstr(buf, "+CMTI:")) { /* SMS Incoming... */
1769 + if ((p = strchr(buf, ','))) {
1773 + sprintf(buf, "AT+CMGR=%d\r", smsi);
1774 + rfcomm_write(pvt, buf);
1775 + pvt->state = MBL_STATE_INSMS;
1780 + } else if (s == 0) { /* Timeouts */
1781 + if (pvt->state == MBL_STATE_INIT2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */
1783 + rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
1784 + } else if (pvt->state == MBL_STATE_INIT3) { /* Some devices dont respond to AT+CMER=3,0,0,1 properly. VK 2020 for example */
1786 + rfcomm_write(pvt, "AT+CLIP=1\r");
1787 + } else if (pvt->state == MBL_STATE_PREIDLE) {
1788 + pvt->connected = 1;
1789 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id);
1790 + pvt->state = MBL_STATE_IDLE;
1791 + } else if (pvt->state == MBL_STATE_DIAL) {
1792 + sprintf(buf, "ATD%s;\r", pvt->dial_number);
1793 + if (!rfcomm_write(pvt, buf)) {
1794 + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
1795 + ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
1796 + pvt->state = MBL_STATE_IDLE;
1798 + pvt->state = MBL_STATE_DIAL1;
1800 + } else if (pvt->state == MBL_STATE_DIAL1) {
1801 + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
1802 + ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
1803 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1804 + pvt->state = MBL_STATE_IDLE;
1805 + } else if (pvt->state == MBL_STATE_RING) { /* No CLIP?, bump it */
1806 + pvt->state = MBL_STATE_RING2;
1807 + } else if (pvt->state == MBL_STATE_HANGUP) {
1808 + if (pvt->hangup_count == 6) {
1809 + ast_debug(1, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id);
1812 + rfcomm_write(pvt, "AT+CHUP\r");
1813 + pvt->hangup_count++;
1814 + } else if (pvt->state == MBL_STATE_OUTSMS) {
1815 + sprintf(buf, "AT+CMGS=\"%s\"\r", pvt->dial_number);
1816 + rfcomm_write(pvt, buf);
1817 + pvt->state = MBL_STATE_OUTSMS1;
1818 + } else if (pvt->state == MBL_STATE_OUTSMS1) {
1819 + if (pvt->rfcomm_buf[0] == '>') {
1820 + sprintf(buf, "%s%c", pvt->sms_txt, 0x1a);
1821 + rfcomm_write(pvt, buf);
1822 + pvt->state = MBL_STATE_OUTSMS2;
1824 + ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
1825 + pvt->state = MBL_STATE_IDLE;
1827 + } else if (pvt->state == MBL_STATE_OUTSMS2) {
1828 + ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
1829 + pvt->state = MBL_STATE_IDLE;
1831 + } else if (s == -1) {
1832 + if (option_verbose > 2)
1833 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
1839 + close(pvt->rfcomm_socket);
1840 + close(pvt->sco_socket);
1841 + pvt->sco_socket = -1;
1842 + pvt->connected = 0;
1843 + pvt->monitor_thread = AST_PTHREADT_NULL;
1851 + Headset Monitor Thread
1853 + This thread is spun once a headset device is discovered and considered capable of being used, i.e. supports Headset Profile,
1854 + and its configured in mobile.conf.
1855 + The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls.
1859 +static void *do_monitor_headset(void *data)
1862 + struct mbl_pvt *pvt = (struct mbl_pvt *)data;
1867 + pvt->state = MBL_STATE_PREIDLE;
1871 + if (pvt->state == MBL_STATE_RING2)
1875 + s = rfcomm_read(pvt, buf, 0, t);
1877 + if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
1878 + ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
1879 + switch (pvt->state) {
1880 + case MBL_STATE_RING2:
1881 + if (strstr(buf, "AT+CKPD=")) {
1882 + ast_channel_lock(pvt->owner);
1883 + pvt->owner->fds[0] = pvt->sco_socket;
1884 + ast_log(LOG_NOTICE,"pvt-sco_socket used for fds in headphone code\n");
1885 + ast_channel_unlock(pvt->owner);
1886 + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
1887 + pvt->state = MBL_STATE_INCOMING;
1888 + rfcomm_write(pvt, "\r\n+VGS=13\r\n");
1889 + rfcomm_write(pvt, "\r\n+VGM=13\r\n");
1892 + case MBL_STATE_INCOMING:
1893 + if (strstr(buf, "AT+CKPD=")) {
1894 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1900 + if (strstr(buf, "AT+VGS=")) {
1901 + rfcomm_write(pvt, "\r\nOK\r\n");
1902 + } else if (strstr(buf, "AT+VGM=")) {
1903 + rfcomm_write(pvt, "\r\nOK\r\n");
1905 + } else if (s == 0) { /* Timeouts */
1906 + if (pvt->state == MBL_STATE_PREIDLE) {
1907 + pvt->connected = 1;
1908 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id);
1909 + pvt->state = MBL_STATE_IDLE;
1910 + } else if (pvt->state == MBL_STATE_RING) {
1911 + pvt->sco_socket = sco_connect(pvt->bdaddr);
1912 + if (pvt->sco_socket > -1) {
1913 + ast_log(LOG_NOTICE,"sco_connect returned -1 in state RING\n");
1914 + ast_setstate(pvt->owner, AST_STATE_RINGING);
1915 + ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
1916 + pvt->state = MBL_STATE_RING2;
1918 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1920 + } else if (pvt->state == MBL_STATE_RING2) {
1921 + rfcomm_write(pvt, "\r\nRING\r\n");
1923 + } else if (s == -1) {
1924 + if (option_verbose > 2)
1925 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
1935 +static int start_monitor(struct mbl_pvt *pvt)
1938 + if (pvt->type == MBL_TYPE_PHONE) {
1939 + if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
1940 + pvt->monitor_thread = AST_PTHREADT_NULL;
1944 + if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) {
1945 + pvt->monitor_thread = AST_PTHREADT_NULL;
1956 + Device Discovery Thread.
1958 + This thread wakes every 'discovery_interval' seconds and trys to connect to
1959 + those configured devices which are not connected. This saves the user from having
1960 + to manually connect his/her cell phone to the asterisk box. Once a successful
1961 + connection is made, a monitor thread is spun on the device which lives for the
1962 + lifetime of the connection.
1966 +static void *do_discovery(void *data)
1969 + struct mbl_pvt *pvt = data;
1972 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
1973 + if (!pvt->connected) {
1974 + if ((pvt->rfcomm_socket = rfcomm_connect(pvt->bdaddr, pvt->rfcomm_port)) > -1) {
1976 + if (start_monitor(pvt)) {
1977 + pvt->connected = 1;
1978 + if (option_verbose > 2)
1979 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id);
1985 + sleep(discovery_interval);
1993 + This thread listens for incoming sco connections.
1994 + Although the Bluetooth Handsfree Profile Specification says that either end may initiate the audio connection,
1995 + in practice some devices (LG TU500) get upset unless they initiate the connection.
1996 + We leave all sco initiation to the device.
1997 + On an inbound sco connection, we need to find the appropriate device, and set the channel fd accordingly.
2001 +static void *do_sco_listen(void *data)
2006 + struct sockaddr_sco addr;
2007 + struct sco_options so;
2011 + socklen_t addrlen;
2012 + struct mbl_pvt *pvt;
2014 + hci_devba(0, &local);
2016 + if ((sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
2017 + ast_log(LOG_ERROR, "Unable to create sco listener socket.\n");
2020 + memset(&addr, 0, sizeof(addr));
2021 + addr.sco_family = AF_BLUETOOTH;
2022 + bacpy(&addr.sco_bdaddr, &local);
2023 + if (bind(sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
2024 + ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
2025 + close(sco_socket);
2028 + if (setsockopt(sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
2029 + ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
2030 + close(sco_socket);
2033 + if (listen(sco_socket, 5) < 0) {
2034 + ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
2035 + close(sco_socket);
2039 + addrlen = sizeof(struct sockaddr);
2040 + ast_log(LOG_NOTICE, "About to accept the sco_socket...\n");
2041 + if ((ns = accept(sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) {
2042 + ast_log(LOG_NOTICE, "sco_socket returns %d...\n",ns);
2043 + ba2str(&addr.sco_bdaddr, saddr);
2046 + getsockopt(ns, SOL_SCO, SCO_OPTIONS, &so, &len);
2048 + ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
2051 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
2052 + if (!strcmp(pvt->bdaddr, saddr))
2056 + ast_log(LOG_NOTICE,"about to close the pvt-sco_socket and set it ns\n");
2057 + if (pvt->sco_socket != -1)
2058 + close(pvt->sco_socket);
2059 + pvt->sco_socket = ns;
2061 + ast_channel_lock(pvt->owner);
2062 + pvt->owner->fds[0] = ns;
2063 + ast_channel_unlock(pvt->owner);
2066 + ast_debug(1, "Could not find device for incoming Audio Connection.\n");
2068 + else ast_log(LOG_NOTICE, "Accept got a -1....");
2075 +static int mbl_load_config(void)
2078 + struct ast_config *cfg = NULL;
2080 + struct ast_variable *var;
2081 + const char *address, *port, *context, *type, *skip;
2082 + struct mbl_pvt *pvt;
2084 + cfg = ast_config_load(MBL_CONFIG);
2088 + for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
2089 + if (!strcasecmp(var->name, "interval"))
2090 + discovery_interval = atoi(var->value);
2093 + cat = ast_category_browse(cfg, NULL);
2095 + if (strcasecmp(cat, "general")) {
2096 + ast_debug(1, "Loading device %s.\n", cat);
2097 + address = ast_variable_retrieve(cfg, cat, "address");
2098 + port = ast_variable_retrieve(cfg, cat, "port");
2099 + context = ast_variable_retrieve(cfg, cat, "context");
2100 + ast_log(LOG_NOTICE, "context for non-general category %s was %s\n", cat, context);
2101 + type = ast_variable_retrieve(cfg, cat, "type");
2102 + skip = ast_variable_retrieve(cfg, cat, "dtmfskip");
2103 + if (address && port) {
2104 + if ((pvt = ast_malloc(sizeof(struct mbl_pvt)))) {
2105 + if (type && !strcmp(type, "headset"))
2106 + pvt->type = MBL_TYPE_HEADSET;
2108 + pvt->type = MBL_TYPE_PHONE;
2109 + ast_copy_string(pvt->id, cat, sizeof(pvt->id));
2110 + ast_copy_string(pvt->bdaddr, address, sizeof(pvt->bdaddr));
2111 + ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context));
2112 + pvt->connected = 0;
2113 + pvt->state = MBL_STATE_INIT;
2114 + pvt->rfcomm_socket = -1;
2115 + pvt->rfcomm_port = atoi(port);
2116 + pvt->rfcomm_buf[0] = 0x00;
2117 + pvt->sco_socket = -1;
2118 + pvt->monitor_thread = AST_PTHREADT_NULL;
2119 + pvt->owner = NULL;
2120 + pvt->no_callsetup = 0;
2122 + pvt->dsp = ast_dsp_new();
2124 + if ((pvt->dtmf_skip = atoi(skip)) == 0)
2125 + pvt->dtmf_skip = 200;
2127 + pvt->dtmf_skip = 200;
2128 + pvt->skip_frames = 0;
2129 + ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT);
2130 + ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
2131 + AST_LIST_INSERT_HEAD(&devices, pvt, entry);
2134 + ast_log(LOG_ERROR, "Device %s has no address/port configured. It wont be enabled.\n", cat);
2137 + cat = ast_category_browse(cfg, cat);
2140 + ast_config_destroy(cfg);
2146 +static int reload_module(void)
2153 +static int unload_module(void)
2156 + struct mbl_pvt *pvt;
2158 + /* First, take us out of the channel loop */
2159 + ast_channel_unregister(&mbl_tech);
2161 + /* Kill the discovery thread */
2162 + if (discovery_thread != AST_PTHREADT_NULL) {
2163 + pthread_cancel(discovery_thread);
2164 + pthread_join(discovery_thread, NULL);
2166 + /* Kill the sco listener thread */
2167 + if (sco_listener_thread != AST_PTHREADT_NULL) {
2168 + pthread_cancel(sco_listener_thread);
2169 + pthread_join(sco_listener_thread, NULL);
2171 + if ((close(sco_socket) == -1))
2172 + ast_log(LOG_ERROR, "Unable to close sco_socket %d.\n", errno);
2174 + /* Unregister the CLI & APP */
2175 + ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
2176 + ast_unregister_application(app_mblstatus);
2177 + ast_unregister_application(app_mblsendsms);
2179 + /* Destroy the device list */
2180 + while ((pvt = AST_LIST_REMOVE_HEAD(&devices, entry))) {
2181 + if (pvt->monitor_thread != AST_PTHREADT_NULL) {
2182 + pthread_cancel(pvt->monitor_thread);
2183 + pthread_join(pvt->monitor_thread, NULL);
2185 + if (pvt->sco_socket > -1) {
2186 + close(pvt->sco_socket);
2188 + if (pvt->rfcomm_socket > -1) {
2189 + close(pvt->rfcomm_socket);
2191 + ast_dsp_free(pvt->dsp);
2196 + sdp_close(sdp_session);
2202 +static int load_module(void)
2208 + /* Check if we have Bluetooth, no point loading otherwise... */
2209 + dev_id = hci_get_route(NULL);
2210 + s = hci_open_dev(dev_id);
2211 + if (dev_id < 0 || s < 0) {
2212 + ast_log(LOG_ERROR, "No Bluetooth device found. Not loading module.\n");
2213 + return AST_MODULE_LOAD_DECLINE;
2216 + hci_read_voice_setting(s, &vs, 1000);
2218 + if (vs != 0x0060) {
2219 + ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060 - see hciconfig hci0 voice.\n");
2221 + return AST_MODULE_LOAD_DECLINE;
2226 + if (!mbl_load_config()) {
2227 + ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", MBL_CONFIG);
2228 + return AST_MODULE_LOAD_DECLINE;
2231 + sdp_session = sdp_register();
2233 + /* Spin the discovery thread */
2234 + if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) {
2235 + ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
2236 + return AST_MODULE_LOAD_DECLINE;
2238 + /* Spin the sco listener thread */
2239 + if (ast_pthread_create_background(&sco_listener_thread, NULL, do_sco_listen, NULL) < 0) {
2240 + ast_log(LOG_ERROR, "Unable to create sco listener thread.\n");
2241 + pthread_cancel(discovery_thread);
2242 + pthread_join(discovery_thread, NULL);
2243 + return AST_MODULE_LOAD_DECLINE;
2246 + ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
2247 + ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc);
2248 + ast_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc);
2250 + /* Make sure we can register our channel type */
2251 + if (ast_channel_register(&mbl_tech)) {
2252 + ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile");
2253 + return AST_MODULE_LOAD_FAILURE;
2259 +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver",
2260 + .load = load_module,
2261 + .unload = unload_module,
2262 + .reload = reload_module,