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