Home | History | Annotate | Line # | Download | only in btdevctl
sdp.c revision 1.5
      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 (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.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