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