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