sdp.c revision 1.1 1 /* $NetBSD: sdp.c,v 1.1 2006/09/10 15:45:56 plunky Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Itronix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of Itronix Inc. may not be used to endorse
16 * or promote products derived from this software without specific
17 * prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 /*
32 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
33 * All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57 #include <sys/cdefs.h>
58 __RCSID("$NetBSD: sdp.c,v 1.1 2006/09/10 15:45:56 plunky Exp $");
59
60 #include <sys/types.h>
61
62 #include <dev/bluetooth/btdev.h>
63 #include <dev/bluetooth/bthidev.h>
64 #include <dev/bluetooth/btsco.h>
65 #include <dev/usb/usb.h>
66 #include <dev/usb/usbhid.h>
67
68 #include <prop/proplib.h>
69
70 #include <bluetooth.h>
71 #include <err.h>
72 #include <errno.h>
73 #include <sdp.h>
74 #include <stdlib.h>
75 #include <usbhid.h>
76
77 #include "btdevctl.h"
78
79 static int32_t parse_l2cap_psm(sdp_attr_t *);
80 static int32_t parse_rfcomm_channel(sdp_attr_t *);
81 static int32_t parse_hid_descriptor(sdp_attr_t *);
82 static int32_t parse_boolean(sdp_attr_t *);
83
84 static int config_hid(prop_dictionary_t);
85 static int config_hset(prop_dictionary_t);
86 static int config_hf(prop_dictionary_t);
87
88 uint16_t hid_services[] = {
89 SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE
90 };
91
92 uint32_t hid_attrs[] = {
93 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
94 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
95 SDP_ATTR_RANGE( SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
96 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
97 SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
98 0x0206), /* HIDDescriptorList */
99 SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */
100 0x0209),
101 SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
102 0x020d)
103 };
104
105 uint16_t hset_services[] = {
106 SDP_SERVICE_CLASS_HEADSET
107 };
108
109 uint32_t hset_attrs[] = {
110 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
111 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
112 };
113
114 uint16_t hf_services[] = {
115 SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY
116 };
117
118 uint32_t hf_attrs[] = {
119 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
120 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
121 };
122
123 #define NUM(v) (sizeof(v) / sizeof(v[0]))
124
125 static struct {
126 const char *name;
127 int (*handler)(prop_dictionary_t);
128 const char *description;
129 uint16_t *services;
130 int nservices;
131 uint32_t *attrs;
132 int nattrs;
133 } cfgtype[] = {
134 {
135 "HID", config_hid, "Human Interface Device",
136 hid_services, NUM(hid_services),
137 hid_attrs, NUM(hid_attrs),
138 },
139 {
140 "HSET", config_hset, "Headset",
141 hset_services, NUM(hset_services),
142 hset_attrs, NUM(hset_attrs),
143 },
144 {
145 "HF", config_hf, "Handsfree",
146 hf_services, NUM(hf_services),
147 hf_attrs, NUM(hf_attrs),
148 },
149 };
150
151 static sdp_attr_t values[8];
152 static uint8_t buffer[NUM(values)][512];
153
154 prop_dictionary_t
155 cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
156 {
157 prop_dictionary_t dict;
158 void *ss;
159 int rv, i;
160
161 dict = prop_dictionary_create();
162 if (dict == NULL)
163 return NULL;
164
165 for (i = 0 ; i < NUM(values) ; i++) {
166 values[i].flags = SDP_ATTR_INVALID;
167 values[i].attr = 0;
168 values[i].vlen = sizeof(buffer[i]);
169 values[i].value = buffer[i];
170 }
171
172 for (i = 0 ; i < NUM(cfgtype) ; i++) {
173 if (strcasecmp(service, cfgtype[i].name) == 0) {
174 ss = sdp_open(laddr, raddr);
175
176 if (ss == NULL || (errno = sdp_error(ss)) != 0)
177 return NULL;
178
179 rv = sdp_search(ss,
180 cfgtype[i].nservices, cfgtype[i].services,
181 cfgtype[i].nattrs, cfgtype[i].attrs,
182 NUM(values), values);
183
184 if (rv != 0) {
185 errno = sdp_error(ss);
186 return NULL;
187 }
188 sdp_close(ss);
189
190 rv = (*cfgtype[i].handler)(dict);
191 if (rv != 0)
192 return NULL;
193
194 return dict;
195 }
196 }
197
198 printf("Known config types:\n");
199 for (i = 0 ; i < NUM(cfgtype) ; i++)
200 printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description);
201
202 exit(EXIT_FAILURE);
203 }
204
205 /*
206 * Configure HID results
207 */
208 static int
209 config_hid(prop_dictionary_t dict)
210 {
211 prop_object_t obj;
212 int32_t control_psm, interrupt_psm,
213 reconnect_initiate, battery_power,
214 normally_connectable, hid_length;
215 uint8_t *hid_descriptor;
216 int i;
217
218 control_psm = -1;
219 interrupt_psm = -1;
220 reconnect_initiate = -1;
221 normally_connectable = 0;
222 battery_power = 0;
223 hid_descriptor = NULL;
224 hid_length = -1;
225
226 for (i = 0; i < NUM(values) ; i++) {
227 if (values[i].flags != SDP_ATTR_OK)
228 continue;
229
230 switch (values[i].attr) {
231 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
232 control_psm = parse_l2cap_psm(&values[i]);
233 break;
234
235 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
236 interrupt_psm = parse_l2cap_psm(&values[i]);
237 break;
238
239 case 0x0205: /* HIDReconnectInitiate */
240 reconnect_initiate = parse_boolean(&values[i]);
241 break;
242
243 case 0x0206: /* HIDDescriptorList */
244 if (parse_hid_descriptor(&values[i]) == 0) {
245 hid_descriptor = values[i].value;
246 hid_length = values[i].vlen;
247 }
248 break;
249
250 case 0x0209: /* HIDBatteryPower */
251 battery_power = parse_boolean(&values[i]);
252 break;
253
254 case 0x020d: /* HIDNormallyConnectable */
255 normally_connectable = parse_boolean(&values[i]);
256 break;
257 }
258 }
259
260 if (control_psm == -1
261 || interrupt_psm == -1
262 || reconnect_initiate == -1
263 || hid_descriptor == NULL
264 || hid_length == -1)
265 return ENOATTR;
266
267 obj = prop_string_create_cstring_nocopy("bthidev");
268 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
269 return errno;
270
271 obj = prop_number_create_integer(control_psm);
272 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVcontrolpsm, obj))
273 return errno;
274
275 obj = prop_number_create_integer(interrupt_psm);
276 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVinterruptpsm, obj))
277 return errno;
278
279 obj = prop_data_create_data(hid_descriptor, hid_length);
280 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj))
281 return errno;
282
283 if (!reconnect_initiate) {
284 obj = prop_bool_create(TRUE);
285 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj))
286 return errno;
287 }
288
289 return 0;
290 }
291
292 /*
293 * Configure HSET results
294 */
295 static int
296 config_hset(prop_dictionary_t dict)
297 {
298 prop_object_t obj;
299 uint32_t channel;
300 int i;
301
302 channel = -1;
303
304 for (i = 0; i < NUM(values) ; i++) {
305 if (values[i].flags != SDP_ATTR_OK)
306 continue;
307
308 switch (values[i].attr) {
309 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
310 channel = parse_rfcomm_channel(&values[i]);
311 break;
312 }
313 }
314
315 if (channel == -1)
316 return ENOATTR;
317
318 obj = prop_string_create_cstring_nocopy("btsco");
319 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
320 return errno;
321
322 obj = prop_number_create_integer(channel);
323 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
324 return errno;
325
326 return 0;
327 }
328
329 /*
330 * Configure HF results
331 */
332 static int
333 config_hf(prop_dictionary_t dict)
334 {
335 prop_object_t obj;
336 uint32_t channel;
337 int i;
338
339 channel = -1;
340
341 for (i = 0 ; i < NUM(values) ; i++) {
342 if (values[i].flags != SDP_ATTR_OK)
343 continue;
344
345 switch (values[i].attr) {
346 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
347 channel = parse_rfcomm_channel(&values[i]);
348 break;
349 }
350 }
351
352 if (channel == -1)
353 return ENOATTR;
354
355 obj = prop_string_create_cstring_nocopy("btsco");
356 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
357 return errno;
358
359 obj = prop_bool_create(TRUE);
360 if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj))
361 return errno;
362
363 obj = prop_number_create_integer(channel);
364 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
365 return errno;
366
367 return 0;
368 }
369
370 /*
371 * Parse [additional] protocol descriptor list for L2CAP PSM
372 *
373 * seq8 len8 2
374 * seq8 len8 2
375 * uuid16 value16 3 L2CAP
376 * uint16 value16 3 PSM
377 * seq8 len8 2
378 * uuid16 value16 3 HID Protocol
379 * ===
380 * 15
381 */
382
383 static int32_t
384 parse_l2cap_psm(sdp_attr_t *a)
385 {
386 uint8_t *ptr = a->value;
387 uint8_t *end = a->value + a->vlen;
388 int32_t type, len, uuid, psm;
389
390 if (end - ptr < 15)
391 return (-1);
392
393 if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
394 SDP_GET8(type, ptr);
395 switch (type) {
396 case SDP_DATA_SEQ8:
397 SDP_GET8(len, ptr);
398 break;
399
400 case SDP_DATA_SEQ16:
401 SDP_GET16(len, ptr);
402 break;
403
404 case SDP_DATA_SEQ32:
405 SDP_GET32(len, ptr);
406 break;
407
408 default:
409 return (-1);
410 }
411 if (ptr + len > end)
412 return (-1);
413 }
414
415 SDP_GET8(type, ptr);
416 switch (type) {
417 case SDP_DATA_SEQ8:
418 SDP_GET8(len, ptr);
419 break;
420
421 case SDP_DATA_SEQ16:
422 SDP_GET16(len, ptr);
423 break;
424
425 case SDP_DATA_SEQ32:
426 SDP_GET32(len, ptr);
427 break;
428
429 default:
430 return (-1);
431 }
432 if (ptr + len > end)
433 return (-1);
434
435 /* Protocol */
436 SDP_GET8(type, ptr);
437 switch (type) {
438 case SDP_DATA_SEQ8:
439 SDP_GET8(len, ptr);
440 break;
441
442 case SDP_DATA_SEQ16:
443 SDP_GET16(len, ptr);
444 break;
445
446 case SDP_DATA_SEQ32:
447 SDP_GET32(len, ptr);
448 break;
449
450 default:
451 return (-1);
452 }
453 if (ptr + len > end)
454 return (-1);
455
456 /* UUID */
457 if (ptr + 3 > end)
458 return (-1);
459 SDP_GET8(type, ptr);
460 switch (type) {
461 case SDP_DATA_UUID16:
462 SDP_GET16(uuid, ptr);
463 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
464 return (-1);
465 break;
466
467 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
468 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
469 default:
470 return (-1);
471 }
472
473 /* PSM */
474 if (ptr + 3 > end)
475 return (-1);
476 SDP_GET8(type, ptr);
477 if (type != SDP_DATA_UINT16)
478 return (-1);
479 SDP_GET16(psm, ptr);
480
481 return (psm);
482 }
483
484 /*
485 * Parse HID descriptor string
486 *
487 * seq8 len8 2
488 * seq8 len8 2
489 * uint8 value8 2
490 * str value 3
491 * ===
492 * 9
493 */
494
495 static int32_t
496 parse_hid_descriptor(sdp_attr_t *a)
497 {
498 uint8_t *ptr = a->value;
499 uint8_t *end = a->value + a->vlen;
500 int32_t type, len, descriptor_type;
501
502 if (end - ptr < 9)
503 return (-1);
504
505 SDP_GET8(type, ptr);
506 switch (type) {
507 case SDP_DATA_SEQ8:
508 SDP_GET8(len, ptr);
509 break;
510
511 case SDP_DATA_SEQ16:
512 SDP_GET16(len, ptr);
513 break;
514
515 case SDP_DATA_SEQ32:
516 SDP_GET32(len, ptr);
517 break;
518
519 default:
520 return (-1);
521 }
522 if (ptr + len > end)
523 return (-1);
524
525 while (ptr < end) {
526 /* Descriptor */
527 SDP_GET8(type, ptr);
528 switch (type) {
529 case SDP_DATA_SEQ8:
530 if (ptr + 1 > end)
531 return (-1);
532 SDP_GET8(len, ptr);
533 break;
534
535 case SDP_DATA_SEQ16:
536 if (ptr + 2 > end)
537 return (-1);
538 SDP_GET16(len, ptr);
539 break;
540
541 case SDP_DATA_SEQ32:
542 if (ptr + 4 > end)
543 return (-1);
544 SDP_GET32(len, ptr);
545 break;
546
547 default:
548 return (-1);
549 }
550
551 /* Descripor type */
552 if (ptr + 1 > end)
553 return (-1);
554 SDP_GET8(type, ptr);
555 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
556 return (-1);
557 SDP_GET8(descriptor_type, ptr);
558
559 /* Descriptor value */
560 if (ptr + 1 > end)
561 return (-1);
562 SDP_GET8(type, ptr);
563 switch (type) {
564 case SDP_DATA_STR8:
565 if (ptr + 1 > end)
566 return (-1);
567 SDP_GET8(len, ptr);
568 break;
569
570 case SDP_DATA_STR16:
571 if (ptr + 2 > end)
572 return (-1);
573 SDP_GET16(len, ptr);
574 break;
575
576 case SDP_DATA_STR32:
577 if (ptr + 4 > end)
578 return (-1);
579 SDP_GET32(len, ptr);
580 break;
581
582 default:
583 return (-1);
584 }
585 if (ptr + len > end)
586 return (-1);
587
588 if (descriptor_type == UDESC_REPORT && len > 0) {
589 a->value = ptr;
590 a->vlen = len;
591
592 return (0);
593 }
594
595 ptr += len;
596 }
597
598 return (-1);
599 }
600
601 /*
602 * Parse boolean value
603 *
604 * bool8 int8
605 */
606
607 static int32_t
608 parse_boolean(sdp_attr_t *a)
609 {
610 if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
611 return (-1);
612
613 return (a->value[1]);
614 }
615
616 /*
617 * Parse protocol descriptor list for the RFCOMM channel
618 *
619 * seq8 len8 2
620 * seq8 len8 2
621 * uuid16 value16 3 L2CAP
622 * seq8 len8 2
623 * uuid16 value16 3 RFCOMM
624 * uint8 value8 2 channel
625 * ===
626 * 14
627 */
628
629 static int32_t
630 parse_rfcomm_channel(sdp_attr_t *a)
631 {
632 uint8_t *ptr = a->value;
633 uint8_t *end = a->value + a->vlen;
634 int32_t type, len, uuid, channel;
635
636 if (end - ptr < 14)
637 return (-1);
638
639 SDP_GET8(type, ptr);
640 switch (type) {
641 case SDP_DATA_SEQ8:
642 SDP_GET8(len, ptr);
643 break;
644
645 case SDP_DATA_SEQ16:
646 SDP_GET16(len, ptr);
647 break;
648
649 case SDP_DATA_SEQ32:
650 SDP_GET32(len, ptr);
651 break;
652
653 default:
654 return (-1);
655 }
656 if (ptr + len > end)
657 return (-1);
658
659 /* Protocol */
660 SDP_GET8(type, ptr);
661 switch (type) {
662 case SDP_DATA_SEQ8:
663 SDP_GET8(len, ptr);
664 break;
665
666 case SDP_DATA_SEQ16:
667 SDP_GET16(len, ptr);
668 break;
669
670 case SDP_DATA_SEQ32:
671 SDP_GET32(len, ptr);
672 break;
673
674 default:
675 return (-1);
676 }
677 if (ptr + len > end)
678 return (-1);
679
680 /* UUID */
681 if (ptr + 3 > end)
682 return (-1);
683 SDP_GET8(type, ptr);
684 switch (type) {
685 case SDP_DATA_UUID16:
686 SDP_GET16(uuid, ptr);
687 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
688 return (-1);
689 break;
690
691 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
692 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
693 default:
694 return (-1);
695 }
696
697 /* Protocol */
698 SDP_GET8(type, ptr);
699 switch (type) {
700 case SDP_DATA_SEQ8:
701 SDP_GET8(len, ptr);
702 break;
703
704 case SDP_DATA_SEQ16:
705 SDP_GET16(len, ptr);
706 break;
707
708 case SDP_DATA_SEQ32:
709 SDP_GET32(len, ptr);
710 break;
711
712 default:
713 return (-1);
714 }
715 if (ptr + len > end)
716 return (-1);
717
718 /* UUID */
719 if (ptr + 3 > end)
720 return (-1);
721 SDP_GET8(type, ptr);
722 switch (type) {
723 case SDP_DATA_UUID16:
724 SDP_GET16(uuid, ptr);
725 if (uuid != SDP_UUID_PROTOCOL_RFCOMM)
726 return (-1);
727 break;
728
729 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
730 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
731 default:
732 return (-1);
733 }
734
735 /* channel */
736 if (ptr + 2 > end)
737 return (-1);
738
739 SDP_GET8(type, ptr);
740 if (type != SDP_DATA_UINT8)
741 return (-1);
742
743 SDP_GET8(channel, ptr);
744
745 return (channel);
746 }
747