xref: /netbsd-src/usr.sbin/btdevctl/sdp.c (revision 9ee9e0d7de4c59c936a17df52be682915dc66f43)
1 /*	$NetBSD: sdp.c,v 1.6 2009/04/15 00:35:04 lukem 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@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.6 2009/04/15 00:35:04 lukem 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 static struct {
124 	const char		*name;
125 	int			(*handler)(prop_dictionary_t);
126 	const char		*description;
127 	uint16_t		*services;
128 	int			nservices;
129 	uint32_t		*attrs;
130 	int			nattrs;
131 } cfgtype[] = {
132     {
133 	"HID",		config_hid,	"Human Interface Device",
134 	hid_services,	__arraycount(hid_services),
135 	hid_attrs,	__arraycount(hid_attrs),
136     },
137     {
138 	"HSET",		config_hset,	"Headset",
139 	hset_services,	__arraycount(hset_services),
140 	hset_attrs,	__arraycount(hset_attrs),
141     },
142     {
143 	"HF",		config_hf,	"Handsfree",
144 	hf_services,	__arraycount(hf_services),
145 	hf_attrs,	__arraycount(hf_attrs),
146     },
147 };
148 
149 static sdp_attr_t	values[8];
150 static uint8_t		buffer[__arraycount(values)][512];
151 
152 prop_dictionary_t
153 cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
154 {
155 	prop_dictionary_t dict;
156 	void *ss;
157 	int rv;
158 	size_t i;
159 
160 	dict = prop_dictionary_create();
161 	if (dict == NULL)
162 		return NULL;
163 
164 	for (i = 0; i < __arraycount(values); i++) {
165 		values[i].flags = SDP_ATTR_INVALID;
166 		values[i].attr = 0;
167 		values[i].vlen = sizeof(buffer[i]);
168 		values[i].value = buffer[i];
169 	}
170 
171 	for (i = 0; i < __arraycount(cfgtype); i++) {
172 		if (strcasecmp(service, cfgtype[i].name) == 0) {
173 			ss = sdp_open(laddr, raddr);
174 
175 			if (ss == NULL || (errno = sdp_error(ss)) != 0)
176 				return NULL;
177 
178 			rv = sdp_search(ss,
179 				cfgtype[i].nservices, cfgtype[i].services,
180 				cfgtype[i].nattrs, cfgtype[i].attrs,
181 				__arraycount(values), values);
182 
183 			if (rv != 0) {
184 				errno = sdp_error(ss);
185 				return NULL;
186 			}
187 			sdp_close(ss);
188 
189 			rv = (*cfgtype[i].handler)(dict);
190 			if (rv != 0)
191 				return NULL;
192 
193 			return dict;
194 		}
195 	}
196 
197 	printf("Known config types:\n");
198 	for (i = 0; i < __arraycount(cfgtype); i++)
199 		printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description);
200 
201 	exit(EXIT_FAILURE);
202 }
203 
204 /*
205  * Configure HID results
206  */
207 static int
208 config_hid(prop_dictionary_t dict)
209 {
210 	prop_object_t obj;
211 	int32_t control_psm, interrupt_psm,
212 		reconnect_initiate, battery_power,
213 		normally_connectable, hid_length;
214 	uint8_t *hid_descriptor;
215 	const char *mode;
216 	size_t 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 < __arraycount(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 	prop_object_release(obj);
272 
273 	obj = prop_number_create_integer(control_psm);
274 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVcontrolpsm, obj))
275 		return errno;
276 
277 	prop_object_release(obj);
278 
279 	obj = prop_number_create_integer(interrupt_psm);
280 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVinterruptpsm, obj))
281 		return errno;
282 
283 	prop_object_release(obj);
284 
285 	obj = prop_data_create_data(hid_descriptor, hid_length);
286 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj))
287 		return errno;
288 
289 	mode = hid_mode(obj);
290 	prop_object_release(obj);
291 
292 	obj = prop_string_create_cstring_nocopy(mode);
293 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVmode, obj))
294 		return errno;
295 
296 	prop_object_release(obj);
297 
298 	if (!reconnect_initiate) {
299 		obj = prop_bool_create(true);
300 		if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj))
301 			return errno;
302 
303 		prop_object_release(obj);
304 	}
305 
306 	return 0;
307 }
308 
309 /*
310  * Configure HSET results
311  */
312 static int
313 config_hset(prop_dictionary_t dict)
314 {
315 	prop_object_t obj;
316 	uint32_t channel;
317 	size_t i;
318 
319 	channel = -1;
320 
321 	for (i = 0; i < __arraycount(values); i++) {
322 		if (values[i].flags != SDP_ATTR_OK)
323 			continue;
324 
325 		switch (values[i].attr) {
326 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
327 			channel = parse_rfcomm_channel(&values[i]);
328 			break;
329 		}
330 	}
331 
332 	if (channel == (uint32_t)-1)
333 		return ENOATTR;
334 
335 	obj = prop_string_create_cstring_nocopy("btsco");
336 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
337 		return errno;
338 
339 	prop_object_release(obj);
340 
341 	obj = prop_number_create_integer(channel);
342 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
343 		return errno;
344 
345 	prop_object_release(obj);
346 
347 	return 0;
348 }
349 
350 /*
351  * Configure HF results
352  */
353 static int
354 config_hf(prop_dictionary_t dict)
355 {
356 	prop_object_t obj;
357 	uint32_t channel;
358 	size_t i;
359 
360 	channel = -1;
361 
362 	for (i = 0; i < __arraycount(values); i++) {
363 		if (values[i].flags != SDP_ATTR_OK)
364 			continue;
365 
366 		switch (values[i].attr) {
367 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
368 			channel = parse_rfcomm_channel(&values[i]);
369 			break;
370 		}
371 	}
372 
373 	if (channel == (uint32_t)-1)
374 		return ENOATTR;
375 
376 	obj = prop_string_create_cstring_nocopy("btsco");
377 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
378 		return errno;
379 
380 	prop_object_release(obj);
381 
382 	obj = prop_bool_create(true);
383 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj))
384 		return errno;
385 
386 	prop_object_release(obj);
387 
388 	obj = prop_number_create_integer(channel);
389 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
390 		return errno;
391 
392 	prop_object_release(obj);
393 
394 	return 0;
395 }
396 
397 /*
398  * Parse [additional] protocol descriptor list for L2CAP PSM
399  *
400  * seq8 len8				2
401  *	seq8 len8			2
402  *		uuid16 value16		3	L2CAP
403  *		uint16 value16		3	PSM
404  *	seq8 len8			2
405  *		uuid16 value16		3	HID Protocol
406  *				      ===
407  *				       15
408  */
409 
410 static int32_t
411 parse_l2cap_psm(sdp_attr_t *a)
412 {
413 	uint8_t	*ptr = a->value;
414 	uint8_t	*end = a->value + a->vlen;
415 	int32_t	 type, len, uuid, psm;
416 
417 	if (end - ptr < 15)
418 		return (-1);
419 
420 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
421 		SDP_GET8(type, ptr);
422 		switch (type) {
423 		case SDP_DATA_SEQ8:
424 			SDP_GET8(len, ptr);
425 			break;
426 
427 		case SDP_DATA_SEQ16:
428 			SDP_GET16(len, ptr);
429 			break;
430 
431 		case SDP_DATA_SEQ32:
432 			SDP_GET32(len, ptr);
433 			break;
434 
435 		default:
436 			return (-1);
437 		}
438 		if (ptr + len > end)
439 			return (-1);
440 	}
441 
442 	SDP_GET8(type, ptr);
443 	switch (type) {
444 	case SDP_DATA_SEQ8:
445 		SDP_GET8(len, ptr);
446 		break;
447 
448 	case SDP_DATA_SEQ16:
449 		SDP_GET16(len, ptr);
450 		break;
451 
452 	case SDP_DATA_SEQ32:
453 		SDP_GET32(len, ptr);
454 		break;
455 
456 	default:
457 		return (-1);
458 	}
459 	if (ptr + len > end)
460 		return (-1);
461 
462 	/* Protocol */
463 	SDP_GET8(type, ptr);
464 	switch (type) {
465 	case SDP_DATA_SEQ8:
466 		SDP_GET8(len, ptr);
467 		break;
468 
469 	case SDP_DATA_SEQ16:
470 		SDP_GET16(len, ptr);
471 		break;
472 
473 	case SDP_DATA_SEQ32:
474 		SDP_GET32(len, ptr);
475 		break;
476 
477 	default:
478 		return (-1);
479 	}
480 	if (ptr + len > end)
481 		return (-1);
482 
483 	/* UUID */
484 	if (ptr + 3 > end)
485 		return (-1);
486 	SDP_GET8(type, ptr);
487 	switch (type) {
488 	case SDP_DATA_UUID16:
489 		SDP_GET16(uuid, ptr);
490 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
491 			return (-1);
492 		break;
493 
494 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
495 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
496 	default:
497 		return (-1);
498 	}
499 
500 	/* PSM */
501 	if (ptr + 3 > end)
502 		return (-1);
503 	SDP_GET8(type, ptr);
504 	if (type != SDP_DATA_UINT16)
505 		return (-1);
506 	SDP_GET16(psm, ptr);
507 
508 	return (psm);
509 }
510 
511 /*
512  * Parse HID descriptor string
513  *
514  * seq8 len8			2
515  *	seq8 len8		2
516  *		uint8 value8	2
517  *		str value	3
518  *			      ===
519  *			        9
520  */
521 
522 static int32_t
523 parse_hid_descriptor(sdp_attr_t *a)
524 {
525 	uint8_t	*ptr = a->value;
526 	uint8_t	*end = a->value + a->vlen;
527 	int32_t	 type, len, descriptor_type;
528 
529 	if (end - ptr < 9)
530 		return (-1);
531 
532 	SDP_GET8(type, ptr);
533 	switch (type) {
534 	case SDP_DATA_SEQ8:
535 		SDP_GET8(len, ptr);
536 		break;
537 
538 	case SDP_DATA_SEQ16:
539 		SDP_GET16(len, ptr);
540 		break;
541 
542 	case SDP_DATA_SEQ32:
543 		SDP_GET32(len, ptr);
544 		break;
545 
546 	default:
547 		return (-1);
548 	}
549 	if (ptr + len > end)
550 		return (-1);
551 
552 	while (ptr < end) {
553 		/* Descriptor */
554 		SDP_GET8(type, ptr);
555 		switch (type) {
556 		case SDP_DATA_SEQ8:
557 			if (ptr + 1 > end)
558 				return (-1);
559 			SDP_GET8(len, ptr);
560 			break;
561 
562 		case SDP_DATA_SEQ16:
563 			if (ptr + 2 > end)
564 				return (-1);
565 			SDP_GET16(len, ptr);
566 			break;
567 
568 		case SDP_DATA_SEQ32:
569 			if (ptr + 4 > end)
570 				return (-1);
571 			SDP_GET32(len, ptr);
572 			break;
573 
574 		default:
575 			return (-1);
576 		}
577 
578 		/* Descripor type */
579 		if (ptr + 1 > end)
580 			return (-1);
581 		SDP_GET8(type, ptr);
582 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
583 			return (-1);
584 		SDP_GET8(descriptor_type, ptr);
585 
586 		/* Descriptor value */
587 		if (ptr + 1 > end)
588 			return (-1);
589 		SDP_GET8(type, ptr);
590 		switch (type) {
591 		case SDP_DATA_STR8:
592 			if (ptr + 1 > end)
593 				return (-1);
594 			SDP_GET8(len, ptr);
595 			break;
596 
597 		case SDP_DATA_STR16:
598 			if (ptr + 2 > end)
599 				return (-1);
600 			SDP_GET16(len, ptr);
601 			break;
602 
603 		case SDP_DATA_STR32:
604 			if (ptr + 4 > end)
605 				return (-1);
606 			SDP_GET32(len, ptr);
607 			break;
608 
609 		default:
610 			return (-1);
611 		}
612 		if (ptr + len > end)
613 			return (-1);
614 
615 		if (descriptor_type == UDESC_REPORT && len > 0) {
616 			a->value = ptr;
617 			a->vlen = len;
618 
619 			return (0);
620 		}
621 
622 		ptr += len;
623 	}
624 
625 	return (-1);
626 }
627 
628 /*
629  * Parse boolean value
630  *
631  * bool8 int8
632  */
633 
634 static int32_t
635 parse_boolean(sdp_attr_t *a)
636 {
637 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
638 		return (-1);
639 
640 	return (a->value[1]);
641 }
642 
643 /*
644  * Parse protocol descriptor list for the RFCOMM channel
645  *
646  * seq8 len8				2
647  *	seq8 len8			2
648  *		uuid16 value16		3	L2CAP
649  *	seq8 len8			2
650  *		uuid16 value16		3	RFCOMM
651  *		uint8 value8		2	channel
652  *				      ===
653  *				       14
654  */
655 
656 static int32_t
657 parse_rfcomm_channel(sdp_attr_t *a)
658 {
659 	uint8_t	*ptr = a->value;
660 	uint8_t	*end = a->value + a->vlen;
661 	int32_t	 type, len, uuid, channel;
662 
663 	if (end - ptr < 14)
664 		return (-1);
665 
666 	SDP_GET8(type, ptr);
667 	switch (type) {
668 	case SDP_DATA_SEQ8:
669 		SDP_GET8(len, ptr);
670 		break;
671 
672 	case SDP_DATA_SEQ16:
673 		SDP_GET16(len, ptr);
674 		break;
675 
676 	case SDP_DATA_SEQ32:
677 		SDP_GET32(len, ptr);
678 		break;
679 
680 	default:
681 		return (-1);
682 	}
683 	if (ptr + len > end)
684 		return (-1);
685 
686 	/* Protocol */
687 	SDP_GET8(type, ptr);
688 	switch (type) {
689 	case SDP_DATA_SEQ8:
690 		SDP_GET8(len, ptr);
691 		break;
692 
693 	case SDP_DATA_SEQ16:
694 		SDP_GET16(len, ptr);
695 		break;
696 
697 	case SDP_DATA_SEQ32:
698 		SDP_GET32(len, ptr);
699 		break;
700 
701 	default:
702 		return (-1);
703 	}
704 	if (ptr + len > end)
705 		return (-1);
706 
707 	/* UUID */
708 	if (ptr + 3 > end)
709 		return (-1);
710 	SDP_GET8(type, ptr);
711 	switch (type) {
712 	case SDP_DATA_UUID16:
713 		SDP_GET16(uuid, ptr);
714 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
715 			return (-1);
716 		break;
717 
718 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
719 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
720 	default:
721 		return (-1);
722 	}
723 
724 	/* Protocol */
725 	SDP_GET8(type, ptr);
726 	switch (type) {
727 	case SDP_DATA_SEQ8:
728 		SDP_GET8(len, ptr);
729 		break;
730 
731 	case SDP_DATA_SEQ16:
732 		SDP_GET16(len, ptr);
733 		break;
734 
735 	case SDP_DATA_SEQ32:
736 		SDP_GET32(len, ptr);
737 		break;
738 
739 	default:
740 		return (-1);
741 	}
742 	if (ptr + len > end)
743 		return (-1);
744 
745 	/* UUID */
746 	if (ptr + 3 > end)
747 		return (-1);
748 	SDP_GET8(type, ptr);
749 	switch (type) {
750 	case SDP_DATA_UUID16:
751 		SDP_GET16(uuid, ptr);
752 		if (uuid != SDP_UUID_PROTOCOL_RFCOMM)
753 			return (-1);
754 		break;
755 
756 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
757 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
758 	default:
759 		return (-1);
760 	}
761 
762 	/* channel */
763 	if (ptr + 2 > end)
764 		return (-1);
765 
766 	SDP_GET8(type, ptr);
767 	if (type != SDP_DATA_UINT8)
768 		return (-1);
769 
770 	SDP_GET8(channel, ptr);
771 
772 	return (channel);
773 }
774 
775 /*
776  * return appropriate mode for HID descriptor
777  */
778 const char *
779 hid_mode(prop_data_t desc)
780 {
781 	report_desc_t r;
782 	hid_data_t d;
783 	struct hid_item h;
784 	const char *mode;
785 
786 	hid_init(NULL);
787 
788 	mode = BTDEVauth;	/* default */
789 
790 	r = hid_use_report_desc(prop_data_data_nocopy(desc),
791 				prop_data_size(desc));
792 	if (r == NULL)
793 		err(EXIT_FAILURE, "hid_use_report_desc");
794 
795 	d = hid_start_parse(r, ~0, -1);
796 	while (hid_get_item(d, &h) > 0) {
797 		if (h.kind == hid_collection
798 		    && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP
799 		    && HID_USAGE(h.usage) == HUG_KEYBOARD)
800 			mode = BTDEVencrypt;
801 	}
802 
803 	hid_end_parse(d);
804 	hid_dispose_report_desc(r);
805 
806 	return mode;
807 }
808