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