]> git.pld-linux.org Git - packages/asterisk.git/blame - asterisk-chan_bluetooth.patch
- add bcond for h323 again
[packages/asterisk.git] / asterisk-chan_bluetooth.patch
CommitLineData
b3826e10 1diff -urN asterisk-1.4.0-beta3-o-o/build_tools/menuselect-deps.in asterisk-1.4.0-beta3/build_tools/menuselect-deps.in
2--- asterisk-1.4.0-beta3-o-o/build_tools/menuselect-deps.in 2006-09-19 11:07:22.000000000 -0600
3+++ asterisk-1.4.0-beta3/build_tools/menuselect-deps.in 2006-11-06 12:45:04.000000000 -0700
4@@ -1,4 +1,5 @@
5 ASOUND=@PBX_ALSA@
6+BLUETOOTH=@PBX_BLUETOOTH@
7 CURL=@PBX_CURL@
8 FREETDS=@PBX_FREETDS@
9 GSM=@PBX_GSM@
10diff -urN asterisk-1.4.0-beta3-o-o/channels/chan_bluetooth.c asterisk-1.4.0-beta3/channels/chan_bluetooth.c
11--- asterisk-1.4.0-beta3-o-o/channels/chan_bluetooth.c 1969-12-31 17:00:00.000000000 -0700
12+++ asterisk-1.4.0-beta3/channels/chan_bluetooth.c 2006-11-06 12:44:39.000000000 -0700
13@@ -0,0 +1,3145 @@
14+/*
15+ * Asterisk -- A telephony toolkit for Linux.
16+ *
17+ * Asterisk Bluetooth Channel
18+ *
19+ * Author: Theo Zourzouvillys <theo@adaptive-it.co.uk>
20+ *
21+ * Adaptive Linux Solutions <http://www.adaptive-it.co.uk>
22+ *
23+ * Copyright (C) 2004 Adaptive Linux Solutions
24+ *
25+ * This program is free software, distributed under the terms of
26+ * the GNU General Public License
27+ *
28+ * ******************* NOTE NOTE NOTE NOTE NOTE *********************
29+ *
30+ * This code is not at all tested, and only been developed with a
31+ * HBH-200 headset and a Nokia 6310i right now.
32+ *
33+ * Expect it to crash, dial random numbers, and steal all your money.
34+ *
35+ * PLEASE try any headsets and phones, and let me know the results,
36+ * working or not, along with all debug output!
37+ *
38+ * ------------------------------------------------------------------
39+ *
40+ * Asterisk Bluetooth Support
41+ *
42+ * Well, here we go - Attempt to provide Handsfree profile support in
43+ * both AG and HF modes, AG (AudioGateway) mode support for using
44+ * headsets, and HF (Handsfree) mode for utilising mobile/cell phones
45+ *
46+ * It would be nice to also provide Headset support at some time in
47+ * the future, however, a working Handsfree profile is nice for now,
48+ * and as far as I can see, almost all new HS devices also support HF
49+ *
50+ * ------------------------------------------------------------------
51+ * INSTRUCTIONS
52+ *
53+ * You need to have bluez's bluetooth stack, along with user space
54+ * tools (>=v2.10), and running hcid and sdsp.
55+ *
56+ * See bluetooth.conf for configuration details.
57+ *
58+ * - Ensure bluetooth subsystem is up and running. 'hciconfig'
59+ * should show interface as UP.
60+ *
61+ * - If you're trying to use a headset/HS, start up asterisk, and try
62+ * to pair it as you normally would.
63+ *
64+ * - If you're trying to use a Phone/AG, just make sure bluetooth is
65+ * enabled on your phone, and start up asterisk.
66+ *
67+ * - 'bluetooth show peers' will show all bluetooth devices states.
68+ *
69+ * - Send a call out by using Dial(BLT/DevName/0123456). Call a HS
70+ * with Dial(BLT/DevName)
71+ *
72+ * ------------------------------------------------------------------
73+ * BUGS
74+ *
75+ * - What should happen when an AG is paired with asterisk and
76+ * someone uses the AG dalling a number manually? My test phone
77+ * seems to try to open an SCO link. Perhaps an extension to
78+ * route the call to, or maybe drop the RFCOM link all together?
79+ *
80+ * ------------------------------------------------------------------
81+ * COMPATIBILITY
82+ *
83+ * PLEASE email <theo@adaptive-it.co.uk> with the results of ANY
84+ * device not listed in here (working or not), or if the device is
85+ * listed and it doesn't work! Please also email full debug output
86+ * for any device not working correctly or generating errors in log.
87+ *
88+ * HandsFree Profile:
89+ *
90+ * HS (HeadSet):
91+ * - Ericsson HBH-200
92+ *
93+ * AG (AudioGateway):
94+ * - Nokia 6310i
95+ *
96+ * ------------------------------------------------------------------
97+ *
98+ * Questions, bugs, or (preferably) patches to:
99+ *
100+ * <theo@adaptive-it.co.uk>
101+ *
102+ * ------------------------------------------------------------------
103+ */
104+
105+/*! \file
106+ *
107+ * \brief Channel driver for Bluetooth phones and headsets
108+ *
109+ * \author Theo Zourzouvillys <theo@adaptive-it.co.uk>
110+ *
111+ * \par See also
112+ * \arg \ref Config_bluetooth
113+ *
114+ * \ingroup channel_drivers
115+ */
116+
117+
118+/*** MODULEINFO
119+ <depend>bluetooth</depend>
120+ ***/
121+
122+
123+/* ---------------------------------- */
124+
125+#include <stdio.h>
126+#include <string.h>
127+#include <asterisk/lock.h>
128+#include <asterisk/utils.h>
129+#include <asterisk/channel.h>
130+#include <asterisk/channel_pvt.h>
131+#include <asterisk/config.h>
132+#include <asterisk/logger.h>
133+#include <asterisk/module.h>
134+#include <asterisk/pbx.h>
135+#include <asterisk/sched.h>
136+#include <asterisk/options.h>
137+#include <asterisk/cli.h>
138+#include <asterisk/callerid.h>
139+#include <sys/socket.h>
140+#include <sys/signal.h>
141+#include <sys/time.h>
142+#include <errno.h>
143+#include <unistd.h>
144+#include <stdlib.h>
145+#include <arpa/inet.h>
146+#include <fcntl.h>
147+#include <sys/ioctl.h>
148+#include <ctype.h>
149+
150+#include <bluetooth/bluetooth.h>
151+#include <bluetooth/hci.h>
152+#include <bluetooth/hci_lib.h>
153+#include <bluetooth/sco.h>
154+#include <bluetooth/rfcomm.h>
155+#include <bluetooth/sdp.h>
156+#include <bluetooth/sdp_lib.h>
157+
158+/* --- Data types and definitions --- */
159+
160+#ifndef HANDSFREE_AUDIO_GW_SVCLASS_ID
161+# define HANDSFREE_AUDIO_GW_SVCLASS_ID 0x111f
162+#endif
163+
164+#define BLUETOOTH_FORMAT AST_FORMAT_SLINEAR
165+#define BLT_CHAN_NAME "BLT"
166+#define BLT_CONFIG_FILE "bluetooth.conf"
167+#define BLT_RDBUFF_MAX 1024
168+#define BLT_DEFAULT_HCI_DEV 0
169+#define BLT_SVN_REVISION "$Rev: 38 $"
170+
171+/* ---------------------------------- */
172+
173+typedef enum {
174+ BLT_ROLE_NONE = 0, // Unknown Device
175+ BLT_ROLE_HS = 1, // Device is a Headset
176+ BLT_ROLE_AG = 2 // Device is an Audio Gateway
177+} blt_role_t;
178+
179+/* State when we're in HS mode */
180+
181+typedef enum {
182+ BLT_STATE_WANT_R = 0,
183+ BLT_STATE_WANT_N = 1,
184+ BLT_STATE_WANT_CMD = 2,
185+ BLT_STATE_WANT_N2 = 3,
186+} blt_state_t;
187+
188+typedef enum {
189+ BLT_STATUS_DOWN,
190+ BLT_STATUS_CONNECTING,
191+ BLT_STATUS_NEGOTIATING,
192+ BLT_STATUS_READY,
193+ BLT_STATUS_RINGING,
194+ BLT_STATUS_IN_CALL,
195+} blt_status_t;
196+
197+/* ---------------------------------- */
198+
199+/* Default config settings */
200+
201+#define BLT_DEFAULT_CHANNEL_AG 5
202+#define BLT_DEFAULT_CHANNEL_HS 6
203+#define BLT_DEFAULT_ROLE BLT_ROLE_HS
204+#define BLT_OBUF_LEN (48 * 25)
205+
206+#define BUFLEN 4800
207+
208+/* ---------------------------------- */
209+
210+typedef struct blt_dev blt_dev_t;
211+
212+// XXX:T: Tidy this lot up.
213+struct blt_dev {
214+
215+ blt_status_t status; /* Device Status */
216+
217+ struct ast_channel * owner; /* Channel we belong to, possibly NULL */
218+ blt_dev_t * dev; /* The bluetooth device channel is for */
219+ struct ast_frame fr; /* Recieved frame */
220+
221+ /* SCO Handler */
222+ int sco_pipe[2]; /* SCO alert pipe */
223+ int sco; /* SCO fd */
224+ int sco_handle; /* SCO Handle */
225+ int sco_mtu; /* SCO MTU */
226+ int sco_running; /* 1 when sCO thread should be running */
227+ pthread_t sco_thread; /* SCO thread */
228+ ast_mutex_t sco_lock; /* SCO lock */
229+ int sco_pos_in; /* Reader in position */
230+ int sco_pos_out; /* Reader out position */
231+ int sco_sending; /* Sending SCO packets */
232+ char buf[1024]; /* Incoming data buffer */
233+ char sco_buf_out[BUFLEN+1]; /* 24 chunks of 48 */
234+ char sco_buf_in[BUFLEN+1]; /* 24 chunks of 48 */
235+
236+ char dnid[1024]; /* Outgoi gncall dialed number */
237+ unsigned char * obuf[BLT_OBUF_LEN]; /* Outgoing data buffer */
238+ int obuf_len; /* Output Buffer Position */
239+ int obuf_wpos; /* Buffer Reader */
240+
241+ // device
242+ int autoconnect; /* 1 for autoconnect */
243+ int outgoing_id; /* Outgoing connection scheduler id */
244+ char * name; /* Devices friendly name */
245+ blt_role_t role; /* Device role (HS or AG) */
246+ bdaddr_t bdaddr; /* remote address */
247+ int channel; /* remote channel */
248+ int rd; /* RFCOMM fd */
249+ int tmp_rd; /* RFCOMM fd */
250+ int call_cnt; /* Number of attempted calls */
251+ ast_mutex_t lock; /* RFCOMM socket lock */
252+ char rd_buff[BLT_RDBUFF_MAX]; /* RFCOMM input buffer */
253+ int rd_buff_pos; /* RFCOMM input buffer position */
254+ int ready; /* 1 When ready */
255+
256+ /* AG mode */
257+ char last_ok_cmd[BLT_RDBUFF_MAX]; /* Runtime[AG]: Last AT command that was OK */
258+ int cind; /* Runtime[AG]: Recieved +CIND */
259+ int call_pos, service_pos, callsetup_pos; /* Runtime[AG]: Positions in CIND/CMER */
260+ int call, service, callsetup; /* Runtime[AG]: Values */
261+
262+ /* HS mode */
263+ blt_state_t state; /* Runtime: Device state (AG mode only) */
264+ int ring_timer; /* Runtime:Ring Timer */
265+ char last_err_cmd[BLT_RDBUFF_MAX]; /* Runtime: Last AT command that was OK */
266+ void (*cb)(blt_dev_t * dev, char * str); /* Runtime: Callback when in HS mode */
267+
268+ int brsf; /* Runtime: Bluetooth Retrieve Supported Features */
269+ int bvra; /* Runtime: Bluetooth Voice Recognised Activation */
270+ int gain_speaker; /* Runtime: Gain Of Speaker */
271+ int clip; /* Runtime: Supports CLID */
272+ int colp; /* Runtime: Connected Line ID */
273+ int elip; /* Runtime: (Ericsson) Supports CLID */
274+ int eolp; /* Runtime: (Ericsson) Connected Line ID */
275+ int ringing; /* Runtime: Device is ringing */
276+
277+ blt_dev_t * next; /* Next in linked list */
278+
279+};
280+
281+typedef struct blt_atcb {
282+
283+ /* The command */
284+ char * str;
285+
286+ /* DTE callbacks: */
287+ int (*set)(blt_dev_t * dev, const char * arg, int len);
288+ int (*read)(blt_dev_t * dev);
289+ int (*execute)(blt_dev_t * dev, const char * data);
290+ int (*test)(blt_dev_t * dev);
291+
292+ /* DCE callbacks: */
293+ int (*unsolicited)(blt_dev_t * dev, const char * value);
294+
295+} blt_atcb_t;
296+
297+/* ---------------------------------- */
298+
299+static void rd_close(blt_dev_t * dev, int reconnect, int err);
300+static int send_atcmd(blt_dev_t * device, const char * fmt, ...);
301+static int sco_connect(blt_dev_t * dev);
302+
303+/* ---------------------------------- */
304+
305+/* RFCOMM channel we listen on*/
306+static int rfcomm_channel_ag = BLT_DEFAULT_CHANNEL_AG;
307+static int rfcomm_channel_hs = BLT_DEFAULT_CHANNEL_HS;
308+
309+/* Address of local bluetooth interface */
310+static int hcidev_id;
311+static bdaddr_t local_bdaddr;
312+
313+/* All the current sockets */
314+AST_MUTEX_DEFINE_STATIC(iface_lock);
315+static blt_dev_t * iface_head;
316+static int ifcount = 0;
317+
318+static int sdp_record_hs = -1;
319+static int sdp_record_ag = -1;
320+
321+/* RFCOMM listen socket */
322+static int rfcomm_sock_ag = -1;
323+static int rfcomm_sock_hs = -1;
324+static int sco_socket = -1;
325+
326+static int monitor_pid = -1;
327+
328+/* The socket monitoring thread */
329+static pthread_t monitor_thread = AST_PTHREADT_NULL;
330+AST_MUTEX_DEFINE_STATIC(monitor_lock);
331+
332+/* Cound how many times this module is currently in use */
333+static int usecnt = 0;
334+AST_MUTEX_DEFINE_STATIC(usecnt_lock);
335+
336+static struct sched_context * sched = NULL;
337+
338+/* ---------------------------------- */
339+
340+static const char *
341+role2str(blt_role_t role)
342+{
343+ switch (role) {
344+ case BLT_ROLE_HS:
345+ return "HS";
346+ case BLT_ROLE_AG:
347+ return "AG";
348+ case BLT_ROLE_NONE:
349+ return "??";
350+ }
351+}
352+
353+static const char *
354+status2str(blt_status_t status)
355+{
356+ switch (status) {
357+ case BLT_STATUS_DOWN:
358+ return "Down";
359+ case BLT_STATUS_CONNECTING:
360+ return "Connecting";
361+ case BLT_STATUS_NEGOTIATING:
362+ return "Negotiating";
363+ case BLT_STATUS_READY:
364+ return "Ready";
365+ case BLT_STATUS_RINGING:
366+ return "Ringing";
367+ case BLT_STATUS_IN_CALL:
368+ return "InCall";
369+ };
370+ return "Unknown";
371+}
372+
373+int sock_err(int fd)
374+{
375+ int ret;
376+ int len = sizeof(ret);
377+ getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
378+ return ret;
379+}
380+
381+/* ---------------------------------- */
382+
383+static const char *
384+parse_cind(const char * str, char * name, int name_len)
385+{
386+ int c = 0;
387+
388+ memset(name, 0, name_len);
389+
390+ while (*str) {
391+ if (*str == '(') {
392+ if (++c == 1 && *(str+1) == '"') {
393+ const char * start = str + 2;
394+ int len = 0;
395+ str += 2;
396+ while (*str && *str != '"') {
397+ len++;
398+ str++;
399+ }
400+ if (len == 0)
401+ return NULL;
402+ strncpy(name, start, (len > name_len) ? name_len : len);
403+ }
404+ } else if (*str == ')')
405+ c--;
406+ else if (c == 0 && *str == ',')
407+ return str + 1;
408+ str++;
409+ }
410+ return NULL;
411+}
412+
413+static void
414+set_cind(blt_dev_t * dev, int indicator, int val)
415+{
416+
417+ ast_log(LOG_DEBUG, "CIND %d set to %d\n", indicator, val);
418+
419+ if (indicator == dev->callsetup_pos) {
420+
421+ // call progress
422+
423+ dev->callsetup = val;
424+
425+ switch (val) {
426+ case 3:
427+ // Outgoign ringing
428+ if (dev->owner && dev->role == BLT_ROLE_AG)
429+ ast_queue_control(dev->owner, AST_CONTROL_RINGING);
430+ break;
431+ case 2:
432+ break;
433+ case 1:
434+ break;
435+ case 0:
436+ if (dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0)
437+ ast_queue_control(dev->owner, AST_CONTROL_CONGESTION);
438+ break;
439+ }
440+
441+ } else if (indicator == dev->service_pos) {
442+
443+ // Signal
444+
445+ if (val == 0)
446+ ast_log(LOG_NOTICE, "Audio Gateway %s lost signal\n", dev->name);
447+ else if (dev->service == 0 && val > 0)
448+ ast_log(LOG_NOTICE, "Audio Gateway %s got signal\n", dev->name);
449+
450+ dev->service = val;
451+
452+ } else if (indicator == dev->call_pos) {
453+
454+ // Call
455+
456+ dev->call = val;
457+
458+ if (dev->owner) {
459+ if (val == 1) {
460+ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
461+ } else if (val == 0)
462+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
463+ }
464+
465+ }
466+
467+
468+}
469+
470+/* ---------------------------------- */
471+
472+int
473+set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len)
474+{
475+ int start_pos = *(pos);
476+ int done = 0;
477+ int copy;
478+
479+ while (data_len) {
480+ // Set can_do to the most we can do in this copy.
481+
482+ copy = MIN(circular_len - start_pos, data_len);
483+ memcpy(ring + start_pos, data + done, copy);
484+
485+ done += copy;
486+ start_pos += copy;
487+ data_len -= copy;
488+
489+ if (start_pos == circular_len)
490+ start_pos = 0;
491+ }
492+ *(pos) = start_pos;
493+ return 0;
494+}
495+
496+int
497+get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy)
498+{
499+ int copy;
500+
501+ // |1|2|3|4|5|6|7|8|9|
502+ // |-----|
503+
504+ while (to_copy) {
505+
506+ // Set can_do to the most we can do in this copy.
507+ copy = MIN(ring_size - *head, to_copy);
508+
509+ // ast_log(LOG_DEBUG, "Getting: %d bytes, From pos %d\n", copy, *head);
510+ memcpy(dst, ring + *head, copy);
511+
512+ dst += copy;
513+ *head += copy;
514+ to_copy -= copy;
515+
516+ if (*head == ring_size )
517+ *head = 0;
518+
519+ }
520+
521+ return 0;
522+}
523+
524+/* Handle SCO audio sync.
525+ *
526+ * If we are the MASTER, then we control the timing,
527+ * in 48 byte chunks. If we're the SLAVE, we send
528+ * as and when we recieve a packet.
529+ *
530+ * Because of packet/timing nessecity, we
531+ * start up a thread when we're passing audio, so
532+ * that things are timed exactly right.
533+ *
534+ * sco_thread() is the function that handles it.
535+ *
536+ */
537+
538+static void *
539+sco_thread(void * data)
540+{
541+ blt_dev_t * dev = (blt_dev_t*)data;
542+ int res;
543+ struct pollfd pfd[2];
544+ int in_pos = 0;
545+ int out_pos = 0;
546+ char c = 1;
547+ int sending;
548+ char buf[1024];
549+ int len;
550+
551+ // Avoid deadlock in odd circumstances
552+
553+ ast_log(LOG_DEBUG, "SCO thread started on fd %d, pid %d\n", dev->sco, getpid());
554+
555+ // dev->status = BLT_STATUS_IN_CALL;
556+ // ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
557+ // Set buffer to silence, just incase.
558+
559+ ast_mutex_lock(&(dev->sco_lock));
560+
561+ memset(dev->sco_buf_in, 0x7f, BUFLEN);
562+ memset(dev->sco_buf_out, 0x7f, BUFLEN);
563+
564+ dev->sco_pos_in = 0;
565+ dev->sco_pos_out = 0;
566+
567+ ast_mutex_unlock(&(dev->sco_lock));
568+
569+ while (1) {
570+
571+ ast_mutex_lock(&(dev->sco_lock));
572+
573+ if (dev->sco_running != 1) {
574+ ast_log(LOG_DEBUG, "SCO stopped.\n");
575+ break;
576+ }
577+
578+ pfd[0].fd = dev->sco;
579+ pfd[0].events = POLLIN;
580+
581+ pfd[1].fd = dev->sco_pipe[1];
582+ pfd[1].events = POLLIN;
583+
584+ ast_mutex_unlock(&(dev->sco_lock));
585+
586+ res = poll(pfd, 2, 50);
587+
588+ if (res == -1 && errno != EINTR) {
589+ ast_log(LOG_DEBUG, "SCO poll() error\n");
590+ break;
591+ }
592+
593+ if (res == 0)
594+ continue;
595+
596+ ast_mutex_lock(&(dev->sco_lock));
597+
598+ if (pfd[0].revents & POLLIN) {
599+
600+ len = read(dev->sco, buf, 48);
601+
602+ if (len) {
603+ ast_mutex_lock(&(dev->lock));
604+ set_buffer(dev->sco_buf_in, buf, BUFLEN, &in_pos, len);
605+ get_buffer(buf, dev->sco_buf_out, BUFLEN, &out_pos, len);
606+ write(dev->sco, buf, len);
607+ if (dev->owner && dev->owner->_state == AST_STATE_UP)
608+ write(dev->sco_pipe[1], &c, 1);
609+ ast_mutex_unlock(&(dev->lock));
610+ }
611+
612+ ast_mutex_unlock(&(dev->sco_lock));
613+
614+ } else if (pfd[0].revents) {
615+
616+ int e = sock_err(pfd[0].fd);
617+ ast_log(LOG_ERROR, "SCO connection error: %s (errno %d)\n", strerror(e), e);
618+ break;
619+
620+ } else if (pfd[1].revents & POLLIN) {
621+
622+ int len;
623+
624+ len = read(pfd[1].fd, &c, 1);
625+ sending = (sending) ? 0 : 1;
626+
627+ ast_mutex_unlock(&(dev->sco_lock));
628+
629+ } else if (pfd[1].revents) {
630+
631+ int e = sock_err(pfd[1].fd);
632+ ast_log(LOG_ERROR, "SCO pipe connection event %d on pipe[1]=%d: %s (errno %d)\n", pfd[1].revents, pfd[1].fd, strerror(e), e);
633+ break;
634+
635+ } else {
636+ ast_log(LOG_NOTICE, "Unhandled poll output\n");
637+ ast_mutex_unlock(&(dev->sco_lock));
638+ }
639+
640+ }
641+
642+ ast_mutex_lock(&(dev->lock));
643+ close(dev->sco);
644+ dev->sco = -1;
645+ dev->sco_running = -1;
646+ ast_mutex_unlock(&(dev->sco_lock));
647+ if (dev->owner)
648+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
649+ ast_mutex_unlock(&(dev->lock));
650+ ast_log(LOG_DEBUG, "SCO thread stopped\n");
651+ return NULL;
652+}
653+
654+/* Start SCO thread. Must be called with dev->lock */
655+
656+static int
657+sco_start(blt_dev_t * dev, int fd)
658+{
659+
660+ if (dev->sco_pipe[1] <= 0) {
661+ ast_log(LOG_ERROR, "SCO pipe[1] == %d\n", dev->sco_pipe[1]);
662+ return -1;
663+ }
664+
665+ ast_mutex_lock(&(dev->sco_lock));
666+
667+ if (dev->sco_running != -1) {
668+ ast_log(LOG_ERROR, "Tried to start SCO thread while already running\n");
669+ ast_mutex_unlock(&(dev->sco_lock));
670+ return -1;
671+ }
672+
673+ if (dev->sco == -1) {
674+ if (fd > 0) {
675+ dev->sco = fd;
676+ } else if (sco_connect(dev) != 0) {
677+ ast_log(LOG_ERROR, "SCO fd invalid\n");
678+ ast_mutex_unlock(&(dev->sco_lock));
679+ return -1;
680+ }
681+ }
682+
683+ dev->sco_running = 1;
684+
685+ if (ast_pthread_create(&(dev->sco_thread), NULL, sco_thread, dev) < 0) {
686+ ast_log(LOG_ERROR, "Unable to start SCO thread.\n");
687+ dev->sco_running = -1;
688+ ast_mutex_unlock(&(dev->sco_lock));
689+ return -1;
690+ }
691+
692+ ast_mutex_unlock(&(dev->sco_lock));
693+
694+ return 0;
695+}
696+
697+/* Stop SCO thread. Must be called with dev->lock */
698+
699+static int
700+sco_stop(blt_dev_t * dev)
701+{
702+ ast_mutex_lock(&(dev->sco_lock));
703+ if (dev->sco_running == 1)
704+ dev->sco_running = 0;
705+ else
706+ dev->sco_running = -1;
707+ dev->sco_sending = 0;
708+ ast_mutex_unlock(&(dev->sco_lock));
709+ return 0;
710+}
711+
712+/* ---------------------------------- */
713+
714+/* Answer the call. Call with lock held on device */
715+
716+static int
717+answer(blt_dev_t * dev)
718+{
719+
720+ if ( (!dev->owner) || (dev->ready != 1) || (dev->status != BLT_STATUS_READY && dev->status != BLT_STATUS_RINGING)) {
721+ ast_log(LOG_ERROR, "Attempt to answer() in invalid state (owner=%p, ready=%d, status=%s)\n",
722+ dev->owner, dev->ready, status2str(dev->status));
723+ return -1;
724+ }
725+
726+ // dev->sd = sco_connect(&local_bdaddr, &(dev->bdaddr), NULL, NULL, 0);
727+ // dev->status = BLT_STATUS_IN_CALL;
728+ // dev->owner->fds[0] = dev->sd;
729+ // if we are answering (hitting button):
730+ ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
731+ // if asterisk signals us to answer:
732+ // ast_setstate(ast, AST_STATE_UP);
733+
734+ /* Start SCO link */
735+ sco_start(dev, -1);
736+ return 0;
737+}
738+
739+/* ---------------------------------- */
740+
741+static int
742+blt_write(struct ast_channel * ast, struct ast_frame * frame)
743+{
744+ blt_dev_t * dev = ast->pvt->pvt;
745+
746+ /* Write a frame of (presumably voice) data */
747+
748+ if (frame->frametype != AST_FRAME_VOICE) {
749+ ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
750+ return 0;
751+ }
752+
753+ if (!(frame->subclass & BLUETOOTH_FORMAT)) {
754+ ast_log(LOG_WARNING, "Cannot handle frames in format %d\n", frame->subclass);
755+ return 0;
756+ }
757+
758+ if (ast->_state != AST_STATE_UP) {
759+ return 0;
760+ }
761+
762+ ast_mutex_lock(&(dev->sco_lock));
763+ set_buffer(dev->sco_buf_out, frame->data, BUFLEN, &(dev->sco_pos_out), MIN(frame->datalen, BUFLEN));
764+ ast_mutex_unlock(&(dev->sco_lock));
765+
766+ return 0;
767+
768+}
769+
770+static struct ast_frame *
771+blt_read(struct ast_channel * ast)
772+{
773+ blt_dev_t * dev = ast->pvt->pvt;
774+ char c = 1;
775+ int len;
776+
777+ /* Some nice norms */
778+
779+ dev->fr.datalen = 0;
780+ dev->fr.samples = 0;
781+ dev->fr.data = NULL;
782+ dev->fr.src = BLT_CHAN_NAME;
783+ dev->fr.offset = 0;
784+ dev->fr.mallocd = 0;
785+ dev->fr.delivery.tv_sec = 0;
786+ dev->fr.delivery.tv_usec = 0;
787+
788+ ast_mutex_lock(&(dev->sco_lock));
789+ dev->sco_sending = 1;
790+ read(dev->sco_pipe[0], &c, 1);
791+ len = get_buffer(dev->buf, dev->sco_buf_in, BUFLEN, &(dev->sco_pos_in), 48);
792+ ast_mutex_unlock(&(dev->sco_lock));
793+
794+ dev->fr.data = dev->buf;
795+ dev->fr.samples = len / 2;
796+ dev->fr.datalen = len;
797+ dev->fr.frametype = AST_FRAME_VOICE;
798+ dev->fr.subclass = BLUETOOTH_FORMAT;
799+ dev->fr.offset = 0;
800+
801+ return &dev->fr;
802+}
803+
804+/* Escape Any '"' in str. Return malloc()ed string */
805+static char *
806+escape_str(char * str)
807+{
808+ char * ptr = str;
809+ char * pret;
810+ char * ret;
811+ int len = 0;
812+
813+ while (*ptr) {
814+ if (*ptr == '"')
815+ len++;
816+ len++;
817+ ptr++;
818+ }
819+
820+ ret = malloc(len + 1);
821+ pret = memset(ret, 0, len + 1);
822+
823+ ptr = str;
824+
825+ while (*ptr) {
826+ if (*ptr == '"')
827+ *pret++ = '\\';
828+ *pret++ = *ptr++;
829+ }
830+
831+ return ret;
832+}
833+
834+static int
835+ring_hs(blt_dev_t * dev)
836+{
837+#if (ASTERISK_VERSION_NUM < 010100)
838+ char tmp[AST_MAX_EXTENSION];
839+ char *name, *num;
840+#endif
841+
842+ ast_mutex_lock(&(dev->lock));
843+
844+ if (dev->owner == NULL) {
845+ ast_mutex_unlock(&(dev->lock));
846+ return 0;
847+ }
848+
849+ dev->ringing = 1;
850+ dev->status = BLT_STATUS_RINGING;
851+
852+ send_atcmd(dev, "RING");
853+
854+ dev->owner->rings++;
855+
856+ // XXX:T: '"' needs to be escaped in ELIP.
857+
858+#if (ASTERISK_VERSION_NUM < 010100)
859+
860+ if (dev->owner->callerid) {
861+
862+ memset(tmp, 0, sizeof(tmp));
863+ strncpy(tmp, dev->owner->callerid, sizeof(tmp)-1);
864+
865+ if (!ast_callerid_parse(tmp, &name, &num)) {
866+
867+ if (dev->clip && num)
868+ send_atcmd(dev, "+CLIP: \"%s\",129", num);
869+
870+ if (dev->elip && name) {
871+ char * esc = escape_str(name);
872+ send_atcmd(dev, "*ELIP: \"%s\"", esc);
873+ free(esc);
874+ }
875+ }
876+ }
877+
878+
879+#else
880+
881+ if (dev->clip && dev->owner->cid.cid_num)
882+ send_atcmd(dev, "+CLIP: \"%s\",129", dev->owner->cid.cid_num);
883+
884+ if (dev->elip && dev->owner->cid.cid_name) {
885+ char * esc = escape_str(dev->owner->cid.cid_name);
886+ send_atcmd(dev, "*ELIP: \"%s\"", esc);
887+ free(esc);
888+ }
889+
890+#endif
891+
892+ ast_mutex_unlock(&(dev->lock));
893+
894+ return 1;
895+}
896+
897+/*
898+ * If the HS is already connected, then just send RING, otherwise, things get a
899+ * little more sticky. We first have to find the channel for HS using SDP,
900+ * then intiate the connection. Once we've done that, we can start the call.
901+ */
902+
903+static int
904+blt_call(struct ast_channel * ast, char * dest, int timeout)
905+{
906+ blt_dev_t * dev = ast->pvt->pvt;
907+
908+ if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
909+ ast_log(LOG_WARNING, "blt_call called on %s, neither down nor reserved\n", ast->name);
910+ return -1;
911+ }
912+
913+ ast_log(LOG_DEBUG, "Calling %s on %s [t: %d]\n", dest, ast->name, timeout);
914+
915+ if (ast_mutex_lock(&iface_lock)) {
916+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
917+ return -1;
918+ }
919+
920+// ast_mutex_lock(&(dev->lock));
921+
922+ if (dev->ready == 0) {
923+ ast_log(LOG_WARNING, "Tried to call a device not ready/connected.\n");
924+ ast_setstate(ast, AST_CONTROL_CONGESTION);
925+// ast_mutex_unlock(&(dev->lock));
926+ ast_mutex_unlock(&iface_lock);
927+ return 0;
928+ }
929+
930+ if (dev->role == BLT_ROLE_HS) {
931+
932+ send_atcmd(dev, "+CIEV: 3,1");
933+
934+ dev->ring_timer = ast_sched_add(sched, 5000, AST_SCHED_CB(ring_hs), dev);
935+
936+ ring_hs(dev);
937+
938+ ast_setstate(ast, AST_STATE_RINGING);
939+ ast_queue_control(ast, AST_CONTROL_RINGING);
940+
941+ } else if (dev->role == BLT_ROLE_AG) {
942+
943+ send_atcmd(dev, "ATD%s;", dev->dnid);
944+
945+ } else {
946+
947+ ast_setstate(ast, AST_CONTROL_CONGESTION);
948+ ast_log(LOG_ERROR, "Unknown device role\n");
949+
950+ }
951+
952+// ast_mutex_unlock(&(dev->lock));
953+ ast_mutex_unlock(&iface_lock);
954+
955+ return 0;
956+}
957+
958+static int
959+blt_hangup(struct ast_channel * ast)
960+{
961+ blt_dev_t * dev = ast->pvt->pvt;
962+
963+ ast_log(LOG_DEBUG, "blt_hangup(%s)\n", ast->name);
964+
965+ if (!ast->pvt->pvt) {
966+ ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
967+ return 0;
968+ }
969+
970+ if (ast_mutex_lock(&iface_lock)) {
971+ ast_log(LOG_ERROR, "Failed to get iface_lock\n");
972+ return 0;
973+ }
974+
975+ ast_mutex_lock(&(dev->lock));
976+
977+ sco_stop(dev);
978+ dev->sco_sending = 0;
979+
980+ if (dev->role == BLT_ROLE_HS) {
981+
982+ if (dev->ringing == 0) {
983+ // Actual call in progress
984+ send_atcmd(dev, "+CIEV: 2,0");
985+ } else {
986+
987+ // Just ringing still
988+
989+ if (dev->role == BLT_ROLE_HS)
990+ send_atcmd(dev, "+CIEV: 3,0");
991+
992+ if (dev->ring_timer >= 0)
993+ ast_sched_del(sched, dev->ring_timer);
994+
995+ dev->ring_timer = -1;
996+ dev->ringing = 0;
997+
998+ }
999+
1000+ } else if (dev->role == BLT_ROLE_AG) {
1001+
1002+ // Cancel call.
1003+ send_atcmd(dev, "AT+CHUP");
1004+
1005+ }
1006+
1007+ if (dev->status == BLT_STATUS_IN_CALL || dev->status == BLT_STATUS_RINGING)
1008+ dev->status = BLT_STATUS_READY;
1009+
1010+ ast->pvt->pvt = NULL;
1011+ dev->owner = NULL;
1012+ ast_mutex_unlock(&(dev->lock));
1013+ ast_setstate(ast, AST_STATE_DOWN);
1014+ ast_mutex_unlock(&(iface_lock));
1015+
1016+ return 0;
1017+}
1018+
1019+static int
1020+blt_indicate(struct ast_channel * c, int condition)
1021+{
1022+ ast_log(LOG_DEBUG, "blt_indicate (%d)\n", condition);
1023+
1024+ switch(condition) {
1025+ case AST_CONTROL_RINGING:
1026+ return -1;
1027+ default:
1028+ ast_log(LOG_WARNING, "Don't know how to condition %d\n", condition);
1029+ break;
1030+ }
1031+ return -1;
1032+}
1033+
1034+static int
1035+blt_answer(struct ast_channel * ast)
1036+{
1037+ blt_dev_t * dev = ast->pvt->pvt;
1038+
1039+ ast_mutex_lock(&dev->lock);
1040+
1041+ // if (dev->ring_timer >= 0)
1042+ // ast_sched_del(sched, dev->ring_timer);
1043+ // dev->ring_timer = -1;
1044+
1045+ ast_log(LOG_DEBUG, "Answering interface\n");
1046+
1047+ if (ast->_state != AST_STATE_UP) {
1048+ send_atcmd(dev, "+CIEV: 2,1");
1049+ send_atcmd(dev, "+CIEV: 3,0");
1050+ sco_start(dev, -1);
1051+ ast_setstate(ast, AST_STATE_UP);
1052+ }
1053+
1054+ ast_mutex_unlock(&dev->lock);
1055+
1056+ return 0;
1057+}
1058+
1059+static struct ast_channel *
1060+blt_new(blt_dev_t * dev, int state, const char * context, const char * number)
1061+{
1062+ struct ast_channel * ast;
1063+ char c = 0;
1064+
1065+ if ((ast = ast_channel_alloc(1)) == NULL) {
1066+ ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
1067+ return NULL;
1068+ }
1069+
1070+ snprintf(ast->name, sizeof(ast->name), "BLT/%s", dev->name);
1071+
1072+ // ast->fds[0] = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
1073+
1074+ ast->nativeformats = BLUETOOTH_FORMAT;
1075+ ast->pvt->rawreadformat = BLUETOOTH_FORMAT;
1076+ ast->pvt->rawwriteformat = BLUETOOTH_FORMAT;
1077+ ast->writeformat = BLUETOOTH_FORMAT;
1078+ ast->readformat = BLUETOOTH_FORMAT;
1079+
1080+ ast_setstate(ast, state);
1081+
1082+ ast->type = BLT_CHAN_NAME;
1083+
1084+ ast->pvt->pvt = dev;
1085+
1086+ ast->pvt->call = blt_call;
1087+ ast->pvt->indicate = blt_indicate;
1088+ ast->pvt->hangup = blt_hangup;
1089+ ast->pvt->read = blt_read;
1090+ ast->pvt->write = blt_write;
1091+ ast->pvt->answer = blt_answer;
1092+
1093+ strncpy(ast->context, context, sizeof(ast->context)-1);
1094+ strncpy(ast->exten, number, sizeof(ast->exten) - 1);
1095+
1096+ ast->language[0] = '\0';
1097+
1098+ ast->fds[0] = dev->sco_pipe[0];
1099+ write(dev->sco_pipe[1], &c, 1);
1100+
1101+ dev->owner = ast;
1102+
1103+ ast_mutex_lock(&usecnt_lock);
1104+ usecnt++;
1105+ ast_mutex_unlock(&usecnt_lock);
1106+
1107+ ast_update_use_count();
1108+
1109+ if (state != AST_STATE_DOWN) {
1110+ if (ast_pbx_start(ast)) {
1111+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast->name);
1112+ ast_hangup(ast);
1113+ }
1114+ }
1115+
1116+ return ast;
1117+}
1118+
1119+static struct ast_channel *
1120+#if (ASTERISK_VERSION_NUM < 010100)
1121+blt_request(char * type, int format, void * local_data)
1122+#else
1123+blt_request(const char * type, int format, void * local_data)
1124+#endif
1125+{
1126+ char * data = (char*)local_data;
1127+ int oldformat;
1128+ blt_dev_t * dev = NULL;
1129+ struct ast_channel * ast = NULL;
1130+ char * number = data, * dname;
1131+
1132+ dname = strsep(&number, "/");
1133+
1134+ oldformat = format;
1135+
1136+ format &= BLUETOOTH_FORMAT;
1137+
1138+ if (!format) {
1139+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
1140+ return NULL;
1141+ }
1142+
1143+ ast_log(LOG_DEBUG, "Dialing '%s' via '%s'\n", number, dname);
1144+
1145+ if (ast_mutex_lock(&iface_lock)) {
1146+ ast_log(LOG_ERROR, "Unable to lock iface_list\n");
1147+ return NULL;
1148+ }
1149+
1150+ dev = iface_head;
1151+
1152+ while (dev) {
1153+ if (strcmp(dev->name, dname) == 0) {
1154+ ast_mutex_lock(&(dev->lock));
1155+ if (!dev->ready) {
1156+ ast_log(LOG_ERROR, "Device %s is not connected\n", dev->name);
1157+ ast_mutex_unlock(&(dev->lock));
1158+ ast_mutex_unlock(&iface_lock);
1159+ return NULL;
1160+ }
1161+ break;
1162+ }
1163+ dev = dev->next;
1164+ }
1165+
1166+ ast_mutex_unlock(&iface_lock);
1167+
1168+ if (!dev) {
1169+ ast_log(LOG_WARNING, "Failed to find device named '%s'\n", dname);
1170+ return NULL;
1171+ }
1172+
1173+ if (number && dev->role != BLT_ROLE_AG) {
1174+ ast_log(LOG_WARNING, "Tried to send a call out on non AG\n");
1175+ ast_mutex_unlock(&(dev->lock));
1176+ return NULL;
1177+ }
1178+
1179+ if (dev->role == BLT_ROLE_AG)
1180+ strncpy(dev->dnid, number, sizeof(dev->dnid) - 1);
1181+
1182+ ast = blt_new(dev, AST_STATE_DOWN, "bluetooth", "s");
1183+
1184+ ast_mutex_unlock(&(dev->lock));
1185+
1186+ return ast;
1187+}
1188+
1189+/* ---------------------------------- */
1190+
1191+
1192+/* ---- AT COMMAND SOCKET STUFF ---- */
1193+
1194+static int
1195+send_atcmd(blt_dev_t * dev, const char * fmt, ...)
1196+{
1197+ char buf[1024];
1198+ va_list ap;
1199+ int len;
1200+
1201+ va_start(ap, fmt);
1202+ len = vsnprintf(buf, 1023, fmt, ap);
1203+ va_end(ap);
1204+
1205+ if (option_verbose)
1206+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < %s\n", role2str(dev->role), 10, dev->name, buf);
1207+
1208+ write(dev->rd, "\r\n", 2);
1209+ len = write(dev->rd, buf, len);
1210+ write(dev->rd, "\r\n", 2);
1211+ return (len) ? 0 : -1;
1212+}
1213+
1214+
1215+static int
1216+send_atcmd_ok(blt_dev_t * dev, const char * cmd)
1217+{
1218+ int len;
1219+ strncpy(dev->last_ok_cmd, cmd, BLT_RDBUFF_MAX - 1);
1220+ if (option_verbose)
1221+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < OK\n", role2str(dev->role), 10, dev->name);
1222+ len = write(dev->rd, "\r\nOK\r\n", 6);
1223+ return (len) ? 0 : -1;
1224+}
1225+
1226+static int
1227+send_atcmd_error(blt_dev_t * dev)
1228+{
1229+ int len;
1230+
1231+ if (option_verbose)
1232+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < ERROR\n", role2str(dev->role), 10, dev->name);
1233+
1234+// write(dev->rd, "\r\n", 2);
1235+// len = write(dev->rd, dev->last_ok_cmd, 5);
1236+ write(dev->rd, "\r\n", 2);
1237+ len = write(dev->rd, "ERROR", 5);
1238+ write(dev->rd, "\r\n", 2);
1239+
1240+ return (len) ? 0 : -1;
1241+}
1242+
1243+
1244+/* ---------------------------------- */
1245+
1246+/* -- Handle negotiation when we're an AG -- */
1247+
1248+/* Bluetooth Support */
1249+
1250+static int
1251+atcmd_brsf_set(blt_dev_t * dev, const char * arg, int len)
1252+{
1253+ ast_log(LOG_DEBUG, "Device Supports: %s\n", arg);
1254+ dev->brsf = atoi(arg);
1255+ send_atcmd(dev, "+BRSF: %d", 23);
1256+ return 0;
1257+}
1258+
1259+/* Bluetooth Voice Recognition */
1260+
1261+static int
1262+atcmd_bvra_set(blt_dev_t * dev, const char * arg, int len)
1263+{
1264+ ast_log(LOG_WARNING, "+BVRA Not Yet Supported\n");
1265+ return -1;
1266+#if 0
1267+ // XXX:T: Fix voice recognition somehow!
1268+ int action = atoi(arg);
1269+ ast_log(LOG_DEBUG, "Voice Recognition: %s\n", (a) ? "ACTIVATED" : "DEACTIVATED");
1270+ if ((action == 0) & (dev->bvra == 1)) {
1271+ /* Disable it */
1272+ dev->bvra = 0;
1273+ // XXX:T: Shutdown any active bvra channel
1274+ ast_log(LOG_DEBUG, "Voice Recognition: DISABLED\n");
1275+ } else if ((action == 1) && (dev->bvra == 0)) {
1276+ /* Enable it */
1277+ dev->bvra = 1;
1278+ // XXX:T: Schedule connection to voice recognition extension/application
1279+ ast_log(LOG_DEBUG, "Voice Recognition: ENABLED\n");
1280+ } else {
1281+ ast_log(LOG_ERROR, "+BVRA out of sync (we think %d, but HS wants %d)\n", dev->bvra, action);
1282+ return -1;
1283+ }
1284+ return 0;
1285+#endif
1286+}
1287+
1288+/* Clock */
1289+
1290+static int
1291+atcmd_cclk_read(blt_dev_t * dev)
1292+{
1293+ struct tm t, *tp;
1294+ const time_t ti = time(0);
1295+ tp = localtime_r(&ti, &t);
1296+ send_atcmd(dev, "+CCLK: \"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"",
1297+ (tp->tm_year % 100), (tp->tm_mon + 1), (tp->tm_mday),
1298+ tp->tm_hour, tp->tm_min, tp->tm_sec, ((tp->tm_gmtoff / 60) / 15));
1299+ return 0;
1300+}
1301+
1302+/* CHUP - Hangup Call */
1303+
1304+static int
1305+atcmd_chup_execute(blt_dev_t * dev, const char * data)
1306+{
1307+ if (!dev->owner) {
1308+ ast_log(LOG_ERROR, "Request to hangup call when none in progress\n");
1309+ return -1;
1310+ }
1311+ ast_log(LOG_DEBUG, "Hangup Call\n");
1312+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
1313+ return 0;
1314+}
1315+
1316+/* CIND - Call Indicator */
1317+
1318+static int
1319+atcmd_cind_read(blt_dev_t * dev)
1320+{
1321+ send_atcmd(dev, "+CIND: 1,0,0");
1322+ return 0;
1323+}
1324+
1325+static int
1326+atcmd_cind_test(blt_dev_t * dev)
1327+{
1328+ send_atcmd(dev, "+CIND: (\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-4))");
1329+ return 0;
1330+}
1331+
1332+/* Set Language */
1333+
1334+static int
1335+atcmd_clan_read(blt_dev_t * dev)
1336+{
1337+ send_atcmd(dev, "+CLAN: \"en\"");
1338+ return 0;
1339+}
1340+
1341+/* Caller Id Presentation */
1342+
1343+static int
1344+atcmd_clip_set(blt_dev_t * dev, const char * arg, int len)
1345+{
1346+ dev->clip = atoi(arg);
1347+ return 0;
1348+}
1349+
1350+/* Conneced Line Identification Presentation */
1351+
1352+static int
1353+atcmd_colp_set(blt_dev_t * dev, const char * arg, int len)
1354+{
1355+ dev->colp = atoi(arg);
1356+ return 0;
1357+}
1358+
1359+/* CMER - Mobile Equipment Event Reporting */
1360+
1361+static int
1362+atcmd_cmer_set(blt_dev_t * dev, const char * arg, int len)
1363+{
1364+ dev->ready = 1;
1365+ dev->status = BLT_STATUS_READY;
1366+ return 0;
1367+}
1368+
1369+/* PhoneBook Types:
1370+ *
1371+ * - FD - SIM Fixed Dialing Phone Book
1372+ * - ME - ME Phone book
1373+ * - SM - SIM Phone Book
1374+ * - DC - ME dialled-calls list
1375+ * - RC - ME recieved-calls lisr
1376+ * - MC - ME missed-calls list
1377+ * - MV - ME Voice Activated Dialing List
1378+ * - HP - Hierachial Phone Book
1379+ * - BC - Own Business Card (PIN2 required)
1380+ *
1381+ */
1382+
1383+/* Read Phone Book Entry */
1384+
1385+static int
1386+atcmd_cpbr_set(blt_dev_t * dev, const char * arg, int len)
1387+{
1388+ // XXX:T: Fix the phone book!
1389+ // * Maybe add res_phonebook or something? */
1390+ send_atcmd(dev, "+CPBR: %d,\"%s\",128,\"%s\"", atoi(arg), arg, arg);
1391+ return 0;
1392+}
1393+
1394+/* Select Phone Book */
1395+
1396+static int
1397+atcmd_cpbs_set(blt_dev_t * dev, const char * arg, int len)
1398+{
1399+ // XXX:T: I guess we'll just accept any?
1400+ return 0;
1401+}
1402+
1403+static int
1404+atcmd_cscs_set(blt_dev_t * dev, const char * arg, int len)
1405+{
1406+ // XXX:T: Language
1407+ return 0;
1408+}
1409+
1410+static int
1411+atcmd_eips_set(blt_dev_t * dev, const char * arg, int len)
1412+{
1413+ ast_log(LOG_DEBUG, "Identify Presentation Set: %s=%s\n",
1414+ (*(arg) == 49) ? "ELIP" : "EOLP",
1415+ (*(arg+2) == 49) ? "ON" : "OFF");
1416+
1417+ if (*(arg) == 49)
1418+ dev->eolp = (*(arg+2) == 49) ? 1 : 0;
1419+ else
1420+ dev->elip = (*(arg+2) == 49) ? 1 : 0;
1421+
1422+ return 0;
1423+}
1424+
1425+/* VGS - Speaker Volume Gain */
1426+
1427+static int
1428+atcmd_vgs_set(blt_dev_t * dev, const char * arg, int len)
1429+{
1430+ dev->gain_speaker = atoi(arg);
1431+ return 0;
1432+}
1433+
1434+/* Dial */
1435+static int
1436+atcmd_dial_execute(blt_dev_t * dev, const char * data)
1437+{
1438+ char * number = NULL;
1439+
1440+ /* Make sure there is a ';' at the end of the line */
1441+ if (*(data + (strlen(data) - 1)) != ';') {
1442+ ast_log(LOG_WARNING, "Can't dial non-voice right now: %s\n", data);
1443+ return -1;
1444+ }
1445+
1446+ number = strndup(data, strlen(data) - 1);
1447+ ast_log(LOG_NOTICE, "Dial: [%s]\n", number);
1448+
1449+ send_atcmd(dev, "+CIEV: 2,1");
1450+ send_atcmd(dev, "+CIEV: 3,0");
1451+
1452+ sco_start(dev, -1);
1453+
1454+ if (blt_new(dev, AST_STATE_UP, "bluetooth", number) == NULL) {
1455+ sco_stop(dev);
1456+ }
1457+
1458+ free(number);
1459+
1460+ return 0;
1461+}
1462+
1463+/* Answer */
1464+
1465+static int
1466+atcmd_answer_execute(blt_dev_t * dev, const char * data)
1467+{
1468+
1469+ if (!dev->ringing || !dev->owner) {
1470+ ast_log(LOG_WARNING, "Can't answer non existant call\n");
1471+ return -1;
1472+ }
1473+
1474+ dev->ringing = 0;
1475+
1476+ if (dev->ring_timer >= 0)
1477+ ast_sched_del(sched, dev->ring_timer);
1478+
1479+ dev->ring_timer = -1;
1480+
1481+ send_atcmd(dev, "+CIEV: 2,1");
1482+ send_atcmd(dev, "+CIEV: 3,0");
1483+
1484+ return answer(dev);
1485+}
1486+
1487+static int
1488+ag_unsol_ciev(blt_dev_t * dev, const char * data)
1489+{
1490+ const char * orig = data;
1491+ int indicator;
1492+ int status;
1493+
1494+ while (*(data) && *(data) == ' ')
1495+ data++;
1496+
1497+ if (*(data) == 0) {
1498+ ast_log(LOG_WARNING, "Invalid value[1] for '+CIEV:%s'\n", orig);
1499+ return -1;
1500+ }
1501+
1502+ indicator = *(data++) - 48;
1503+
1504+ if (*(data++) != ',') {
1505+ ast_log(LOG_WARNING, "Invalid value[2] for '+CIEV:%s'\n", orig);
1506+ return -1;
1507+ }
1508+
1509+ if (*(data) == 0) {
1510+ ast_log(LOG_WARNING, "Invalid value[3] for '+CIEV:%s'\n", orig);
1511+ return -1;
1512+ }
1513+
1514+ status = *(data) - 48;
1515+
1516+ set_cind(dev, indicator, status);
1517+
1518+ return 0;
1519+}
1520+
1521+static int
1522+ag_unsol_cind(blt_dev_t * dev, const char * data)
1523+{
1524+
1525+ while (*(data) && *(data) == ' ')
1526+ data++;
1527+
1528+
1529+ if (dev->cind == 0)
1530+ {
1531+ int pos = 1;
1532+ char name[1024];
1533+
1534+ while ((data = parse_cind(data, name, 1023)) != NULL) {
1535+ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
1536+ if (strcmp(name, "call") == 0)
1537+ dev->call_pos = pos;
1538+ else if (strcmp(name, "service") == 0)
1539+ dev->service_pos = pos;
1540+ else if (strcmp(name, "call_setup") == 0 || strcmp(name, "callsetup") == 0)
1541+ dev->callsetup_pos = pos;
1542+ pos++;
1543+ }
1544+
1545+ ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
1546+
1547+ } else {
1548+
1549+ int pos = 1, len = 0;
1550+ char val[128];
1551+ const char * start = data;
1552+
1553+ while (*data) {
1554+ if (*data == ',') {
1555+ memset(val, 0, 128);
1556+ strncpy(val, start, len);
1557+ set_cind(dev, pos, atoi(val));
1558+ pos++;
1559+ len = 0;
1560+ data++;
1561+ start = data;
1562+ continue;
1563+ }
1564+ len++;
1565+ data++;
1566+ }
1567+
1568+ memset(val, 0, 128);
1569+ strncpy(val, start, len);
1570+ ast_log(LOG_DEBUG, "CIND IND %d set to %d [%s]\n", pos, atoi(val), val);
1571+
1572+
1573+ }
1574+
1575+ return 0;
1576+}
1577+
1578+static blt_atcb_t
1579+atcmd_list[] =
1580+{
1581+ { "A", NULL, NULL, atcmd_answer_execute, NULL, NULL },
1582+ { "D", NULL, NULL, atcmd_dial_execute, NULL, NULL },
1583+ { "+BRSF", atcmd_brsf_set, NULL, NULL, NULL, NULL },
1584+ { "+BVRA", atcmd_bvra_set, NULL, NULL, NULL, NULL },
1585+ { "+CCLK", NULL, atcmd_cclk_read, NULL, NULL, NULL },
1586+ { "+CHUP", NULL, NULL, atcmd_chup_execute, NULL, NULL },
1587+ { "+CIEV", NULL, NULL, NULL, NULL, ag_unsol_ciev },
1588+ { "+CIND", NULL, atcmd_cind_read, NULL, atcmd_cind_test, ag_unsol_cind },
1589+ { "+CLAN", NULL, atcmd_clan_read, NULL, NULL, NULL },
1590+ { "+CLIP", atcmd_clip_set, NULL, NULL, NULL, NULL },
1591+ { "+COLP", atcmd_colp_set, NULL, NULL, NULL, NULL },
1592+ { "+CMER", atcmd_cmer_set, NULL, NULL, NULL, NULL },
1593+ { "+CPBR", atcmd_cpbr_set, NULL, NULL, NULL, NULL },
1594+ { "+CPBS", atcmd_cpbs_set, NULL, NULL, NULL, NULL },
1595+ { "+CSCS", atcmd_cscs_set, NULL, NULL, NULL, NULL },
1596+ { "*EIPS", atcmd_eips_set, NULL, NULL, NULL, NULL },
1597+ { "+VGS", atcmd_vgs_set, NULL, NULL, NULL, NULL },
1598+};
1599+
1600+#define ATCMD_LIST_LEN (sizeof(atcmd_list) / sizeof(blt_atcb_t))
1601+
1602+/* ---------------------------------- */
1603+
1604+/* -- Handle negotiation when we're a HS -- */
1605+
1606+void
1607+ag_unknown_response(blt_dev_t * dev, char * cmd)
1608+{
1609+ ast_log(LOG_DEBUG, "Got UNKN response: %s\n", cmd);
1610+
1611+ // DELAYED
1612+ // NO CARRIER
1613+
1614+}
1615+
1616+void
1617+ag_cgmi_response(blt_dev_t * dev, char * cmd)
1618+{
1619+ // CGMM - Phone Model
1620+ // CGMR - Phone Revision
1621+ // CGSN - IMEI
1622+ // AT*
1623+ // VTS - send tone
1624+ // CREG
1625+ // CBC - BATTERY
1626+ // CSQ - SIGANL
1627+ // CSMS - SMS STUFFS
1628+ // CMGL
1629+ // CMGR
1630+ // CMGS
1631+ // CSCA - sms CENTER NUMBER
1632+ // CNMI - SMS INDICATION
1633+ // ast_log(LOG_DEBUG, "Manufacturer: %s\n", cmd);
1634+ dev->cb = ag_unknown_response;
1635+}
1636+
1637+void
1638+ag_cgmi_valid_response(blt_dev_t * dev, char * cmd)
1639+{
1640+ // send_atcmd(dev, "AT+WS46?");
1641+ // send_atcmd(dev, "AT+CRC=1");
1642+ // send_atcmd(dev, "AT+CNUM");
1643+
1644+ if (strcmp(cmd, "OK") == 0) {
1645+ send_atcmd(dev, "AT+CGMI");
1646+ dev->cb = ag_cgmi_response;
1647+ } else {
1648+ dev->cb = ag_unknown_response;
1649+ }
1650+}
1651+
1652+void
1653+ag_clip_response(blt_dev_t * dev, char * cmd)
1654+{
1655+ send_atcmd(dev, "AT+CGMI=?");
1656+ dev->cb = ag_cgmi_valid_response;
1657+}
1658+
1659+void
1660+ag_cmer_response(blt_dev_t * dev, char * cmd)
1661+{
1662+ dev->cb = ag_clip_response;
1663+ dev->ready = 1;
1664+ dev->status = BLT_STATUS_READY;
1665+ send_atcmd(dev, "AT+CLIP=1");
1666+}
1667+
1668+void
1669+ag_cind_status_response(blt_dev_t * dev, char * cmd)
1670+{
1671+ // XXX:T: Handle response.
1672+ dev->cb = ag_cmer_response;
1673+ send_atcmd(dev, "AT+CMER=3,0,0,1");
1674+ // Initiase SCO link!
1675+}
1676+
1677+void
1678+ag_cind_response(blt_dev_t * dev, char * cmd)
1679+{
1680+ dev->cb = ag_cind_status_response;
1681+ dev->cind = 1;
1682+ send_atcmd(dev, "AT+CIND?");
1683+}
1684+
1685+void
1686+ag_brsf_response(blt_dev_t * dev, char * cmd)
1687+{
1688+ dev->cb = ag_cind_response;
1689+ ast_log(LOG_DEBUG, "Bluetooth features: %s\n", cmd);
1690+ dev->cind = 0;
1691+ send_atcmd(dev, "AT+CIND=?");
1692+}
1693+
1694+/* ---------------------------------- */
1695+
1696+static int
1697+sdp_register(sdp_session_t * session)
1698+{
1699+ // XXX:T: Fix this horrible function so it makes some sense and is extensible!
1700+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
1701+ uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
1702+ sdp_profile_desc_t profile;
1703+ sdp_list_t *aproto, *proto[2];
1704+ sdp_record_t record;
1705+ uint8_t u8 = rfcomm_channel_ag;
1706+ uint8_t u8_hs = rfcomm_channel_hs;
1707+ sdp_data_t *channel;
1708+ int ret = 0;
1709+
1710+ memset((void *)&record, 0, sizeof(sdp_record_t));
1711+ record.handle = 0xffffffff;
1712+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
1713+ root = sdp_list_append(0, &root_uuid);
1714+ sdp_set_browse_groups(&record, root);
1715+
1716+ // Register as an AG
1717+
1718+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
1719+ svclass_id = sdp_list_append(0, &svclass_uuid);
1720+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
1721+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
1722+ sdp_set_service_classes(&record, svclass_id);
1723+ sdp_uuid16_create(&profile.uuid, 0x111f);
1724+ profile.version = 0x0100;
1725+ pfseq = sdp_list_append(0, &profile);
1726+
1727+ sdp_set_profile_descs(&record, pfseq);
1728+
1729+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
1730+ proto[0] = sdp_list_append(0, &l2cap_uuid);
1731+ apseq = sdp_list_append(0, proto[0]);
1732+
1733+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
1734+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
1735+ channel = sdp_data_alloc(SDP_UINT8, &u8);
1736+ proto[1] = sdp_list_append(proto[1], channel);
1737+ apseq = sdp_list_append(apseq, proto[1]);
1738+
1739+ aproto = sdp_list_append(0, apseq);
1740+ sdp_set_access_protos(&record, aproto);
1741+
1742+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
1743+
1744+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
1745+ ast_log(LOG_ERROR, "Service Record registration failed\n");
1746+ ret = -1;
1747+ goto end;
1748+ }
1749+
1750+ sdp_record_ag = record.handle;
1751+
1752+ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
1753+
1754+ sdp_data_free(channel);
1755+ sdp_list_free(proto[0], 0);
1756+ sdp_list_free(proto[1], 0);
1757+ sdp_list_free(apseq, 0);
1758+ sdp_list_free(aproto, 0);
1759+
1760+ // -------------
1761+
1762+ memset((void *)&record, 0, sizeof(sdp_record_t));
1763+ record.handle = 0xffffffff;
1764+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
1765+ root = sdp_list_append(0, &root_uuid);
1766+ sdp_set_browse_groups(&record, root);
1767+
1768+ // Register as an HS
1769+
1770+ sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
1771+ svclass_id = sdp_list_append(0, &svclass_uuid);
1772+ sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
1773+ svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
1774+ sdp_set_service_classes(&record, svclass_id);
1775+ sdp_uuid16_create(&profile.uuid, 0x111e);
1776+ profile.version = 0x0100;
1777+ pfseq = sdp_list_append(0, &profile);
1778+ sdp_set_profile_descs(&record, pfseq);
1779+
1780+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
1781+ proto[0] = sdp_list_append(0, &l2cap_uuid);
1782+ apseq = sdp_list_append(0, proto[0]);
1783+
1784+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
1785+ proto[1] = sdp_list_append(0, &rfcomm_uuid);
1786+ channel = sdp_data_alloc(SDP_UINT8, &u8_hs);
1787+ proto[1] = sdp_list_append(proto[1], channel);
1788+ apseq = sdp_list_append(apseq, proto[1]);
1789+
1790+ aproto = sdp_list_append(0, apseq);
1791+ sdp_set_access_protos(&record, aproto);
1792+ sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
1793+
1794+ if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
1795+ ast_log(LOG_ERROR, "Service Record registration failed\n");
1796+ ret = -1;
1797+ goto end;
1798+ }
1799+
1800+ sdp_record_hs = record.handle;
1801+
1802+ ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
1803+
1804+end:
1805+ sdp_data_free(channel);
1806+ sdp_list_free(proto[0], 0);
1807+ sdp_list_free(proto[1], 0);
1808+ sdp_list_free(apseq, 0);
1809+ sdp_list_free(aproto, 0);
1810+
1811+ return ret;
1812+}
1813+
1814+static int
1815+rfcomm_listen(bdaddr_t * bdaddr, int channel)
1816+{
1817+
1818+ int sock = -1;
1819+ struct sockaddr_rc loc_addr;
1820+ int on = 1;
1821+
1822+ if ((sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
1823+ ast_log(LOG_ERROR, "Can't create socket: %s (errno: %d)\n", strerror(errno), errno);
1824+ return -1;
1825+ }
1826+
1827+ loc_addr.rc_family = AF_BLUETOOTH;
1828+
1829+ /* Local Interface Address */
1830+ bacpy(&loc_addr.rc_bdaddr, bdaddr);
1831+
1832+ /* Channel */
1833+ loc_addr.rc_channel = channel;
1834+
1835+ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
1836+ ast_log(LOG_ERROR, "Can't bind socket: %s (errno: %d)\n", strerror(errno), errno);
1837+ close(sock);
1838+ return -1;
1839+ }
1840+
1841+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
1842+ ast_log(LOG_ERROR, "Set socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
1843+ close(sock);
1844+ return -1;
1845+ }
1846+
1847+ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
1848+ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
1849+
1850+ if (listen(sock, 10) < 0) {
1851+ ast_log(LOG_ERROR,"Can not listen on the socket. %s(%d)\n", strerror(errno), errno);
1852+ close(sock);
1853+ return -1;
1854+ }
1855+
1856+ ast_log(LOG_NOTICE, "Listening for RFCOMM channel %d connections on FD %d\n", channel, sock);
1857+
1858+ return sock;
1859+}
1860+
1861+
1862+static int
1863+sco_listen(bdaddr_t * bdaddr)
1864+{
1865+ int sock = -1;
1866+ int on = 1;
1867+ struct sockaddr_sco loc_addr;
1868+
1869+ memset(&loc_addr, 0, sizeof(loc_addr));
1870+
1871+ if ((sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
1872+ ast_log(LOG_ERROR, "Can't create SCO socket: %s (errno: %d)\n", strerror(errno), errno);
1873+ return -1;
1874+ }
1875+
1876+ loc_addr.sco_family = AF_BLUETOOTH;
1877+ bacpy(&loc_addr.sco_bdaddr, BDADDR_ANY);
1878+
1879+ if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
1880+ ast_log(LOG_ERROR, "Can't bind SCO socket: %s (errno: %d)\n", strerror(errno), errno);
1881+ close(sock);
1882+ return -1;
1883+ }
1884+
1885+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
1886+ ast_log(LOG_ERROR, "Set SCO socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
1887+ close(sock);
1888+ return -1;
1889+ }
1890+
1891+ if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
1892+ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
1893+
1894+ if (listen(sock, 10) < 0) {
1895+ ast_log(LOG_ERROR,"Can not listen on SCO socket: %s(%d)\n", strerror(errno), errno);
1896+ close(sock);
1897+ return -1;
1898+ }
1899+
1900+ ast_log(LOG_NOTICE, "Listening for SCO connections on FD %d\n", sock);
1901+
1902+ return sock;
1903+}
1904+
1905+static int
1906+rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, int channel, int nbio)
1907+{
1908+ struct sockaddr_rc addr;
1909+ int s;
1910+
1911+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
1912+ return -1;
1913+ }
1914+
1915+ memset(&addr, 0, sizeof(addr));
1916+ addr.rc_family = AF_BLUETOOTH;
1917+ bacpy(&addr.rc_bdaddr, src);
1918+ addr.rc_channel = 0;
1919+
1920+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1921+ close(s);
1922+ return -1;
1923+ }
1924+
1925+ memset(&addr, 0, sizeof(addr));
1926+ addr.rc_family = AF_BLUETOOTH;
1927+ bacpy(&addr.rc_bdaddr, dst);
1928+ addr.rc_channel = channel;
1929+
1930+ if (nbio) {
1931+ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
1932+ ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
1933+ }
1934+
1935+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 && (nbio != 1 || (errno != EAGAIN))) {
1936+ close(s);
1937+ return -1;
1938+ }
1939+
1940+ return s;
1941+}
1942+
1943+/* Must be called with dev->lock held */
1944+
1945+static int
1946+sco_connect(blt_dev_t * dev)
1947+{
1948+ struct sockaddr_sco addr;
1949+ // struct sco_conninfo conn;
1950+ // struct sco_options opts;
1951+ // int size;
1952+ // bdaddr_t * src = &local_bdaddr;
1953+
1954+ int s;
1955+ bdaddr_t * dst = &(dev->bdaddr);
1956+
1957+ if (dev->sco != -1) {
1958+ ast_log(LOG_ERROR, "SCO fd already open.\n");
1959+ return -1;
1960+ }
1961+
1962+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
1963+ ast_log(LOG_ERROR, "Can't create SCO socket(): %s\n", strerror(errno));
1964+ return -1;
1965+ }
1966+
1967+ memset(&addr, 0, sizeof(addr));
1968+
1969+ addr.sco_family = AF_BLUETOOTH;
1970+ bacpy(&addr.sco_bdaddr, BDADDR_ANY);
1971+
1972+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1973+ ast_log(LOG_ERROR, "Can't bind() SCO socket: %s\n", strerror(errno));
1974+ close(s);
1975+ return -1;
1976+ }
1977+
1978+ memset(&addr, 0, sizeof(addr));
1979+ addr.sco_family = AF_BLUETOOTH;
1980+ bacpy(&addr.sco_bdaddr, dst);
1981+
1982+ if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
1983+ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
1984+
1985+ if ((connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) && (errno != EAGAIN)) {
1986+ ast_log(LOG_ERROR, "Can't connect() SCO socket: %s (errno %d)\n", strerror(errno), errno);
1987+ close(s);
1988+ return -1;
1989+ }
1990+
1991+ //size = sizeof(conn);
1992+
1993+
1994+/* XXX:T: HERE, fix getting SCO conninfo.
1995+
1996+ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
1997+ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
1998+ close(s);
1999+ return -1;
2000+ }
2001+
2002+ size = sizeof(opts);
2003+
2004+ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
2005+ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
2006+ close(s);
2007+ return -1;
2008+ }
2009+
2010+ dev->sco_handle = conn.hci_handle;
2011+ dev->sco_mtu = opts.mtu;
2012+
2013+*/
2014+
2015+ ast_log(LOG_DEBUG, "SCO: %d\n", s);
2016+
2017+ dev->sco = s;
2018+
2019+ return 0;
2020+}
2021+
2022+
2023+/* ---------------------------------- */
2024+
2025+/* Non blocking (async) outgoing bluetooth connection */
2026+
2027+static int
2028+try_connect(blt_dev_t * dev)
2029+{
2030+ int fd;
2031+ ast_mutex_lock(&(dev->lock));
2032+
2033+ if (dev->status != BLT_STATUS_CONNECTING && dev->status != BLT_STATUS_DOWN) {
2034+ ast_mutex_unlock(&(dev->lock));
2035+ return 0;
2036+ }
2037+
2038+ if (dev->rd != -1) {
2039+
2040+ int ret;
2041+ struct pollfd pfd;
2042+
2043+ if (dev->status != BLT_STATUS_CONNECTING) {
2044+ ast_mutex_unlock(&(dev->lock));
2045+ dev->outgoing_id = -1;
2046+ return 0;
2047+ }
2048+
2049+ // ret = connect(dev->rd, (struct sockaddr *)&(dev->addr), sizeof(struct sockaddr_rc)); //
2050+
2051+ pfd.fd = dev->rd;
2052+ pfd.events = POLLIN | POLLOUT;
2053+
2054+ ret = poll(&pfd, 1, 0);
2055+
2056+ if (ret == -1) {
2057+ close(dev->rd);
2058+ dev->rd = -1;
2059+ dev->status = BLT_STATUS_DOWN;
2060+ dev->outgoing_id = ast_sched_add(sched, 10000, AST_SCHED_CB(try_connect), dev);
2061+ ast_mutex_unlock(&(dev->lock));
2062+ return 0;
2063+ }
2064+
2065+ if (ret > 0) {
2066+
2067+ int len = sizeof(ret);
2068+ getsockopt(dev->rd, SOL_SOCKET, SO_ERROR, &ret, &len);
2069+
2070+ if (ret == 0) {
2071+
2072+ ast_log(LOG_NOTICE, "Initialised bluetooth link to device %s\n", dev->name);
2073+
2074+#if 0
2075+ {
2076+ struct hci_conn_info_req * cr;
2077+ int dd;
2078+ char name[248];
2079+
2080+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
2081+ dd = hci_open_dev(hcidev_id);
2082+ cr->type = ACL_LINK;
2083+ bacpy(&cr->bdaddr, &(dev->bdaddr));
2084+
2085+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long)cr) < 0) {
2086+ ast_log(LOG_ERROR, "Failed to get connection info: %s\n", strerror(errno));
2087+ } else {
2088+ ast_log(LOG_DEBUG, "HCI Handle: %d\n", cr->conn_info->handle);
2089+ }
2090+
2091+ if (hci_read_remote_name(dd, &(dev->bdaddr), sizeof(name), name, 25000) == 0)
2092+ ast_log(LOG_DEBUG, "Remote Name: %s\n", name);
2093+ free(cr);
2094+ }
2095+#endif
2096+
2097+ dev->status = BLT_STATUS_NEGOTIATING;
2098+
2099+ /* If this device is a AG, we initiate the negotiation. */
2100+
2101+ if (dev->role == BLT_ROLE_AG) {
2102+ dev->cb = ag_brsf_response;
2103+ send_atcmd(dev, "AT+BRSF=23");
2104+ }
2105+
2106+ dev->outgoing_id = -1;
2107+ ast_mutex_unlock(&(dev->lock));
2108+ return 0;
2109+
2110+ } else {
2111+
2112+ if (ret != EHOSTDOWN)
2113+ ast_log(LOG_NOTICE, "Connect to device %s failed: %s (errno %d)\n", dev->name, strerror(ret), ret);
2114+
2115+ close(dev->rd);
2116+ dev->rd = -1;
2117+ dev->status = BLT_STATUS_DOWN;
2118+ dev->outgoing_id = ast_sched_add(sched, (ret == EHOSTDOWN) ? 10000 : 2500, AST_SCHED_CB(try_connect), dev);
2119+ ast_mutex_unlock(&(dev->lock));
2120+ return 0;
2121+
2122+ }
2123+
2124+ }
2125+
2126+ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
2127+ ast_mutex_unlock(&(dev->lock));
2128+ return 0;
2129+ }
2130+
2131+ fd = rfcomm_connect(&local_bdaddr, &(dev->bdaddr), dev->channel, 1);
2132+
2133+ if (fd == -1) {
2134+ ast_log(LOG_WARNING, "NBIO connect() to %s returned %d: %s\n", dev->name, errno, strerror(errno));
2135+ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
2136+ ast_mutex_unlock(&(dev->lock));
2137+ return 0;
2138+ }
2139+
2140+ dev->rd = fd;
2141+ dev->status = BLT_STATUS_CONNECTING;
2142+ dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
2143+ ast_mutex_unlock(&(dev->lock));
2144+ return 0;
2145+}
2146+
2147+
2148+/* Called whenever a new command is recieved while we're the AG */
2149+
2150+
2151+static int
2152+process_rfcomm_cmd(blt_dev_t * dev, char * cmd)
2153+{
2154+ int i;
2155+ char * fullcmd = cmd;
2156+
2157+ if (option_verbose)
2158+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, cmd);
2159+
2160+ /* Read the 'AT' from the start of the string */
2161+ if (strncmp(cmd, "AT", 2)) {
2162+ ast_log(LOG_WARNING, "Unknown command without 'AT': %s\n", cmd);
2163+ send_atcmd_error(dev);
2164+ return 0;
2165+ }
2166+
2167+ cmd += 2;
2168+
2169+ // Don't forget 'AT' on it's own is OK.
2170+
2171+ if (strlen(cmd) == 0) {
2172+ send_atcmd_ok(dev, fullcmd);
2173+ return 0;
2174+ }
2175+
2176+ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
2177+ if (strncmp(atcmd_list[i].str, cmd, strlen(atcmd_list[i].str)) == 0) {
2178+ char * pos = (cmd + strlen(atcmd_list[i].str));
2179+ if ((strncmp(pos, "=?", 2) == 0) && (strlen(pos) == 2)) {
2180+ /* TEST command */
2181+ if (atcmd_list[i].test) {
2182+ if (atcmd_list[i].test(dev) == 0)
2183+ send_atcmd_ok(dev, fullcmd);
2184+ else
2185+ send_atcmd_error(dev);
2186+ } else {
2187+ send_atcmd_ok(dev, fullcmd);
2188+ }
2189+ } else if ((strncmp(pos, "?", 1) == 0) && (strlen(pos) == 1)) {
2190+ /* READ command */
2191+ if (atcmd_list[i].read) {
2192+ if (atcmd_list[i].read(dev) == 0)
2193+ send_atcmd_ok(dev, fullcmd);
2194+ else
2195+ send_atcmd_error(dev);
2196+ } else {
2197+ ast_log(LOG_WARNING, "AT Command: '%s' missing READ function\n", fullcmd);
2198+ send_atcmd_error(dev);
2199+ }
2200+ } else if (strncmp(pos, "=", 1) == 0) {
2201+ /* SET command */
2202+ if (atcmd_list[i].set) {
2203+ if (atcmd_list[i].set(dev, (pos + 1), (*(pos + 1)) ? strlen(pos + 1) : 0) == 0)
2204+ send_atcmd_ok(dev, fullcmd);
2205+ else
2206+ send_atcmd_error(dev);
2207+ } else {
2208+ ast_log(LOG_WARNING, "AT Command: '%s' missing SET function\n", fullcmd);
2209+ send_atcmd_error(dev);
2210+ }
2211+ } else {
2212+ /* EXECUTE command */
2213+ if (atcmd_list[i].execute) {
2214+ if (atcmd_list[i].execute(dev, cmd + strlen(atcmd_list[i].str)) == 0)
2215+ send_atcmd_ok(dev, fullcmd);
2216+ else
2217+ send_atcmd_error(dev);
2218+ } else {
2219+ ast_log(LOG_WARNING, "AT Command: '%s' missing EXECUTE function\n", fullcmd);
2220+ send_atcmd_error(dev);
2221+ }
2222+ }
2223+ return 0;
2224+ }
2225+ }
2226+
2227+ ast_log(LOG_WARNING, "Unknown AT Command: '%s' (%s)\n", fullcmd, cmd);
2228+ send_atcmd_error(dev);
2229+
2230+ return 0;
2231+}
2232+
2233+/* Called when a socket is incoming */
2234+
2235+static void
2236+handle_incoming(int fd, blt_role_t role)
2237+{
2238+ blt_dev_t * dev;
2239+ struct sockaddr_rc addr;
2240+ int len = sizeof(addr);
2241+
2242+ // Got a new incoming socket.
2243+ ast_log(LOG_DEBUG, "Incoming RFCOMM socket\n");
2244+
2245+ ast_mutex_lock(&iface_lock);
2246+
2247+ fd = accept(fd, (struct sockaddr*)&addr, &len);
2248+
2249+ dev = iface_head;
2250+ while (dev) {
2251+ if (bacmp(&(dev->bdaddr), &addr.rc_bdaddr) == 0) {
2252+ ast_log(LOG_DEBUG, "Connect from %s\n", dev->name);
2253+ ast_mutex_lock(&(dev->lock));
2254+ /* Kill any outstanding connect attempt. */
2255+ if (dev->outgoing_id > -1) {
2256+ ast_sched_del(sched, dev->outgoing_id);
2257+ dev->outgoing_id = -1;
2258+ }
2259+
2260+ rd_close(dev, 0, 0);
2261+
2262+ dev->status = BLT_STATUS_NEGOTIATING;
2263+ dev->rd = fd;
2264+
2265+ if (dev->role == BLT_ROLE_AG) {
2266+ dev->cb = ag_brsf_response;
2267+ send_atcmd(dev, "AT+BRSF=23");
2268+ }
2269+
2270+ ast_mutex_unlock(&(dev->lock));
2271+ break;
2272+ }
2273+ dev = dev->next;
2274+ }
2275+
2276+ if (dev == NULL) {
2277+ ast_log(LOG_WARNING, "Connect from unknown device\n");
2278+ close(fd);
2279+ }
2280+ ast_mutex_unlock(&iface_lock);
2281+
2282+ return;
2283+}
2284+
2285+static void
2286+handle_incoming_sco(int master)
2287+{
2288+
2289+ blt_dev_t * dev;
2290+ struct sockaddr_sco addr;
2291+ struct sco_conninfo conn;
2292+ struct sco_options opts;
2293+ int len = sizeof(addr);
2294+ int fd;
2295+
2296+ ast_log(LOG_DEBUG, "Incoming SCO socket\n");
2297+
2298+ fd = accept(master, (struct sockaddr*)&addr, &len);
2299+
2300+ if (fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK) != 0) {
2301+ ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
2302+ close(fd);
2303+ return;
2304+ }
2305+
2306+ len = sizeof(conn);
2307+
2308+ if (getsockopt(fd, SOL_SCO, SCO_CONNINFO, &conn, &len) < 0) {
2309+ ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
2310+ close(fd);
2311+ return;
2312+ }
2313+
2314+ len = sizeof(opts);
2315+
2316+ if (getsockopt(fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) {
2317+ ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
2318+ close(fd);
2319+ return;
2320+ }
2321+
2322+ ast_mutex_lock(&iface_lock);
2323+ dev = iface_head;
2324+ while (dev) {
2325+ if (bacmp(&(dev->bdaddr), &addr.sco_bdaddr) == 0) {
2326+ ast_log(LOG_DEBUG, "SCO Connect from %s\n", dev->name);
2327+ ast_mutex_lock(&(dev->lock));
2328+ if (dev->sco_running != -1) {
2329+ ast_log(LOG_ERROR, "Incoming SCO socket, but SCO thread already running.\n");
2330+ } else {
2331+ sco_start(dev, fd);
2332+ }
2333+ ast_mutex_unlock(&(dev->lock));
2334+ break;
2335+ }
2336+ dev = dev->next;
2337+ }
2338+
2339+ ast_mutex_unlock(&iface_lock);
2340+
2341+ if (dev == NULL) {
2342+ ast_log(LOG_WARNING, "SCO Connect from unknown device\n");
2343+ close(fd);
2344+ } else {
2345+ // XXX:T: We need to handle the fact we might have an outgoing connection attempt in progress.
2346+ ast_log(LOG_DEBUG, "SCO: %d, HCIHandle=%d, MUT=%d\n", fd, conn.hci_handle, opts.mtu);
2347+ }
2348+
2349+
2350+
2351+ return;
2352+}
2353+
2354+/* Called when there is data waiting on a socket */
2355+
2356+static int
2357+handle_rd_data(blt_dev_t * dev)
2358+{
2359+ char c;
2360+ int ret;
2361+
2362+ while ((ret = read(dev->rd, &c, 1)) == 1) {
2363+
2364+ // log_buf[i++] = c;
2365+
2366+ if (dev->role == BLT_ROLE_HS) {
2367+
2368+ if (c == '\r') {
2369+ ret = process_rfcomm_cmd(dev, dev->rd_buff);
2370+ dev->rd_buff_pos = 0;
2371+ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
2372+ return ret;
2373+ }
2374+
2375+ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX)
2376+ return 0;
2377+
2378+ dev->rd_buff[dev->rd_buff_pos++] = c;
2379+
2380+ } else if (dev->role == BLT_ROLE_AG) {
2381+
2382+ switch (dev->state) {
2383+
2384+ case BLT_STATE_WANT_R:
2385+ if (c == '\r') {
2386+ dev->state = BLT_STATE_WANT_N;
2387+ } else if (c == '+') {
2388+ dev->state = BLT_STATE_WANT_CMD;
2389+ dev->rd_buff[dev->rd_buff_pos++] = '+';
2390+ } else {
2391+ ast_log(LOG_ERROR, "Device %s: Expected '\\r', got %d. state=BLT_STATE_WANT_R\n", dev->name, c);
2392+ return -1;
2393+ }
2394+ break;
2395+
2396+ case BLT_STATE_WANT_N:
2397+ if (c == '\n')
2398+ dev->state = BLT_STATE_WANT_CMD;
2399+ else {
2400+ ast_log(LOG_ERROR, "Device %s: Expected '\\n', got %d. state=BLT_STATE_WANT_N\n", dev->name, c);
2401+ return -1;
2402+ }
2403+ break;
2404+
2405+ case BLT_STATE_WANT_CMD:
2406+ if (c == '\r')
2407+ dev->state = BLT_STATE_WANT_N2;
2408+ else {
2409+ if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) {
2410+ ast_log(LOG_ERROR, "Device %s: Buffer exceeded\n", dev->name);
2411+ return -1;
2412+ }
2413+ dev->rd_buff[dev->rd_buff_pos++] = c;
2414+ }
2415+ break;
2416+
2417+ case BLT_STATE_WANT_N2:
2418+ if (c == '\n') {
2419+
2420+ dev->state = BLT_STATE_WANT_R;
2421+
2422+ if (dev->rd_buff[0] == '+') {
2423+ int i;
2424+ // find unsolicited
2425+ for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
2426+ if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
2427+ if (atcmd_list[i].unsolicited)
2428+ atcmd_list[i].unsolicited(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
2429+ else
2430+ ast_log(LOG_WARNING, "Device %s: Unhandled Unsolicited: %s\n", dev->name, dev->rd_buff);
2431+ break;
2432+ }
2433+ }
2434+
2435+ if (option_verbose)
2436+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
2437+
2438+ if (i == ATCMD_LIST_LEN)
2439+ ast_log(LOG_DEBUG, "Device %s: Got unsolicited message: %s\n", dev->name, dev->rd_buff);
2440+
2441+ } else {
2442+
2443+ if (
2444+ strcmp(dev->rd_buff, "OK") != 0 &&
2445+ strcmp(dev->rd_buff, "CONNECT") != 0 &&
2446+ strcmp(dev->rd_buff, "RING") != 0 &&
2447+ strcmp(dev->rd_buff, "NO CARRIER") != 0 &&
2448+ strcmp(dev->rd_buff, "ERROR") != 0 &&
2449+ strcmp(dev->rd_buff, "NO DIALTONE") != 0 &&
2450+ strcmp(dev->rd_buff, "BUSY") != 0 &&
2451+ strcmp(dev->rd_buff, "NO ANSWER") != 0 &&
2452+ strcmp(dev->rd_buff, "DELAYED") != 0
2453+ ){
2454+ // It must be a multiline error
2455+ strncpy(dev->last_err_cmd, dev->rd_buff, 1023);
2456+ if (option_verbose)
2457+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
2458+ } else if (dev->cb) {
2459+ if (option_verbose)
2460+ ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
2461+ dev->cb(dev, dev->rd_buff);
2462+ } else {
2463+ ast_log(LOG_ERROR, "Device %s: Data on socket in HS mode, but no callback\n", dev->name);
2464+ }
2465+
2466+ }
2467+
2468+ dev->rd_buff_pos = 0;
2469+ memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
2470+
2471+ } else {
2472+
2473+ ast_log(LOG_ERROR, "Device %s: Expected '\\n' got %d. state = BLT_STATE_WANT_N2:\n", dev->name, c);
2474+ return -1;
2475+
2476+ }
2477+
2478+ break;
2479+
2480+ default:
2481+ ast_log(LOG_ERROR, "Device %s: Unknown device state %d\n", dev->name, dev->state);
2482+ return -1;
2483+
2484+ }
2485+
2486+ }
2487+
2488+ }
2489+
2490+ return 0;
2491+}
2492+
2493+/* Close the devices RFCOMM socket, and SCO if it exists. Must hold dev->lock */
2494+
2495+static void
2496+rd_close(blt_dev_t * dev, int reconnect, int e)
2497+{
2498+ dev->ready = 0;
2499+
2500+ if (dev->rd)
2501+ close(dev->rd);
2502+
2503+ dev->rd = -1;
2504+
2505+ dev->status = BLT_STATUS_DOWN;
2506+
2507+ sco_stop(dev);
2508+
2509+ if (dev->owner) {
2510+ ast_setstate(dev->owner, AST_STATE_DOWN);
2511+ ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
2512+ }
2513+
2514+ /* Schedule a reconnect */
2515+ if (reconnect && dev->autoconnect) {
2516+ dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
2517+
2518+ if (monitor_thread == pthread_self()) {
2519+ // Because we're not the monitor thread, we needd to inturrupt poll().
2520+ pthread_kill(monitor_thread, SIGURG);
2521+ }
2522+
2523+ if (e)
2524+ ast_log(LOG_NOTICE, "Device %s disconnected, scheduled reconnect in 5 seconds: %s (errno %d)\n", dev->name, strerror(e), e);
2525+ } else if (e) {
2526+ ast_log(LOG_NOTICE, "Device %s disconnected: %s (errno %d)\n", dev->name, strerror(e), e);
2527+ }
2528+
2529+ return;
2530+}
2531+
2532+/*
2533+ * Remember that we can only add to the scheduler from
2534+ * the do_monitor thread, as it calculates time to next one from
2535+ * this loop.
2536+ */
2537+
2538+static void *
2539+do_monitor(void * data)
2540+{
2541+#define SRV_SOCK_CNT 3
2542+
2543+ int res = 0;
2544+ blt_dev_t * dev;
2545+ struct pollfd * pfds = malloc(sizeof(struct pollfd) * (ifcount + SRV_SOCK_CNT));
2546+
2547+ /* -- We start off by trying to connect all of our devices (non blocking) -- */
2548+
2549+ monitor_pid = getpid();
2550+
2551+ if (ast_mutex_lock(&iface_lock)) {
2552+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
2553+ return NULL;
2554+ }
2555+
2556+ dev = iface_head;
2557+ while (dev) {
2558+
2559+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, dev->sco_pipe) != 0) {
2560+ ast_log(LOG_ERROR, "Failed to create socket pair: %s (errno %d)\n", strerror(errno), errno);
2561+ ast_mutex_unlock(&iface_lock);
2562+ return NULL;
2563+ }
2564+
2565+ if (dev->autoconnect && dev->status == BLT_STATUS_DOWN)
2566+ dev->outgoing_id = ast_sched_add(sched, 1500, AST_SCHED_CB(try_connect), dev);
2567+ dev = dev->next;
2568+ }
2569+ ast_mutex_unlock(&iface_lock);
2570+
2571+ /* -- Now, Scan all sockets, and service scheduler -- */
2572+
2573+ pfds[0].fd = rfcomm_sock_ag;
2574+ pfds[0].events = POLLIN;
2575+
2576+ pfds[1].fd = rfcomm_sock_hs;
2577+ pfds[1].events = POLLIN;
2578+
2579+ pfds[2].fd = sco_socket;
2580+ pfds[2].events = POLLIN;
2581+
2582+ while (1) {
2583+ int cnt = SRV_SOCK_CNT;
2584+ int i;
2585+
2586+ /* -- Build pfds -- */
2587+
2588+ if (ast_mutex_lock(&iface_lock)) {
2589+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
2590+ return NULL;
2591+ }
2592+ dev = iface_head;
2593+ while (dev) {
2594+ ast_mutex_lock(&(dev->lock));
2595+ if (dev->rd > 0 && ((dev->status != BLT_STATUS_DOWN) && (dev->status != BLT_STATUS_CONNECTING))) {
2596+ pfds[cnt].fd = dev->rd;
2597+ pfds[cnt].events = POLLIN;
2598+ cnt++;
2599+ }
2600+ ast_mutex_unlock(&(dev->lock));
2601+ dev = dev->next;
2602+ }
2603+ ast_mutex_unlock(&iface_lock);
2604+
2605+ /* -- End Build pfds -- */
2606+
2607+ res = ast_sched_wait(sched);
2608+ res = poll(pfds, cnt, MAX(100, MIN(100, res)));
2609+
2610+ if (res == 0)
2611+ ast_sched_runq(sched);
2612+
2613+ if (pfds[0].revents) {
2614+ handle_incoming(rfcomm_sock_ag, BLT_ROLE_AG);
2615+ res--;
2616+ }
2617+
2618+ if (pfds[1].revents) {
2619+ handle_incoming(rfcomm_sock_hs, BLT_ROLE_HS);
2620+ res--;
2621+ }
2622+
2623+ if (pfds[2].revents) {
2624+ handle_incoming_sco(sco_socket);
2625+ res--;
2626+ }
2627+
2628+ if (res == 0)
2629+ continue;
2630+
2631+ for (i = SRV_SOCK_CNT ; i < cnt ; i++) {
2632+
2633+ /* Optimise a little bit */
2634+ if (res == 0)
2635+ break;
2636+ else if (pfds[i].revents == 0)
2637+ continue;
2638+
2639+ /* -- Find the socket that has activity -- */
2640+
2641+ if (ast_mutex_lock(&iface_lock)) {
2642+ ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
2643+ return NULL;
2644+ }
2645+
2646+ dev = iface_head;
2647+
2648+ while (dev) {
2649+ if (pfds[i].fd == dev->rd) {
2650+ ast_mutex_lock(&(dev->lock));
2651+ if (pfds[i].revents & POLLIN) {
2652+ if (handle_rd_data(dev) == -1) {
2653+ rd_close(dev, 0, 0);
2654+ }
2655+ } else {
2656+ rd_close(dev, 1, sock_err(dev->rd));
2657+ }
2658+ ast_mutex_unlock(&(dev->lock));
2659+ res--;
2660+ break;
2661+ }
2662+ dev = dev->next;
2663+ }
2664+
2665+ if (dev == NULL) {
2666+ ast_log(LOG_ERROR, "Unhandled fd from poll()\n");
2667+ close(pfds[i].fd);
2668+ }
2669+
2670+ ast_mutex_unlock(&iface_lock);
2671+
2672+ /* -- End find socket with activity -- */
2673+
2674+ }
2675+
2676+ }
2677+
2678+ return NULL;
2679+}
2680+
2681+static int
2682+restart_monitor(void)
2683+{
2684+
2685+ if (monitor_thread == AST_PTHREADT_STOP)
2686+ return 0;
2687+
2688+ if (ast_mutex_lock(&monitor_lock)) {
2689+ ast_log(LOG_WARNING, "Unable to lock monitor\n");
2690+ return -1;
2691+ }
2692+
2693+ if (monitor_thread == pthread_self()) {
2694+ ast_mutex_unlock(&monitor_lock);
2695+ ast_log(LOG_WARNING, "Cannot kill myself\n");
2696+ return -1;
2697+ }
2698+
2699+ if (monitor_thread != AST_PTHREADT_NULL) {
2700+
2701+ /* Just signal it to be sure it wakes up */
2702+ pthread_cancel(monitor_thread);
2703+ pthread_kill(monitor_thread, SIGURG);
2704+ ast_log(LOG_DEBUG, "Waiting for monitor thread to join...\n");
2705+ pthread_join(monitor_thread, NULL);
2706+ ast_log(LOG_DEBUG, "joined\n");
2707+
2708+ } else {
2709+
2710+ /* Start a new monitor */
2711+ if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
2712+ ast_mutex_unlock(&monitor_lock);
2713+ ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
2714+ return -1;
2715+ }
2716+
2717+ }
2718+
2719+ ast_mutex_unlock(&monitor_lock);
2720+ return 0;
2721+}
2722+
2723+static int
2724+blt_parse_config(void)
2725+{
2726+ struct ast_config * cfg;
2727+ struct ast_variable * v;
2728+ char * cat;
2729+
2730+ cfg = ast_load(BLT_CONFIG_FILE);
2731+
2732+ if (!cfg) {
2733+ ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE);
2734+ return -1;
2735+ }
2736+
2737+ v = ast_variable_browse(cfg, "general");
2738+
2739+ while (v) {
2740+ if (!strcasecmp(v->name, "rfchannel_ag")) {
2741+ rfcomm_channel_ag = atoi(v->value);
2742+ } else if (!strcasecmp(v->name, "rfchannel_hs")) {
2743+ rfcomm_channel_hs = atoi(v->value);
2744+ } else if (!strcasecmp(v->name, "interface")) {
2745+ hcidev_id = atoi(v->value);
2746+ } else {
2747+ ast_log(LOG_WARNING, "Unknown config key '%s' in section [general]\n", v->name);
2748+ }
2749+ v = v->next;
2750+ }
2751+ cat = ast_category_browse(cfg, NULL);
2752+
2753+ while(cat) {
2754+
2755+ char * str;
2756+
2757+ if (strcasecmp(cat, "general")) {
2758+ blt_dev_t * device = malloc(sizeof(blt_dev_t));
2759+ memset(device, 0, sizeof(blt_dev_t));
2760+ device->sco_running = -1;
2761+ device->sco = -1;
2762+ device->rd = -1;
2763+ device->outgoing_id = -1;
2764+ device->status = BLT_STATUS_DOWN;
2765+ str2ba(cat, &(device->bdaddr));
2766+ device->name = ast_variable_retrieve(cfg, cat, "name");
2767+
2768+ str = ast_variable_retrieve(cfg, cat, "type");
2769+
2770+ if (str == NULL) {
2771+ ast_log(LOG_ERROR, "Device [%s] has no role. Specify type=<HS/AG>\n", cat);
2772+ return -1;
2773+ } else if (strcasecmp(str, "HS") == 0)
2774+ device->role = BLT_ROLE_HS;
2775+ else if (strcasecmp(str, "AG") == 0) {
2776+ device->role = BLT_ROLE_AG;
2777+ } else {
2778+ ast_log(LOG_ERROR, "Device [%s] has invalid role '%s'\n", cat, str);
2779+ return -1;
2780+ }
2781+
2782+ /* XXX:T: Find channel to use using SDP.
2783+ * However, this needs to be non blocking, and I can't see
2784+ * anything in sdp_lib.h that will allow non blocking calls.
2785+ */
2786+
2787+ device->channel = 1;
2788+
2789+ if ((str = ast_variable_retrieve(cfg, cat, "channel")) != NULL)
2790+ device->channel = atoi(str);
2791+
2792+ if ((str = ast_variable_retrieve(cfg, cat, "autoconnect")) != NULL)
2793+ device->autoconnect = (strcasecmp(str, "yes") == 0 || strcmp(str, "1") == 0) ? 1 : 0;
2794+
2795+ device->next = iface_head;
2796+ iface_head = device;
2797+ ifcount++;
2798+ }
2799+
2800+ cat = ast_category_browse(cfg, cat);
2801+ }
2802+ return 0;
2803+}
2804+
2805+
2806+static int
2807+blt_show_peers(int fd, int argc, char *argv[])
2808+{
2809+ blt_dev_t * dev;
2810+
2811+ if (ast_mutex_lock(&iface_lock)) {
2812+ ast_log(LOG_ERROR, "Failed to get Iface lock\n");
2813+ ast_cli(fd, "Failed to get iface lock\n");
2814+ return RESULT_FAILURE;
2815+ }
2816+
2817+ dev = iface_head;
2818+
2819+ ast_cli(fd, "BDAddr Name Role Status A/C SCOCon/Fd/Th Sig\n");
2820+ ast_cli(fd, "----------------- ---------- ---- ----------- --- ------------ ---\n");
2821+
2822+ while (dev) {
2823+ char b1[18];
2824+ ba2str(&(dev->bdaddr), b1);
2825+ ast_cli(fd, "%s %-10s %-4s %-11s %-3s %2d/%02d/%-6ld %s\n",
2826+ b1, dev->name, (dev->role == BLT_ROLE_HS) ? "HS" : "AG", status2str(dev->status),
2827+ (dev->autoconnect) ? "Yes" : "No",
2828+ dev->sco_running,
2829+ dev->sco,
2830+ dev->sco_thread,
2831+ (dev->role == BLT_ROLE_AG) ? (dev->service) ? "Yes" : "No" : "N/A"
2832+ );
2833+ dev = dev->next;
2834+ }
2835+
2836+ ast_mutex_unlock(&iface_lock);
2837+ return RESULT_SUCCESS;
2838+}
2839+
2840+static int
2841+blt_show_information(int fd, int argc, char *argv[])
2842+{
2843+ char b1[18];
2844+ ba2str(&local_bdaddr, b1);
2845+ ast_cli(fd, "-------------------------------------------\n");
2846+ ast_cli(fd, " Version : %s\n", BLT_SVN_REVISION);
2847+ ast_cli(fd, " Monitor PID : %d\n", monitor_pid);
2848+ ast_cli(fd, " RFCOMM AG : Channel %d, FD %d\n", rfcomm_channel_ag, rfcomm_sock_ag);
2849+ ast_cli(fd, " RFCOMM HS : Channel %d, FD %d\n", rfcomm_channel_hs, rfcomm_sock_hs);
2850+ ast_cli(fd, " Device : hci%d, MAC Address %s\n", hcidev_id, b1);
2851+ ast_cli(fd, "-------------------------------------------\n");
2852+ return RESULT_SUCCESS;
2853+}
2854+
2855+static int
2856+blt_ag_sendcmd(int fd, int argc, char *argv[])
2857+{
2858+ blt_dev_t * dev;
2859+
2860+ if (argc != 4)
2861+ return RESULT_SHOWUSAGE;
2862+
2863+ ast_mutex_lock(&iface_lock);
2864+ dev = iface_head;
2865+ while (dev) {
2866+ if (!strcasecmp(argv[2], dev->name))
2867+ break;
2868+ dev = dev->next;
2869+ }
2870+ ast_mutex_unlock(&iface_lock);
2871+
2872+ if (!dev) {
2873+ ast_cli(fd, "Device '%s' does not exist\n", argv[2]);
2874+ return RESULT_FAILURE;
2875+ }
2876+
2877+ if (dev->role != BLT_ROLE_AG) {
2878+ ast_cli(fd, "Device '%s' is not an AudioGateway\n", argv[2]);
2879+ return RESULT_FAILURE;
2880+ }
2881+
2882+ if (dev->status == BLT_STATUS_DOWN || dev->status == BLT_STATUS_NEGOTIATING) {
2883+ ast_cli(fd, "Device '%s' is not connected\n", argv[2]);
2884+ return RESULT_FAILURE;
2885+ }
2886+
2887+ if (*(argv[3] + strlen(argv[3]) - 1) == '.')
2888+ *(argv[3] + strlen(argv[3]) - 1) = '?';
2889+
2890+ ast_cli(fd, "Sending AT command to %s: %s\n", dev->name, argv[3]);
2891+
2892+ ast_mutex_lock(&(dev->lock));
2893+ send_atcmd(dev, argv[3]);
2894+ ast_mutex_unlock(&(dev->lock));
2895+
2896+ return RESULT_SUCCESS;
2897+}
2898+
2899+static char *
2900+complete_device(char * line, char * word, int pos, int state, int rpos, blt_role_t role)
2901+{
2902+ blt_dev_t * dev;
2903+ int which = 0;
2904+ char *ret;
2905+
2906+ if (pos != rpos)
2907+ return NULL;
2908+
2909+ ast_mutex_lock(&iface_lock);
2910+
2911+ dev = iface_head;
2912+
2913+ while (dev) {
2914+
2915+ if ((dev->role == role) && (!strncasecmp(word, dev->name, strlen(word)))) {
2916+ if (++which > state)
2917+ break;
2918+ }
2919+
2920+ dev = dev->next;
2921+ }
2922+
2923+ if (dev)
2924+ ret = strdup(dev->name);
2925+ else
2926+ ret = NULL;
2927+
2928+ ast_mutex_unlock(&iface_lock);
2929+
2930+ return ret;
2931+}
2932+
2933+static char *
2934+complete_device_2_ag(char * line, char * word, int pos, int state)
2935+{
2936+ return complete_device(line, word, pos, state, 2, BLT_ROLE_AG);
2937+}
2938+
2939+static char show_peers_usage[] =
2940+"Usage: bluetooth show peers\n"
2941+" List all bluetooth peers and their status\n";
2942+
2943+static struct ast_cli_entry
2944+cli_show_peers =
2945+ { { "bluetooth", "show", "peers", NULL }, blt_show_peers, "List Bluetooth Peers", show_peers_usage };
2946+
2947+
2948+static char ag_sendcmd[] =
2949+"Usage: bluetooth ag <device> sendcmd <cmd>\n"
2950+" Sends a AT cmd over the RFCOMM link, and print result (AG only)\n";
2951+
2952+static struct ast_cli_entry
2953+cli_ag_sendcmd =
2954+ { { "bluetooth", "sendcmd", NULL }, blt_ag_sendcmd, "Send AG an AT command", ag_sendcmd, complete_device_2_ag };
2955+
2956+static char show_information[] =
2957+"Usage: bluetooth show information\n"
2958+" Lists information about the bluetooth subsystem\n";
2959+
2960+static struct ast_cli_entry
2961+cli_show_information =
2962+ { { "bluetooth", "show", "information", NULL }, blt_show_information, "List Bluetooth Info", show_information };
2963+
2964+void
2965+remove_sdp_records(void)
2966+{
2967+
2968+ sdp_session_t * sdp;
2969+ sdp_list_t * attr;
2970+ sdp_record_t * rec;
2971+ int res = -1;
2972+ uint32_t range = 0x0000ffff;
2973+
2974+ if (sdp_record_ag == -1 || sdp_record_hs == -1)
2975+ return;
2976+
2977+ ast_log(LOG_DEBUG, "Removing SDP records\n");
2978+
2979+ sdp = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
2980+
2981+ if (!sdp)
2982+ return;
2983+
2984+ attr = sdp_list_append(0, &range);
2985+ rec = sdp_service_attr_req(sdp, sdp_record_ag, SDP_ATTR_REQ_RANGE, attr);
2986+ sdp_list_free(attr, 0);
2987+
2988+ if (rec)
2989+ if (sdp_record_unregister(sdp, rec) == 0)
2990+ res = 0;
2991+
2992+ attr = sdp_list_append(0, &range);
2993+ rec = sdp_service_attr_req(sdp, sdp_record_hs, SDP_ATTR_REQ_RANGE, attr);
2994+ sdp_list_free(attr, 0);
2995+
2996+ if (rec)
2997+ if (sdp_record_unregister(sdp, rec) == 0)
2998+ res = 0;
2999+
3000+ sdp_close(sdp);
3001+
3002+ if (res == 0)
3003+ ast_log(LOG_NOTICE, "Removed SDP records\n");
3004+ else
3005+ ast_log(LOG_ERROR, "Failed to remove SDP records\n");
3006+
3007+}
3008+
3009+static int
3010+__unload_module(void)
3011+{
3012+
3013+ ast_channel_unregister(BLT_CHAN_NAME);
3014+
3015+ if (monitor_thread != AST_PTHREADT_NULL) {
3016+
3017+ if (ast_mutex_lock(&monitor_lock)) {
3018+
3019+ if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
3020+ pthread_cancel(monitor_thread);
3021+ pthread_kill(monitor_thread, SIGURG);
3022+ fprintf(stderr, "Waiting for monitor thread to join...\n");
3023+ pthread_join(monitor_thread, NULL);
3024+ fprintf(stderr, "joined\n");
3025+ }
3026+ monitor_thread = AST_PTHREADT_STOP;
3027+ ast_mutex_unlock(&monitor_lock);
3028+
3029+ } else {
3030+
3031+ ast_log(LOG_WARNING, "Unable to lock the monitor\n");
3032+ return -1;
3033+
3034+ }
3035+
3036+ }
3037+
3038+ ast_unregister_atexit(remove_sdp_records);
3039+ remove_sdp_records();
3040+ return 0;
3041+}
3042+
3043+int
3044+load_module()
3045+{
3046+ sdp_session_t * sess;
3047+ int dd;
3048+ uint16_t vs;
3049+
3050+ hcidev_id = BLT_DEFAULT_HCI_DEV;
3051+
3052+ if (blt_parse_config() != 0) {
3053+ ast_log(LOG_ERROR, "Bluetooth configuration error. Bluetooth Disabled\n");
3054+ return unload_module();
3055+ }
3056+
3057+ dd = hci_open_dev(hcidev_id);
3058+ if (dd == -1) {
3059+ ast_log(LOG_ERROR, "Unable to open interface hci%d: %s.\n", hcidev_id, strerror(errno));
3060+ return -1;
3061+ }
3062+
3063+ hci_read_voice_setting(dd, &vs, 1000);
3064+ vs = htobs(vs);
3065+ close(dd);
3066+
3067+ if (vs != 0x0060) {
3068+ ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060, not 0x%04x\n", vs);
3069+ unload_module();
3070+ return 0;
3071+ }
3072+
3073+ if ((sched = sched_context_create()) == NULL) {
3074+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
3075+ return -1;
3076+ }
3077+
3078+ memset(&local_bdaddr, 0, sizeof(local_bdaddr));
3079+
3080+ hci_devba(hcidev_id, &local_bdaddr);
3081+
3082+ /* --- Add SDP record --- */
3083+
3084+ sess = sdp_connect(&local_bdaddr, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
3085+
3086+ if ((rfcomm_sock_ag = rfcomm_listen(&local_bdaddr, rfcomm_channel_ag)) < 0) {
3087+ return -1;
3088+ }
3089+
3090+ if ((rfcomm_sock_hs = rfcomm_listen(&local_bdaddr, rfcomm_channel_hs)) < 0)
3091+ return -1;
3092+
3093+ if ((sco_socket = sco_listen(&local_bdaddr)) < 0)
3094+ return -1;
3095+
3096+ if (!sess) {
3097+ ast_log(LOG_ERROR, "Failed to connect to SDP server: %s\n", strerror(errno));
3098+ return -1;
3099+ }
3100+
3101+ if (sdp_register(sess) != 0) {
3102+ ast_log(LOG_ERROR, "Failed to register HeadsetAudioGateway in SDP\n");
3103+ return -1;
3104+ }
3105+
3106+ sdp_close(sess);
3107+
3108+ if (restart_monitor() != 0)
3109+ return -1;
3110+
3111+ if (ast_channel_register(BLT_CHAN_NAME, "Bluetooth Driver", BLUETOOTH_FORMAT, blt_request)) {
3112+ ast_log(LOG_ERROR, "Unable to register channel class BTL\n");
3113+ __unload_module();
3114+ return -1;
3115+ }
3116+
3117+ ast_cli_register(&cli_show_information);
3118+ ast_cli_register(&cli_show_peers);
3119+ ast_cli_register(&cli_ag_sendcmd);
3120+
3121+ ast_register_atexit(remove_sdp_records);
3122+
3123+ ast_log(LOG_NOTICE, "Loaded Bluetooth support, %s\n", BLT_SVN_REVISION + 1);
3124+
3125+ return 0;
3126+}
3127+
3128+int
3129+unload_module(void)
3130+{
3131+ ast_cli_unregister(&cli_ag_sendcmd);
3132+ ast_cli_unregister(&cli_show_peers);
3133+ ast_cli_unregister(&cli_show_information);
3134+ return __unload_module();
3135+}
3136+
3137+int
3138+usecount()
3139+{
3140+ int res;
3141+ ast_mutex_lock(&usecnt_lock);
3142+ res = usecnt;
3143+ ast_mutex_unlock(&usecnt_lock);
3144+ return res;
3145+}
3146+
3147+char *description()
3148+{
3149+ return "Bluetooth Channel Driver";
3150+}
3151+
3152+char *
3153+key()
3154+{
3155+ return ASTERISK_GPL_KEY;
3156+}
3157+
3158+
3159diff -urN asterisk-1.4.0-beta3-o-o/configs/bluetooth.conf.sample asterisk-1.4.0-beta3/configs/bluetooth.conf.sample
3160--- asterisk-1.4.0-beta3-o-o/configs/bluetooth.conf.sample 1969-12-31 17:00:00.000000000 -0700
3161+++ asterisk-1.4.0-beta3/configs/bluetooth.conf.sample 2006-11-06 12:44:39.000000000 -0700
3162@@ -0,0 +1,33 @@
3163+[general]
3164+; Channel we listen on as a HS (Headset)
3165+rfchannel_hs = 2
3166+; Channel we listen on as an AG (AudioGateway)
3167+rfchannel_ag = 3
3168+; hci interface to use (number - e.g '0')
3169+interface = 0
3170+
3171+;; A HBH-500 Handsfree Kit
3172+[00:0A:D9:A1:AA:D2]
3173+; Any name to use, this is what we use to send calls to (BLT/<name>).
3174+name = HBH-500
3175+; IS this a HS or AG?
3176+type = HS
3177+;
3178+;
3179+; RFCOMM channel to connect to. For a HandsSet:
3180+; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111E
3181+; or,for an AudioGateway (Phone):
3182+; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111F
3183+;
3184+; Find the 'channel' value under RFCOMM.
3185+;
3186+channel = 2
3187+; Automatically conenct?
3188+autoconnect = yes
3189+
3190+;; A Nokia 6310i
3191+[00:60:57:1C:00:99]
3192+name = Neil
3193+type = AG
3194+channel = 13
3195+autoconnect = yes
3196diff -urN asterisk-1.4.0-beta3-o-o/configure.ac asterisk-1.4.0-beta3/configure.ac
3197--- asterisk-1.4.0-beta3-o-o/configure.ac 2006-10-13 09:41:14.000000000 -0600
3198+++ asterisk-1.4.0-beta3/configure.ac 2006-11-06 12:44:39.000000000 -0700
3199@@ -152,6 +152,7 @@
3200 # by the --with option name, to make things easier for the users :-)
3201
3202 AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound])
3203+AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth], [bluetooth])
3204 AST_EXT_LIB_SETUP([CURL], [cURL], [curl])
3205 AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
3206 AST_EXT_LIB_SETUP([GNUTLS], [GNU TLS support (used for iksemel only)], [gnutls])
3207@@ -314,6 +315,8 @@
3208
3209 AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl])
3210
3211+AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [bt_malloc], [bluetooth/bluetooth.h])
3212+
3213 AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
3214
3215 GSM_INTERNAL="yes"
3216diff -urN asterisk-1.4.0-beta3-o-o/makeopts.in asterisk-1.4.0-beta3/makeopts.in
3217--- asterisk-1.4.0-beta3-o-o/makeopts.in 2006-09-19 08:04:15.000000000 -0600
3218+++ asterisk-1.4.0-beta3/makeopts.in 2006-11-06 12:44:39.000000000 -0700
3219@@ -157,3 +157,6 @@
3220
3221 SUPPSERV_INCLUDE=@SUPPSERV_INCLUDE@
3222 SUPPSERV_LIB=@SUPPSERV_LIB@
3223+
3224+BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
3225+BLUETOOTH_LIB=@BLUETOOTH_LIB@
This page took 0.360741 seconds and 4 git commands to generate.