1 /*
2
3 $Id$
4
5 G N O K I I
6
7 A Linux/Unix toolset and driver for the mobile phones.
8
9 This file is part of gnokii.
10
11 Gnokii is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 Gnokii is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with gnokii; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
25 Copyright (C) 1999-2000 Hugh Blemings & Pavel Jan�k ml.
26 Copyright (C) 2002 Ladis Michl, Marcel Holtmann <marcel@holtmann.org>
27 Copyright (C) 2003 BORBELY Zoltan, Pawel Kot
28
29 PhoneManager Utilities: rfcomm channel autodetection
30 Copyright (C) 2003-2004 Edd Dumbill <edd@usefulinc.com>
31 Copyright (C) 2005-2007 Bastien Nocera <hadess@hadess.net>
32
33 */
34
35 #include "config.h"
36 #include "compat.h"
37 #include "misc.h"
38 #include "gnokii.h"
39 #include "devices/unixbluetooth.h"
40
41 #if defined(HAVE_BLUETOOTH_BLUEZ) || defined(HAVE_BLUETOOTH_NETGRAPH) || defined(HAVE_BLUETOOTH_NETBT)
42
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <sys/time.h>
49 #include <sys/socket.h>
50
51 #ifdef HAVE_BLUETOOTH_NETGRAPH /* FreeBSD / netgraph */
52
53 #include <bluetooth.h>
54 #include </usr/include/sdp.h>
55
56 #define BTPROTO_RFCOMM BLUETOOTH_PROTO_RFCOMM
57 #define BDADDR_ANY NG_HCI_BDADDR_ANY
58 #define GNOKII_SERIAL_PORT_CLASS SDP_SERVICE_CLASS_SERIAL_PORT
59 #define GNOKII_DIALUP_NETWORK_CLASS SDP_SERVICE_CLASS_DIALUP_NETWORKING
60 #define sockaddr_rc sockaddr_rfcomm
61 #define rc_family rfcomm_family
62 #define rc_bdaddr rfcomm_bdaddr
63 #define rc_channel rfcomm_channel
64
65 #else
66 #ifdef HAVE_BLUETOOTH_NETBT /* FreeBSD / netbt */
67
68 #include <bluetooth.h>
69 #include </usr/include/sdp.h>
70
71 #define GNOKII_SERIAL_PORT_CLASS SDP_SERVICE_CLASS_SERIAL_PORT
72 #define GNOKII_DIALUP_NETWORK_CLASS SDP_SERVICE_CLASS_DIALUP_NETWORKING
73 #define sockaddr_rc sockaddr_bt
74 #define rc_family bt_family
75 #define rc_bdaddr bt_bdaddr
76 #define rc_channel bt_channel
77
78 #else /* Linux / BlueZ support */
79
80 #include <bluetooth/bluetooth.h>
81 #include <bluetooth/rfcomm.h>
82 #include <bluetooth/sdp.h>
83 #include <bluetooth/sdp_lib.h>
84
85 #define GNOKII_SERIAL_PORT_CLASS SERIAL_PORT_SVCLASS_ID
86 #define GNOKII_DIALUP_NETWORK_CLASS DIALUP_NET_SVCLASS_ID
87
88 #endif /* HAVE_BLUETOOTH_NETBT */
89 #endif /* HAVE_BLUETOOTH_NETGRAPH */
90
91 #if defined(HAVE_BLUETOOTH_NETGRAPH) || defined(HAVE_BLUETOOTH_NETBT) /* FreeBSD / NetBSD */
92
93 /*
94 ** FreeBSD version of the find_service_channel function.
95 ** Written by Guido Falsi <mad@madpilot.net>.
96 ** Contains code taken from FreeBSD's sdpcontrol and rfcomm_sppd
97 ** programs, which are Copyright (c) 2001-2003 Maksim Yevmenkin
98 ** <m_evmenkin@yahoo.com>.
99 **
100 ** Also thanks to Iain Hibbert for his suggestions.
101 */
102
103 #define attrs_len (sizeof(attrs)/sizeof(attrs[0]))
104 #define NRECS 25 /* request this much records from the SDP server */
105 #define BSIZE 256 /* one attribute buffer size */
106 #define values_len (sizeof(values)/sizeof(values[0]))
107
find_service_channel(bdaddr_t * adapter,bdaddr_t * device,int only_gnapplet,uint16_t svclass_id)108 static int find_service_channel(bdaddr_t *adapter, bdaddr_t *device, int only_gnapplet, uint16_t svclass_id)
109 {
110 int i, channel = -1;
111 char name[64];
112 void *ss = NULL;
113 uint32_t attrs[] = {
114 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
115 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
116 SDP_ATTR_RANGE( SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
117 SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET),
118 };
119 /* Buffer for the attributes */
120 static uint8_t buffer[NRECS * attrs_len][BSIZE];
121 /* SDP attributes */
122 static sdp_attr_t values[NRECS * attrs_len];
123
124 /* Initialize attribute values array */
125 for (i = 0; i < values_len; i ++) {
126 values[i].flags = SDP_ATTR_INVALID;
127 values[i].attr = 0;
128 values[i].vlen = BSIZE;
129 values[i].value = buffer[i];
130 }
131
132 if ((ss = sdp_open(adapter, device)) == NULL)
133 return -1;
134
135 if (sdp_error(ss) != 0)
136 goto end;
137
138 if (sdp_search(ss, 1, &svclass_id, attrs_len, attrs, values_len, values) != 0)
139 goto end;
140
141 for (i = 0; i < values_len; i++) {
142 union {
143 uint8_t uint8;
144 uint16_t uint16;
145 uint32_t uint32;
146 uint64_t uint64;
147 int128_t int128;
148 } value;
149 uint8_t *start, *end;
150 uint32_t type, len;
151
152 if (values[i].flags != SDP_ATTR_OK)
153 break;
154
155 start = values[i].value;
156 end = values[i].value + values[i].vlen;
157
158 switch (values[i].attr) {
159 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
160 SDP_GET8(type, start);
161 switch (type) {
162 case SDP_DATA_SEQ8:
163 SDP_GET8(len, start);
164 break;
165
166 case SDP_DATA_SEQ16:
167 SDP_GET16(len, start);
168 break;
169
170 case SDP_DATA_SEQ32:
171 SDP_GET32(len, start);
172 break;
173
174 default:
175 goto end;
176 break;
177 }
178
179 SDP_GET8(type, start);
180 switch (type) {
181 case SDP_DATA_SEQ8:
182 SDP_GET8(len, start);
183 break;
184
185 case SDP_DATA_SEQ16:
186 SDP_GET16(len, start);
187 break;
188
189 case SDP_DATA_SEQ32:
190 SDP_GET32(len, start);
191 break;
192
193 default:
194 goto end;
195 }
196
197 while (start < end) {
198 SDP_GET8(type, start);
199 switch (type) {
200 case SDP_DATA_UUID16:
201 SDP_GET16(value.uint16, start);
202 break;
203
204 case SDP_DATA_UUID32:
205 SDP_GET32(value.uint32, start);
206 break;
207
208 case SDP_DATA_UUID128:
209 SDP_GET_UUID128(&value.int128, start);
210 break;
211
212 default:
213 goto end;
214 }
215 if (value.uint16 == 3) {
216 SDP_GET8(type, start);
217 switch (type) {
218 case SDP_DATA_UINT8:
219 case SDP_DATA_INT8:
220 SDP_GET8(value.uint8, start);
221 channel = value.uint8;
222 break;
223
224 case SDP_DATA_UINT16:
225 case SDP_DATA_INT16:
226 SDP_GET16(value.uint16, start);
227 channel = value.uint16;
228 break;
229
230 case SDP_DATA_UINT32:
231 case SDP_DATA_INT32:
232 SDP_GET32(value.uint32, start);
233 channel = value.uint32;
234 break;
235
236 default:
237 goto end;
238 }
239 } else {
240 SDP_GET8(type, start);
241 switch (type) {
242 case SDP_DATA_SEQ8:
243 case SDP_DATA_UINT8:
244 case SDP_DATA_INT8:
245 case SDP_DATA_BOOL:
246 SDP_GET8(value.uint8, start);
247 break;
248
249 case SDP_DATA_SEQ16:
250 case SDP_DATA_UINT16:
251 case SDP_DATA_INT16:
252 case SDP_DATA_UUID16:
253 SDP_GET16(value.uint16, start);
254 break;
255
256 case SDP_DATA_SEQ32:
257 case SDP_DATA_UINT32:
258 case SDP_DATA_INT32:
259 case SDP_DATA_UUID32:
260 SDP_GET32(value.uint32, start);
261 break;
262
263 case SDP_DATA_UINT64:
264 case SDP_DATA_INT64:
265 SDP_GET64(value.uint64, start);
266 break;
267
268 case SDP_DATA_UINT128:
269 case SDP_DATA_INT128:
270 SDP_GET128(&value.int128, start);
271 break;
272
273 default:
274 goto end;
275 }
276 }
277 }
278 start += len;
279 break;
280
281 case SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET:
282 if (channel == -1)
283 break;
284
285 SDP_GET8(type, start);
286 switch (type) {
287 case SDP_DATA_STR8:
288 case SDP_DATA_URL8:
289 SDP_GET8(len, start);
290 snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
291 start += len;
292 break;
293
294 case SDP_DATA_STR16:
295 case SDP_DATA_URL16:
296 SDP_GET16(len, start);
297 snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
298 start += len;
299 break;
300
301 case SDP_DATA_STR32:
302 case SDP_DATA_URL32:
303 SDP_GET32(len, start);
304 snprintf(name, sizeof(name), "%*.*s", len, len, (char *) start);
305 start += len;
306 break;
307
308 default:
309 goto end;
310 }
311 if (name == NULL)
312 break;
313
314 if (only_gnapplet != 0) {
315 if (strcmp(name, "gnapplet") == 0)
316 goto end;
317 else {
318 channel = -1;
319 break;
320 }
321 }
322
323 if (strstr(name, "Nokia PC Suite") != NULL) {
324 channel = -1;
325 break;
326 }
327
328 if (strstr(name, "Bluetooth Serial Port") != NULL) {
329 channel = -1;
330 break;
331 }
332
333 if (strstr(name, "m-Router Connectivity") != NULL) {
334 channel = -1;
335 break;
336 }
337
338 goto end;
339 }
340 }
341
342 end:
343 sdp_close(ss);
344 return channel;
345 }
346
347 #else
348 /*
349 * Taken from gnome-phone-manager
350 */
get_rfcomm_channel(sdp_record_t * rec,int only_gnapplet)351 static int get_rfcomm_channel(sdp_record_t *rec, int only_gnapplet)
352 {
353 int channel = -1;
354 sdp_list_t *protos = NULL;
355 sdp_data_t *data;
356 char name[64];
357
358 if (sdp_get_access_protos(rec, &protos) != 0)
359 goto end;
360
361 data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
362 if (data)
363 snprintf(name, sizeof(name), "%.*s", data->unitSize, data->val.str);
364
365 if (name == NULL)
366 goto end;
367
368 /*
369 * If we're only supposed to check for gnapplet, do it here
370 * We ignore it if we're not supposed to check for it.
371 */
372 if (only_gnapplet != 0) {
373 if (strcmp(name, "gnapplet") == 0)
374 channel = sdp_get_proto_port(protos, RFCOMM_UUID);
375 goto end;
376 }
377
378 /*
379 * We can't seem to connect to the PC Suite channel.
380 */
381 if (strstr(name, "Nokia PC Suite") != NULL)
382 goto end;
383 /*
384 * And that type of channel on Nokia Symbian phones doesn't
385 * work either.
386 */
387 if (strstr(name, "Bluetooth Serial Port") != NULL)
388 goto end;
389 /*
390 * Avoid the m-Router channel, same as the PC Suite on Sony Ericsson
391 * phones.
392 */
393 if (strstr(name, "m-Router Connectivity") != NULL)
394 goto end;
395
396 channel = sdp_get_proto_port(protos, RFCOMM_UUID);
397 end:
398 sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, 0);
399 sdp_list_free(protos, 0);
400 return channel;
401 }
402
403 /*
404 * Determine whether the given device supports Serial or Dial-Up Networking,
405 * and if so what the RFCOMM channel number for the service is.
406 */
find_service_channel(bdaddr_t * adapter,bdaddr_t * device,int only_gnapplet,uint16_t svclass_id)407 static int find_service_channel(bdaddr_t *adapter, bdaddr_t *device, int only_gnapplet, uint16_t svclass_id)
408 {
409 sdp_session_t *sdp = NULL;
410 sdp_list_t *search = NULL, *attrs = NULL, *recs = NULL, *tmp;
411 uuid_t browse_uuid, service_id;
412 uint32_t range = 0x0000ffff;
413 int channel = -1;
414
415 sdp = sdp_connect(adapter, device, SDP_RETRY_IF_BUSY);
416 if (!sdp)
417 goto end;
418
419 sdp_uuid16_create(&browse_uuid, PUBLIC_BROWSE_GROUP);
420 sdp_uuid16_create(&service_id, svclass_id);
421 search = sdp_list_append(NULL, &browse_uuid);
422 search = sdp_list_append(search, &service_id);
423
424 attrs = sdp_list_append(NULL, &range);
425
426 if (sdp_service_search_attr_req(sdp, search,
427 SDP_ATTR_REQ_RANGE, attrs,
428 &recs))
429 goto end;
430
431 for (tmp = recs; tmp != NULL; tmp = tmp->next) {
432 sdp_record_t *rec = tmp->data;
433
434 /*
435 * If this service is better than what we've
436 * previously seen, try and get the channel number.
437 */
438 channel = get_rfcomm_channel(rec, only_gnapplet);
439 if (channel > 0)
440 goto end;
441 }
442
443 end:
444 sdp_list_free(recs, (sdp_free_func_t)sdp_record_free);
445 sdp_list_free(search, NULL);
446 sdp_list_free(attrs, NULL);
447 sdp_close(sdp);
448
449 return channel;
450 }
451
452 #endif
453
get_serial_channel(bdaddr_t * device,int only_gnapplet)454 static uint8_t get_serial_channel(bdaddr_t *device, int only_gnapplet)
455 {
456 bdaddr_t src;
457 int channel;
458 uint8_t retval;
459
460 bacpy(&src, BDADDR_ANY);
461
462 channel = find_service_channel(&src, device, only_gnapplet, GNOKII_SERIAL_PORT_CLASS);
463 if (channel < 0)
464 channel = find_service_channel(&src, device, only_gnapplet, GNOKII_DIALUP_NETWORK_CLASS);
465
466 if (channel < 0)
467 retval = 0;
468 else
469 retval = channel;
470 return retval;
471 }
472
473 /* From: http://www.kegel.com/dkftpbench/nonblocking.html */
setNonblocking(int fd)474 static int setNonblocking(int fd)
475 {
476 int retcode, flags;
477
478 #if defined(O_NONBLOCK)
479 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
480 flags = 0;
481 retcode = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
482 #else
483 flags = 1;
484 retcode = ioctl(fd, FIOASYNC, &flags);
485 #endif
486
487 return retcode;
488 }
489
bluetooth_open(const char * addr,uint8_t channel,struct gn_statemachine * state)490 int bluetooth_open(const char *addr, uint8_t channel, struct gn_statemachine *state)
491 {
492 bdaddr_t bdaddr;
493 struct sockaddr_rc raddr;
494 int fd;
495
496 if (str2ba((char *)addr, &bdaddr)) {
497 fprintf(stderr, _("Invalid bluetooth address \"%s\"\n"), addr);
498 return -1;
499 }
500
501 if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
502 perror(_("Can't create socket"));
503 return -1;
504 }
505
506 memset(&raddr, 0, sizeof(raddr));
507 raddr.rc_family = AF_BLUETOOTH;
508 bacpy(&raddr.rc_bdaddr, &bdaddr);
509 dprintf("Channel: %d\n", channel);
510 if (channel < 1) {
511 if (!strcmp(state->config.model, "gnapplet") ||
512 !strcmp(state->config.model, "symbian"))
513 channel = get_serial_channel(&bdaddr, 1);
514 else
515 channel = get_serial_channel(&bdaddr, 0);
516 }
517 dprintf("Channel: %d\n", channel);
518
519 /* If none channel found, fail. */
520 if (channel < 1) {
521 fprintf(stderr, _("Cannot find any appropriate rfcomm channel and none was specified in the config.\n"));
522 close(fd);
523 return -1;
524 }
525
526 dprintf("Using channel: %d\n", channel);
527 raddr.rc_channel = channel;
528
529 if (connect(fd, (struct sockaddr *)&raddr, sizeof(raddr)) < 0) {
530 perror(_("Can't connect"));
531 close(fd);
532 return -1;
533 }
534
535 /* Ignore errors. If the socket was not set in the async way,
536 * we can live with that.
537 */
538 setNonblocking(fd);
539
540 return fd;
541 }
542
bluetooth_close(int fd,struct gn_statemachine * state)543 int bluetooth_close(int fd, struct gn_statemachine *state)
544 {
545 sleep(2);
546 return close(fd);
547 }
548
bluetooth_write(int fd,const __ptr_t bytes,int size,struct gn_statemachine * state)549 int bluetooth_write(int fd, const __ptr_t bytes, int size, struct gn_statemachine *state)
550 {
551 return write(fd, bytes, size);
552 }
553
bluetooth_read(int fd,__ptr_t bytes,int size,struct gn_statemachine * state)554 int bluetooth_read(int fd, __ptr_t bytes, int size, struct gn_statemachine *state)
555 {
556 return read(fd, bytes, size);
557 }
558
bluetooth_select(int fd,struct timeval * timeout,struct gn_statemachine * state)559 int bluetooth_select(int fd, struct timeval *timeout, struct gn_statemachine *state)
560 {
561 fd_set readfds;
562
563 FD_ZERO(&readfds);
564 FD_SET(fd, &readfds);
565
566 return select(fd + 1, &readfds, NULL, NULL, timeout);
567 }
568
569 #endif /* HAVE_BLUETOOTH_BLUEZ || HAVE_BLUETOOTH_NETGRAPH || HAVE_BLUETOOTH_NETBT */
570