1 diff -ruN asterisk-1.0.9-old/channels/Makefile asterisk-1.0.9-new/channels/Makefile
2 --- asterisk-1.0.9-old/channels/Makefile 2005-08-22 20:42:22.000000000 +0200
3 +++ asterisk-1.0.9-new/channels/Makefile 2005-08-22 21:12:14.000000000 +0200
6 #CHANNEL_LIBS+=chan_vofr
9 +# Asterisk Bluetooth Support
10 +# http://www.crazygreek.co.uk/content/chan_bluetooth
12 +CHANNEL_LIBS += chan_bluetooth.so
14 ifeq (${OSARCH},OpenBSD)
15 MYSQLLIB=-L/usr/local/lib/mysql -lmysqlclient
16 CFLAGS+=-I/usr/local/include
18 chan_h323.so: chan_h323.o h323/libchanh323.a
19 $(CC) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat
21 +chan_bluetooth.so: chan_bluetooth.o
22 + $(CC) $(SOLINK) -o $@ $< $(LDFLAGS_EXTRA) -lbluetooth
25 #chan_modem.so : chan_modem.o
26 # $(CC) -rdynamic -shared -Xlinker -x -o $@ $<
27 diff -ruN asterisk-1.0.9-old/channels/chan_bluetooth.c asterisk-1.0.9-new/channels/chan_bluetooth.c
28 --- asterisk-1.0.9-old/channels/chan_bluetooth.c 1970-01-01 01:00:00.000000000 +0100
29 +++ asterisk-1.0.9-new/channels/chan_bluetooth.c 2005-09-06 22:51:30.000000000 +0200
32 + * Asterisk -- A telephony toolkit for Linux.
34 + * Asterisk Bluetooth Channel
36 + * Author: Theo Zourzouvillys <theo@adaptive-it.co.uk>
38 + * Adaptive Linux Solutions <http://www.adaptive-it.co.uk>
40 + * Copyright (C) 2004 Adaptive Linux Solutions
42 + * This program is free software, distributed under the terms of
43 + * the GNU General Public License
45 + * ******************* NOTE NOTE NOTE NOTE NOTE *********************
47 + * This code is not at all tested, and only been developed with a
48 + * HBH-200 headset and a Nokia 6310i right now.
50 + * Expect it to crash, dial random numbers, and steal all your money.
52 + * PLEASE try any headsets and phones, and let me know the results,
53 + * working or not, along with all debug output!
55 + * ------------------------------------------------------------------
57 + * Asterisk Bluetooth Support
59 + * Well, here we go - Attempt to provide Handsfree profile support in
60 + * both AG and HF modes, AG (AudioGateway) mode support for using
61 + * headsets, and HF (Handsfree) mode for utilising mobile/cell phones
63 + * It would be nice to also provide Headset support at some time in
64 + * the future, however, a working Handsfree profile is nice for now,
65 + * and as far as I can see, almost all new HS devices also support HF
67 + * ------------------------------------------------------------------
70 + * You need to have bluez's bluetooth stack, along with user space
71 + * tools (>=v2.10), and running hcid and sdsp.
73 + * See bluetooth.conf for configuration details.
75 + * - Ensure bluetooth subsystem is up and running. 'hciconfig'
76 + * should show interface as UP.
78 + * - If you're trying to use a headset/HS, start up asterisk, and try
79 + * to pair it as you normally would.
81 + * - If you're trying to use a Phone/AG, just make sure bluetooth is
82 + * enabled on your phone, and start up asterisk.
84 + * - 'bluetooth show peers' will show all bluetooth devices states.
86 + * - Send a call out by using Dial(BLT/DevName/0123456). Call a HS
87 + * with Dial(BLT/DevName)
89 + * ------------------------------------------------------------------
92 + * - What should happen when an AG is paired with asterisk and
93 + * someone uses the AG dalling a number manually? My test phone
94 + * seems to try to open an SCO link. Perhaps an extension to
95 + * route the call to, or maybe drop the RFCOM link all together?
97 + * ------------------------------------------------------------------
100 + * PLEASE email <theo@adaptive-it.co.uk> with the results of ANY
101 + * device not listed in here (working or not), or if the device is
102 + * listed and it doesn't work! Please also email full debug output
103 + * for any device not working correctly or generating errors in log.
105 + * HandsFree Profile:
108 + * - Ericsson HBH-200
110 + * AG (AudioGateway):
113 + * ------------------------------------------------------------------
115 + * Questions, bugs, or (preferably) patches to:
117 + * <theo@adaptive-it.co.uk>
119 + * ------------------------------------------------------------------
122 +/* ---------------------------------- */
126 +#include <asterisk/lock.h>
127 +#include <asterisk/utils.h>
128 +#include <asterisk/channel.h>
129 +#include <asterisk/config.h>
130 +#include <asterisk/logger.h>
131 +#include <asterisk/module.h>
132 +#include <asterisk/pbx.h>
133 +#include <asterisk/sched.h>
134 +#include <asterisk/options.h>
135 +#include <asterisk/cli.h>
136 +#include <asterisk/callerid.h>
137 +#include <sys/socket.h>
138 +#include <sys/signal.h>
139 +#include <sys/time.h>
143 +#include <arpa/inet.h>
145 +#include <sys/ioctl.h>
149 +#include <bluetooth/bluetooth.h>
150 +#include <bluetooth/hci.h>
151 +#include <bluetooth/hci_lib.h>
152 +#include <bluetooth/sco.h>
153 +#include <bluetooth/rfcomm.h>
154 +#include <bluetooth/sdp.h>
155 +#include <bluetooth/sdp_lib.h>
157 +/* --- Data types and definitions --- */
159 +#ifndef HANDSFREE_AUDIO_GW_SVCLASS_ID
160 +# define HANDSFREE_AUDIO_GW_SVCLASS_ID 0x111f
162 +#define BLUETOOTH_FORMAT AST_FORMAT_SLINEAR
163 +#define BLT_CHAN_NAME "BLT"
164 +#define BLT_CONFIG_FILE "bluetooth.conf"
165 +#define BLT_RDBUFF_MAX 1024
166 +#define BLT_DEFAULT_HCI_DEV 0
167 +#define BLT_SVN_REVISION "$Rev: 38 $"
169 +/* ---------------------------------- */
172 + BLT_ROLE_NONE = 0, // Unknown Device
173 + BLT_ROLE_HS = 1, // Device is a Headset
174 + BLT_ROLE_AG = 2, // Device is an Audio Gateway
175 + BLT_ROLE_GUI = 3 // Device is used as an GUI
178 +/* State when we're in HS mode */
181 + BLT_STATE_WANT_R = 0,
182 + BLT_STATE_WANT_N = 1,
183 + BLT_STATE_WANT_CMD = 2,
184 + BLT_STATE_WANT_N2 = 3,
189 + BLT_STATUS_CONNECTING,
190 + BLT_STATUS_NEGOTIATING,
192 + BLT_STATUS_RINGING,
193 + BLT_STATUS_IN_CALL,
196 +/* ---------------------------------- */
198 +/* Default config settings */
200 +#define BLT_DEFAULT_CHANNEL_AG 5
201 +#define BLT_DEFAULT_CHANNEL_HS 6
202 +#define BLT_DEFAULT_CHANNEL_GUI 1
203 +#define BLT_DEFAULT_ROLE BLT_ROLE_HS
204 +#define BLT_OBUF_LEN (48 * 25)
206 +#define BUFLEN (4800)
208 +/* ---------------------------------- */
210 +typedef struct blt_dev blt_dev_t;
212 +void ag_cgmi_response(blt_dev_t * dev, char * cmd);
213 +void ag_unknown_response(blt_dev_t * dev, char * cmd);
214 +void ag_cgmi_valid_response(blt_dev_t * dev, char * cmd);
215 +void ag_clip_response(blt_dev_t * dev, char * cmd);
216 +void ag_cmer_response(blt_dev_t * dev, char * cmd);
217 +void ag_cind_status_response(blt_dev_t * dev, char * cmd);
218 +void ag_cind_response(blt_dev_t * dev, char * cmd);
219 +void ag_brsf_response(blt_dev_t * dev, char * cmd);
220 +void remove_sdp_records(void);
222 +void gui_easm_response(blt_dev_t * dev, char * cmd);
224 +int sock_err(int fd);
225 +int parse_clip(const char * str, char *number, int number_len, char * name, int name_len, int *type);
226 +int set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len);
227 +int get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy);
228 +void gui_eaid_response(blt_dev_t * dev, char * cmd);
233 + unsigned char buf[BUFLEN];
235 +// XXX:T: Tidy this lot up.
238 + blt_status_t status; /* Device Status */
240 + struct ast_channel * owner; /* Channel we belong to, possibly NULL */
241 + blt_dev_t * dev; /* The bluetooth device channel is for */
242 + struct ast_frame fr; /* Recieved frame */
245 + int sco_pipe[2]; /* SCO alert pipe */
246 + int sco; /* SCO fd */
247 + int sco_handle; /* SCO Handle */
248 + int sco_mtu; /* SCO MTU */
249 + int sco_running; /* 1 when sCO thread should be running */
250 + pthread_t sco_thread; /* SCO thread */
251 + ast_mutex_t sco_lock; /* SCO lock */
252 + int sco_pos_in; /* Reader in position (drain)*/
253 + int sco_pos_inrcv; /* Reader in position (fill) */
254 + int wakeread; /* blt_read() needs to be woken */
255 + int sco_pos_out; /* Reader out position */
256 + int sco_sending; /* Sending SCO packets */
257 + char buf[1200]; /* Incoming data buffer */
259 + char sco_buf_out[BUFLEN]; /* 24 chunks of 48 */
260 + char sco_buf_in[BUFLEN]; /* 24 chunks of 48 */
262 + char dnid[1024]; /* Outgoi gncall dialed number */
263 + unsigned char * obuf[BLT_OBUF_LEN]; /* Outgoing data buffer */
264 + int obuf_len; /* Output Buffer Position */
265 + int obuf_wpos; /* Buffer Reader */
268 + int autoconnect; /* 1 for autoconnect */
269 + int outgoing_id; /* Outgoing connection scheduler id */
270 + char * name; /* Devices friendly name */
271 + blt_role_t role; /* Device role (HS or AG) */
272 + bdaddr_t bdaddr; /* remote address */
273 + int channel; /* remote channel */
274 + int rd; /* RFCOMM fd */
275 + int tmp_rd; /* RFCOMM fd */
276 + int call_cnt; /* Number of attempted calls */
277 + ast_mutex_t lock; /* RFCOMM socket lock */
278 + char rd_buff[BLT_RDBUFF_MAX]; /* RFCOMM input buffer */
279 + int rd_buff_pos; /* RFCOMM input buffer position */
280 + int ready; /* 1 When ready */
284 + char last_ok_cmd[BLT_RDBUFF_MAX]; /* Runtime[AG]: Last AT command that was OK */
285 + int cind; /* Runtime[AG]: Recieved +CIND */
286 + int call_pos, service_pos, callsetup_pos; /* Runtime[AG]: Positions in CIND/CMER */
287 + int call, service, callsetup; /* Runtime[AG]: Values */
288 + char cid_num[AST_MAX_EXTENSION];
289 + char cid_name[AST_MAX_EXTENSION];
292 + blt_state_t state; /* Runtime: Device state (AG mode only) */
293 + int ring_timer; /* Runtime:Ring Timer */
294 + char last_err_cmd[BLT_RDBUFF_MAX]; /* Runtime: Last AT command that was OK */
295 + void (*cb)(blt_dev_t * dev, char * str); /* Runtime: Callback when in HS mode */
297 + int brsf; /* Runtime: Bluetooth Retrieve Supported Features */
298 + int bvra; /* Runtime: Bluetooth Voice Recognised Activation */
299 + int gain_speaker; /* Runtime: Gain Of Speaker */
300 + int clip; /* Runtime: Supports CLID */
301 + int colp; /* Runtime: Connected Line ID */
302 + int elip; /* Runtime: (Ericsson) Supports CLID */
303 + int eolp; /* Runtime: (Ericsson) Connected Line ID */
304 + int ringing; /* Runtime: Device is ringing */
306 + blt_dev_t * next; /* Next in linked list */
310 +typedef struct blt_atcb {
315 + /* DTE callbacks: */
316 + int (*set)(blt_dev_t * dev, const char * arg, int len);
317 + int (*read)(blt_dev_t * dev);
318 + int (*execute)(blt_dev_t * dev, const char * data);
319 + int (*test)(blt_dev_t * dev);
321 + /* DCE callbacks: */
322 + int (*unsolicited)(blt_dev_t * dev, const char * value);
326 +/* ---------------------------------- */
328 +static void rd_close(blt_dev_t * dev, int reconnect, int err);
329 +static int send_atcmd(blt_dev_t * device, const char * fmt, ...);
330 +static int sco_connect(blt_dev_t * dev);
331 +static int sco_start(blt_dev_t * dev, int fd);
333 +/* ---------------------------------- */
335 +/* RFCOMM channel we listen on*/
336 +static int rfcomm_channel_ag = BLT_DEFAULT_CHANNEL_AG;
337 +static int rfcomm_channel_hs = BLT_DEFAULT_CHANNEL_HS;
338 +static int rfcomm_channel_gui = BLT_DEFAULT_CHANNEL_GUI;
340 +static char* gui_default_sip_number = "";
341 +static char* gui_default_sip_address = "";
343 +/* Address of local bluetooth interface */
344 +static int hcidev_id;
345 +static bdaddr_t local_bdaddr;
347 +/* All the current sockets */
348 +AST_MUTEX_DEFINE_STATIC(iface_lock);
349 +static blt_dev_t * iface_head;
350 +static int ifcount = 0;
352 +static int sdp_record_hs = -1;
353 +static int sdp_record_ag = -1;
354 +static int sdp_record_gui = -1;
356 +/* RFCOMM listen socket */
357 +static int rfcomm_sock_ag = -1;
358 +static int rfcomm_sock_hs = -1;
359 +static int rfcomm_sock_gui = -1;
361 +static int sco_socket = -1;
363 +static int monitor_pid = -1;
365 +/* The socket monitoring thread */
366 +static pthread_t monitor_thread = AST_PTHREADT_NULL;
367 +AST_MUTEX_DEFINE_STATIC(monitor_lock);
369 +/* Count how many times this module is currently in use */
370 +static int usecnt = 0;
371 +AST_MUTEX_DEFINE_STATIC(usecnt_lock);
373 +static struct sched_context * sched = NULL;
375 +/* ---------------------------------- */
377 +#if ASTERISK_VERSION_NUM <= 010107
378 +#include <asterisk/channel_pvt.h>
379 +#define tech_pvt pvt->pvt
380 +#else /* CVS. FIXME: Version number */
381 +static struct ast_channel *blt_request(const char *type, int format, void *data, int *cause);
382 +static int blt_hangup(struct ast_channel *c);
383 +static int blt_answer(struct ast_channel *c);
384 +static struct ast_frame *blt_read(struct ast_channel *chan);
385 +static int blt_call(struct ast_channel *c, char *dest, int timeout);
386 +static int blt_write(struct ast_channel *chan, struct ast_frame *f);
387 +static int blt_indicate(struct ast_channel *chan, int cond);
389 +static const struct ast_channel_tech blt_tech = {
390 + .type = BLT_CHAN_NAME,
391 + .description = "Bluetooth Channel Driver",
392 + .capabilities = BLUETOOTH_FORMAT,
393 + .requester = blt_request,
394 + .hangup = blt_hangup,
395 + .answer = blt_answer,
398 + .write = blt_write,
399 + .indicate = blt_indicate,
402 +/* ---------------------------------- */
405 +role2str(blt_role_t role)
414 + case BLT_ROLE_NONE:
421 +status2str(blt_status_t status)
424 + case BLT_STATUS_DOWN:
426 + case BLT_STATUS_CONNECTING:
427 + return "Connecting";
428 + case BLT_STATUS_NEGOTIATING:
429 + return "Negotiating";
430 + case BLT_STATUS_READY:
432 + case BLT_STATUS_RINGING:
434 + case BLT_STATUS_IN_CALL:
440 +int sock_err(int fd)
443 + int len = sizeof(ret);
444 + getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
448 +/* ---------------------------------- */
449 +int parse_clip(const char * str, char *number, int number_len, char * name, int name_len, int *type)
451 + const char *c = str;
456 + memset(number, 0, number_len);
457 + memset(name, 0, name_len);
462 + while(*c && *c != '"')
466 + while(*c && *c != '"')
468 + length = c - start < number_len ? c - start : number_len;
469 + strncpy(number, start, length);
470 + number[length] = '\0';
472 + while(*c && *c != ',')
476 + while(*c && *c != ',')
478 + length = c - start < number_len ? c - start : number_len;
479 + strncpy(typestr, start, length);
480 + typestr[length] = '\0';
481 + *type = atoi(typestr);
483 + while(*c && *c != ',')
486 + while(*c && *c != ',')
489 + while(*c && *c != '"')
493 + while(*c && *c != '"')
495 + length = c - start < number_len ? c - start : number_len;
496 + strncpy(name, start, length);
497 + name[length] = '\0';
504 +parse_cind(const char * str, char * name, int name_len)
508 + memset(name, 0, name_len);
512 + if (++c == 1 && *(str+1) == '"') {
513 + const char * start = str + 2;
516 + while (*str && *str != '"') {
522 + strncpy(name, start, (len > name_len) ? name_len : len);
524 + } else if (*str == ')')
526 + else if (c == 0 && *str == ',')
534 +set_cind(blt_dev_t * dev, int indicator, int val)
537 + ast_log(LOG_DEBUG, "CIND %d set to %d\n", indicator, val);
539 + if (indicator == dev->callsetup_pos) {
543 + dev->callsetup = val;
547 + // Outgoing ringing
548 + if ((dev->owner && dev->role == BLT_ROLE_AG) ||
549 + (dev->owner && dev->role == BLT_ROLE_GUI))
550 + ast_queue_control(dev->owner, AST_CONTROL_RINGING);
557 + if ((dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0) ||
558 + (dev->owner && dev->role == BLT_ROLE_AG && dev->call == 0))
559 + ast_queue_control(dev->owner, AST_CONTROL_CONGESTION);
563 + } else if (indicator == dev->service_pos) {
568 + ast_log(LOG_NOTICE, "Audio Gateway %s lost signal\n", dev->name);
569 + else if (dev->service == 0 && val > 0)
570 + ast_log(LOG_NOTICE, "Audio Gateway %s got signal\n", dev->name);
572 + dev->service = val;
574 + } else if (indicator == dev->call_pos) {
582 + sco_start(dev, -1);
583 + ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
584 + } else if (val == 0)
585 + ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
593 +/* ---------------------------------- */
596 +set_buffer(char * ring, char * data, int circular_len, int * pos, int data_len)
598 + int start_pos = *(pos);
603 + // Set can_do to the most we can do in this copy.
605 + copy = MIN(circular_len - start_pos, data_len);
606 + memcpy(ring + start_pos, data + done, copy);
611 + if (start_pos == circular_len) {
615 + *(pos) = start_pos;
620 +get_buffer(char * dst, char * ring, int ring_size, int * head, int to_copy)
624 + // |1|2|3|4|5|6|7|8|9|
629 + // Set can_do to the most we can do in this copy.
630 + copy = MIN(ring_size - *head, to_copy);
632 + // ast_log(LOG_DEBUG, "Getting: %d bytes, From pos %d\n", copy, *head);
633 +#if __BYTE_ORDER == __LITTLE_ENDIAN
634 + memcpy(dst, ring + *head, copy);
636 + // memcpy(dst, ring + *head, copy);
637 + ast_swapcopy_samples(dst, ring+*head, copy/2);
639 + memset(ring+*head, 0, copy);
644 + if (*head == ring_size ) {
653 +/* Handle SCO audio sync.
655 + * If we are the MASTER, then we control the timing,
656 + * in 48 byte chunks. If we're the SLAVE, we send
657 + * as and when we recieve a packet.
659 + * Because of packet/timing nessecity, we
660 + * start up a thread when we're passing audio, so
661 + * that things are timed exactly right.
663 + * sco_thread() is the function that handles it.
668 +sco_thread(void * data)
670 + blt_dev_t * dev = (blt_dev_t*)data;
672 + struct pollfd pfd[2];
680 + // Avoid deadlock in odd circumstances
682 + ast_log(LOG_WARNING, "SCO thread started on fd %d, pid %d\n", dev->sco, getpid());
684 + if (fcntl(dev->sco_pipe[1], F_SETFL, O_RDWR|O_NONBLOCK)) {
685 + ast_log(LOG_WARNING, "fcntl failed on sco_pipe\n");
688 + // dev->status = BLT_STATUS_IN_CALL;
689 + // ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
690 + // Set buffer to silence, just incase.
692 + ast_mutex_lock(&(dev->sco_lock));
694 + memset(dev->sco_buf_in, 0, BUFLEN);
695 + memset(dev->sco_buf_out, 0, BUFLEN);
697 + dev->sco_pos_in = 0;
698 + dev->sco_pos_out = 0;
699 + dev->sco_pos_inrcv = 0;
702 + ast_mutex_unlock(&(dev->sco_lock));
706 + ast_mutex_lock(&(dev->sco_lock));
708 + if (dev->sco_running != 1) {
709 + ast_log(LOG_DEBUG, "SCO stopped.\n");
713 + pfd[0].fd = dev->sco;
714 + pfd[0].events = POLLIN;
716 + pfd[1].fd = dev->sco_pipe[1];
717 + pfd[1].events = POLLIN;
719 + ast_mutex_unlock(&(dev->sco_lock));
721 + res = poll(pfd, 2, 50);
723 + if (res == -1 && errno != EINTR) {
724 + ast_log(LOG_DEBUG, "SCO poll() error\n");
732 + if (pfd[0].revents & POLLIN) {
734 + len = read(dev->sco, buf, 48);
737 + ast_mutex_lock(&(dev->lock));
739 + if (dev->owner && dev->owner->_state == AST_STATE_UP) {
740 + ast_mutex_lock(&(dev->sco_lock));
741 + set_buffer(dev->sco_buf_in, buf, BUFLEN, &in_pos, len);
742 + dev->sco_pos_inrcv = in_pos;
744 + get_buffer(buf, dev->sco_buf_out, BUFLEN, &out_pos, len);
745 + if (write(dev->sco, buf, len) != len)
746 + ast_log(LOG_WARNING, "Wrote <48 to sco\n");
748 + if (dev->wakeread) {
749 + /* blt_read has caught up. Kick it */
751 + if(write(dev->sco_pipe[1], &c, 1) != 1)
752 + ast_log(LOG_WARNING, "write to kick sco_pipe failed\n");
754 + ast_mutex_unlock(&(dev->sco_lock));
756 + ast_mutex_unlock(&(dev->lock));
759 + } else if (pfd[0].revents) {
761 + int e = sock_err(pfd[0].fd);
762 + ast_log(LOG_ERROR, "SCO connection error: %s (errno %d)\n", strerror(e), e);
765 + } else if (pfd[1].revents & POLLIN) {
769 + len = read(pfd[1].fd, &c, 1);
770 + sending = (sending) ? 0 : 1;
772 + ast_mutex_unlock(&(dev->sco_lock));
774 + } else if (pfd[1].revents) {
776 + int e = sock_err(pfd[1].fd);
777 + 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);
781 + ast_log(LOG_NOTICE, "Unhandled poll output\n");
782 + ast_mutex_unlock(&(dev->sco_lock));
787 + ast_mutex_lock(&(dev->lock));
790 + dev->sco_running = -1;
792 + memset(dev->sco_buf_in, 0, BUFLEN);
793 + memset(dev->sco_buf_out, 0, BUFLEN);
795 + dev->sco_pos_in = 0;
796 + dev->sco_pos_out = 0;
797 + dev->sco_pos_inrcv = 0;
799 + ast_mutex_unlock(&(dev->sco_lock));
801 + ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
802 + ast_mutex_unlock(&(dev->lock));
803 + ast_log(LOG_DEBUG, "SCO thread stopped\n");
807 +/* Start SCO thread. Must be called with dev->lock */
810 +sco_start(blt_dev_t * dev, int fd)
813 + if (dev->sco_pipe[1] <= 0) {
814 + ast_log(LOG_ERROR, "SCO pipe[1] == %d\n", dev->sco_pipe[1]);
818 + ast_mutex_lock(&(dev->sco_lock));
820 + if (dev->sco_running != -1) {
821 + ast_log(LOG_ERROR, "Tried to start SCO thread while already running\n");
822 + ast_mutex_unlock(&(dev->sco_lock));
826 + if (dev->sco == -1) {
829 + } else if (sco_connect(dev) != 0) {
830 + ast_log(LOG_ERROR, "SCO fd invalid\n");
831 + ast_mutex_unlock(&(dev->sco_lock));
836 + dev->sco_running = 1;
838 + if (ast_pthread_create(&(dev->sco_thread), NULL, sco_thread, dev) < 0) {
839 + ast_log(LOG_ERROR, "Unable to start SCO thread.\n");
840 + dev->sco_running = -1;
841 + ast_mutex_unlock(&(dev->sco_lock));
845 + ast_mutex_unlock(&(dev->sco_lock));
850 +/* Stop SCO thread. Must be called with dev->lock */
853 +sco_stop(blt_dev_t * dev)
855 + ast_mutex_lock(&(dev->sco_lock));
856 + if (dev->sco_running == 1)
857 + dev->sco_running = 0;
859 + dev->sco_running = -1;
860 + dev->sco_sending = 0;
861 + ast_mutex_unlock(&(dev->sco_lock));
865 +/* ---------------------------------- */
867 +/* Answer the call. Call with lock held on device */
870 +answer(blt_dev_t * dev)
873 + if ( (!dev->owner) || (dev->ready != 1) || (dev->status != BLT_STATUS_READY && dev->status != BLT_STATUS_RINGING)) {
874 + ast_log(LOG_ERROR, "Attempt to answer() in invalid state (owner=%p, ready=%d, status=%s)\n",
875 + dev->owner, dev->ready, status2str(dev->status));
879 + // dev->sd = sco_connect(&local_bdaddr, &(dev->bdaddr), NULL, NULL, 0);
880 + // dev->status = BLT_STATUS_IN_CALL;
881 + // dev->owner->fds[0] = dev->sd;
882 + // if we are answering (hitting button):
883 + ast_queue_control(dev->owner, AST_CONTROL_ANSWER);
884 + // if asterisk signals us to answer:
885 + // ast_setstate(ast, AST_STATE_UP);
887 + /* Start SCO link */
888 + sco_start(dev, -1);
892 +/* ---------------------------------- */
895 +blt_write(struct ast_channel * ast, struct ast_frame * frame)
897 + blt_dev_t * dev = ast->tech_pvt;
899 + /* Write a frame of (presumably voice) data */
901 + if (frame->frametype != AST_FRAME_VOICE) {
902 + ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
906 + if (!(frame->subclass & BLUETOOTH_FORMAT)) {
907 + static int fish = 5;
909 + ast_log(LOG_WARNING, "Cannot handle frames in format %d\n", frame->subclass);
915 + if (ast->_state != AST_STATE_UP) {
919 + ast_mutex_lock(&(dev->sco_lock));
920 + set_buffer(dev->sco_buf_out, frame->data, BUFLEN, &(dev->sco_pos_out), MIN(frame->datalen, BUFLEN));
921 + ast_mutex_unlock(&(dev->sco_lock));
927 +static struct ast_frame *
928 +blt_read(struct ast_channel * ast)
930 + blt_dev_t * dev = ast->tech_pvt;
933 + static int fish = 0;
934 + /* Some nice norms */
936 + dev->fr.datalen = 0;
937 + dev->fr.samples = 0;
938 + dev->fr.data = NULL;
939 + dev->fr.src = BLT_CHAN_NAME;
940 + dev->fr.offset = 0;
941 + dev->fr.mallocd = AST_MALLOCD_DATA;
942 + dev->fr.delivery.tv_sec = 0;
943 + dev->fr.delivery.tv_usec = 0;
944 + read(dev->sco_pipe[0], &c, 1);
945 + ast_mutex_lock(&(dev->sco_lock));
946 + dev->sco_sending = 1;
948 + if (dev->sco_pos_inrcv < dev->sco_pos_in) {
949 + /* Buffer wrapped. Read only till the end */
950 + len = BUFLEN - dev->sco_pos_in + dev->sco_pos_inrcv;
952 + len = dev->sco_pos_inrcv - dev->sco_pos_in;
954 + dev->fr.data = malloc(AST_FRIENDLY_OFFSET+len) + AST_FRIENDLY_OFFSET;
956 + get_buffer(dev->fr.data, dev->sco_buf_in, BUFLEN, &(dev->sco_pos_in), len);
958 + ast_mutex_unlock(&(dev->sco_lock));
960 + unsigned char *x = dev->fr.data;
961 + ast_log(LOG_WARNING, "blt_read %d: %02x %02x %02x %02x %02x %02x\n",
962 + dev->fr.datalen, x[0], x[1], x[2], x[3], x[4], x[5]);
966 + dev->fr.samples = len / 2;
967 + dev->fr.datalen = len;
968 + dev->fr.frametype = AST_FRAME_VOICE;
969 + dev->fr.subclass = BLUETOOTH_FORMAT;
970 + dev->fr.offset = AST_FRIENDLY_OFFSET;
974 +/* Escape Any '"' in str. Return malloc()ed string */
976 +escape_str(char * str)
990 + ret = malloc(len + 1);
991 + pret = memset(ret, 0, len + 1);
1005 +ring_hs(blt_dev_t * dev)
1007 +#if (ASTERISK_VERSION_NUM < 010100)
1008 + char tmp[AST_MAX_EXTENSION];
1012 + ast_mutex_lock(&(dev->lock));
1014 + if (dev->owner == NULL) {
1015 + ast_mutex_unlock(&(dev->lock));
1020 + dev->status = BLT_STATUS_RINGING;
1022 + send_atcmd(dev, "RING");
1024 + dev->owner->rings++;
1026 + // XXX:T: '"' needs to be escaped in ELIP.
1028 +#if (ASTERISK_VERSION_NUM < 010100)
1030 + if (dev->owner->callerid) {
1032 + memset(tmp, 0, sizeof(tmp));
1033 + strncpy(tmp, dev->owner->callerid, sizeof(tmp)-1);
1035 + if (!ast_callerid_parse(tmp, &name, &num)) {
1037 + if (dev->clip && num)
1038 + send_atcmd(dev, "+CLIP: \"%s\",129", num);
1040 + if (dev->elip && name) {
1041 + char * esc = escape_str(name);
1042 + send_atcmd(dev, "*ELIP: \"%s\"", esc);
1051 + if (dev->clip && dev->owner->cid.cid_num)
1052 + send_atcmd(dev, "+CLIP: \"%s\",129", dev->owner->cid.cid_num);
1054 + if (dev->elip && dev->owner->cid.cid_name) {
1055 + char * esc = escape_str(dev->owner->cid.cid_name);
1056 + send_atcmd(dev, "*ELIP: \"%s\"", esc);
1062 + ast_mutex_unlock(&(dev->lock));
1068 + * If the HS is already connected, then just send RING, otherwise, things get a
1069 + * little more sticky. We first have to find the channel for HS using SDP,
1070 + * then initiate the connection. Once we've done that, we can start the call.
1074 +blt_call(struct ast_channel * ast, char * dest, int timeout)
1076 + blt_dev_t * dev = ast->tech_pvt;
1078 + if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
1079 + ast_log(LOG_WARNING, "blt_call called on %s, neither down nor reserved\n", ast->name);
1083 + ast_log(LOG_DEBUG, "Calling %s on %s [t: %d]\n", dest, ast->name, timeout);
1085 + if (ast_mutex_lock(&iface_lock)) {
1086 + ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
1090 +// ast_mutex_lock(&(dev->lock));
1092 + if (dev->ready == 0) {
1093 + ast_log(LOG_WARNING, "Tried to call a device not ready/connected.\n");
1094 + ast_setstate(ast, AST_CONTROL_CONGESTION);
1095 +// ast_mutex_unlock(&(dev->lock));
1096 + ast_mutex_unlock(&iface_lock);
1100 + if (dev->role == BLT_ROLE_HS) {
1102 + send_atcmd(dev, "+CIEV: 3,1");
1104 + dev->ring_timer = ast_sched_add(sched, 5000, AST_SCHED_CB(ring_hs), dev);
1108 + ast_setstate(ast, AST_STATE_RINGING);
1109 + ast_queue_control(ast, AST_CONTROL_RINGING);
1111 + } else if (dev->role == BLT_ROLE_AG) {
1113 + send_atcmd(dev, "ATD%s;", dev->dnid);
1114 +// it does not seem like we should start the audio until the call is connected
1115 +// sco_start(dev, -1);
1116 + } else if (dev->role == BLT_ROLE_GUI) {
1118 + send_atcmd(dev, "ATD%s;", dev->dnid);
1122 + ast_setstate(ast, AST_CONTROL_CONGESTION);
1123 + ast_log(LOG_ERROR, "Unknown device role\n");
1127 +// ast_mutex_unlock(&(dev->lock));
1128 + ast_mutex_unlock(&iface_lock);
1134 +blt_hangup(struct ast_channel * ast)
1136 + blt_dev_t * dev = ast->tech_pvt;
1138 + ast_log(LOG_DEBUG, "blt_hangup(%s)\n", ast->name);
1140 + if (!ast->tech_pvt) {
1141 + ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
1145 + if (ast_mutex_lock(&iface_lock)) {
1146 + ast_log(LOG_ERROR, "Failed to get iface_lock\n");
1150 + ast_mutex_lock(&(dev->lock));
1153 + dev->sco_sending = 0;
1155 + if (dev->role == BLT_ROLE_HS) {
1157 + if (dev->ringing == 0) {
1158 + // Actual call in progress
1159 + send_atcmd(dev, "+CIEV: 2,0");
1162 + // Just ringing still
1164 + if (dev->role == BLT_ROLE_HS)
1165 + send_atcmd(dev, "+CIEV: 3,0");
1167 + if (dev->ring_timer >= 0)
1168 + ast_sched_del(sched, dev->ring_timer);
1170 + dev->ring_timer = -1;
1175 + } else if (dev->role == BLT_ROLE_AG) {
1178 + send_atcmd(dev, "ATH");
1179 + send_atcmd(dev, "AT+CHUP");
1183 + if (dev->status == BLT_STATUS_IN_CALL || dev->status == BLT_STATUS_RINGING)
1184 + dev->status = BLT_STATUS_READY;
1186 + ast->tech_pvt = NULL;
1187 + dev->owner = NULL;
1188 + ast_mutex_unlock(&(dev->lock));
1189 + ast_setstate(ast, AST_STATE_DOWN);
1190 + ast_mutex_unlock(&(iface_lock));
1196 +blt_indicate(struct ast_channel * c, int condition)
1198 + ast_log(LOG_DEBUG, "blt_indicate (%d)\n", condition);
1200 + switch(condition) {
1201 + case AST_CONTROL_RINGING:
1204 + ast_log(LOG_WARNING, "Don't know how to condition %d\n", condition);
1211 +blt_answer(struct ast_channel * ast)
1213 + blt_dev_t * dev = ast->tech_pvt;
1215 + ast_mutex_lock(&dev->lock);
1217 + // if (dev->ring_timer >= 0)
1218 + // ast_sched_del(sched, dev->ring_timer);
1219 + // dev->ring_timer = -1;
1221 + ast_log(LOG_DEBUG, "Answering interface\n");
1223 + if (ast->_state != AST_STATE_UP) {
1224 + send_atcmd(dev, "+CIEV: 2,1");
1225 + send_atcmd(dev, "+CIEV: 3,0");
1226 + sco_start(dev, -1);
1227 + ast_setstate(ast, AST_STATE_UP);
1230 + ast_mutex_unlock(&dev->lock);
1235 +static struct ast_channel *
1236 +blt_new(blt_dev_t * dev, int state, const char * context, const char * number)
1238 + struct ast_channel * ast;
1241 + if ((ast = ast_channel_alloc(1)) == NULL) {
1242 + ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
1246 + snprintf(ast->name, sizeof(ast->name), "BLT/%s", dev->name);
1248 + // ast->fds[0] = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
1250 + ast->nativeformats = BLUETOOTH_FORMAT;
1251 + //ast->rawreadformat = BLUETOOTH_FORMAT;
1252 + //ast->rawwriteformat = BLUETOOTH_FORMAT;
1253 + ast->writeformat = BLUETOOTH_FORMAT;
1254 + ast->readformat = BLUETOOTH_FORMAT;
1256 + ast_setstate(ast, state);
1258 + ast->type = BLT_CHAN_NAME;
1260 + ast->tech_pvt = dev;
1261 +#if ASTERISK_VERSION_NUM > 010107
1262 + ast->tech = &blt_tech;
1264 + ast->pvt->call = blt_call;
1265 + ast->pvt->indicate = blt_indicate;
1266 + ast->pvt->hangup = blt_hangup;
1267 + ast->pvt->read = blt_read;
1268 + ast->pvt->write = blt_write;
1269 + ast->pvt->answer = blt_answer;
1271 + strncpy(ast->context, context, sizeof(ast->context)-1);
1272 + strncpy(ast->exten, number, sizeof(ast->exten) - 1);
1273 + if(0 == strcmp(number, "s"))
1275 + //ast_set_callerid(ast, dev->cid_num, dev->cid_name, dev->cid_num);
1278 + ast->language[0] = '\0';
1280 + ast->fds[0] = dev->sco_pipe[0];
1281 + write(dev->sco_pipe[1], &c, 1);
1285 + ast_mutex_lock(&usecnt_lock);
1287 + ast_mutex_unlock(&usecnt_lock);
1289 + ast_update_use_count();
1291 + if (state != AST_STATE_DOWN) {
1292 + if (ast_pbx_start(ast)) {
1293 + ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast->name);
1301 +static struct ast_channel *
1302 +#if (ASTERISK_VERSION_NUM < 010100)
1303 +blt_request(char * type, int format, void * local_data)
1304 +#elif (ASTERISK_VERSION_NUM <= 010107)
1305 +blt_request(const char * type, int format, void * local_data)
1307 +blt_request(const char * type, int format, void * local_data, int *cause)
1310 + char * data = (char*)local_data;
1312 + blt_dev_t * dev = NULL;
1313 + struct ast_channel * ast = NULL;
1314 + char * number = data, * dname;
1316 + dname = strsep(&number, "/");
1318 + oldformat = format;
1320 + format &= BLUETOOTH_FORMAT;
1323 + ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
1327 + ast_log(LOG_DEBUG, "Dialing '%s' via '%s'\n", number, dname);
1329 + if (ast_mutex_lock(&iface_lock)) {
1330 + ast_log(LOG_ERROR, "Unable to lock iface_list\n");
1337 + if (strcmp(dev->name, dname) == 0) {
1338 + ast_mutex_lock(&(dev->lock));
1339 + if (!dev->ready) {
1340 + ast_log(LOG_ERROR, "Device %s is not connected\n", dev->name);
1341 + ast_mutex_unlock(&(dev->lock));
1342 + ast_mutex_unlock(&iface_lock);
1350 + ast_mutex_unlock(&iface_lock);
1353 + ast_log(LOG_WARNING, "Failed to find device named '%s'\n", dname);
1357 + if (number && dev->role != BLT_ROLE_AG) {
1358 + ast_log(LOG_WARNING, "Tried to send a call out on non AG\n");
1359 + ast_mutex_unlock(&(dev->lock));
1363 + if (dev->role == BLT_ROLE_AG)
1364 + strncpy(dev->dnid, number, sizeof(dev->dnid) - 1);
1366 + ast = blt_new(dev, AST_STATE_DOWN, dev->context, "s");
1368 + ast_mutex_unlock(&(dev->lock));
1373 +/* ---------------------------------- */
1376 +/* ---- AT COMMAND SOCKET STUFF ---- */
1379 +send_atcmd(blt_dev_t * dev, const char * fmt, ...)
1385 + va_start(ap, fmt);
1386 + len = vsnprintf(buf, 1023, fmt, ap);
1389 + if (option_verbose)
1390 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < %s\n", role2str(dev->role), 10, dev->name, buf);
1392 + write(dev->rd, "\r\n", 2);
1393 + len = write(dev->rd, buf, len);
1394 + write(dev->rd, "\r\n", 2);
1395 + return (len) ? 0 : -1;
1400 +send_atcmd_ok(blt_dev_t * dev, const char * cmd)
1403 + strncpy(dev->last_ok_cmd, cmd, BLT_RDBUFF_MAX - 1);
1404 + if (option_verbose)
1405 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < OK\n", role2str(dev->role), 10, dev->name);
1406 + len = write(dev->rd, "\r\nOK\r\n", 6);
1407 + return (len) ? 0 : -1;
1411 +send_atcmd_error(blt_dev_t * dev)
1415 + if (option_verbose)
1416 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s < ERROR\n", role2str(dev->role), 10, dev->name);
1418 +// write(dev->rd, "\r\n", 2);
1419 +// len = write(dev->rd, dev->last_ok_cmd, 5);
1420 + write(dev->rd, "\r\n", 2);
1421 + len = write(dev->rd, "ERROR", 5);
1422 + write(dev->rd, "\r\n", 2);
1424 + return (len) ? 0 : -1;
1428 +/* ---------------------------------- */
1430 +/* -- Handle negotiation when we're an AG -- */
1432 +/* Bluetooth Support */
1435 +atcmd_brsf_set(blt_dev_t * dev, const char * arg, int len)
1437 + ast_log(LOG_DEBUG, "Device Supports: %s\n", arg);
1438 + dev->brsf = atoi(arg);
1439 + send_atcmd(dev, "+BRSF: %d", 23);
1443 +/* Bluetooth Voice Recognition */
1446 +atcmd_bvra_set(blt_dev_t * dev, const char * arg, int len)
1448 + ast_log(LOG_WARNING, "+BVRA Not Yet Supported\n");
1451 + // XXX:T: Fix voice recognition somehow!
1452 + int action = atoi(arg);
1453 + ast_log(LOG_DEBUG, "Voice Recognition: %s\n", (a) ? "ACTIVATED" : "DEACTIVATED");
1454 + if ((action == 0) & (dev->bvra == 1)) {
1457 + // XXX:T: Shutdown any active bvra channel
1458 + ast_log(LOG_DEBUG, "Voice Recognition: DISABLED\n");
1459 + } else if ((action == 1) && (dev->bvra == 0)) {
1462 + // XXX:T: Schedule connection to voice recognition extension/application
1463 + ast_log(LOG_DEBUG, "Voice Recognition: ENABLED\n");
1465 + ast_log(LOG_ERROR, "+BVRA out of sync (we think %d, but HS wants %d)\n", dev->bvra, action);
1475 +atcmd_cclk_read(blt_dev_t * dev)
1478 + const time_t ti = time(0);
1479 + tp = localtime_r(&ti, &t);
1480 + send_atcmd(dev, "+CCLK: \"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"",
1481 + (tp->tm_year % 100), (tp->tm_mon + 1), (tp->tm_mday),
1482 + tp->tm_hour, tp->tm_min, tp->tm_sec, ((tp->tm_gmtoff / 60) / 15));
1486 +/* CHUP - Hangup Call */
1489 +atcmd_chup_execute(blt_dev_t * dev, const char * data)
1491 + if (!dev->owner) {
1492 + ast_log(LOG_ERROR, "Request to hangup call when none in progress\n");
1495 + ast_log(LOG_DEBUG, "Hangup Call\n");
1496 + ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
1500 +/* CIND - Call Indicator */
1503 +atcmd_cind_read(blt_dev_t * dev)
1505 + send_atcmd(dev, "+CIND: 1,0,0");
1510 +atcmd_cind_test(blt_dev_t * dev)
1512 + send_atcmd(dev, "+CIND: (\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-4))");
1519 +atcmd_clan_read(blt_dev_t * dev)
1521 + send_atcmd(dev, "+CLAN: \"en\"");
1525 +/* Caller Id Presentation */
1528 +atcmd_clip_set(blt_dev_t * dev, const char * arg, int len)
1530 + dev->clip = atoi(arg);
1534 +/* Connected Line Identification Presentation */
1537 +atcmd_colp_set(blt_dev_t * dev, const char * arg, int len)
1539 + dev->colp = atoi(arg);
1543 +/* CMER - Mobile Equipment Event Reporting */
1546 +atcmd_cmer_set(blt_dev_t * dev, const char * arg, int len)
1549 + dev->status = BLT_STATUS_READY;
1553 +/* PhoneBook Types:
1555 + * - FD - SIM Fixed Dialing Phone Book
1556 + * - ME - ME Phone book
1557 + * - SM - SIM Phone Book
1558 + * - DC - ME dialled-calls list
1559 + * - RC - ME recieved-calls lisr
1560 + * - MC - ME missed-calls list
1561 + * - MV - ME Voice Activated Dialing List
1562 + * - HP - Hierachial Phone Book
1563 + * - BC - Own Business Card (PIN2 required)
1567 +/* Read Phone Book Entry */
1570 +atcmd_cpbr_set(blt_dev_t * dev, const char * arg, int len)
1572 + // XXX:T: Fix the phone book!
1573 + // * Maybe add res_phonebook or something? */
1574 + send_atcmd(dev, "+CPBR: %d,\"%s\",128,\"%s\"", atoi(arg), arg, arg);
1578 +/* Select Phone Book */
1581 +atcmd_cpbs_set(blt_dev_t * dev, const char * arg, int len)
1583 + // XXX:T: I guess we'll just accept any?
1588 +atcmd_cscs_set(blt_dev_t * dev, const char * arg, int len)
1590 + // XXX:T: Language
1595 +atcmd_eips_set(blt_dev_t * dev, const char * arg, int len)
1597 + ast_log(LOG_DEBUG, "Identify Presentation Set: %s=%s\n",
1598 + (*(arg) == 49) ? "ELIP" : "EOLP",
1599 + (*(arg+2) == 49) ? "ON" : "OFF");
1602 + dev->eolp = (*(arg+2) == 49) ? 1 : 0;
1604 + dev->elip = (*(arg+2) == 49) ? 1 : 0;
1609 +/* VGS - Speaker Volume Gain */
1612 +atcmd_vgs_set(blt_dev_t * dev, const char * arg, int len)
1614 + dev->gain_speaker = atoi(arg);
1619 +gui_eaid_response(blt_dev_t * dev, char * cmd)
1621 + ast_log(LOG_NOTICE, "Submenu displayed.\n");
1625 +atcmd_eami_execute(blt_dev_t * dev, const char * data)
1627 + char * number = NULL;
1629 + number = strndup(data, strlen(data));
1630 + int menuitem = atoi(number);
1632 + ast_log(LOG_NOTICE, "Menu Item '%d'.\n", menuitem);
1634 + dev->cb = gui_eaid_response;
1636 + if (menuitem == 1) {
1637 + char command[1024] = "";
1638 + const char* c1 = "AT*EAID=8,1,\"Make a SIP call\",\"Number\",\"";
1639 + const char* c2 = "\"";
1641 + (void)strncat(command, c1, sizeof(command) - strlen(command) - 1);
1642 + (void)strncat(command, gui_default_sip_number, sizeof(command) - strlen(command) - 1);
1643 + (void)strncat(command, c2, sizeof(command) - strlen(command) - 1);
1645 + //strcat(command, "AT*EAID=8,1,\"Make a SIP call\",\"Number\",\"");
1646 + //strcat(command, gui_default_sip_number);
1647 + //strcat(command, "\"");
1648 + send_atcmd(dev, command);
1649 + } else if (menuitem == 2) {
1650 + char command[1024] = "";
1651 + const char* c1 = "AT*EAID=11,1,\"Make a SIP call\",\"SIP Address\",100,\"";
1652 + const char* c2 = "\"";
1654 + (void)strncat(command, c1, sizeof(command) - strlen(command) - 1);
1655 + (void)strncat(command, gui_default_sip_address, sizeof(command) - strlen(command) - 1);
1656 + (void)strncat(command, c2, sizeof(command) - strlen(command) - 1);
1658 + //strcat(command, "AT*EAID=11,1,\"Make a SIP call\",\"SIP Address\",100,\"");
1659 + //strcat(command, gui_default_sip_address);
1660 + //strcat(command, "\"");
1661 + send_atcmd(dev, command);
1662 + } else if (menuitem == 0) {
1663 + dev->cb = gui_easm_response;
1664 +// send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,3,\"Call Number\",\"Call Address\",\"More Options\",1");
1665 + send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,2,\"Call Number\",\"Call Address\",1");
1667 + ast_log(LOG_ERROR, "Menu item not implementented.\n");
1673 +atcmd_eaii_execute(blt_dev_t * dev, const char * data)
1675 + int pos = 1, len = 0;
1678 + const char * start = data;
1679 + struct sockaddr_in addr;
1682 + if (*data == ',') {
1683 + memset(type, 0, 128);
1684 + strncpy(type, start, len);
1686 + ast_log(LOG_NOTICE, "Number(8)/Address(11): '%s'.\n", type);
1698 + memset(val, 0, 128);
1699 + strncpy(val, start, len);
1703 + address = strtok(val, del);
1704 + int type_int = atoi(type);
1706 + if (strcmp(address, " 0") == 0) {
1707 + ast_log(LOG_NOTICE, "Spurious EAII:\n");
1708 + ast_log(LOG_NOTICE, data);
1712 + if (type_int == 8) {
1713 + (void)strncat(address, "@sipgate.de", sizeof(address) - strlen(address) - 1);
1716 + ast_log(LOG_NOTICE, "SIP number/address: '%i','%s'.\n", type_int, address);
1718 + if (type_int == 8 || type_int == 11) {
1720 + char messagebox[1024] = "";
1721 + const char* mb1 = "AT*EAID=1,1,\"Setting up SIP call to ";
1722 + const char* mb2 = "\",30";
1724 + (void)strncat(messagebox, mb1, sizeof(messagebox) - strlen(messagebox) - 1);
1725 + (void)strncat(messagebox, address, sizeof(messagebox) - strlen(messagebox) - 1);
1726 + (void)strncat(messagebox, mb2, sizeof(messagebox) - strlen(messagebox) - 1);
1728 + //strcat(messagebox, "AT*EAID=1,1,\"Setting up SIP call to ");
1729 + //strcat(messagebox, address);
1730 + //strcat(messagebox, "\",30");
1731 + send_atcmd(dev, messagebox);
1733 + send_atcmd(dev, "AT*ESKS=2");
1734 + send_atcmd(dev, "AT*EKSP");
1735 + send_atcmd(dev, "AT*ESKS=0");
1737 + //Create manager connection to create call
1738 + int s = socket(AF_INET,SOCK_STREAM,0);
1740 + ast_log(LOG_ERROR, "Manager connection failed.");
1742 + dev->cb = ag_cgmi_response;
1743 + send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
1746 + addr.sin_family = AF_INET;
1747 + addr.sin_port = htons(5038);
1748 + addr.sin_addr.s_addr = inet_addr("127.0.0.1");
1749 + memset(&(addr.sin_zero), '\0', 8);
1751 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1752 + ast_log(LOG_ERROR, "Manager connection failed. (2)");
1753 + dev->cb = ag_cgmi_response;
1754 + send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
1757 + char* command = "Action: login\r\nUsername: markus\r\nSecret: supAEr\r\n\r\n";
1758 + if (write(s,command,strlen(command)) < 0) {
1759 + ast_log(LOG_ERROR, "Manager connection failed. (3)");
1760 + dev->cb = ag_cgmi_response;
1761 + send_atcmd(dev, "AT*EAID=1,1,\"Call failed\"");
1765 + char command3[1024] = "";
1766 + const char* action = "Action: Originate\r\nChannel: SIP/";
1767 + const char* action2 = "\r\nExten: 1235\r\nPriority: 1\r\nContext: sipgate.de\r\n\r\nAction: logoff\r\n\r\n";
1769 + (void)strncat(command3, action, sizeof(command3) - strlen(command3) - 1);
1770 + (void)strncat(command3, address, sizeof(command3) - strlen(command3) - 1);
1771 + (void)strncat(command3, action2, sizeof(command3) - strlen(command3) - 1);
1773 + //strcat(command3, "Action: Originate\r\nChannel: SIP/");
1774 + //strcat(command3, address);
1775 + //strcat(command3, "\r\nExten: 1235\r\nPriority: 1\r\nContext: sipgate.de\r\n\r\n");
1776 + ast_log(LOG_NOTICE, command3);
1778 + if (write(s,command3,strlen(command3)) < 0) {
1779 + ast_log(LOG_ERROR, "Manager connection failed. (5)");
1783 + //dev->cb = ag_cgmi_response;
1789 +atcmd_dial_execute(blt_dev_t * dev, const char * data)
1791 + char * number = NULL;
1793 + /* Make sure there is a ';' at the end of the line */
1794 + if (*(data + (strlen(data) - 1)) != ';') {
1795 + ast_log(LOG_WARNING, "Can't dial non-voice right now: %s\n", data);
1799 + number = strndup(data, strlen(data) - 1);
1800 + ast_log(LOG_NOTICE, "Dial: [%s]\n", number);
1802 + send_atcmd(dev, "+CIEV: 2,1");
1803 + send_atcmd(dev, "+CIEV: 3,0");
1805 + sco_start(dev, -1);
1807 + if (blt_new(dev, AST_STATE_UP, dev->context, number) == NULL) {
1816 +static int atcmd_bldn_execute(blt_dev_t * dev, const char *data)
1818 + return atcmd_dial_execute(dev, "bldn;");
1824 +atcmd_answer_execute(blt_dev_t * dev, const char * data)
1827 + if (!dev->ringing || !dev->owner) {
1828 + ast_log(LOG_WARNING, "Can't answer non existant call\n");
1834 + if (dev->ring_timer >= 0)
1835 + ast_sched_del(sched, dev->ring_timer);
1837 + dev->ring_timer = -1;
1839 + send_atcmd(dev, "+CIEV: 2,1");
1840 + send_atcmd(dev, "+CIEV: 3,0");
1842 + return answer(dev);
1846 +ag_unsol_ciev(blt_dev_t * dev, const char * data)
1848 + const char * orig = data;
1852 + while (*(data) && *(data) == ' ')
1855 + if (*(data) == 0) {
1856 + ast_log(LOG_WARNING, "Invalid value[1] for '+CIEV:%s'\n", orig);
1860 + indicator = *(data++) - 48;
1862 + if (*(data++) != ',') {
1863 + ast_log(LOG_WARNING, "Invalid value[2] for '+CIEV:%s'\n", orig);
1867 + if (*(data) == 0) {
1868 + ast_log(LOG_WARNING, "Invalid value[3] for '+CIEV:%s'\n", orig);
1872 + status = *(data) - 48;
1874 + set_cind(dev, indicator, status);
1880 +ag_unsol_cind(blt_dev_t * dev, const char * data)
1883 + while (*(data) && *(data) == ' ')
1887 + if (dev->cind == 0)
1892 + while ((data = parse_cind(data, name, 1023)) != NULL) {
1893 + ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
1894 + if (strcmp(name, "call") == 0)
1895 + dev->call_pos = pos;
1896 + else if (strcmp(name, "service") == 0)
1897 + dev->service_pos = pos;
1898 + else if (strcmp(name, "call_setup") == 0 || strcmp(name, "callsetup") == 0)
1899 + dev->callsetup_pos = pos;
1903 + ast_log(LOG_DEBUG, "CIND: %d=%s\n", pos, name);
1907 + int pos = 1, len = 0;
1909 + const char * start = data;
1912 + if (*data == ',') {
1913 + memset(val, 0, 128);
1914 + strncpy(val, start, len);
1915 + set_cind(dev, pos, atoi(val));
1926 + memset(val, 0, 128);
1927 + strncpy(val, start, len);
1928 + ast_log(LOG_DEBUG, "CIND IND %d set to %d [%s]\n", pos, atoi(val), val);
1937 + * handle an incoming call
1940 +ag_unsol_clip(blt_dev_t * dev, const char * data)
1942 + const char * orig = data;
1947 + while (*(data) && *(data) == ' ')
1950 + if (*(data) == 0) {
1951 + ast_log(LOG_WARNING, "Invalid value[1] for '+CLIP:%s'\n", orig);
1955 + parse_clip(data, number, sizeof(number)-1, name, sizeof(name)-1, &type);
1956 + ast_log(LOG_NOTICE, "Parsed '+CLIP: %s' number='%s' type='%d' name='%s'\n", data, number, type, name);
1958 + blt_new(dev, AST_STATE_RING, dev->context, "s");
1968 + { "A", NULL, NULL, atcmd_answer_execute, NULL, NULL },
1969 + { "D", NULL, NULL, atcmd_dial_execute, NULL, NULL },
1970 + { "+BRSF", atcmd_brsf_set, NULL, NULL, NULL, NULL },
1971 + { "+BVRA", atcmd_bvra_set, NULL, NULL, NULL, NULL },
1972 + { "+CCLK", NULL, atcmd_cclk_read, NULL, NULL, NULL },
1973 + { "+CHUP", NULL, NULL, atcmd_chup_execute, NULL, NULL },
1974 + { "+CIEV", NULL, NULL, NULL, NULL, ag_unsol_ciev },
1975 + { "+CIND", NULL, atcmd_cind_read, NULL, atcmd_cind_test, ag_unsol_cind },
1976 + { "*EAMI", NULL, NULL, atcmd_eami_execute, NULL, NULL},
1977 + { "*EAII", NULL, NULL, atcmd_eaii_execute, NULL, NULL},
1979 + { "+CLAN", NULL, atcmd_clan_read, NULL, NULL, NULL },
1980 + { "+CLIP", atcmd_clip_set, NULL, NULL, NULL, ag_unsol_clip },
1981 + { "+COLP", atcmd_colp_set, NULL, NULL, NULL, NULL },
1982 + { "+CMER", atcmd_cmer_set, NULL, NULL, NULL, NULL },
1983 + { "+CPBR", atcmd_cpbr_set, NULL, NULL, NULL, NULL },
1984 + { "+CPBS", atcmd_cpbs_set, NULL, NULL, NULL, NULL },
1985 + { "+CSCS", atcmd_cscs_set, NULL, NULL, NULL, NULL },
1986 + { "*EIPS", atcmd_eips_set, NULL, NULL, NULL, NULL },
1987 + { "+VGS", atcmd_vgs_set, NULL, NULL, NULL, NULL },
1988 + { "+BLDN", NULL, NULL, atcmd_bldn_execute, NULL, NULL },
1991 +#define ATCMD_LIST_LEN (sizeof(atcmd_list) / sizeof(blt_atcb_t))
1993 +/* ---------------------------------- */
1995 +/* -- Handle negotiation when we're a HS -- */
1998 +ag_unknown_response(blt_dev_t * dev, char * cmd)
2000 + ast_log(LOG_DEBUG, "Got UNKN response: %s\n", cmd);
2008 +gui_easm_response(blt_dev_t * dev, char * cmd)
2010 + ast_log(LOG_NOTICE, "Menu displayed.\n");
2014 +ag_cgmi_response(blt_dev_t * dev, char * cmd)
2016 + // CGMM - Phone Model
2017 + // CGMR - Phone Revision
2020 + // VTS - send tone
2024 + // CSMS - SMS STUFFS
2028 + // CSCA - sms CENTER NUMBER
2029 + // CNMI - SMS INDICATION
2030 + // ast_log(LOG_DEBUG, "Manufacturer: %s\n", cmd);
2032 + if (dev->role == BLT_ROLE_GUI) {
2033 + ast_log(LOG_NOTICE, "Displaying Menu.\n");
2034 + dev->cb = gui_easm_response;
2035 +// send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,3,\"Call Number\",\"Call Address\",\"More Options\",1");
2036 + send_atcmd(dev,"AT*EASM=\"SIP Menu\",1,1,2,\"Call Number\",\"Call Address\",1");
2038 + dev->cb = ag_unknown_response;
2043 +ag_cgmi_valid_response(blt_dev_t * dev, char * cmd)
2045 + // send_atcmd(dev, "AT+WS46?");
2046 + // send_atcmd(dev, "AT+CRC=1");
2047 + // send_atcmd(dev, "AT+CNUM");
2049 + if (strcmp(cmd, "OK") == 0) {
2050 + send_atcmd(dev, "AT+CGMI");
2051 + dev->cb = ag_cgmi_response;
2053 + dev->cb = ag_unknown_response;
2058 +ag_clip_response(blt_dev_t * dev, char * cmd)
2060 + send_atcmd(dev, "AT+CGMI=?");
2061 + dev->cb = ag_cgmi_valid_response;
2065 +ag_cmer_response(blt_dev_t * dev, char * cmd)
2067 + dev->cb = ag_clip_response;
2069 + dev->status = BLT_STATUS_READY;
2070 + send_atcmd(dev, "AT+CLIP=1");
2074 +ag_cind_status_response(blt_dev_t * dev, char * cmd)
2076 + // XXX:T: Handle response.
2077 + dev->cb = ag_cmer_response;
2078 + send_atcmd(dev, "AT+CMER=3,0,0,1");
2079 + // Initiliase SCO link!
2083 +ag_cind_response(blt_dev_t * dev, char * cmd)
2085 + dev->cb = ag_cind_status_response;
2087 + send_atcmd(dev, "AT+CIND?");
2091 +ag_brsf_response(blt_dev_t * dev, char * cmd)
2093 + dev->cb = ag_cind_response;
2094 + ast_log(LOG_DEBUG, "Bluetooth features: %s\n", cmd);
2096 + send_atcmd(dev, "AT+CIND=?");
2099 +/* ---------------------------------- */
2102 +sdp_register(sdp_session_t * session)
2104 + // XXX:T: Fix this horrible function so it makes some sense and is extensible!
2105 + sdp_list_t *svclass_id, *pfseq, *apseq, *root;
2106 + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
2107 + sdp_profile_desc_t profile;
2108 + sdp_list_t *aproto, *proto[2];
2109 + sdp_record_t record;
2110 + uint8_t u8 = rfcomm_channel_ag;
2111 + uint8_t u8_hs = rfcomm_channel_hs;
2112 + sdp_data_t *channel;
2115 + memset((void *)&record, 0, sizeof(sdp_record_t));
2116 + record.handle = 0xffffffff;
2117 + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
2118 + root = sdp_list_append(0, &root_uuid);
2119 + sdp_set_browse_groups(&record, root);
2121 + // Register as an AG
2123 + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
2124 + svclass_id = sdp_list_append(0, &svclass_uuid);
2125 + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
2126 + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
2127 + sdp_set_service_classes(&record, svclass_id);
2128 + sdp_uuid16_create(&profile.uuid, 0x111f);
2129 + profile.version = 0x0100;
2130 + pfseq = sdp_list_append(0, &profile);
2132 + sdp_set_profile_descs(&record, pfseq);
2134 + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
2135 + proto[0] = sdp_list_append(0, &l2cap_uuid);
2136 + apseq = sdp_list_append(0, proto[0]);
2138 + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
2139 + proto[1] = sdp_list_append(0, &rfcomm_uuid);
2140 + channel = sdp_data_alloc(SDP_UINT8, &u8);
2141 + proto[1] = sdp_list_append(proto[1], channel);
2142 + apseq = sdp_list_append(apseq, proto[1]);
2144 + aproto = sdp_list_append(0, apseq);
2145 + sdp_set_access_protos(&record, aproto);
2147 + sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
2149 + if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
2150 + ast_log(LOG_ERROR, "Service Record registration failed\n");
2155 + sdp_record_ag = record.handle;
2156 + sdp_record_gui = record.handle;
2158 + ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
2160 + sdp_data_free(channel);
2161 + sdp_list_free(proto[0], 0);
2162 + sdp_list_free(proto[1], 0);
2163 + sdp_list_free(apseq, 0);
2164 + sdp_list_free(aproto, 0);
2168 + memset((void *)&record, 0, sizeof(sdp_record_t));
2169 + record.handle = 0xffffffff;
2170 + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
2171 + root = sdp_list_append(0, &root_uuid);
2172 + sdp_set_browse_groups(&record, root);
2174 + // Register as an HS
2176 + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AUDIO_GW_SVCLASS_ID);
2177 + svclass_id = sdp_list_append(0, &svclass_uuid);
2178 + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
2179 + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
2180 + sdp_set_service_classes(&record, svclass_id);
2181 + sdp_uuid16_create(&profile.uuid, 0x111e);
2182 + profile.version = 0x0100;
2183 + pfseq = sdp_list_append(0, &profile);
2184 + sdp_set_profile_descs(&record, pfseq);
2186 + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
2187 + proto[0] = sdp_list_append(0, &l2cap_uuid);
2188 + apseq = sdp_list_append(0, proto[0]);
2190 + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
2191 + proto[1] = sdp_list_append(0, &rfcomm_uuid);
2192 + channel = sdp_data_alloc(SDP_UINT8, &u8_hs);
2193 + proto[1] = sdp_list_append(proto[1], channel);
2194 + apseq = sdp_list_append(apseq, proto[1]);
2196 + aproto = sdp_list_append(0, apseq);
2197 + sdp_set_access_protos(&record, aproto);
2198 + sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
2200 + if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
2201 + ast_log(LOG_ERROR, "Service Record registration failed\n");
2206 + sdp_record_hs = record.handle;
2208 + ast_log(LOG_NOTICE, "HeadsetAudioGateway service registered\n");
2211 + sdp_data_free(channel);
2212 + sdp_list_free(proto[0], 0);
2213 + sdp_list_free(proto[1], 0);
2214 + sdp_list_free(apseq, 0);
2215 + sdp_list_free(aproto, 0);
2221 +rfcomm_listen(bdaddr_t * bdaddr, int channel)
2225 + struct sockaddr_rc loc_addr;
2228 + if ((sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
2229 + ast_log(LOG_ERROR, "Can't create socket: %s (errno: %d)\n", strerror(errno), errno);
2233 + loc_addr.rc_family = AF_BLUETOOTH;
2235 + /* Local Interface Address */
2236 + bacpy(&loc_addr.rc_bdaddr, bdaddr);
2239 + loc_addr.rc_channel = channel;
2241 + if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
2242 + ast_log(LOG_ERROR, "Can't bind socket: %s (errno: %d)\n", strerror(errno), errno);
2247 + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
2248 + ast_log(LOG_ERROR, "Set socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
2253 + if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
2254 + ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
2256 + if (listen(sock, 10) < 0) {
2257 + ast_log(LOG_ERROR,"Can not listen on the socket. %s(%d)\n", strerror(errno), errno);
2262 + ast_log(LOG_NOTICE, "Listening for RFCOMM channel %d connections on FD %d\n", channel, sock);
2269 +sco_listen(bdaddr_t * bdaddr)
2273 + struct sockaddr_sco loc_addr;
2275 + memset(&loc_addr, 0, sizeof(loc_addr));
2277 + if ((sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
2278 + ast_log(LOG_ERROR, "Can't create SCO socket: %s (errno: %d)\n", strerror(errno), errno);
2282 + loc_addr.sco_family = AF_BLUETOOTH;
2283 + bacpy(&loc_addr.sco_bdaddr, BDADDR_ANY);
2285 + if (bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) < 0) {
2286 + ast_log(LOG_ERROR, "Can't bind SCO socket: %s (errno: %d)\n", strerror(errno), errno);
2291 + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
2292 + ast_log(LOG_ERROR, "Set SCO socket SO_REUSEADDR option on failed: errno %d, %s", errno, strerror(errno));
2297 + if (fcntl(sock, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
2298 + ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
2300 + if (listen(sock, 10) < 0) {
2301 + ast_log(LOG_ERROR,"Can not listen on SCO socket: %s(%d)\n", strerror(errno), errno);
2306 + ast_log(LOG_NOTICE, "Listening for SCO connections on FD %d\n", sock);
2312 +rfcomm_connect(bdaddr_t * src, bdaddr_t * dst, int channel, int nbio)
2314 + struct sockaddr_rc addr;
2317 + if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
2321 + memset(&addr, 0, sizeof(addr));
2322 + addr.rc_family = AF_BLUETOOTH;
2323 + bacpy(&addr.rc_bdaddr, src);
2324 + addr.rc_channel = 0;
2326 + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
2331 + memset(&addr, 0, sizeof(addr));
2332 + addr.rc_family = AF_BLUETOOTH;
2333 + bacpy(&addr.rc_bdaddr, dst);
2334 + addr.rc_channel = channel;
2337 + if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
2338 + ast_log(LOG_ERROR, "Can't set RFCOMM socket to NBIO\n");
2341 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 && (nbio != 1 || (errno != EAGAIN))) {
2349 +/* Must be called with dev->lock held */
2352 +sco_connect(blt_dev_t * dev)
2354 + struct sockaddr_sco addr;
2355 + // struct sco_conninfo conn;
2356 + // struct sco_options opts;
2358 + // bdaddr_t * src = &local_bdaddr;
2361 + bdaddr_t * dst = &(dev->bdaddr);
2363 + if (dev->sco != -1) {
2364 + ast_log(LOG_ERROR, "SCO fd already open.\n");
2368 + if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
2369 + ast_log(LOG_ERROR, "Can't create SCO socket(): %s\n", strerror(errno));
2373 + memset(&addr, 0, sizeof(addr));
2375 + addr.sco_family = AF_BLUETOOTH;
2376 + bacpy(&addr.sco_bdaddr, BDADDR_ANY);
2378 + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
2379 + ast_log(LOG_ERROR, "Can't bind() SCO socket: %s\n", strerror(errno));
2384 + memset(&addr, 0, sizeof(addr));
2385 + addr.sco_family = AF_BLUETOOTH;
2386 + bacpy(&addr.sco_bdaddr, dst);
2388 + if (fcntl(s, F_SETFL, O_RDWR|O_NONBLOCK) != 0)
2389 + ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
2391 + if ((connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) && (errno != EAGAIN)) {
2392 + ast_log(LOG_ERROR, "Can't connect() SCO socket: %s (errno %d)\n", strerror(errno), errno);
2397 + //size = sizeof(conn);
2400 +/* XXX:T: HERE, fix getting SCO conninfo.
2402 + if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
2403 + ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
2408 + size = sizeof(opts);
2410 + if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
2411 + ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
2416 + dev->sco_handle = conn.hci_handle;
2417 + dev->sco_mtu = opts.mtu;
2421 + ast_log(LOG_DEBUG, "SCO: %d\n", s);
2429 +/* ---------------------------------- */
2431 +/* Non blocking (async) outgoing bluetooth connection */
2434 +try_connect(blt_dev_t * dev)
2437 + ast_mutex_lock(&(dev->lock));
2439 + if (dev->status != BLT_STATUS_CONNECTING && dev->status != BLT_STATUS_DOWN) {
2440 + ast_mutex_unlock(&(dev->lock));
2444 + if (dev->rd != -1) {
2447 + struct pollfd pfd;
2449 + if (dev->status != BLT_STATUS_CONNECTING) {
2450 + ast_mutex_unlock(&(dev->lock));
2451 + dev->outgoing_id = -1;
2455 + // ret = connect(dev->rd, (struct sockaddr *)&(dev->addr), sizeof(struct sockaddr_rc)); //
2458 + pfd.events = POLLIN | POLLOUT;
2460 + ret = poll(&pfd, 1, 0);
2465 + dev->status = BLT_STATUS_DOWN;
2466 + dev->outgoing_id = ast_sched_add(sched, 10000, AST_SCHED_CB(try_connect), dev);
2467 + ast_mutex_unlock(&(dev->lock));
2473 + int len = sizeof(ret);
2474 + getsockopt(dev->rd, SOL_SOCKET, SO_ERROR, &ret, &len);
2478 + ast_log(LOG_NOTICE, "Initialised bluetooth link to device %s\n", dev->name);
2482 + struct hci_conn_info_req * cr;
2486 + cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
2487 + dd = hci_open_dev(hcidev_id);
2488 + cr->type = ACL_LINK;
2489 + bacpy(&cr->bdaddr, &(dev->bdaddr));
2491 + if (ioctl(dd, HCIGETCONNINFO, (unsigned long)cr) < 0) {
2492 + ast_log(LOG_ERROR, "Failed to get connection info: %s\n", strerror(errno));
2494 + ast_log(LOG_DEBUG, "HCI Handle: %d\n", cr->conn_info->handle);
2497 + if (hci_read_remote_name(dd, &(dev->bdaddr), sizeof(name), name, 25000) == 0)
2498 + ast_log(LOG_DEBUG, "Remote Name: %s\n", name);
2503 + dev->status = BLT_STATUS_NEGOTIATING;
2505 + /* If this device is an AG/GUI, we initiate the negotiation. */
2507 + if (dev->role == BLT_ROLE_AG ||
2508 + dev->role == BLT_ROLE_GUI) {
2509 + dev->cb = ag_brsf_response;
2510 + send_atcmd(dev, "AT+BRSF=23");
2513 + dev->outgoing_id = -1;
2514 + ast_mutex_unlock(&(dev->lock));
2519 + if (ret != EHOSTDOWN)
2520 + ast_log(LOG_NOTICE, "Connect to device %s failed: %s (errno %d)\n", dev->name, strerror(ret), ret);
2524 + dev->status = BLT_STATUS_DOWN;
2525 + dev->outgoing_id = ast_sched_add(sched, (ret == EHOSTDOWN) ? 10000 : 2500, AST_SCHED_CB(try_connect), dev);
2526 + ast_mutex_unlock(&(dev->lock));
2533 + dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
2534 + ast_mutex_unlock(&(dev->lock));
2538 + ast_log(LOG_NOTICE, "RFCOMM connect start.\n");
2539 + fd = rfcomm_connect(&local_bdaddr, &(dev->bdaddr), dev->channel, 1);
2540 + ast_log(LOG_NOTICE, "RFCOMM connect done.\n");
2543 + ast_log(LOG_WARNING, "NBIO connect() to %s returned %d: %s\n", dev->name, errno, strerror(errno));
2544 + dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
2545 + ast_mutex_unlock(&(dev->lock));
2550 + dev->status = BLT_STATUS_CONNECTING;
2551 + dev->outgoing_id = ast_sched_add(sched, 100, AST_SCHED_CB(try_connect), dev);
2552 + ast_mutex_unlock(&(dev->lock));
2557 +/* Called whenever a new command is received while we're the AG */
2561 +process_rfcomm_cmd(blt_dev_t * dev, char * cmd)
2564 + char * fullcmd = cmd;
2566 + if (option_verbose)
2567 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, cmd);
2569 + /* Read the 'AT' from the start of the string */
2570 + if (strncmp(cmd, "AT", 2)) {
2571 + ast_log(LOG_WARNING, "Unknown command without 'AT': %s\n", cmd);
2572 + send_atcmd_error(dev);
2578 + // Don't forget 'AT' on its own is OK.
2580 + if (strlen(cmd) == 0) {
2581 + send_atcmd_ok(dev, fullcmd);
2585 + for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
2586 + if (strncmp(atcmd_list[i].str, cmd, strlen(atcmd_list[i].str)) == 0) {
2587 + char * pos = (cmd + strlen(atcmd_list[i].str));
2588 + if ((strncmp(pos, "=?", 2) == 0) && (strlen(pos) == 2)) {
2589 + /* TEST command */
2590 + if (atcmd_list[i].test) {
2591 + if (atcmd_list[i].test(dev) == 0)
2592 + send_atcmd_ok(dev, fullcmd);
2594 + send_atcmd_error(dev);
2596 + send_atcmd_ok(dev, fullcmd);
2598 + } else if ((strncmp(pos, "?", 1) == 0) && (strlen(pos) == 1)) {
2599 + /* READ command */
2600 + if (atcmd_list[i].read) {
2601 + if (atcmd_list[i].read(dev) == 0)
2602 + send_atcmd_ok(dev, fullcmd);
2604 + send_atcmd_error(dev);
2606 + ast_log(LOG_WARNING, "AT Command: '%s' missing READ function\n", fullcmd);
2607 + send_atcmd_error(dev);
2609 + } else if (strncmp(pos, "=", 1) == 0) {
2611 + if (atcmd_list[i].set) {
2612 + if (atcmd_list[i].set(dev, (pos + 1), (*(pos + 1)) ? strlen(pos + 1) : 0) == 0)
2613 + send_atcmd_ok(dev, fullcmd);
2615 + send_atcmd_error(dev);
2617 + ast_log(LOG_WARNING, "AT Command: '%s' missing SET function\n", fullcmd);
2618 + send_atcmd_error(dev);
2621 + /* EXECUTE command */
2622 + if (atcmd_list[i].execute) {
2623 + if (atcmd_list[i].execute(dev, cmd + strlen(atcmd_list[i].str)) == 0)
2624 + send_atcmd_ok(dev, fullcmd);
2626 + send_atcmd_error(dev);
2628 + ast_log(LOG_WARNING, "AT Command: '%s' missing EXECUTE function\n", fullcmd);
2629 + send_atcmd_error(dev);
2636 + ast_log(LOG_NOTICE, "Unknown AT Command: '%s' (%s)\n", fullcmd, cmd);
2637 + send_atcmd_error(dev);
2642 +/* Called when a socket is incoming */
2645 +handle_incoming(int fd, blt_role_t role)
2648 + struct sockaddr_rc addr;
2649 + int len = sizeof(addr);
2651 + // Got a new incoming socket.
2652 + ast_log(LOG_DEBUG, "Incoming RFCOMM socket\n");
2654 + ast_mutex_lock(&iface_lock);
2656 + fd = accept(fd, (struct sockaddr*)&addr, &len);
2660 + if (bacmp(&(dev->bdaddr), &addr.rc_bdaddr) == 0) {
2661 + ast_log(LOG_DEBUG, "Connect from %s\n", dev->name);
2662 + ast_mutex_lock(&(dev->lock));
2663 + /* Kill any outstanding connect attempt. */
2664 + if (dev->outgoing_id > -1) {
2665 + ast_sched_del(sched, dev->outgoing_id);
2666 + dev->outgoing_id = -1;
2669 + rd_close(dev, 0, 0);
2671 + dev->status = BLT_STATUS_NEGOTIATING;
2674 + if (dev->role == BLT_ROLE_AG ||
2675 + dev->role == BLT_ROLE_GUI) {
2676 + dev->cb = ag_brsf_response;
2677 + send_atcmd(dev, "AT+BRSF=23");
2679 + ast_mutex_unlock(&(dev->lock));
2685 + if (dev == NULL) {
2686 + ast_log(LOG_WARNING, "Connect from unknown device\n");
2689 + ast_mutex_unlock(&iface_lock);
2695 +handle_incoming_sco(int master)
2699 + struct sockaddr_sco addr;
2700 + struct sco_conninfo conn;
2701 + struct sco_options opts;
2702 + int len = sizeof(addr);
2705 + ast_log(LOG_DEBUG, "Incoming SCO socket\n");
2707 + fd = accept(master, (struct sockaddr*)&addr, &len);
2709 + if (fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK) != 0) {
2710 + ast_log(LOG_ERROR, "Can't set SCO socket to NBIO\n");
2715 + len = sizeof(conn);
2717 + if (getsockopt(fd, SOL_SCO, SCO_CONNINFO, &conn, &len) < 0) {
2718 + ast_log(LOG_ERROR, "Can't getsockopt SCO_CONNINFO on SCO socket: %s\n", strerror(errno));
2723 + len = sizeof(opts);
2725 + if (getsockopt(fd, SOL_SCO, SCO_OPTIONS, &opts, &len) < 0) {
2726 + ast_log(LOG_ERROR, "Can't getsockopt SCO_OPTIONS on SCO socket: %s\n", strerror(errno));
2731 + ast_mutex_lock(&iface_lock);
2734 + if (bacmp(&(dev->bdaddr), &addr.sco_bdaddr) == 0) {
2735 + ast_log(LOG_DEBUG, "SCO Connect from %s\n", dev->name);
2736 + ast_mutex_lock(&(dev->lock));
2737 + if (dev->sco_running != -1) {
2738 + ast_log(LOG_ERROR, "Incoming SCO socket, but SCO thread already running.\n");
2740 + sco_start(dev, fd);
2742 + ast_mutex_unlock(&(dev->lock));
2748 + ast_mutex_unlock(&iface_lock);
2750 + if (dev == NULL) {
2751 + ast_log(LOG_WARNING, "SCO Connect from unknown device\n");
2754 + // XXX:T: We need to handle the fact we might have an outgoing connection attempt in progress.
2755 + ast_log(LOG_DEBUG, "SCO: %d, HCIHandle=%d, MUT=%d\n", fd, conn.hci_handle, opts.mtu);
2763 +/* Called when there is data waiting on a socket */
2766 +handle_rd_data(blt_dev_t * dev)
2771 + while ((ret = read(dev->rd, &c, 1)) == 1) {
2773 + // log_buf[i++] = c;
2775 + if (dev->role == BLT_ROLE_HS) {
2778 + ret = process_rfcomm_cmd(dev, dev->rd_buff);
2779 + dev->rd_buff_pos = 0;
2780 + memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
2784 + if (dev->rd_buff_pos >= BLT_RDBUFF_MAX)
2787 + dev->rd_buff[dev->rd_buff_pos++] = c;
2789 + } else if (dev->role == BLT_ROLE_AG ||
2790 + dev->role == BLT_ROLE_GUI) {
2792 + //ast_log(LOG_ERROR, "%s: %c\n", dev->name, c);
2794 + switch (dev->state) {
2795 + case BLT_STATE_WANT_R:
2796 + if (c == '\r' || c == 10) {
2797 + dev->state = BLT_STATE_WANT_N;
2798 + } else if (c == '+') {
2799 + dev->state = BLT_STATE_WANT_CMD;
2800 + dev->rd_buff[dev->rd_buff_pos++] = '+';
2802 + ast_log(LOG_ERROR, "Device %s: Expected '\\r', got %d. state=BLT_STATE_WANT_R\n", dev->name, c);
2807 + case BLT_STATE_WANT_N:
2808 + if (c == '\n' || c == 13)
2809 + dev->state = BLT_STATE_WANT_CMD;
2811 + ast_log(LOG_ERROR, "Device %s: Expected '\\n', got %d. state=BLT_STATE_WANT_N\n", dev->name, c);
2816 + case BLT_STATE_WANT_CMD:
2817 + if (c == '\r' || c == 10)
2818 + dev->state = BLT_STATE_WANT_N2;
2820 + if (dev->rd_buff_pos >= BLT_RDBUFF_MAX) {
2821 + ast_log(LOG_ERROR, "Device %s: Buffer exceeded\n", dev->name);
2824 + dev->rd_buff[dev->rd_buff_pos++] = c;
2828 + case BLT_STATE_WANT_N2:
2829 + if (c == '\n' || c == 13) {
2831 + dev->state = BLT_STATE_WANT_R;
2833 + if (dev->rd_buff[0] == '+') {
2835 + // find unsolicited
2836 + for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
2837 + if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
2838 + if (atcmd_list[i].unsolicited)
2839 + atcmd_list[i].unsolicited(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
2841 + ast_log(LOG_WARNING, "Device %s: Unhandled Unsolicited: %s\n", dev->name, dev->rd_buff);
2846 + if (option_verbose)
2847 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
2849 + if (i == ATCMD_LIST_LEN)
2850 + ast_log(LOG_NOTICE, "Device %s: Got unsolicited message: %s\n", dev->name, dev->rd_buff);
2852 + } else if (dev->rd_buff[0] == '*') {
2853 + if (option_verbose)
2854 + ast_verbose(VERBOSE_PREFIX_1 "[%s]* %*s > %s\n", role2str(dev->role), 9, dev->name, dev->rd_buff);
2858 + for (i = 0 ; i < ATCMD_LIST_LEN ; i++) {
2859 + if (strncmp(atcmd_list[i].str, dev->rd_buff, strlen(atcmd_list[i].str)) == 0) {
2860 + if (atcmd_list[i].execute)
2861 + atcmd_list[i].execute(dev, dev->rd_buff + strlen(atcmd_list[i].str) + 1);
2863 + ast_log(LOG_ERROR, "Device %s: Unhandled Execute: %s\n", dev->name, dev->rd_buff);
2872 + strcmp(dev->rd_buff, "OK") != 0 &&
2873 + strcmp(dev->rd_buff, "CONNECT") != 0 &&
2874 + strcmp(dev->rd_buff, "RING") != 0 &&
2875 + strcmp(dev->rd_buff, "NO CARRIER") != 0 &&
2876 + strcmp(dev->rd_buff, "ERROR") != 0 &&
2877 + strcmp(dev->rd_buff, "NO DIALTONE") != 0 &&
2878 + strcmp(dev->rd_buff, "BUSY") != 0 &&
2879 + strcmp(dev->rd_buff, "NO ANSWER") != 0 &&
2880 + strcmp(dev->rd_buff, "DELAYED") != 0
2882 + // It must be a multiline error
2883 + strncpy(dev->last_err_cmd, dev->rd_buff, 1023);
2884 + if (option_verbose)
2885 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
2886 + } else if (dev->cb) {
2887 + if (option_verbose)
2888 + ast_verbose(VERBOSE_PREFIX_1 "[%s] %*s > %s\n", role2str(dev->role), 10, dev->name, dev->rd_buff);
2889 + dev->cb(dev, dev->rd_buff);
2891 + ast_log(LOG_ERROR, "Device %s: Data on socket in HS mode, but no callback\n", dev->name);
2896 + dev->rd_buff_pos = 0;
2897 + memset(dev->rd_buff, 0, BLT_RDBUFF_MAX);
2900 + ast_log(LOG_ERROR, "Device %s: Expected '\\n' got %d. state = BLT_STATE_WANT_N2:\n", dev->name, c);
2908 + ast_log(LOG_ERROR, "Device %s: Unknown device state %d\n", dev->name, dev->state);
2920 +/* Close the devices RFCOMM socket, and SCO if it exists. Must hold dev->lock */
2923 +rd_close(blt_dev_t * dev, int reconnect, int e)
2932 + dev->status = BLT_STATUS_DOWN;
2937 + ast_setstate(dev->owner, AST_STATE_DOWN);
2938 + ast_queue_control(dev->owner, AST_CONTROL_HANGUP);
2941 + /* Schedule a reconnect */
2942 + if (reconnect && dev->autoconnect) {
2943 + dev->outgoing_id = ast_sched_add(sched, 5000, AST_SCHED_CB(try_connect), dev);
2945 + if (monitor_thread == pthread_self()) {
2946 + // Because we're not the monitor thread, we needd to inturrupt poll().
2947 + pthread_kill(monitor_thread, SIGURG);
2951 + ast_log(LOG_NOTICE, "Device %s disconnected, scheduled reconnect in 5 seconds: %s (errno %d)\n", dev->name, strerror(e), e);
2953 + ast_log(LOG_NOTICE, "Device %s disconnected: %s (errno %d)\n", dev->name, strerror(e), e);
2960 + * Remember that we can only add to the scheduler from
2961 + * the do_monitor thread, as it calculates time to next one from
2966 +do_monitor(void * data)
2968 +#define SRV_SOCK_CNT 4
2972 + struct pollfd * pfds = malloc(sizeof(struct pollfd) * (ifcount + SRV_SOCK_CNT));
2974 + /* -- We start off by trying to connect all of our devices (non blocking) -- */
2976 + monitor_pid = getpid();
2978 + if (ast_mutex_lock(&iface_lock)) {
2979 + ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
2986 + if (socketpair(PF_UNIX, SOCK_STREAM, 0, dev->sco_pipe) != 0) {
2987 + ast_log(LOG_ERROR, "Failed to create socket pair: %s (errno %d)\n", strerror(errno), errno);
2988 + ast_mutex_unlock(&iface_lock);
2992 + if (dev->autoconnect && dev->status == BLT_STATUS_DOWN)
2993 + dev->outgoing_id = ast_sched_add(sched, 1500, AST_SCHED_CB(try_connect), dev);
2996 + ast_mutex_unlock(&iface_lock);
2998 + /* -- Now, Scan all sockets, and service scheduler -- */
3000 + pfds[0].fd = rfcomm_sock_ag;
3001 + pfds[0].events = POLLIN;
3003 + pfds[1].fd = rfcomm_sock_hs;
3004 + pfds[1].events = POLLIN;
3006 + pfds[2].fd = rfcomm_sock_gui;
3007 + pfds[2].events = POLLIN;
3009 + pfds[3].fd = sco_socket;
3010 + pfds[3].events = POLLIN;
3013 + int cnt = SRV_SOCK_CNT;
3016 + /* -- Build pfds -- */
3018 + if (ast_mutex_lock(&iface_lock)) {
3019 + ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
3024 + ast_mutex_lock(&(dev->lock));
3025 + if (dev->rd > 0 && ((dev->status != BLT_STATUS_DOWN) && (dev->status != BLT_STATUS_CONNECTING))) {
3026 + pfds[cnt].fd = dev->rd;
3027 + pfds[cnt].events = POLLIN;
3030 + ast_mutex_unlock(&(dev->lock));
3033 + ast_mutex_unlock(&iface_lock);
3035 + /* -- End Build pfds -- */
3037 + res = ast_sched_wait(sched);
3038 + res = poll(pfds, cnt, MAX(100, MIN(100, res)));
3041 + ast_sched_runq(sched);
3043 + if (pfds[0].revents) {
3044 + handle_incoming(rfcomm_sock_ag, BLT_ROLE_AG);
3048 + if (pfds[1].revents) {
3049 + handle_incoming(rfcomm_sock_hs, BLT_ROLE_HS);
3053 + if (pfds[2].revents) {
3054 + handle_incoming(rfcomm_sock_gui, BLT_ROLE_GUI);
3058 + if (pfds[3].revents) {
3059 + handle_incoming_sco(sco_socket);
3066 + for (i = SRV_SOCK_CNT ; i < cnt ; i++) {
3068 + /* Optimise a little bit */
3071 + else if (pfds[i].revents == 0)
3074 + /* -- Find the socket that has activity -- */
3076 + if (ast_mutex_lock(&iface_lock)) {
3077 + ast_log(LOG_ERROR, "Failed to get iface_lock.\n");
3084 + if (pfds[i].fd == dev->rd) {
3085 + ast_mutex_lock(&(dev->lock));
3086 + if (pfds[i].revents & POLLIN) {
3087 + if (handle_rd_data(dev) == -1) {
3088 + rd_close(dev, 0, 0);
3091 + rd_close(dev, 1, sock_err(dev->rd));
3093 + ast_mutex_unlock(&(dev->lock));
3100 + if (dev == NULL) {
3101 + ast_log(LOG_ERROR, "Unhandled fd from poll()\n");
3102 + close(pfds[i].fd);
3105 + ast_mutex_unlock(&iface_lock);
3107 + /* -- End find socket with activity -- */
3117 +restart_monitor(void)
3120 + if (monitor_thread == AST_PTHREADT_STOP)
3123 + if (ast_mutex_lock(&monitor_lock)) {
3124 + ast_log(LOG_WARNING, "Unable to lock monitor\n");
3128 + if (monitor_thread == pthread_self()) {
3129 + ast_mutex_unlock(&monitor_lock);
3130 + ast_log(LOG_WARNING, "Cannot kill myself\n");
3134 + if (monitor_thread != AST_PTHREADT_NULL) {
3136 + /* Just signal it to be sure it wakes up */
3137 + pthread_cancel(monitor_thread);
3138 + pthread_kill(monitor_thread, SIGURG);
3139 + ast_log(LOG_DEBUG, "Waiting for monitor thread to join...\n");
3140 + pthread_join(monitor_thread, NULL);
3141 + ast_log(LOG_DEBUG, "joined\n");
3145 + /* Start a new monitor */
3146 + if (ast_pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
3147 + ast_mutex_unlock(&monitor_lock);
3148 + ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
3154 + ast_mutex_unlock(&monitor_lock);
3159 +blt_parse_config(void)
3161 + struct ast_config * cfg;
3162 + struct ast_variable * v;
3165 + cfg = ast_load(BLT_CONFIG_FILE);
3168 + ast_log(LOG_NOTICE, "Unable to load Bluetooth config: %s. Bluetooth disabled\n", BLT_CONFIG_FILE);
3172 + v = ast_variable_browse(cfg, "general");
3175 + if (!strcasecmp(v->name, "rfchannel_ag")) {
3176 + rfcomm_channel_ag = atoi(v->value);
3177 + } else if (!strcasecmp(v->name, "rfchannel_hs")) {
3178 + rfcomm_channel_hs = atoi(v->value);
3179 + } else if (!strcasecmp(v->name, "rfchannel_gui")) {
3180 + rfcomm_channel_gui = atoi(v->value);
3181 + } else if (!strcasecmp(v->name, "interface")) {
3182 + hcidev_id = atoi(v->value);
3183 + } else if (!strcasecmp(v->name, "gui_default_sip_number")) {
3184 + gui_default_sip_number = v->value;
3185 + } else if (!strcasecmp(v->name, "gui_default_sip_address")) {
3186 + gui_default_sip_address = v->value;
3188 + ast_log(LOG_WARNING, "Unknown config key '%s' in section [general]\n", v->name);
3192 + cat = ast_category_browse(cfg, NULL);
3198 + if (strcasecmp(cat, "general")) {
3199 + blt_dev_t * device = malloc(sizeof(blt_dev_t));
3200 + memset(device, 0, sizeof(blt_dev_t));
3201 + device->sco_running = -1;
3204 + device->outgoing_id = -1;
3205 + device->status = BLT_STATUS_DOWN;
3206 + str2ba(cat, &(device->bdaddr));
3207 + device->name = ast_variable_retrieve(cfg, cat, "name");
3209 + str = ast_variable_retrieve(cfg, cat, "type");
3211 + if (str == NULL) {
3212 + ast_log(LOG_ERROR, "Device [%s] has no role. Specify type=<HS/AG>\n", cat);
3214 + } else if (strcasecmp(str, "HS") == 0) {
3215 + device->role = BLT_ROLE_HS;
3216 + } else if (strcasecmp(str, "AG") == 0) {
3217 + device->role = BLT_ROLE_AG;
3218 + } else if (strcasecmp(str, "GUI") == 0) {
3219 + device->role = BLT_ROLE_GUI;
3221 + ast_log(LOG_ERROR, "Device [%s] has invalid role '%s'\n", cat, str);
3225 + /* XXX:T: Find channel to use using SDP.
3226 + * However, this needs to be non blocking, and I can't see
3227 + * anything in sdp_lib.h that will allow non blocking calls.
3230 + device->channel = 1;
3232 + if ((str = ast_variable_retrieve(cfg, cat, "channel")) != NULL)
3233 + device->channel = atoi(str);
3235 + if ((str = ast_variable_retrieve(cfg, cat, "autoconnect")) != NULL)
3236 + device->autoconnect = (strcasecmp(str, "yes") == 0 || strcmp(str, "1") == 0) ? 1 : 0;
3238 + if ((str = ast_variable_retrieve(cfg, cat, "context")) != NULL)
3239 + device->context = str;
3241 + device->context = "bluetooth";
3243 + device->next = iface_head;
3244 + iface_head = device;
3248 + cat = ast_category_browse(cfg, cat);
3255 +blt_show_peers(int fd, int argc, char *argv[])
3259 + if (ast_mutex_lock(&iface_lock)) {
3260 + ast_log(LOG_ERROR, "Failed to get Iface lock\n");
3261 + ast_cli(fd, "Failed to get iface lock\n");
3262 + return RESULT_FAILURE;
3267 + ast_cli(fd, "BDAddr Name Role Status A/C SCOCon/Fd/Th Sig\n");
3268 + ast_cli(fd, "----------------- ---------- ---- ----------- --- ------------ ---\n");
3272 + ba2str(&(dev->bdaddr), b1);
3273 + ast_cli(fd, "%s %-10s %-4s %-11s %-3s %2d/%02d/%-6ld %s\n",
3275 +// (dev->role == BLT_ROLE_HS) ? "HS" : "AG",
3276 + (dev->role == BLT_ROLE_HS) ? "HS" : (dev->role == BLT_ROLE_AG) ? "AG" : "GUI",
3277 + status2str(dev->status),
3278 + (dev->autoconnect) ? "Yes" : "No",
3282 + (dev->role == BLT_ROLE_AG) ? (dev->service) ? "Yes" : "No" : "N/A"
3287 + ast_mutex_unlock(&iface_lock);
3288 + return RESULT_SUCCESS;
3292 +blt_show_information(int fd, int argc, char *argv[])
3295 + ba2str(&local_bdaddr, b1);
3296 + ast_cli(fd, "-------------------------------------------\n");
3297 + ast_cli(fd, " Version : %s\n", BLT_SVN_REVISION);
3298 + ast_cli(fd, " Monitor PID : %d\n", monitor_pid);
3299 + ast_cli(fd, " RFCOMM AG : Channel %d, FD %d\n", rfcomm_channel_ag, rfcomm_sock_ag);
3300 + ast_cli(fd, " RFCOMM HS : Channel %d, FD %d\n", rfcomm_channel_hs, rfcomm_sock_hs);
3301 + ast_cli(fd, " RFCOMM GUI : Channel %d, FD %d\n", rfcomm_channel_gui, rfcomm_sock_gui);
3302 + ast_cli(fd, " Device : hci%d, MAC Address %s\n", hcidev_id, b1);
3303 + ast_cli(fd, "-------------------------------------------\n");
3304 + return RESULT_SUCCESS;
3308 +blt_ag_sendcmd(int fd, int argc, char *argv[])
3313 + return RESULT_SHOWUSAGE;
3315 + ast_mutex_lock(&iface_lock);
3318 + if (!strcasecmp(argv[2], dev->name))
3322 + ast_mutex_unlock(&iface_lock);
3325 + ast_cli(fd, "Device '%s' does not exist\n", argv[2]);
3326 + return RESULT_FAILURE;
3329 + if ((dev->role != BLT_ROLE_AG) && (dev->role != BLT_ROLE_GUI)) {
3330 + ast_cli(fd, "Device '%s' is not an AG or GUI\n", argv[2]);
3331 + return RESULT_FAILURE;
3334 + if (dev->status == BLT_STATUS_DOWN || dev->status == BLT_STATUS_NEGOTIATING) {
3335 + ast_cli(fd, "Device '%s' is not connected\n", argv[2]);
3336 + return RESULT_FAILURE;
3339 + if (*(argv[3] + strlen(argv[3]) - 1) == '.')
3340 + *(argv[3] + strlen(argv[3]) - 1) = '?';
3342 + ast_cli(fd, "Sending AT command to %s: %s\n", dev->name, argv[3]);
3344 + ast_mutex_lock(&(dev->lock));
3345 + send_atcmd(dev, argv[3]);
3346 + ast_mutex_unlock(&(dev->lock));
3348 + return RESULT_SUCCESS;
3352 +complete_device(char * line, char * word, int pos, int state, int rpos, blt_role_t role)
3361 + ast_mutex_lock(&iface_lock);
3367 + if ((dev->role == role) && (!strncasecmp(word, dev->name, strlen(word)))) {
3368 + if (++which > state)
3376 + ret = strdup(dev->name);
3380 + ast_mutex_unlock(&iface_lock);
3386 +complete_device_2_ag_gui(char * line, char * word, int pos, int state)
3388 + return complete_device(line, word, pos, state, 2, BLT_ROLE_AG);
3391 +static char show_peers_usage[] =
3392 +"Usage: bluetooth show peers\n"
3393 +" List all bluetooth peers and their status\n";
3395 +static struct ast_cli_entry
3397 + { { "bluetooth", "show", "peers", NULL }, blt_show_peers, "List Bluetooth Peers", show_peers_usage };
3400 +static char ag_sendcmd[] =
3401 +"Usage: bluetooth <device> sendcmd <cmd>\n"
3402 +" Sends a AT cmd over the RFCOMM link, and print result (AG only)\n";
3404 +static struct ast_cli_entry
3406 + { { "bluetooth", "sendcmd", NULL }, blt_ag_sendcmd, "Send AG/GUI an AT command", ag_sendcmd, complete_device_2_ag_gui };
3408 +static char show_information[] =
3409 +"Usage: bluetooth show information\n"
3410 +" Lists information about the bluetooth subsystem\n";
3412 +static struct ast_cli_entry
3413 +cli_show_information =
3414 + { { "bluetooth", "show", "information", NULL }, blt_show_information, "List Bluetooth Info", show_information };
3417 +remove_sdp_records(void)
3420 + sdp_session_t * sdp;
3421 + sdp_list_t * attr;
3422 + sdp_record_t * rec;
3424 + uint32_t range = 0x0000ffff;
3426 + if (sdp_record_ag == -1 || sdp_record_gui == -1 || sdp_record_hs == -1)
3429 + ast_log(LOG_DEBUG, "Removing SDP records\n");
3431 + sdp = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
3436 + attr = sdp_list_append(0, &range);
3437 + rec = sdp_service_attr_req(sdp, sdp_record_ag, SDP_ATTR_REQ_RANGE, attr);
3438 + sdp_list_free(attr, 0);
3441 + if (sdp_record_unregister(sdp, rec) == 0)
3444 + rec = sdp_service_attr_req(sdp, sdp_record_gui, SDP_ATTR_REQ_RANGE, attr);
3445 + sdp_list_free(attr, 0);
3448 + if (sdp_record_unregister(sdp, rec) == 0)
3451 + attr = sdp_list_append(0, &range);
3452 + rec = sdp_service_attr_req(sdp, sdp_record_hs, SDP_ATTR_REQ_RANGE, attr);
3453 + sdp_list_free(attr, 0);
3456 + if (sdp_record_unregister(sdp, rec) == 0)
3462 + ast_log(LOG_NOTICE, "Removed SDP records\n");
3464 + ast_log(LOG_ERROR, "Failed to remove SDP records\n");
3469 +__unload_module(void)
3472 +#if ASTERISK_VERSION_NUM <= 010107
3473 + ast_channel_unregister(BLT_CHAN_NAME);
3475 + ast_channel_unregister(&blt_tech);
3478 + if (monitor_thread != AST_PTHREADT_NULL) {
3480 + if (ast_mutex_lock(&monitor_lock)) {
3482 + if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
3483 + pthread_cancel(monitor_thread);
3484 + pthread_kill(monitor_thread, SIGURG);
3485 + fprintf(stderr, "Waiting for monitor thread to join...\n");
3486 + pthread_join(monitor_thread, NULL);
3487 + fprintf(stderr, "joined\n");
3489 + monitor_thread = AST_PTHREADT_STOP;
3490 + ast_mutex_unlock(&monitor_lock);
3494 + ast_log(LOG_WARNING, "Unable to lock the monitor\n");
3501 + ast_unregister_atexit(remove_sdp_records);
3502 + remove_sdp_records();
3509 + sdp_session_t * sess;
3513 + hcidev_id = BLT_DEFAULT_HCI_DEV;
3515 + if (blt_parse_config() != 0) {
3516 + ast_log(LOG_ERROR, "Bluetooth configuration error. Bluetooth Disabled\n");
3517 + return unload_module();
3520 + dd = hci_open_dev(hcidev_id);
3522 + ast_log(LOG_ERROR, "Unable to open interface hci%d: %s.\n", hcidev_id, strerror(errno));
3526 + hci_read_voice_setting(dd, &vs, 1000);
3530 + if (vs != 0x0060) {
3531 + ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060, not 0x%04x\n", vs);
3536 + if ((sched = sched_context_create()) == NULL) {
3537 + ast_log(LOG_WARNING, "Unable to create schedule context\n");
3541 + memset(&local_bdaddr, 0, sizeof(local_bdaddr));
3543 + hci_devba(hcidev_id, &local_bdaddr);
3545 + /* --- Add SDP record --- */
3547 + sess = sdp_connect(&local_bdaddr, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
3549 + if ((rfcomm_sock_ag = rfcomm_listen(&local_bdaddr, rfcomm_channel_ag)) < 0) {
3553 + if ((rfcomm_sock_hs = rfcomm_listen(&local_bdaddr, rfcomm_channel_hs)) < 0)
3556 + if ((rfcomm_sock_gui = rfcomm_listen(&local_bdaddr, rfcomm_channel_gui)) < 0)
3559 + if ((sco_socket = sco_listen(&local_bdaddr)) < 0)
3563 + ast_log(LOG_ERROR, "Failed to connect to SDP server: %s\n", strerror(errno));
3567 + if (sdp_register(sess) != 0) {
3568 + ast_log(LOG_ERROR, "Failed to register HeadsetAudioGateway in SDP\n");
3574 + if (restart_monitor() != 0)
3577 +#if ASTERISK_VERSION_NUM <= 010107
3578 + if (ast_channel_register(BLT_CHAN_NAME, "Bluetooth Driver", BLUETOOTH_FORMAT, blt_request)) {
3580 + if (ast_channel_register(&blt_tech)) {
3582 + ast_log(LOG_ERROR, "Unable to register channel class BTL\n");
3583 + __unload_module();
3587 + ast_cli_register(&cli_show_information);
3588 + ast_cli_register(&cli_show_peers);
3589 + ast_cli_register(&cli_ag_sendcmd);
3591 + ast_register_atexit(remove_sdp_records);
3593 + ast_log(LOG_NOTICE, "Loaded Bluetooth support, %s\n", BLT_SVN_REVISION + 1);
3599 +unload_module(void)
3601 + ast_cli_unregister(&cli_ag_sendcmd);
3602 + ast_cli_unregister(&cli_show_peers);
3603 + ast_cli_unregister(&cli_show_information);
3604 + return __unload_module();
3611 + ast_mutex_lock(&usecnt_lock);
3613 + ast_mutex_unlock(&usecnt_lock);
3617 +char *description()
3619 + return "Bluetooth Channel Driver";
3625 + return ASTERISK_GPL_KEY;
3629 diff -ruN asterisk-1.0.9-old/configs/bluetooth.conf asterisk-1.0.9-new/configs/bluetooth.conf
3630 --- asterisk-1.0.9-old/configs/bluetooth.conf 1970-01-01 01:00:00.000000000 +0100
3631 +++ asterisk-1.0.9-new/configs/bluetooth.conf 2005-09-06 22:51:38.000000000 +0200
3634 +; Channel we listen on as a HS (Headset)
3636 +; Channel we listen on as an AG (AudioGateway)
3638 +; Channel we listen on as GUI
3640 +; hci interface to use (number - e.g '0')
3643 +; RFCOMM channel to connect to. For a HandsSet:
3644 +; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111E
3645 +; or,for an AudioGateway (Phone):
3646 +; sdptool search --bdaddr xx:xx:xx:xx:xx:xx 0x111F
3648 +; Find the 'channel' value under RFCOMM.
3651 +; Automatically connect?
3654 +;example for a SonyEricsson mobile as a GUI device
3655 +[00:0F:DE:6E:77:6B]
3662 +;[00:0E:6D:1A:3D:86]
3668 +[00:0E:A1:01:49:AE]
3674 +;[00:0A:D9:EB:FD:D8]