2c22aa8e6324a72d72352e57c118d64a68d55dc5
[openwrt/svn-archive/archive.git] / net / asterisk-addons-1.4.x / patches / 011-chan_mobile.patch
1 diff -Nru asterisk-addons-1.4.6.org/build_tools/menuselect-deps.in asterisk-addons-1.4.6/build_tools/menuselect-deps.in
2 --- asterisk-addons-1.4.6.org/build_tools/menuselect-deps.in 2007-05-14 18:22:44.000000000 +0200
3 +++ asterisk-addons-1.4.6/build_tools/menuselect-deps.in 2008-03-06 08:38:14.000000000 +0100
4 @@ -1,2 +1,3 @@
5 +BLUETOOTH=@PBX_BLUETOOTH@
6 MYSQLCLIENT=@PBX_MYSQLCLIENT@
7 ASTERISK=@PBX_ASTERISK@
8 diff -Nru asterisk-addons-1.4.6.org/channels/chan_mobile.c asterisk-addons-1.4.6/channels/chan_mobile.c
9 --- asterisk-addons-1.4.6.org/channels/chan_mobile.c 1970-01-01 01:00:00.000000000 +0100
10 +++ asterisk-addons-1.4.6/channels/chan_mobile.c 2008-03-06 08:38:57.000000000 +0100
11 @@ -0,0 +1,1867 @@
12 +/*
13 + * Asterisk -- An open source telephony toolkit.
14 + *
15 + * Copyright (C) 1999 - 2006, Digium, Inc.
16 + *
17 + * Mark Spencer <markster@digium.com>
18 + *
19 + * See http://www.asterisk.org for more information about
20 + * the Asterisk project. Please do not directly contact
21 + * any of the maintainers of this project for assistance;
22 + * the project provides a web site, mailing lists and IRC
23 + * channels for your use.
24 + *
25 + * This program is free software, distributed under the terms of
26 + * the GNU General Public License Version 2. See the LICENSE file
27 + * at the top of the source tree.
28 + */
29 +
30 +/*! \file
31 + *
32 + * \brief Bluetooth Mobile Device channel driver
33 + *
34 + * \author Dave Bowerman <david.bowerman@gmail.com>
35 + *
36 + * \ingroup channel_drivers
37 + */
38 +
39 +/*** MODULEINFO
40 + <depend>bluetooth</depend>
41 + ***/
42 +
43 +#include <asterisk.h>
44 +
45 +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 416 $")
46 +
47 +#include <stdio.h>
48 +#include <string.h>
49 +#include <sys/socket.h>
50 +#include <sys/time.h>
51 +#include <errno.h>
52 +#include <unistd.h>
53 +#include <stdlib.h>
54 +#include <arpa/inet.h>
55 +#include <fcntl.h>
56 +#include <sys/ioctl.h>
57 +#include <signal.h>
58 +
59 +#include <bluetooth/bluetooth.h>
60 +#include <bluetooth/hci.h>
61 +#include <bluetooth/hci_lib.h>
62 +#include <bluetooth/sdp.h>
63 +#include <bluetooth/sdp_lib.h>
64 +#include <bluetooth/rfcomm.h>
65 +#include <bluetooth/sco.h>
66 +
67 +#include <asterisk/lock.h>
68 +#include <asterisk/channel.h>
69 +#include <asterisk/config.h>
70 +#include <asterisk/logger.h>
71 +#include <asterisk/module.h>
72 +#include <asterisk/pbx.h>
73 +#include <asterisk/options.h>
74 +#include <asterisk/utils.h>
75 +#include <asterisk/linkedlists.h>
76 +#include <asterisk/cli.h>
77 +#include <asterisk/devicestate.h>
78 +#include <asterisk/causes.h>
79 +#include <asterisk/dsp.h>
80 +
81 +#ifndef ast_debug
82 +#define ast_debug(level, ...) do { \
83 + if (option_debug >= (level)) { \
84 + ast_log(LOG_DEBUG, __VA_ARGS__); \
85 + } \
86 +} while (0)
87 +#endif
88 +
89 +#define AST_MODULE "chan_mobile"
90 +
91 +#define MBL_CONFIG "mobile.conf"
92 +
93 +static int prefformat = AST_FORMAT_SLINEAR;
94 +
95 +static int discovery_interval = 60; /* The device discovery interval, default 60 seconds. */
96 +static int sco_socket; /* This is global so it can be closed on module unload outside of the listener thread */
97 +static sdp_session_t *sdp_session;
98 +
99 +enum mbl_type {
100 + MBL_TYPE_PHONE,
101 + MBL_TYPE_HEADSET
102 +};
103 +
104 +enum mbl_state {
105 + MBL_STATE_INIT = 0,
106 + MBL_STATE_INIT1,
107 + MBL_STATE_INIT2,
108 + MBL_STATE_INIT3,
109 + MBL_STATE_INIT4,
110 + MBL_STATE_INIT5,
111 + MBL_STATE_INIT6,
112 + MBL_STATE_PREIDLE,
113 + MBL_STATE_IDLE,
114 + MBL_STATE_DIAL,
115 + MBL_STATE_DIAL1,
116 + MBL_STATE_OUTGOING,
117 + MBL_STATE_RING,
118 + MBL_STATE_RING2,
119 + MBL_STATE_RING3,
120 + MBL_STATE_INCOMING,
121 + MBL_STATE_HANGUP,
122 + MBL_STATE_INSMS,
123 + MBL_STATE_OUTSMS,
124 + MBL_STATE_OUTSMS1,
125 + MBL_STATE_OUTSMS2
126 +};
127 +
128 +struct mbl_pvt {
129 + struct ast_channel *owner; /* Channel we belong to, possibly NULL */
130 + struct ast_frame fr; /* "null" frame */
131 + enum mbl_type type; /* Phone or Headset */
132 + char id[31]; /* The id from mobile.conf */
133 + char bdaddr[18]; /* the bdaddr of the device */
134 + char context[AST_MAX_CONTEXT]; /* the context for incoming calls */
135 + char connected; /* is it connected? */
136 + int rfcomm_port; /* rfcomm port number */
137 + int rfcomm_socket; /* rfcomm socket descriptor */
138 + char rfcomm_buf[256];
139 + int sco_socket; /* sco socket descriptor */
140 + enum mbl_state state; /* monitor thread current state */
141 + pthread_t monitor_thread; /* monitor thread handle */
142 + char sco_in_buf[48 + AST_FRIENDLY_OFFSET];
143 + char sco_out_buf[352];
144 + char *sco_out_ptr;
145 + int sco_out_len;
146 + char dial_number[AST_MAX_EXTENSION]; /* number for the monitor thread to dial */
147 + int dial_timeout;
148 + char ciev_call_0[4]; /* dynamically build reponse strings */
149 + char ciev_call_1[4];
150 + char ciev_callsetup_0[4];
151 + char ciev_callsetup_1[4];
152 + char ciev_callsetup_2[4];
153 + char ciev_callsetup_3[4];
154 + char no_callsetup;
155 + char has_sms;
156 + char sms_txt[161];
157 + struct ast_dsp *dsp;
158 + struct ast_frame *dsp_fr;
159 + int dtmf_skip;
160 + int skip_frames;
161 + char sent_answer;
162 + char hangup_count;
163 + AST_LIST_ENTRY(mbl_pvt) entry;
164 +};
165 +
166 +static AST_LIST_HEAD_STATIC(devices, mbl_pvt);
167 +
168 +/* The discovery thread */
169 +static pthread_t discovery_thread = AST_PTHREADT_NULL;
170 +/* The sco listener thread */
171 +static pthread_t sco_listener_thread = AST_PTHREADT_NULL;
172 +
173 +/* CLI stuff */
174 +static const char show_usage[] =
175 +"Usage: mobile show devices\n"
176 +" Shows the state of Bluetooth Cell / Mobile devices.\n";
177 +
178 +static const char search_usage[] =
179 +"Usage: mobile search\n"
180 +" Searches for Bluetooth Cell / Mobile devices in range.\n";
181 +
182 +static const char rfcomm_usage[] =
183 +"Usage: mobile rfcomm command\n"
184 +" Send command to the rfcomm port.\n";
185 +
186 +static int do_show_devices(int, int, char **);
187 +static int do_search_devices(int, int, char **);
188 +static int do_send_rfcomm(int, int, char **);
189 +
190 +static struct ast_cli_entry mbl_cli[] = {
191 + {{"mobile", "show", "devices", NULL}, do_show_devices, "Show Bluetooth Cell / Mobile devices", show_usage},
192 + {{"mobile", "search", NULL}, do_search_devices, "Search for Bluetooth Cell / Mobile devices", search_usage},
193 + {{"mobile", "rfcomm", NULL}, do_send_rfcomm, "Send commands to the rfcomm port for debugging", rfcomm_usage}
194 +};
195 +
196 +/* App stuff */
197 +static char *app_mblstatus = "MobileStatus";
198 +static char *mblstatus_synopsis = "MobileStatus(Device,Variable)";
199 +static char *mblstatus_desc =
200 +"MobileStatus(Device,Variable)\n"
201 +" Device - Id of mobile device from mobile.conf\n"
202 +" Variable - Variable to store status in will be 1-3.\n"
203 +" In order, Disconnected, Connected & Free, Connected & Busy.\n";
204 +
205 +static char *app_mblsendsms = "MobileSendSMS";
206 +static char *mblsendsms_synopsis = "MobileSendSMS(Device,Dest,Message)";
207 +static char *mblsendsms_desc =
208 +"MobileSendSms(Device,Dest,Message)\n"
209 +" Device - Id of device from mobile.conf\n"
210 +" Dest - destination\n"
211 +" Message - text of the message\n";
212 +
213 +static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause);
214 +static int mbl_call(struct ast_channel *ast, char *dest, int timeout);
215 +static int mbl_hangup(struct ast_channel *ast);
216 +static int mbl_answer(struct ast_channel *ast);
217 +static int mbl_digit_begin(struct ast_channel *ast, char digit);
218 +static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
219 +static struct ast_frame *mbl_read(struct ast_channel *ast);
220 +static int mbl_write(struct ast_channel *ast, struct ast_frame *frame);
221 +static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
222 +static int mbl_devicestate(void *data);
223 +static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num);
224 +
225 +static int rfcomm_write(struct mbl_pvt *pvt, char *buf);
226 +static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout);
227 +static int sco_connect(char *bdaddr);
228 +static int sdp_search(char *addr, int profile);
229 +
230 +static const struct ast_channel_tech mbl_tech = {
231 + .type = "Mobile",
232 + .description = "Bluetooth Mobile Device Channel Driver",
233 + .capabilities = AST_FORMAT_SLINEAR,
234 + .requester = mbl_request,
235 + .call = mbl_call,
236 + .hangup = mbl_hangup,
237 + .answer = mbl_answer,
238 + .send_digit_begin = mbl_digit_begin,
239 + .send_digit_end = mbl_digit_end,
240 + .read = mbl_read,
241 + .write = mbl_write,
242 + .fixup = mbl_fixup,
243 + .devicestate = mbl_devicestate
244 +};
245 +
246 +static int do_show_devices(int fd, int argc, char **argv)
247 +{
248 +
249 + struct mbl_pvt *pvt;
250 +
251 + #define FORMAT "%-15.15s %-17.17s %-9.9s %-5.5s %-3.3s\n"
252 +
253 + ast_cli(fd, FORMAT, "ID", "Address", "Connected", "State", "SMS");
254 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
255 + ast_cli(fd, FORMAT, pvt->id, pvt->bdaddr, pvt->connected?"Yes":"No", (pvt->state == MBL_STATE_IDLE)?"Free":(pvt->state < MBL_STATE_IDLE)?"Init":"Busy", (pvt->has_sms)?"Yes":"No");
256 + }
257 +
258 + return RESULT_SUCCESS;
259 +
260 +}
261 +
262 +static int do_search_devices(int fd, int argc, char **argv)
263 +{
264 +
265 + int hci_socket;
266 + inquiry_info *ii = NULL;
267 + int max_rsp, num_rsp;
268 + int dev_id, len, flags;
269 + int i, phport, hsport;
270 + char addr[19] = {0};
271 + char name[31] = {0};
272 +
273 + #define FORMAT2 "%-17.17s %-30.30s %-6.6s %-7.7s %-4.4s\n"
274 + #define FORMAT3 "%-17.17s %-30.30s %-6.6s %-7.7s %d\n"
275 +
276 + dev_id = hci_get_route(NULL);
277 + hci_socket = hci_open_dev(dev_id);
278 + len = 8;
279 + max_rsp = 255;
280 + flags = IREQ_CACHE_FLUSH;
281 +
282 + ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
283 + num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
284 + if (num_rsp > 0) {
285 + ast_cli(fd, FORMAT2, "Address", "Name", "Usable", "Type", "Port");
286 + for (i = 0; i < num_rsp; i++) {
287 + ba2str(&(ii+i)->bdaddr, addr);
288 + name[0] = 0x00;
289 + if (hci_read_remote_name(hci_socket, &(ii+i)->bdaddr, sizeof(name)-1, name, 0) < 0)
290 + strcpy(name, "[unknown]");
291 + phport = sdp_search(addr, HANDSFREE_AGW_PROFILE_ID);
292 + if (!phport)
293 + hsport = sdp_search(addr, HEADSET_PROFILE_ID);
294 + else
295 + hsport = 0;
296 + ast_cli(fd, FORMAT3, addr, name, (phport > 0 || hsport > 0)?"Yes":"No", (phport > 0)?"Phone":"Headset", (phport > 0)?phport:hsport);
297 + }
298 + } else
299 + ast_cli(fd, "No Bluetooth Cell / Mobile devices found.\n");
300 +
301 + free(ii);
302 +
303 + hci_close_dev(hci_socket);
304 +
305 + return RESULT_SUCCESS;
306 +
307 +}
308 +
309 +static int do_send_rfcomm(int fd, int argc, char **argv)
310 +{
311 +
312 + struct mbl_pvt *pvt;
313 + char buf[128];
314 +
315 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
316 + if (!strcmp(pvt->id, argv[2]))
317 + break;
318 + }
319 +
320 + if (!pvt || !pvt->connected) {
321 + sprintf(buf, "Device %s not found.\n", argv[2]);
322 + ast_cli(fd, buf);
323 + return RESULT_SUCCESS;
324 + }
325 +
326 + sprintf(buf, "%s\r", argv[3]);
327 + rfcomm_write(pvt, buf);
328 +
329 + return RESULT_SUCCESS;
330 +
331 +}
332 +
333 +static int mbl_status_exec(struct ast_channel *ast, void *data)
334 +{
335 +
336 + struct mbl_pvt *pvt;
337 + char *args = NULL, *device = NULL, *variable = NULL;
338 + int stat;
339 + char status[2];
340 +
341 + if (!data)
342 + return -1;
343 +
344 + args = ast_strdupa((char *)data);
345 + device = strsep(&args, "|");
346 + if (device && (device[0] != 0x00)) {
347 + variable = args;
348 + } else
349 + return -1;
350 +
351 + stat = 1;
352 +
353 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
354 + if (!strcmp(pvt->id, device))
355 + break;
356 + }
357 +
358 + if (pvt) {
359 + if (pvt->connected)
360 + stat = 2;
361 + if (pvt->owner)
362 + stat = 3;
363 + }
364 +
365 + sprintf(status, "%d", stat);
366 + pbx_builtin_setvar_helper(ast, variable, status);
367 +
368 + return 0;
369 +
370 +}
371 +
372 +static int mbl_sendsms_exec(struct ast_channel *ast, void *data)
373 +{
374 +
375 + struct mbl_pvt *pvt;
376 + char *args = NULL, *device = NULL, *dest = NULL, *message = NULL;
377 +
378 + if (!data)
379 + return -1;
380 +
381 + args = ast_strdupa((char *)data);
382 + device = strsep(&args, "|");
383 + if (device && (device[0] != 0x00)) {
384 + dest = strsep(&args, "|");
385 + if (dest && (dest[0] != 0x00)) {
386 + message = args;
387 + if (!message || (message[0] == 0x00)) {
388 + ast_log(LOG_ERROR,"NULL Message to be sent-- SMS will not be sent.\n");
389 + return -1;
390 + }
391 + } else {
392 + ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
393 + return -1;
394 + }
395 +
396 + } else {
397 + ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
398 + return -1;
399 + }
400 +
401 +
402 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
403 + if (!strcmp(pvt->id, device))
404 + break;
405 + }
406 +
407 + if (!pvt) {
408 + ast_log(LOG_ERROR,"Bluetooth device %s wasn't found in the list -- SMS will not be sent.\n",device);
409 + return -1;
410 + }
411 +
412 + if (!pvt->connected) {
413 + ast_log(LOG_ERROR,"bluetooth device %s wasn't connected -- SMS will not be sent.\n",device);
414 + return -1;
415 + }
416 +
417 + if (!pvt->has_sms) {
418 + ast_log(LOG_ERROR,"bluetooth device %s doesn't handle SMS -- SMS will not be sent.\n",device);
419 + return -1;
420 + }
421 +
422 + if (pvt->state != MBL_STATE_IDLE) {
423 + ast_log(LOG_ERROR,"bluetooth device %s isn't IDLE -- SMS will not be sent.\n",device);
424 + return -1;
425 + }
426 +
427 + strcpy(pvt->dial_number, dest);
428 + memset(pvt->sms_txt, 0x0, sizeof(pvt->sms_txt));
429 + strncpy(pvt->sms_txt, message, 160);
430 + pvt->state = MBL_STATE_OUTSMS;
431 +
432 + return 0;
433 +
434 +}
435 +
436 +static struct ast_channel *mbl_request(const char *type, int format, void *data, int *cause)
437 +{
438 +
439 + struct ast_channel *chn = NULL;
440 + struct mbl_pvt *pvt;
441 + char *dest_dev = NULL;
442 + char *dest_num = NULL;
443 + int oldformat;
444 +
445 + if (!data) {
446 + ast_log(LOG_WARNING, "Channel requested with no data\n");
447 + *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
448 + return NULL;
449 + }
450 +
451 + oldformat = format;
452 + format &= (AST_FORMAT_SLINEAR);
453 + if (!format) {
454 + ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat);
455 + *cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
456 + return NULL;
457 + }
458 +
459 + dest_dev = ast_strdupa((char *)data);
460 +
461 + dest_num = strchr(dest_dev, '/');
462 + if (dest_num)
463 + *dest_num++ = 0x00;
464 +
465 + /* Find requested device and make sure its connected. */
466 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
467 + if (!strcmp(pvt->id, dest_dev)) {
468 + break;
469 + }
470 + }
471 + if (!pvt || !pvt->connected || pvt->owner) {
472 + ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
473 + *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
474 + return NULL;
475 + }
476 +
477 + if ((pvt->type == MBL_TYPE_PHONE) && !dest_num) {
478 + ast_log(LOG_WARNING, "Cant determine destination number.\n");
479 + *cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
480 + return NULL;
481 + }
482 +
483 + pvt->sco_out_ptr = pvt->sco_out_buf;
484 + pvt->sco_out_len = 0;
485 +
486 + chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
487 + if (!chn) {
488 + ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
489 + *cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
490 + return NULL;
491 + }
492 +
493 + return chn;
494 +
495 +}
496 +
497 +static int mbl_call(struct ast_channel *ast, char *dest, int timeout)
498 +{
499 +
500 + struct mbl_pvt *pvt;
501 + char *dest_dev = NULL;
502 + char *dest_num = NULL;
503 +
504 + dest_dev = ast_strdupa((char *)dest);
505 +
506 + pvt = ast->tech_pvt;
507 +
508 + if (pvt->type == MBL_TYPE_PHONE) {
509 + dest_num = strchr(dest_dev, '/');
510 + if (!dest_num) {
511 + ast_log(LOG_WARNING, "Cant determine destination number.\n");
512 + return -1;
513 + }
514 + *dest_num++ = 0x00;
515 + }
516 +
517 + if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
518 + ast_log(LOG_WARNING, "mbl_call called on %s, neither down nor reserved\n", ast->name);
519 + return -1;
520 + }
521 +
522 + ast_debug(1, "Calling %s on %s\n", dest, ast->name);
523 +
524 + if (pvt->type == MBL_TYPE_PHONE) {
525 + ast_copy_string(pvt->dial_number, dest_num, sizeof(pvt->dial_number));
526 + pvt->state = MBL_STATE_DIAL;
527 + pvt->dial_timeout = (timeout == 0) ? 30 : timeout;
528 + } else {
529 + pvt->state = MBL_STATE_RING;
530 + }
531 +
532 +
533 + return 0;
534 +
535 +}
536 +
537 +static int mbl_hangup(struct ast_channel *ast)
538 +{
539 +
540 + struct mbl_pvt *pvt;
541 +
542 + if (!ast->tech_pvt) {
543 + ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
544 + return 0;
545 + }
546 + pvt = ast->tech_pvt;
547 +
548 + ast_debug(1, "Hanging up device %s.\n", pvt->id);
549 +
550 + ast_channel_lock(ast);
551 + ast->fds[0] = -1;
552 + ast_channel_unlock(ast);
553 +
554 + if (pvt->type == MBL_TYPE_HEADSET && pvt->sco_socket != -1) {
555 + close(pvt->sco_socket);
556 + pvt->sco_socket = -1;
557 + }
558 +
559 + if ((pvt->state == MBL_STATE_INCOMING || pvt->state == MBL_STATE_OUTGOING || pvt->state == MBL_STATE_DIAL1 || pvt->state == MBL_STATE_RING3) && pvt->type == MBL_TYPE_PHONE) {
560 + rfcomm_write(pvt, "AT+CHUP\r");
561 + pvt->state = MBL_STATE_HANGUP;
562 + pvt->hangup_count = 0;
563 + } else
564 + pvt->state = MBL_STATE_IDLE;
565 +
566 + pvt->owner = NULL;
567 + ast->tech_pvt = NULL;
568 + ast_setstate(ast, AST_STATE_DOWN);
569 +
570 + return 0;
571 +
572 +}
573 +
574 +static int mbl_answer(struct ast_channel *ast)
575 +{
576 +
577 + struct mbl_pvt *pvt;
578 +
579 + pvt = ast->tech_pvt;
580 +
581 + rfcomm_write(pvt, "ATA\r");
582 +
583 + ast_setstate(ast, AST_STATE_UP);
584 +
585 + pvt->sent_answer = 1;
586 +
587 + return 0;
588 +
589 +}
590 +
591 +static int mbl_digit_begin(struct ast_channel *chan, char digit)
592 +{
593 +
594 + return 0;
595 +
596 +}
597 +
598 +static int mbl_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
599 +{
600 +
601 + struct mbl_pvt *pvt;
602 + char buf[11];
603 +
604 + pvt = ast->tech_pvt;
605 +
606 + if (pvt->type == MBL_TYPE_HEADSET)
607 + return 0;
608 +
609 + ast_debug(1, "Dialed %c\n", digit);
610 +
611 + switch(digit) {
612 + case '0':
613 + case '1':
614 + case '2':
615 + case '3':
616 + case '4':
617 + case '5':
618 + case '6':
619 + case '7':
620 + case '8':
621 + case '9':
622 + case '*':
623 + case '#':
624 + sprintf(buf, "AT+VTS=%c\r", digit);
625 + rfcomm_write(pvt, buf);
626 + break;
627 + default:
628 + ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
629 + return -1;
630 + }
631 +
632 + return 0;
633 +
634 +}
635 +
636 +/*
637 +
638 + The SCO protocol basically delivers audio in 48 byte 'frames' in slin format.
639 + Here we just package these into an ast_frame and return them.
640 + The SCO connection from the device to Asterisk happens asynchronously, so it is feasible
641 + that Asterisk will call mbl_read() before the device has connected. In that case we just return
642 + a null frame.
643 +
644 +*/
645 +
646 +static struct ast_frame *mbl_read(struct ast_channel *ast)
647 +{
648 +
649 + struct mbl_pvt *pvt = ast->tech_pvt;
650 + int r;
651 + struct ast_frame *f;
652 +
653 + if (!pvt->owner) {
654 + return &ast_null_frame;
655 + }
656 +
657 + if (pvt->state == MBL_STATE_HANGUP) {
658 + return &ast_null_frame;
659 + }
660 +
661 + if (pvt->sco_socket == -1) {
662 + return &ast_null_frame;
663 + }
664 +
665 + pvt->fr.frametype = AST_FRAME_VOICE;
666 + pvt->fr.subclass = AST_FORMAT_SLINEAR;
667 + pvt->fr.data = pvt->sco_in_buf + AST_FRIENDLY_OFFSET;
668 +
669 + if ((r = read(pvt->sco_socket, pvt->fr.data, 48)) == 48) {
670 + if (pvt->skip_frames == 0) {
671 + f = ast_dsp_process(0, pvt->dsp, &pvt->fr);
672 + if (f && (f->frametype == AST_FRAME_DTMF_END)) {
673 + pvt->fr.frametype = AST_FRAME_DTMF_END;
674 + pvt->fr.subclass = f->subclass;
675 + pvt->skip_frames = pvt->dtmf_skip;
676 + }
677 + return &pvt->fr;
678 + } else {
679 + pvt->skip_frames--;
680 + }
681 + } else if (r == -1) {
682 + ast_debug(1, "mbl_read() read error %d.\n", errno);
683 + close(pvt->sco_socket);
684 + pvt->sco_socket = -1;
685 + ast_channel_lock(ast);
686 + ast->fds[0] = -1;
687 + ast_channel_unlock(ast);
688 + } else {
689 + ast_debug(1, "mbl_read() read short frame. (%d)\n", r);
690 + }
691 +
692 + return &ast_null_frame;
693 +
694 +}
695 +
696 +/*
697 +
698 + We need to deliver 48 byte 'frames' of slin format audio to the device.
699 + mbl_write() handles this by buffering short frames until the next time we are called.
700 +
701 +*/
702 +
703 +static int mbl_write(struct ast_channel *ast, struct ast_frame *frame)
704 +{
705 +
706 + struct mbl_pvt *pvt = ast->tech_pvt;
707 + int num_frames, i, r;
708 + char *pfr;
709 +
710 + if (frame->frametype != AST_FRAME_VOICE) {
711 + return 0;
712 + }
713 + if (pvt->sco_socket == -1) {
714 + return 0;
715 + }
716 +
717 + if (pvt->state == MBL_STATE_HANGUP) {
718 + return 0;
719 + }
720 +
721 + if (frame->datalen > sizeof(pvt->sco_out_buf) - pvt->sco_out_len) {
722 + frame->datalen = sizeof(pvt->sco_out_buf) - pvt->sco_out_len;
723 + ast_debug(1, "Overrun on sco_out_buf detected.\n");
724 + }
725 +
726 + memmove(pvt->sco_out_ptr, frame->data, frame->datalen);
727 + pvt->sco_out_len += frame->datalen;
728 + num_frames = pvt->sco_out_len / 48;
729 +
730 + pfr = pvt->sco_out_buf;
731 + for (i=0; i<num_frames; i++) {
732 + if ((r = write(pvt->sco_socket, pfr, 48)) == -1) {
733 + close(pvt->sco_socket);
734 + pvt->sco_socket = -1;
735 + }
736 + pfr += 48;
737 + }
738 +
739 + pvt->sco_out_len = pvt->sco_out_len - (num_frames * 48);
740 + memmove(pvt->sco_out_buf, pfr, pvt->sco_out_len);
741 + pvt->sco_out_ptr = pvt->sco_out_buf + pvt->sco_out_len;
742 +
743 + return 0;
744 +
745 +}
746 +
747 +static int mbl_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
748 +{
749 +
750 + struct mbl_pvt *pvt = oldchan->tech_pvt;
751 +
752 + if (pvt && pvt->owner == oldchan)
753 + pvt->owner = newchan;
754 +
755 + return 0;
756 +
757 +}
758 +
759 +static int mbl_devicestate(void *data)
760 +{
761 +
762 + char *device;
763 + int res = AST_DEVICE_INVALID;
764 + struct mbl_pvt *pvt;
765 +
766 + device = ast_strdupa(S_OR(data, ""));
767 +
768 + ast_debug(1, "Checking device state for device %s\n", device);
769 +
770 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
771 + if (!strcmp(pvt->id, device))
772 + break;
773 + }
774 +
775 + if (pvt) {
776 + if (pvt->connected) {
777 + if (pvt->owner)
778 + res = AST_DEVICE_INUSE;
779 + else
780 + res = AST_DEVICE_NOT_INUSE;
781 + }
782 + }
783 +
784 + return res;
785 +
786 +}
787 +
788 +static struct ast_channel *mbl_new(int state, struct mbl_pvt *pvt, char *cid_num)
789 +{
790 +
791 + struct ast_channel *chn;
792 +
793 + chn = ast_channel_alloc(1, state, 0, 0, 0, 0, 0, 0, "Mobile/%s-%04lx", pvt->id, ast_random() & 0xffff);
794 + if (chn) {
795 + chn->tech = &mbl_tech;
796 + chn->nativeformats = prefformat;
797 + chn->rawreadformat = prefformat;
798 + chn->rawwriteformat = prefformat;
799 + chn->writeformat = prefformat;
800 + chn->readformat = prefformat;
801 + chn->readq.first = NULL;
802 + pvt->fr.frametype = AST_FRAME_VOICE;
803 + pvt->fr.subclass = AST_FORMAT_SLINEAR;
804 + pvt->fr.datalen = 48;
805 + pvt->fr.samples = 24;
806 + pvt->fr.src = "Mobile";
807 + pvt->fr.offset = AST_FRIENDLY_OFFSET;
808 + pvt->fr.mallocd = 0;
809 + pvt->fr.delivery.tv_sec = 0;
810 + pvt->fr.delivery.tv_usec = 0;
811 + pvt->fr.data = pvt->sco_in_buf + AST_FRIENDLY_OFFSET;
812 + chn->tech_pvt = pvt;
813 + if (state == AST_STATE_RING)
814 + chn->rings = 1;
815 + ast_copy_string(chn->context, pvt->context, sizeof(chn->context));
816 + ast_copy_string(chn->exten, "s", sizeof(chn->exten));
817 + ast_string_field_set(chn, language, "en");
818 + if (cid_num)
819 + chn->cid.cid_num = ast_strdup(cid_num);
820 + chn->cid.cid_name = ast_strdup(pvt->id);
821 + pvt->owner = chn;
822 +
823 + }
824 +
825 + return chn;
826 +
827 +}
828 +
829 +static int rfcomm_connect(char *bdaddr, int remote_channel) {
830 +
831 + bdaddr_t dst;
832 + struct sockaddr_rc addr;
833 + int s;
834 +
835 + str2ba(bdaddr, &dst);
836 +
837 + if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
838 + ast_debug(1, "socket() failed (%d).\n", errno);
839 + return -1;
840 + }
841 +
842 + memset(&addr, 0, sizeof(addr));
843 + addr.rc_family = AF_BLUETOOTH;
844 + bacpy(&addr.rc_bdaddr, &dst);
845 + addr.rc_channel = remote_channel;
846 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
847 + ast_debug(1, "connect() failed (%d).\n", errno);
848 + close(s);
849 + return -1;
850 + }
851 +
852 + return s;
853 +
854 +}
855 +
856 +static int rfcomm_write(struct mbl_pvt *pvt, char *buf)
857 +{
858 +
859 + char *p;
860 + ssize_t num_write;
861 + int len;
862 +
863 + ast_debug(1, "rfcomm_write() (%s) [%s]\n", pvt->id, buf);
864 + len = strlen(buf);
865 + p = buf;
866 + while (len > 0) {
867 + if ((num_write = write(pvt->rfcomm_socket, p, len)) == -1) {
868 + ast_debug(1, "rfcomm_write() error [%d]\n", errno);
869 + return 0;
870 + }
871 + len -= num_write;
872 + p += num_write;
873 + }
874 +
875 + return 1;
876 +
877 +}
878 +
879 +/*
880 +
881 + Here we need to return complete '\r' terminated single responses to the devices monitor thread, or
882 + a timeout if nothing is available.
883 + The rfcomm connection to the device is asynchronous, so there is no guarantee that responses will
884 + be returned in a single read() call. We handle this by buffering the input and returning one response
885 + per call, or a timeout if nothing is available.
886 +
887 +*/
888 +
889 +static int rfcomm_read(struct mbl_pvt *pvt, char *buf, char flush, int timeout)
890 +{
891 +
892 + int sel, rlen, slen;
893 + fd_set rfds;
894 + struct timeval tv;
895 + char *p;
896 +
897 + if (!flush) {
898 + if ((p = strchr(pvt->rfcomm_buf, '\r'))) {
899 + *p++ = 0x00;
900 + if (*p == '\n')
901 + p++;
902 + memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf));
903 + *(buf + strlen(pvt->rfcomm_buf)) = 0x00;
904 + memmove(pvt->rfcomm_buf, p, strlen(p));
905 + *(pvt->rfcomm_buf+strlen(p)) = 0x00;
906 + return 1;
907 + }
908 + } else {
909 + pvt->rfcomm_buf[0] = 0x00;
910 + }
911 +
912 + FD_ZERO(&rfds);
913 + FD_SET(pvt->rfcomm_socket, &rfds);
914 +
915 + tv.tv_sec = timeout;
916 + tv.tv_usec = 0;
917 +
918 + if ((sel = select(pvt->rfcomm_socket + 1, &rfds, NULL, NULL, &tv)) > 0) {
919 + if (FD_ISSET(pvt->rfcomm_socket, &rfds)) {
920 + slen = strlen(pvt->rfcomm_buf);
921 + rlen = read(pvt->rfcomm_socket, pvt->rfcomm_buf + slen, sizeof(pvt->rfcomm_buf) - slen - 1);
922 + if (rlen > 0) {
923 + pvt->rfcomm_buf[slen+rlen] = 0x00;
924 + if ((p = strchr(pvt->rfcomm_buf, '\r'))) {
925 + *p++ = 0x00;
926 + if (*p == '\n')
927 + p++;
928 + memmove(buf, pvt->rfcomm_buf, strlen(pvt->rfcomm_buf));
929 + *(buf + strlen(pvt->rfcomm_buf)) = 0x00;
930 + memmove(pvt->rfcomm_buf, p, strlen(p));
931 + *(pvt->rfcomm_buf+strlen(p)) = 0x00;
932 + return 1;
933 + }
934 + } else
935 + return rlen;
936 + }
937 + } else if (sel == 0) { /* timeout */
938 + return 0;
939 + }
940 +
941 + return 1;
942 +
943 +}
944 +
945 +static int sco_connect(char *bdaddr)
946 +{
947 +
948 + bdaddr_t dst;
949 + struct sockaddr_sco addr;
950 + int s;
951 +
952 + str2ba(bdaddr, &dst);
953 +
954 + if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
955 + ast_debug(1, "socket() failed (%d).\n", errno);
956 + return -1;
957 + }
958 +
959 + memset(&addr, 0, sizeof(addr));
960 + addr.sco_family = AF_BLUETOOTH;
961 + bacpy(&addr.sco_bdaddr, &dst);
962 +
963 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
964 + ast_debug(1, "sco connect() failed (%d).\n", errno);
965 + close(s);
966 + return -1;
967 + }
968 +
969 + return s;
970 +
971 +}
972 +
973 +/*
974 +
975 + sdp_search() performs a service discovery on the given device to determine whether
976 + or not it supports the Handsfree Profile.
977 +
978 +*/
979 +
980 +static int sdp_search(char *addr, int profile)
981 +{
982 +
983 + sdp_session_t *session = 0;
984 + bdaddr_t bdaddr;
985 + uuid_t svc_uuid;
986 + uint32_t range = 0x0000ffff;
987 + sdp_list_t *response_list, *search_list, *attrid_list;
988 + int status, port;
989 + sdp_list_t *proto_list;
990 + sdp_record_t *sdprec;
991 +
992 + str2ba(addr, &bdaddr);
993 + port = 0;
994 + session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
995 + if (!session) {
996 + ast_debug(1, "sdp_connect() failed on device %s.\n", addr);
997 + return 0;
998 + }
999 +
1000 + sdp_uuid32_create(&svc_uuid, profile);
1001 + search_list = sdp_list_append(0, &svc_uuid);
1002 + attrid_list = sdp_list_append(0, &range);
1003 + response_list = 0x00;
1004 + status = sdp_service_search_attr_req(session, search_list, SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
1005 + if (status == 0) {
1006 + if (response_list) {
1007 + sdprec = (sdp_record_t *) response_list->data;
1008 + proto_list = 0x00;
1009 + if (sdp_get_access_protos(sdprec, &proto_list) == 0) {
1010 + port = sdp_get_proto_port(proto_list, RFCOMM_UUID);
1011 + sdp_list_free(proto_list, 0);
1012 + }
1013 + sdp_record_free(sdprec);
1014 + sdp_list_free(response_list, 0);
1015 + } else
1016 + ast_debug(1, "No responses returned for device %s.\n", addr);
1017 + } else
1018 + ast_debug(1, "sdp_service_search_attr_req() failed on device %s.\n", addr);
1019 +
1020 +
1021 + sdp_list_free(search_list, 0);
1022 + sdp_list_free(attrid_list, 0);
1023 + sdp_close(session);
1024 +
1025 + return port;
1026 +
1027 +}
1028 +
1029 +/*
1030 +
1031 + sdp_register()
1032 +
1033 + Register GENERIC_AUDIO & HEADSET with the SDP daemon on the Asterisk Box.
1034 + This assists connections to phones/pda's with dud bluetooth stacks like the IMate Jasjam
1035 + and some Nokia's
1036 +
1037 +*/
1038 +
1039 +static sdp_session_t *sdp_register(void)
1040 +{
1041 +
1042 + uint32_t service_uuid_int[] = {0, 0, 0, GENERIC_AUDIO_SVCLASS_ID};
1043 + uint8_t rfcomm_channel = 1;
1044 + const char *service_name = "Asterisk PABX";
1045 + const char *service_dsc = "Asterisk PABX";
1046 + const char *service_prov = "Asterisk";
1047 +
1048 + uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid, svc_class1_uuid, svc_class2_uuid;
1049 + sdp_list_t *l2cap_list = 0, *rfcomm_list = 0, *root_list = 0, *proto_list = 0, *access_proto_list = 0, *svc_uuid_list = 0;
1050 + sdp_data_t *channel = 0;
1051 +
1052 + int err = 0;
1053 + sdp_session_t *session = 0;
1054 +
1055 + sdp_record_t *record = sdp_record_alloc();
1056 +
1057 + sdp_uuid128_create(&svc_uuid, &service_uuid_int);
1058 + sdp_set_service_id(record, svc_uuid);
1059 +
1060 + sdp_uuid32_create(&svc_class1_uuid, GENERIC_AUDIO_SVCLASS_ID);
1061 + sdp_uuid32_create(&svc_class2_uuid, HEADSET_PROFILE_ID);
1062 +
1063 + svc_uuid_list = sdp_list_append(0, &svc_class1_uuid);
1064 + svc_uuid_list = sdp_list_append(svc_uuid_list, &svc_class2_uuid);
1065 + sdp_set_service_classes(record, svc_uuid_list);
1066 +
1067 + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
1068 + root_list = sdp_list_append(0, &root_uuid);
1069 + sdp_set_browse_groups( record, root_list );
1070 +
1071 + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
1072 + l2cap_list = sdp_list_append(0, &l2cap_uuid);
1073 + proto_list = sdp_list_append(0, l2cap_list);
1074 +
1075 + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
1076 + channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
1077 + rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
1078 + sdp_list_append(rfcomm_list, channel);
1079 + sdp_list_append(proto_list, rfcomm_list);
1080 +
1081 + access_proto_list = sdp_list_append(0, proto_list);
1082 + sdp_set_access_protos(record, access_proto_list);
1083 +
1084 + sdp_set_info_attr(record, service_name, service_prov, service_dsc);
1085 +
1086 + if (!(session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY)))
1087 + ast_log(LOG_WARNING, "Failed to connect sdp and create session.\n");
1088 + else
1089 + err = sdp_record_register(session, record, 0);
1090 +
1091 + sdp_data_free(channel);
1092 + sdp_list_free(rfcomm_list, 0);
1093 + sdp_list_free(root_list, 0);
1094 + sdp_list_free(access_proto_list, 0);
1095 + sdp_list_free(svc_uuid_list, 0);
1096 +
1097 + return session;
1098 +
1099 +}
1100 +
1101 +/*
1102 +
1103 + Phone Monitor Thread
1104 +
1105 + This thread is spun once a phone device is discovered and considered capable of being used, i.e. supports Handsfree Profile,
1106 + and its configured in mobile.conf.
1107 + The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls.
1108 +
1109 +*/
1110 +
1111 +static void *do_monitor_phone(void *data)
1112 +{
1113 +
1114 + struct mbl_pvt *pvt = (struct mbl_pvt *)data;
1115 + struct ast_channel *chn;
1116 + char monitor = 1;
1117 + char buf[256];
1118 + char cid_num[AST_MAX_EXTENSION], *pcids, *pcide;
1119 + int s, t, i, smsi;
1120 + int group, group2;
1121 + int callp = 0, callsetupp;
1122 + char brsf, nsmode, *p, *p1;
1123 + char sms_src[13];
1124 + char sms_txt[161];
1125 +
1126 + brsf = nsmode = 0;
1127 +
1128 + if (!rfcomm_write(pvt, "AT+BRSF=4\r"))
1129 + monitor = 0;
1130 +
1131 + while (monitor) {
1132 +
1133 + if (pvt->state == MBL_STATE_DIAL1)
1134 + t = pvt->dial_timeout;
1135 + else if (pvt->state == MBL_STATE_HANGUP)
1136 + t = 2;
1137 + else if (pvt->state == MBL_STATE_OUTSMS1)
1138 + t = 2;
1139 + else if (pvt->state == MBL_STATE_OUTSMS2)
1140 + t = 10;
1141 + else
1142 + t = 1;
1143 +
1144 + s = rfcomm_read(pvt, buf, 0, t);
1145 +
1146 + if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
1147 + ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
1148 + switch (pvt->state) {
1149 + case MBL_STATE_INIT:
1150 + if (strstr(buf, "+BRSF:")) {
1151 + brsf = 1;
1152 + } else if (strstr(buf, "ERROR") && !nsmode) { /* Hmmm, Non-Standard Phone, just continue */
1153 + rfcomm_write(pvt, "AT+CIND=?\r");
1154 + pvt->state++;
1155 + nsmode = 1;
1156 + } else if (strstr(buf, "OK") && brsf) {
1157 + rfcomm_write(pvt, "AT+CIND=?\r");
1158 + pvt->state++;
1159 + }
1160 + break;
1161 + case MBL_STATE_INIT1:
1162 + if (strstr(buf, "+CIND:")) {
1163 + group = callp = callsetupp = 0;
1164 + group2 = 1;
1165 + for (i=0; i<strlen(buf); i++) {
1166 + if (buf[i] == '(')
1167 + group++;
1168 + if (buf[i] == ')') {
1169 + group--;
1170 + if (group == 0)
1171 + group2++;
1172 + }
1173 + if (strstr(buf+i, "\"call\""))
1174 + callp = group2;
1175 + if (strstr(buf+i, "\"call_setup\""))
1176 + callsetupp = group2;
1177 + if (strstr(buf+i, "\"callsetup\""))
1178 + callsetupp = group2;
1179 + }
1180 + sprintf(pvt->ciev_call_0, "%d,0", callp);
1181 + sprintf(pvt->ciev_call_1, "%d,1", callp);
1182 + sprintf(pvt->ciev_callsetup_0, "%d,0", callsetupp);
1183 + sprintf(pvt->ciev_callsetup_1, "%d,1", callsetupp);
1184 + sprintf(pvt->ciev_callsetup_2, "%d,2", callsetupp);
1185 + sprintf(pvt->ciev_callsetup_3, "%d,3", callsetupp);
1186 + if (callsetupp == 0) /* This phone has no call setup indication!! ... */
1187 + pvt->no_callsetup = 1;
1188 + ast_debug(1, "CIEV_CALL=%d CIEV_CALLSETUP=%d\n", callp, callsetupp);
1189 + }
1190 + if (strstr(buf, "OK")) {
1191 + rfcomm_write(pvt, "AT+CIND?\r");
1192 + pvt->state++;
1193 + }
1194 + break;
1195 + case MBL_STATE_INIT2:
1196 + if ((p = strstr(buf, "+CIND:"))) {
1197 + p += 5;
1198 + if (*(p+(callp*2)) == '1') {
1199 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has a call in progress - delaying connection.\n", pvt->id);
1200 + monitor = 0;
1201 + }
1202 + } else if (strstr(buf, "OK")) {
1203 + rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
1204 + pvt->state++;
1205 + }
1206 + break;
1207 + case MBL_STATE_INIT3:
1208 + if (strstr(buf, "OK")) {
1209 + rfcomm_write(pvt, "AT+CLIP=1\r");
1210 + pvt->state++;
1211 + }
1212 + break;
1213 + case MBL_STATE_INIT4:
1214 + if (strstr(buf, "OK")) {
1215 + rfcomm_write(pvt, "AT+CMGF=1\r");
1216 + pvt->state++;
1217 + }
1218 + break;
1219 + case MBL_STATE_INIT5:
1220 + if (strstr(buf, "ERROR")) { /* No SMS Support ! */
1221 + pvt->state = MBL_STATE_PREIDLE;
1222 + } else if (strstr(buf, "OK")) {
1223 + rfcomm_write(pvt, "AT+CNMI=2,1,0,1,0\r");
1224 + pvt->state++;
1225 + }
1226 + break;
1227 + case MBL_STATE_INIT6:
1228 + if (strstr(buf, "OK")) { /* We have SMS Support */
1229 + pvt->has_sms = 1;
1230 + pvt->state = MBL_STATE_PREIDLE;
1231 + } else if (strstr(buf, "ERROR")) {
1232 + pvt->has_sms = 0;
1233 + ast_log(LOG_NOTICE,"Device %s has no bluetooth SMS capability.\n", pvt->id);
1234 + pvt->state = MBL_STATE_PREIDLE;
1235 + }
1236 + break;
1237 + case MBL_STATE_PREIDLE: /* Nothing handled here, wait for timeout, then off we go... */
1238 + break;
1239 + case MBL_STATE_IDLE:
1240 + ast_debug(1, "Device %s %s [%s]\n", pvt->bdaddr, pvt->id, buf);
1241 + if (strstr(buf, "RING")) {
1242 + pvt->state = MBL_STATE_RING;
1243 + } else if (strstr(buf, "+CIEV:")) {
1244 + if (strstr(buf, pvt->ciev_callsetup_3)) { /* User has dialed out on handset */
1245 + monitor = 0; /* We disconnect now, as he is */
1246 + } /* probably leaving BT range... */
1247 + }
1248 + break;
1249 + case MBL_STATE_DIAL: /* Nothing handled here, we need to wait for a timeout */
1250 + break;
1251 + case MBL_STATE_DIAL1:
1252 + if (strstr(buf, "OK")) {
1253 + if (pvt->no_callsetup) {
1254 + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
1255 + } else {
1256 + ast_setstate(pvt->owner, AST_STATE_RINGING);
1257 + }
1258 + pvt->state = MBL_STATE_OUTGOING;
1259 + }
1260 + break;
1261 + case MBL_STATE_OUTGOING:
1262 + if (strstr(buf, "+CIEV")) {
1263 + if (strstr(buf, pvt->ciev_call_0)) { /* call was hung up */
1264 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1265 + } else if (strstr(buf, pvt->ciev_callsetup_3)) { /* b-party ringing */
1266 + ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
1267 + } else if (strstr(buf, pvt->ciev_call_1) && !pvt->no_callsetup) { /* b-party answer */
1268 + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
1269 + }
1270 + }
1271 + break;
1272 + case MBL_STATE_RING:
1273 + cid_num[0] = 0x00;
1274 + if ((pcids = strstr(buf, "+CLIP:"))) {
1275 + if ((pcids = strchr(pcids, '"'))) {
1276 + if ((pcide = strchr(pcids+1, '"'))) {
1277 + strncpy(cid_num, pcids+1, pcide - pcids - 1);
1278 + cid_num[pcide - pcids - 1] = 0x00;
1279 + }
1280 + }
1281 + pvt->sco_out_ptr = pvt->sco_out_buf;
1282 + pvt->sco_out_len = 0;
1283 + pvt->sent_answer = 0;
1284 + chn = mbl_new(AST_STATE_RING, pvt, cid_num);
1285 + if (chn) {
1286 + if (ast_pbx_start(chn)) {
1287 + ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
1288 + ast_hangup(chn);
1289 + } else
1290 + pvt->state = MBL_STATE_RING3;
1291 + } else {
1292 + ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
1293 + rfcomm_write(pvt, "AT+CHUP\r");
1294 + pvt->state = MBL_STATE_IDLE;
1295 + }
1296 + }
1297 + break;
1298 + case MBL_STATE_RING2:
1299 + pvt->sco_out_ptr = pvt->sco_out_buf;
1300 + pvt->sco_out_len = 0;
1301 + pvt->sent_answer = 0;
1302 + chn = mbl_new(AST_STATE_RING, pvt, cid_num);
1303 + if (chn) {
1304 + if (ast_pbx_start(chn)) {
1305 + ast_log(LOG_ERROR, "Unable to start pbx on incoming call.\n");
1306 + ast_hangup(chn);
1307 + } else
1308 + pvt->state = MBL_STATE_RING3;
1309 + } else {
1310 + ast_log(LOG_ERROR, "Unable to allocate channel for incoming call.\n");
1311 + rfcomm_write(pvt, "AT+CHUP\r");
1312 + pvt->state = MBL_STATE_IDLE;
1313 + }
1314 + break;
1315 + case MBL_STATE_RING3:
1316 + if (strstr(buf, "+CIEV")) {
1317 + if (strstr(buf, pvt->ciev_call_1)) {
1318 + if (pvt->sent_answer) { /* We answered */
1319 + pvt->state = MBL_STATE_INCOMING;
1320 + } else { /* User answered on handset!, disconnect */
1321 + pvt->state = MBL_STATE_IDLE;
1322 + ast_log(LOG_NOTICE,"Closing the sco_socket in RING3 with CIEV\n");
1323 + if (pvt->sco_socket > -1)
1324 + close(pvt->sco_socket);
1325 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1326 + }
1327 + }
1328 + if ((strstr(buf, pvt->ciev_callsetup_0) || strstr(buf, pvt->ciev_call_0))) { /* Caller disconnected */
1329 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1330 + }
1331 + }
1332 + break;
1333 + case MBL_STATE_INCOMING:
1334 + if (strstr(buf, "+CIEV")) {
1335 + if (strstr(buf, pvt->ciev_call_0)) {
1336 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1337 + }
1338 + }
1339 + break;
1340 + case MBL_STATE_HANGUP:
1341 + if (strstr(buf, "OK") || strstr(buf, pvt->ciev_call_0)) {
1342 + pvt->state = MBL_STATE_IDLE;
1343 + }
1344 + break;
1345 + case MBL_STATE_INSMS:
1346 + if (strstr(buf, "+CMGR:")) {
1347 + memset(sms_src, 0x00, sizeof(sms_src));
1348 + if ((p = strchr(buf, ','))) {
1349 + if (*(++p) == '"')
1350 + p++;
1351 + if ((p1 = strchr(p, ','))) {
1352 + if (*(--p1) == '"')
1353 + p1--;
1354 + memset(sms_src, 0x00, sizeof(sms_src));
1355 + strncpy(sms_src, p, p1 - p + 1);
1356 + }
1357 + }
1358 + } else if (strstr(buf, "OK")) {
1359 + chn = mbl_new(AST_STATE_DOWN, pvt, NULL);
1360 + strcpy(chn->exten, "sms");
1361 + pbx_builtin_setvar_helper(chn, "SMSSRC", sms_src);
1362 + pbx_builtin_setvar_helper(chn, "SMSTXT", sms_txt);
1363 + if (ast_pbx_start(chn))
1364 + ast_log(LOG_ERROR, "Unable to start pbx on incoming sms.\n");
1365 + pvt->state = MBL_STATE_IDLE;
1366 + } else {
1367 + memset(sms_txt, 0x00, sizeof(sms_txt));
1368 + strncpy(sms_txt, buf, strlen(buf));
1369 + }
1370 + break;
1371 + case MBL_STATE_OUTSMS:
1372 + break;
1373 + case MBL_STATE_OUTSMS1:
1374 + break;
1375 + case MBL_STATE_OUTSMS2:
1376 + if (strstr(buf, "OK")) {
1377 + pvt->state = MBL_STATE_IDLE;
1378 + }
1379 + break;
1380 + }
1381 + /* Unsolicited responses */
1382 +
1383 + if (strstr(buf, "+CMTI:")) { /* SMS Incoming... */
1384 + if ((p = strchr(buf, ','))) {
1385 + p++;
1386 + smsi = atoi(p);
1387 + if (smsi > 0) {
1388 + sprintf(buf, "AT+CMGR=%d\r", smsi);
1389 + rfcomm_write(pvt, buf);
1390 + pvt->state = MBL_STATE_INSMS;
1391 + }
1392 + }
1393 + }
1394 +
1395 + } else if (s == 0) { /* Timeouts */
1396 + if (pvt->state == MBL_STATE_INIT2) { /* Some devices dont respond to AT+CIND? properly. RIM Blackberry 4 example */
1397 + pvt->state++;
1398 + rfcomm_write(pvt, "AT+CMER=3,0,0,1\r");
1399 + } else if (pvt->state == MBL_STATE_INIT3) { /* Some devices dont respond to AT+CMER=3,0,0,1 properly. VK 2020 for example */
1400 + pvt->state++;
1401 + rfcomm_write(pvt, "AT+CLIP=1\r");
1402 + } else if (pvt->state == MBL_STATE_PREIDLE) {
1403 + pvt->connected = 1;
1404 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id);
1405 + pvt->state = MBL_STATE_IDLE;
1406 + } else if (pvt->state == MBL_STATE_DIAL) {
1407 + sprintf(buf, "ATD%s;\r", pvt->dial_number);
1408 + if (!rfcomm_write(pvt, buf)) {
1409 + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
1410 + ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
1411 + pvt->state = MBL_STATE_IDLE;
1412 + } else {
1413 + pvt->state = MBL_STATE_DIAL1;
1414 + }
1415 + } else if (pvt->state == MBL_STATE_DIAL1) {
1416 + ast_log(LOG_ERROR, "Dial failed on %s state %d\n", pvt->owner->name, pvt->state);
1417 + ast_queue_control(pvt->owner, AST_CONTROL_CONGESTION);
1418 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1419 + pvt->state = MBL_STATE_IDLE;
1420 + } else if (pvt->state == MBL_STATE_RING) { /* No CLIP?, bump it */
1421 + pvt->state = MBL_STATE_RING2;
1422 + } else if (pvt->state == MBL_STATE_HANGUP) {
1423 + if (pvt->hangup_count == 6) {
1424 + ast_debug(1, "Device %s failed to hangup after 6 tries, disconnecting.\n", pvt->id);
1425 + monitor = 0;
1426 + }
1427 + rfcomm_write(pvt, "AT+CHUP\r");
1428 + pvt->hangup_count++;
1429 + } else if (pvt->state == MBL_STATE_OUTSMS) {
1430 + sprintf(buf, "AT+CMGS=\"%s\"\r", pvt->dial_number);
1431 + rfcomm_write(pvt, buf);
1432 + pvt->state = MBL_STATE_OUTSMS1;
1433 + } else if (pvt->state == MBL_STATE_OUTSMS1) {
1434 + if (pvt->rfcomm_buf[0] == '>') {
1435 + sprintf(buf, "%s%c", pvt->sms_txt, 0x1a);
1436 + rfcomm_write(pvt, buf);
1437 + pvt->state = MBL_STATE_OUTSMS2;
1438 + } else {
1439 + ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
1440 + pvt->state = MBL_STATE_IDLE;
1441 + }
1442 + } else if (pvt->state == MBL_STATE_OUTSMS2) {
1443 + ast_log(LOG_ERROR, "Failed to send SMS to %s on device %s\n", pvt->dial_number, pvt->id);
1444 + pvt->state = MBL_STATE_IDLE;
1445 + }
1446 + } else if (s == -1) {
1447 + if (option_verbose > 2)
1448 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
1449 + monitor = 0;
1450 + }
1451 +
1452 + }
1453 +
1454 + close(pvt->rfcomm_socket);
1455 + close(pvt->sco_socket);
1456 + pvt->sco_socket = -1;
1457 + pvt->connected = 0;
1458 + pvt->monitor_thread = AST_PTHREADT_NULL;
1459 +
1460 + return NULL;
1461 +
1462 +}
1463 +
1464 +/*
1465 +
1466 + Headset Monitor Thread
1467 +
1468 + This thread is spun once a headset device is discovered and considered capable of being used, i.e. supports Headset Profile,
1469 + and its configured in mobile.conf.
1470 + The thread lives for the lifetime of the bluetooth connection, and handles the 'control' side of all calls.
1471 +
1472 +*/
1473 +
1474 +static void *do_monitor_headset(void *data)
1475 +{
1476 +
1477 + struct mbl_pvt *pvt = (struct mbl_pvt *)data;
1478 + char monitor = 1;
1479 + char buf[256];
1480 + int s, t;
1481 +
1482 + pvt->state = MBL_STATE_PREIDLE;
1483 +
1484 + while (monitor) {
1485 +
1486 + if (pvt->state == MBL_STATE_RING2)
1487 + t = 2;
1488 + else
1489 + t = 1;
1490 + s = rfcomm_read(pvt, buf, 0, t);
1491 +
1492 + if ((s > 0) && (buf[0] != 0x0) && (buf[0] != '\r')) {
1493 + ast_debug(1, "rfcomm_read() (%s) [%s]\n", pvt->id, buf);
1494 + switch (pvt->state) {
1495 + case MBL_STATE_RING2:
1496 + if (strstr(buf, "AT+CKPD=")) {
1497 + ast_channel_lock(pvt->owner);
1498 + pvt->owner->fds[0] = pvt->sco_socket;
1499 + ast_log(LOG_NOTICE,"pvt-sco_socket used for fds in headphone code\n");
1500 + ast_channel_unlock(pvt->owner);
1501 + ast_queue_control(pvt->owner, AST_CONTROL_ANSWER);
1502 + pvt->state = MBL_STATE_INCOMING;
1503 + rfcomm_write(pvt, "\r\n+VGS=13\r\n");
1504 + rfcomm_write(pvt, "\r\n+VGM=13\r\n");
1505 + }
1506 + break;
1507 + case MBL_STATE_INCOMING:
1508 + if (strstr(buf, "AT+CKPD=")) {
1509 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1510 + }
1511 + break;
1512 + default:
1513 + break;
1514 + }
1515 + if (strstr(buf, "AT+VGS=")) {
1516 + rfcomm_write(pvt, "\r\nOK\r\n");
1517 + } else if (strstr(buf, "AT+VGM=")) {
1518 + rfcomm_write(pvt, "\r\nOK\r\n");
1519 + }
1520 + } else if (s == 0) { /* Timeouts */
1521 + if (pvt->state == MBL_STATE_PREIDLE) {
1522 + pvt->connected = 1;
1523 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s initialised and ready.\n", pvt->id);
1524 + pvt->state = MBL_STATE_IDLE;
1525 + } else if (pvt->state == MBL_STATE_RING) {
1526 + pvt->sco_socket = sco_connect(pvt->bdaddr);
1527 + if (pvt->sco_socket > -1) {
1528 + ast_log(LOG_NOTICE,"sco_connect returned -1 in state RING\n");
1529 + ast_setstate(pvt->owner, AST_STATE_RINGING);
1530 + ast_queue_control(pvt->owner, AST_CONTROL_RINGING);
1531 + pvt->state = MBL_STATE_RING2;
1532 + } else {
1533 + ast_queue_control(pvt->owner, AST_CONTROL_HANGUP);
1534 + }
1535 + } else if (pvt->state == MBL_STATE_RING2) {
1536 + rfcomm_write(pvt, "\r\nRING\r\n");
1537 + }
1538 + } else if (s == -1) {
1539 + if (option_verbose > 2)
1540 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has disconnected, reason (%d).\n", pvt->id, errno);
1541 + monitor = 0;
1542 + }
1543 +
1544 + }
1545 +
1546 + return NULL;
1547 +
1548 +}
1549 +
1550 +static int start_monitor(struct mbl_pvt *pvt)
1551 +{
1552 +
1553 + if (pvt->type == MBL_TYPE_PHONE) {
1554 + if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
1555 + pvt->monitor_thread = AST_PTHREADT_NULL;
1556 + return 0;
1557 + }
1558 + } else {
1559 + if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_headset, pvt) < 0) {
1560 + pvt->monitor_thread = AST_PTHREADT_NULL;
1561 + return 0;
1562 + }
1563 + }
1564 +
1565 + return 1;
1566 +
1567 +}
1568 +
1569 +/*
1570 +
1571 + Device Discovery Thread.
1572 +
1573 + This thread wakes every 'discovery_interval' seconds and trys to connect to
1574 + those configured devices which are not connected. This saves the user from having
1575 + to manually connect his/her cell phone to the asterisk box. Once a successful
1576 + connection is made, a monitor thread is spun on the device which lives for the
1577 + lifetime of the connection.
1578 +
1579 +*/
1580 +
1581 +static void *do_discovery(void *data)
1582 +{
1583 +
1584 + struct mbl_pvt *pvt = data;
1585 +
1586 + for (;;) {
1587 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
1588 + if (!pvt->connected) {
1589 + if ((pvt->rfcomm_socket = rfcomm_connect(pvt->bdaddr, pvt->rfcomm_port)) > -1) {
1590 + pvt->state = 0;
1591 + if (start_monitor(pvt)) {
1592 + pvt->connected = 1;
1593 + if (option_verbose > 2)
1594 + ast_verbose(VERBOSE_PREFIX_3 "Bluetooth Device %s has connected.\n", pvt->id);
1595 + }
1596 + }
1597 + }
1598 + }
1599 + /* Go to sleep */
1600 + sleep(discovery_interval);
1601 + }
1602 +
1603 + return NULL;
1604 +}
1605 +
1606 +/*
1607 +
1608 + This thread listens for incoming sco connections.
1609 + Although the Bluetooth Handsfree Profile Specification says that either end may initiate the audio connection,
1610 + in practice some devices (LG TU500) get upset unless they initiate the connection.
1611 + We leave all sco initiation to the device.
1612 + On an inbound sco connection, we need to find the appropriate device, and set the channel fd accordingly.
1613 +
1614 +*/
1615 +
1616 +static void *do_sco_listen(void *data)
1617 +{
1618 +
1619 + int ns;
1620 + bdaddr_t local;
1621 + struct sockaddr_sco addr;
1622 + struct sco_options so;
1623 + socklen_t len;
1624 + int opt = 1;
1625 + char saddr[18];
1626 + socklen_t addrlen;
1627 + struct mbl_pvt *pvt;
1628 +
1629 + hci_devba(0, &local);
1630 +
1631 + if ((sco_socket = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
1632 + ast_log(LOG_ERROR, "Unable to create sco listener socket.\n");
1633 + return NULL;
1634 + }
1635 + memset(&addr, 0, sizeof(addr));
1636 + addr.sco_family = AF_BLUETOOTH;
1637 + bacpy(&addr.sco_bdaddr, &local);
1638 + if (bind(sco_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1639 + ast_log(LOG_ERROR, "Unable to bind sco listener socket. (%d)\n", errno);
1640 + close(sco_socket);
1641 + return NULL;
1642 + }
1643 + if (setsockopt(sco_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
1644 + ast_log(LOG_ERROR, "Unable to setsockopt sco listener socket.\n");
1645 + close(sco_socket);
1646 + return NULL;
1647 + }
1648 + if (listen(sco_socket, 5) < 0) {
1649 + ast_log(LOG_ERROR, "Unable to listen sco listener socket.\n");
1650 + close(sco_socket);
1651 + return NULL;
1652 + }
1653 + while (1) {
1654 + addrlen = sizeof(struct sockaddr);
1655 + ast_log(LOG_NOTICE, "About to accept the sco_socket...\n");
1656 + if ((ns = accept(sco_socket, (struct sockaddr *)&addr, &addrlen)) > -1) {
1657 + ast_log(LOG_NOTICE, "sco_socket returns %d...\n",ns);
1658 + ba2str(&addr.sco_bdaddr, saddr);
1659 +
1660 + len = sizeof(so);
1661 + getsockopt(ns, SOL_SCO, SCO_OPTIONS, &so, &len);
1662 +
1663 + ast_debug(1, "Incoming Audio Connection from device %s MTU is %d\n", saddr, so.mtu);
1664 +
1665 + pvt = NULL;
1666 + AST_LIST_TRAVERSE(&devices, pvt, entry) {
1667 + if (!strcmp(pvt->bdaddr, saddr))
1668 + break;
1669 + }
1670 + if (pvt) {
1671 + ast_log(LOG_NOTICE,"about to close the pvt-sco_socket and set it ns\n");
1672 + if (pvt->sco_socket != -1)
1673 + close(pvt->sco_socket);
1674 + pvt->sco_socket = ns;
1675 + if (pvt->owner) {
1676 + ast_channel_lock(pvt->owner);
1677 + pvt->owner->fds[0] = ns;
1678 + ast_channel_unlock(pvt->owner);
1679 + }
1680 + } else
1681 + ast_debug(1, "Could not find device for incoming Audio Connection.\n");
1682 + }
1683 + else ast_log(LOG_NOTICE, "Accept got a -1....");
1684 + }
1685 +
1686 + return NULL;
1687 +
1688 +}
1689 +
1690 +static int mbl_load_config(void)
1691 +{
1692 +
1693 + struct ast_config *cfg = NULL;
1694 + char *cat = NULL;
1695 + struct ast_variable *var;
1696 + const char *address, *port, *context, *type, *skip;
1697 + struct mbl_pvt *pvt;
1698 +
1699 + cfg = ast_config_load(MBL_CONFIG);
1700 + if (!cfg)
1701 + return 0;
1702 +
1703 + for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
1704 + if (!strcasecmp(var->name, "interval"))
1705 + discovery_interval = atoi(var->value);
1706 + }
1707 +
1708 + cat = ast_category_browse(cfg, NULL);
1709 + while (cat) {
1710 + if (strcasecmp(cat, "general")) {
1711 + ast_debug(1, "Loading device %s.\n", cat);
1712 + address = ast_variable_retrieve(cfg, cat, "address");
1713 + port = ast_variable_retrieve(cfg, cat, "port");
1714 + context = ast_variable_retrieve(cfg, cat, "context");
1715 + ast_log(LOG_NOTICE, "context for non-general category %s was %s\n", cat, context);
1716 + type = ast_variable_retrieve(cfg, cat, "type");
1717 + skip = ast_variable_retrieve(cfg, cat, "dtmfskip");
1718 + if (address && port) {
1719 + if ((pvt = ast_malloc(sizeof(struct mbl_pvt)))) {
1720 + if (type && !strcmp(type, "headset"))
1721 + pvt->type = MBL_TYPE_HEADSET;
1722 + else
1723 + pvt->type = MBL_TYPE_PHONE;
1724 + ast_copy_string(pvt->id, cat, sizeof(pvt->id));
1725 + ast_copy_string(pvt->bdaddr, address, sizeof(pvt->bdaddr));
1726 + ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context));
1727 + pvt->connected = 0;
1728 + pvt->state = MBL_STATE_INIT;
1729 + pvt->rfcomm_socket = -1;
1730 + pvt->rfcomm_port = atoi(port);
1731 + pvt->rfcomm_buf[0] = 0x00;
1732 + pvt->sco_socket = -1;
1733 + pvt->monitor_thread = AST_PTHREADT_NULL;
1734 + pvt->owner = NULL;
1735 + pvt->no_callsetup = 0;
1736 + pvt->has_sms = 0;
1737 + pvt->dsp = ast_dsp_new();
1738 + if (skip) {
1739 + if ((pvt->dtmf_skip = atoi(skip)) == 0)
1740 + pvt->dtmf_skip = 200;
1741 + } else
1742 + pvt->dtmf_skip = 200;
1743 + pvt->skip_frames = 0;
1744 + ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DTMF_DETECT);
1745 + ast_dsp_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
1746 + AST_LIST_INSERT_HEAD(&devices, pvt, entry);
1747 + }
1748 + } else {
1749 + ast_log(LOG_ERROR, "Device %s has no address/port configured. It wont be enabled.\n", cat);
1750 + }
1751 + }
1752 + cat = ast_category_browse(cfg, cat);
1753 + }
1754 +
1755 + ast_config_destroy(cfg);
1756 +
1757 + return 1;
1758 +
1759 +}
1760 +
1761 +static int reload_module(void)
1762 +{
1763 +
1764 + return 0;
1765 +
1766 +}
1767 +
1768 +static int unload_module(void)
1769 +{
1770 +
1771 + struct mbl_pvt *pvt;
1772 +
1773 + /* First, take us out of the channel loop */
1774 + ast_channel_unregister(&mbl_tech);
1775 +
1776 + /* Kill the discovery thread */
1777 + if (discovery_thread != AST_PTHREADT_NULL) {
1778 + pthread_cancel(discovery_thread);
1779 + pthread_join(discovery_thread, NULL);
1780 + }
1781 + /* Kill the sco listener thread */
1782 + if (sco_listener_thread != AST_PTHREADT_NULL) {
1783 + pthread_cancel(sco_listener_thread);
1784 + pthread_join(sco_listener_thread, NULL);
1785 + }
1786 + if ((close(sco_socket) == -1))
1787 + ast_log(LOG_ERROR, "Unable to close sco_socket %d.\n", errno);
1788 +
1789 + /* Unregister the CLI & APP */
1790 + ast_cli_unregister_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
1791 + ast_unregister_application(app_mblstatus);
1792 + ast_unregister_application(app_mblsendsms);
1793 +
1794 + /* Destroy the device list */
1795 + while ((pvt = AST_LIST_REMOVE_HEAD(&devices, entry))) {
1796 + if (pvt->monitor_thread != AST_PTHREADT_NULL) {
1797 + pthread_cancel(pvt->monitor_thread);
1798 + pthread_join(pvt->monitor_thread, NULL);
1799 + }
1800 + if (pvt->sco_socket > -1) {
1801 + close(pvt->sco_socket);
1802 + }
1803 + if (pvt->rfcomm_socket > -1) {
1804 + close(pvt->rfcomm_socket);
1805 + }
1806 + ast_dsp_free(pvt->dsp);
1807 + free(pvt);
1808 + }
1809 +
1810 + if (sdp_session)
1811 + sdp_close(sdp_session);
1812 +
1813 + return 0;
1814 +
1815 +}
1816 +
1817 +static int load_module(void)
1818 +{
1819 +
1820 + int dev_id, s;
1821 + uint16_t vs;
1822 +
1823 + /* Check if we have Bluetooth, no point loading otherwise... */
1824 + dev_id = hci_get_route(NULL);
1825 + s = hci_open_dev(dev_id);
1826 + if (dev_id < 0 || s < 0) {
1827 + ast_log(LOG_ERROR, "No Bluetooth device found. Not loading module.\n");
1828 + return AST_MODULE_LOAD_DECLINE;
1829 + }
1830 +
1831 + hci_read_voice_setting(s, &vs, 1000);
1832 + vs = htobs(vs);
1833 + if (vs != 0x0060) {
1834 + ast_log(LOG_ERROR, "Bluetooth voice setting must be 0x0060 - see hciconfig hci0 voice.\n");
1835 + hci_close_dev(s);
1836 + return AST_MODULE_LOAD_DECLINE;
1837 + }
1838 +
1839 + hci_close_dev(s);
1840 +
1841 + if (!mbl_load_config()) {
1842 + ast_log(LOG_ERROR, "Unable to read config file %s. Not loading module.\n", MBL_CONFIG);
1843 + return AST_MODULE_LOAD_DECLINE;
1844 + }
1845 +
1846 + sdp_session = sdp_register();
1847 +
1848 + /* Spin the discovery thread */
1849 + if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) {
1850 + ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
1851 + return AST_MODULE_LOAD_DECLINE;
1852 + }
1853 + /* Spin the sco listener thread */
1854 + if (ast_pthread_create_background(&sco_listener_thread, NULL, do_sco_listen, NULL) < 0) {
1855 + ast_log(LOG_ERROR, "Unable to create sco listener thread.\n");
1856 + pthread_cancel(discovery_thread);
1857 + pthread_join(discovery_thread, NULL);
1858 + return AST_MODULE_LOAD_DECLINE;
1859 + }
1860 +
1861 + ast_cli_register_multiple(mbl_cli, sizeof(mbl_cli) / sizeof(mbl_cli[0]));
1862 + ast_register_application(app_mblstatus, mbl_status_exec, mblstatus_synopsis, mblstatus_desc);
1863 + ast_register_application(app_mblsendsms, mbl_sendsms_exec, mblsendsms_synopsis, mblsendsms_desc);
1864 +
1865 + /* Make sure we can register our channel type */
1866 + if (ast_channel_register(&mbl_tech)) {
1867 + ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Mobile");
1868 + return AST_MODULE_LOAD_FAILURE;
1869 + }
1870 +
1871 + return 0;
1872 +}
1873 +
1874 +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Bluetooth Mobile Device Channel Driver",
1875 + .load = load_module,
1876 + .unload = unload_module,
1877 + .reload = reload_module,
1878 +);
1879 diff -Nru asterisk-addons-1.4.6.org/configs/mobile.conf.sample asterisk-addons-1.4.6/configs/mobile.conf.sample
1880 --- asterisk-addons-1.4.6.org/configs/mobile.conf.sample 1970-01-01 01:00:00.000000000 +0100
1881 +++ asterisk-addons-1.4.6/configs/mobile.conf.sample 2008-03-06 08:38:14.000000000 +0100
1882 @@ -0,0 +1,30 @@
1883 +;
1884 +; mobile.conf
1885 +;
1886 +
1887 +[general]
1888 +interval=60 ; Number of seconds between trying to connect to devices.
1889 +
1890 +; The following is a list of the devices we deal with.
1891 +; Every device listed below will be available for calls in and out of Asterisk.
1892 +; Discovered devices not in this list are not available.
1893 +; Use the CLI command 'mobile search' to discover devices.
1894 +; Use the CLI command 'mobile show devices' to see device status.
1895 +;
1896 +; To place out through a cell phone use Dial(Mobile/[device]/NNN.....) in your dialplan.
1897 +; To call a headset use Dial(Mobile/[device]).
1898 +
1899 +;[dave]
1900 +;address=00:12:56:90:6E:00
1901 +;port=4
1902 +;context=incoming-mobile
1903 +
1904 +;[blackberry]
1905 +;address=00:0F:86:0E:AE:42
1906 +;port=2
1907 +;context=incoming-mobile
1908 +
1909 +;[headset]
1910 +;address=00:0B:9E:11:74:A5
1911 +;port=1
1912 +;type=headset
1913 diff -Nru asterisk-addons-1.4.6.org/configure.ac asterisk-addons-1.4.6/configure.ac
1914 --- asterisk-addons-1.4.6.org/configure.ac 2008-02-13 23:58:11.000000000 +0100
1915 +++ asterisk-addons-1.4.6/configure.ac 2008-03-06 08:38:14.000000000 +0100
1916 @@ -161,13 +161,14 @@
1917 # from here on down, library checking should be done in alphabetical order
1918 # by the --with option name, to make things easier for the users :-)
1919
1920 +AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth Support], [bluetooth])
1921 AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
1922 AST_EXT_LIB_SETUP([NCURSES], [ncurses], [ncurses])
1923 AST_EXT_LIB_SETUP([MYSQLCLIENT], [mysqlclient], [mysqlclient])
1924 AST_EXT_LIB_SETUP([ASTERISK], [asterisk], [asterisk])
1925
1926 +AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h])
1927 AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
1928 -
1929 AST_EXT_LIB_CHECK([NCURSES], [ncurses], [initscr], [curses.h])
1930
1931 MYSQL_CONFIG=No
1932 diff -Nru asterisk-addons-1.4.6.org/doc/chan_mobile.txt asterisk-addons-1.4.6/doc/chan_mobile.txt
1933 --- asterisk-addons-1.4.6.org/doc/chan_mobile.txt 1970-01-01 01:00:00.000000000 +0100
1934 +++ asterisk-addons-1.4.6/doc/chan_mobile.txt 2008-03-06 08:38:14.000000000 +0100
1935 @@ -0,0 +1,262 @@
1936 +chan_mobile
1937 +
1938 +Asterisk Channel Driver to allow Bluetooth Cell/Mobile Phones to be used as FXO devices, and Headsets as FXS devices.
1939 +
1940 +Features :-
1941 +
1942 +Multiple cell phones can be connected.
1943 +Multiple headsets can be connected.
1944 +Asterisk automatically connects to each configured cell phone / headset when it comes in range.
1945 +CLI command to discover bluetooth devices.
1946 +Inbound calls on the cell network to the cell phones are handled by Asterisk, just like inbound calls on a Zap channel.
1947 +CLI passed through on inbound calls.
1948 +Dial outbound on a cell phone using Dial(Mobile/device/nnnnnnn) in the dialplan.
1949 +Dial a headset using Dial(Mobile/device) in the dialplan.
1950 +Application MobileStatus can be used in the dialplan to see if a cell phone / headset is connected.
1951 +Supports devicestate for dialplan hinting.
1952 +Supports Inbound and Outbound SMS.
1953 +
1954 +Using chan_mobile :-
1955 +
1956 +In order to use chan_mobile, you must have a working bluetooth subsystem on your Asterisk box.
1957 +This means a working bluetooth adapter, and the BlueZ packages.
1958 +
1959 +Any bluetooth adapter supported by the Linux kernel will do, including usb bluetooth dongles.
1960 +
1961 +The BlueZ package you need is bluez-utils. If you are using a GUI then you might want to install bluez-pin also.
1962 +You also need libbluetooth, and libbluetooth-dev if you are compiling Asterisk from source.
1963 +
1964 +You need to get bluetooth working with your phone before attempting to use chan_mobile.
1965 +This means 'pairing' your phone with your Asterisk box. I dont describe how to do this here as the process
1966 +differs from distro to distro. You only need to pair once.
1967 +
1968 +However, the easist way to pair, is to use you cell phone to search for bluetooth devices, select your Asterisk box
1969 +and enter the requested PIN.
1970 +
1971 +See www.bluez.org for other details about setting up Bluetooth under Linux.
1972 +
1973 +Assuming you have bluetooth working ok:-
1974 +
1975 +Load chan_mobile.so
1976 +
1977 +Search for your bluetooth devices using the CLI command 'mobile search'. Be patient with this command as
1978 +it will take 8 - 10 seconds to do the discovery.
1979 +
1980 +Headsets will generally have to be put into 'pairing' mode before they will show up here.
1981 +
1982 +This will return something like the following :-
1983 +
1984 +*CLI> mobile search
1985 +Address Name Usable Type Port
1986 +00:12:56:90:6E:00 LG TU500 Yes Phone 4
1987 +00:80:C8:35:52:78 Toaster No Headset 0
1988 +00:0B:9E:11:74:A5 Hello II Plus Yes Headset 1
1989 +00:0F:86:0E:AE:42 Daves Blackberry Yes Phone 7
1990 +
1991 +This is a list of all bluetooth devices seen and whether or not they are usable with chan_cellphone.
1992 +The Address field contains the 'bd address' of the device. This is like an ethernet mac address.
1993 +The Name field is whatever is configured into the device as its name.
1994 +The Usable field tells you whether or not the device supports the Bluetooth Handsfree Profile or Headset profile.
1995 +The Type field tells you whether the device is usable as a Phone line (FXO) or a headset (FXS)
1996 +The Port field is the number to put in the configuration file.
1997 +
1998 +Choose which device(s) you want to use and edit /etc/asterisk/mobile.conf. There is a sample included
1999 +with the Asterisk source under configs/mobile.conf.sample.
2000 +
2001 +Assuming we want to use the devices above, mobile.conf needs to look like this :-
2002 +
2003 +===================================================================================
2004 +;
2005 +; mobile.conf
2006 +;
2007 +
2008 +[general]
2009 +interval=60 ; Number of seconds between trying to connect to devices.
2010 +
2011 +; The following is a list of the devices we deal with.
2012 +; Every device listed below will be available for calls in and out of Asterisk.
2013 +; Discovered devices not in this list are not available.
2014 +; Use the CLI command 'mobile search' to discover devices.
2015 +; Use the CLI command 'mobile show devices' to see device status.
2016 +;
2017 +; To place a call use Dial(Mobile/[device]/NNN.....) in your dialplan.
2018 +
2019 +[dave]
2020 +address=00:12:56:90:6E:00
2021 +port=4
2022 +context=incoming-mobile
2023 +
2024 +[headset]
2025 +address=00:0B:9E:11:74:A5
2026 +port=1
2027 +type=headset
2028 +===================================================================================
2029 +
2030 +Be sure to configure the right bd address and port number from the search. If you want inbound
2031 +calls on a device to go to a specific context, add a context= line, otherwise the default will
2032 +be used. The 'id' of the device [bitinbrackets] can be anything you like, just make the unique.
2033 +
2034 +If your are configuring a Headset be sure to include the type=headset line, if left out it defaults
2035 +to phone.
2036 +
2037 +Having done this, unload chan_mobile and load it again.
2038 +
2039 +The CLI command 'mobile show devices' can be used at any time to show the status of configured devices,
2040 +and whether or not the device is capable of sending / receiving SMS via bluetooth.
2041 +
2042 +*CLI> mobile show devices
2043 +ID Address Connected State SMS
2044 +blackberry 00:0F:86:0E:AE:42 Yes Free Yes
2045 +dave 00:12:56:90:6E:00 Yes Free No
2046 +headset 00:0B:9E:11:74:A5 Yes Free No
2047 +*CLI>
2048 +
2049 +
2050 +All being well Asterisk will now try and establish a connection to each configured device. If it cant
2051 +it will retry after 'interval' seconds, infinately.
2052 +
2053 +This means that as your cell phone comes into range and goes out of range, Asterisk will automatically
2054 +connect and disconnect from it. You dont need to worry about it.
2055 +
2056 +As each phone is connected you will see a message on the Asterisk console :-
2057 +
2058 + Loaded chan_mobile.so => (Bluetooth Mobile Device Channel Driver)
2059 + -- Bluetooth Device blackberry has connected.
2060 + -- Bluetooth Device dave has connected.
2061 +
2062 +If someone calls your cell phone now, Asterisk will handle the call and it will be sent into the
2063 +context you specified, or the default context. Mostly likely this means some SIP phone somewhere will
2064 +ring, pick it up and take the call.
2065 +
2066 +To make outbound calls, add something to you Dialplan like the following :- (modify to suit)
2067 +
2068 +; Calls via TU500
2069 +exten => _9X.,1,Dial(Mobile/dave/${EXTEN:1},45)
2070 +exten => _9X.,n,Hangup
2071 +; Calls via Blackberry
2072 +exten => _8X.,1,Dial(Mobile/blackberry/${EXTEN:1},45)
2073 +exten => _8X.,n,Hangup
2074 +
2075 +Pick up a SIP phone and dial 9<number of pizza shop> and the call vill go via the device 'dave' in
2076 +mobile.conf.
2077 +
2078 +To incoming calls to a headset do something like this :-
2079 +
2080 +[incoming-context]
2081 +exten => s,1,Dial(Mobile/headset,30)
2082 +exten => s,n,Hangup()
2083 +
2084 +To dial out on a headset, you need to use some other mechanism, because the headset is not likely
2085 +to have all the needed buttons on it. res_clioriginate is good for this :-
2086 +
2087 +*CLI> originate Mobile/headset extension NNNNN@context
2088 +
2089 +This will call your headset, once you answer Asterisk will call NNNNN at context context
2090 +
2091 +Dialplan hints :-
2092 +
2093 +chan_mobile supports 'device status' so you can do somthing like
2094 +
2095 +exten => 1234,hint,SIP/30&Mobile/dave&Mobile/blackberry
2096 +
2097 +
2098 +MobileStatus Application :-
2099 +
2100 +chan_mobile also registers an application named MobileStatus. You can use this in your Dialplan
2101 +to determine the 'state' of a device.
2102 +
2103 +For example, suppose you wanted to call dave's extension, but only if he was in the office. You could
2104 +test to see if his cell phone was attached to Asterisk, if it is dial his extension, otherwise dial his
2105 +cell phone.
2106 +
2107 +exten => 40,1,MobileStatus(dave,DAVECELL)
2108 +exten => 40,2,GotoIf($["${DAVECELL}" = "1"]?3:5)
2109 +exten => 40,3,Dial(ZAP/g1/0427466412,45,tT)
2110 +exten => 40,4,Hangup
2111 +exten => 40,5,Dial(SIP/40,45,tT)
2112 +exten => 40,6,Hangup
2113 +
2114 +MobileStatus sets the value of the given variable to :-
2115 +
2116 +1 = Disconnected. i.e. Device not in range of Asterisk, or turned off etc etc
2117 +2 = Connected and Not on a call. i.e. Free
2118 +3 = Connected and on a call. i.e. Busy
2119 +
2120 +
2121 +SMS Sending / Receiving
2122 +
2123 +If Asterisk has detected your cell phone is capable of SMS via bluetooth, you will be able to send and
2124 +receive SMS.
2125 +
2126 +Incoming SMS's cause Asterisk to create an inbound call to the context you defined in mobile.conf or the default
2127 +context if you did not define one. The call will start at extension 'sms'. Two channel variables will be available,
2128 +SMSSRC = the number of the originator of the SMS and SMSTXT which is the text of the SMS.
2129 +This is not a voice call, so grab the values of the variables and hang the call up.
2130 +
2131 +So, to handle incoming SMS's, do something like the following in your dialplan
2132 +
2133 +[incoming-mobile]
2134 +exten => sms,1,Verbose(Incoming SMS from ${SMSSRC} ${SMSTXT})
2135 +exten => sms,n,Hangup()
2136 +
2137 +The above will just print the message on the console.
2138 +
2139 +If you use res_jabber, you could do something like this :-
2140 +
2141 +[incoming-mobile]
2142 +exten => sms,1,JabberSend(transport,user@jabber.somewhere.com,SMS from ${SMSRC} ${SMSTXT})
2143 +exten => sms,2,Hangup()
2144 +
2145 +To send an SMS, use the application MobileSendSMS like the following :-
2146 +
2147 +exten => 99,1,MobileSendSMS(dave,0427123456,Hello World)
2148 +
2149 +This will send 'Hello World' via device 'dave' to '0427123456'
2150 +
2151 +
2152 +DTMF Debouncing :-
2153 +
2154 +DTMF detection varies from phone to phone. There is a configuration variable that allows you to tune
2155 +this to your needs. e.g. in mobile.conf
2156 +
2157 +[dave]
2158 +address=00:12:56:90:6E:00
2159 +port=4
2160 +context=incoming-mobile
2161 +dtmfskip=50
2162 +
2163 +change dtmfskip to suit your phone. The default is 200. The larger the number, the more chance of missed DTMF.
2164 +The smaller the number the more chance of multiple digits being detected.
2165 +
2166 +
2167 +Debugging :-
2168 +
2169 +Different phone manufacturers have different interpretations of the Bluetooth Handsfree Profile Spec.
2170 +This means that not all phones work the same way, particularly in the connection setup / initialisation
2171 +sequence. I've tried to make chan_cellphone as general as possible, but it may need modification to
2172 +support some phone i've never tested.
2173 +
2174 +The RIM Blackberry 7250 works extremely well. So does the LG TU500.
2175 +
2176 +Some phones, most notably Sony Ericsson 'T' series, dont quite conform to the Bluetooth HFP spec.
2177 +chan_mobile will detect these and adapt accordingly. The T-610 and T-630 have been tested and
2178 +work fine.
2179 +
2180 +If your phone doesnt behave has expected, turn on Asterisk debugging with 'core set debug 1'.
2181 +
2182 +This will log a bunch of debug messages indicating what the phone is doing, importantly the rfcomm
2183 +conversation between Asterisk and the phone. This can be used to sort out what your phone is doing
2184 +and make chan_mobile support it.
2185 +
2186 +Be aware also, that just about all cell phones behave differently. For example my LG TU500 wont dial unless
2187 +the phone is a the 'idle' screen. i.e. if the phone is showing a 'menu' on the display, when you dial via
2188 +Asterisk, the call will not work. chan_mobile handles this, but there may be other phones that do
2189 +other things too...
2190 +
2191 +Important: Watch what your cell phone is doing the first few times. Asterisk wont make random calls but
2192 +if chan_mobile fails to hangup for some reason and you get a huge bill from your telco, dont blame me.
2193 +
2194 +
2195 +Feedback, Support, Please can you make Cell Phone X work... etc :-
2196 +
2197 +email me at david.bowerman at gmail.com or dseeb_ on #asterisk & #asterisk-dev irc.
2198 diff -Nru asterisk-addons-1.4.6.org/Makefile asterisk-addons-1.4.6/Makefile
2199 --- asterisk-addons-1.4.6.org/Makefile 2008-02-13 23:58:11.000000000 +0100
2200 +++ asterisk-addons-1.4.6/Makefile 2008-03-06 08:38:14.000000000 +0100
2201 @@ -215,6 +215,8 @@
2202
2203 gmenuconfig: gmenuselect
2204
2205 +menuconfig: menuselect
2206 +
2207 menuselect: menuselect/menuselect menuselect-tree
2208 -@menuselect/menuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && echo "menuselect changes saved!" || echo "menuselect changes NOT saved!"
2209
2210 diff -Nru asterisk-addons-1.4.6.org/makeopts.in asterisk-addons-1.4.6/makeopts.in
2211 --- asterisk-addons-1.4.6.org/makeopts.in 2008-02-13 23:58:11.000000000 +0100
2212 +++ asterisk-addons-1.4.6/makeopts.in 2008-03-06 08:38:14.000000000 +0100
2213 @@ -34,6 +34,9 @@
2214 sharedstatedir = @sharedstatedir@
2215 sysconfdir = @sysconfdir@
2216
2217 +BLUETOOTH_LIB=@BLUETOOTH_LIB@
2218 +BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
2219 +
2220 CURSES_LIB=@CURSES_LIB@
2221 CURSES_INCLUDE=@CURSES_INCLUDE@
2222