Home | History | Annotate | Line # | Download | only in btdevctl
sdp.c revision 1.2
      1 /*	$NetBSD: sdp.c,v 1.2 2007/04/11 20:01:01 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.2 2007/04/11 20:01:01 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 #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 	int 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 < NUM(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 	prop_object_release(obj);
    290 
    291 	if (!reconnect_initiate) {
    292 		obj = prop_bool_create(TRUE);
    293 		if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj))
    294 			return errno;
    295 
    296 		prop_object_release(obj);
    297 	}
    298 
    299 	return 0;
    300 }
    301 
    302 /*
    303  * Configure HSET results
    304  */
    305 static int
    306 config_hset(prop_dictionary_t dict)
    307 {
    308 	prop_object_t obj;
    309 	uint32_t channel;
    310 	int i;
    311 
    312 	channel = -1;
    313 
    314 	for (i = 0; i < NUM(values) ; i++) {
    315 		if (values[i].flags != SDP_ATTR_OK)
    316 			continue;
    317 
    318 		switch (values[i].attr) {
    319 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
    320 			channel = parse_rfcomm_channel(&values[i]);
    321 			break;
    322 		}
    323 	}
    324 
    325 	if (channel == -1)
    326 		return ENOATTR;
    327 
    328 	obj = prop_string_create_cstring_nocopy("btsco");
    329 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
    330 		return errno;
    331 
    332 	prop_object_release(obj);
    333 
    334 	obj = prop_number_create_integer(channel);
    335 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
    336 		return errno;
    337 
    338 	prop_object_release(obj);
    339 
    340 	return 0;
    341 }
    342 
    343 /*
    344  * Configure HF results
    345  */
    346 static int
    347 config_hf(prop_dictionary_t dict)
    348 {
    349 	prop_object_t obj;
    350 	uint32_t channel;
    351 	int i;
    352 
    353 	channel = -1;
    354 
    355 	for (i = 0 ; i < NUM(values) ; i++) {
    356 		if (values[i].flags != SDP_ATTR_OK)
    357 			continue;
    358 
    359 		switch (values[i].attr) {
    360 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
    361 			channel = parse_rfcomm_channel(&values[i]);
    362 			break;
    363 		}
    364 	}
    365 
    366 	if (channel == -1)
    367 		return ENOATTR;
    368 
    369 	obj = prop_string_create_cstring_nocopy("btsco");
    370 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
    371 		return errno;
    372 
    373 	prop_object_release(obj);
    374 
    375 	obj = prop_bool_create(TRUE);
    376 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj))
    377 		return errno;
    378 
    379 	prop_object_release(obj);
    380 
    381 	obj = prop_number_create_integer(channel);
    382 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
    383 		return errno;
    384 
    385 	prop_object_release(obj);
    386 
    387 	return 0;
    388 }
    389 
    390 /*
    391  * Parse [additional] protocol descriptor list for L2CAP PSM
    392  *
    393  * seq8 len8				2
    394  *	seq8 len8			2
    395  *		uuid16 value16		3	L2CAP
    396  *		uint16 value16		3	PSM
    397  *	seq8 len8			2
    398  *		uuid16 value16		3	HID Protocol
    399  *				      ===
    400  *				       15
    401  */
    402 
    403 static int32_t
    404 parse_l2cap_psm(sdp_attr_t *a)
    405 {
    406 	uint8_t	*ptr = a->value;
    407 	uint8_t	*end = a->value + a->vlen;
    408 	int32_t	 type, len, uuid, psm;
    409 
    410 	if (end - ptr < 15)
    411 		return (-1);
    412 
    413 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
    414 		SDP_GET8(type, ptr);
    415 		switch (type) {
    416 		case SDP_DATA_SEQ8:
    417 			SDP_GET8(len, ptr);
    418 			break;
    419 
    420 		case SDP_DATA_SEQ16:
    421 			SDP_GET16(len, ptr);
    422 			break;
    423 
    424 		case SDP_DATA_SEQ32:
    425 			SDP_GET32(len, ptr);
    426 			break;
    427 
    428 		default:
    429 			return (-1);
    430 		}
    431 		if (ptr + len > end)
    432 			return (-1);
    433 	}
    434 
    435 	SDP_GET8(type, ptr);
    436 	switch (type) {
    437 	case SDP_DATA_SEQ8:
    438 		SDP_GET8(len, ptr);
    439 		break;
    440 
    441 	case SDP_DATA_SEQ16:
    442 		SDP_GET16(len, ptr);
    443 		break;
    444 
    445 	case SDP_DATA_SEQ32:
    446 		SDP_GET32(len, ptr);
    447 		break;
    448 
    449 	default:
    450 		return (-1);
    451 	}
    452 	if (ptr + len > end)
    453 		return (-1);
    454 
    455 	/* Protocol */
    456 	SDP_GET8(type, ptr);
    457 	switch (type) {
    458 	case SDP_DATA_SEQ8:
    459 		SDP_GET8(len, ptr);
    460 		break;
    461 
    462 	case SDP_DATA_SEQ16:
    463 		SDP_GET16(len, ptr);
    464 		break;
    465 
    466 	case SDP_DATA_SEQ32:
    467 		SDP_GET32(len, ptr);
    468 		break;
    469 
    470 	default:
    471 		return (-1);
    472 	}
    473 	if (ptr + len > end)
    474 		return (-1);
    475 
    476 	/* UUID */
    477 	if (ptr + 3 > end)
    478 		return (-1);
    479 	SDP_GET8(type, ptr);
    480 	switch (type) {
    481 	case SDP_DATA_UUID16:
    482 		SDP_GET16(uuid, ptr);
    483 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
    484 			return (-1);
    485 		break;
    486 
    487 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
    488 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
    489 	default:
    490 		return (-1);
    491 	}
    492 
    493 	/* PSM */
    494 	if (ptr + 3 > end)
    495 		return (-1);
    496 	SDP_GET8(type, ptr);
    497 	if (type != SDP_DATA_UINT16)
    498 		return (-1);
    499 	SDP_GET16(psm, ptr);
    500 
    501 	return (psm);
    502 }
    503 
    504 /*
    505  * Parse HID descriptor string
    506  *
    507  * seq8 len8			2
    508  *	seq8 len8		2
    509  *		uint8 value8	2
    510  *		str value	3
    511  *			      ===
    512  *			        9
    513  */
    514 
    515 static int32_t
    516 parse_hid_descriptor(sdp_attr_t *a)
    517 {
    518 	uint8_t	*ptr = a->value;
    519 	uint8_t	*end = a->value + a->vlen;
    520 	int32_t	 type, len, descriptor_type;
    521 
    522 	if (end - ptr < 9)
    523 		return (-1);
    524 
    525 	SDP_GET8(type, ptr);
    526 	switch (type) {
    527 	case SDP_DATA_SEQ8:
    528 		SDP_GET8(len, ptr);
    529 		break;
    530 
    531 	case SDP_DATA_SEQ16:
    532 		SDP_GET16(len, ptr);
    533 		break;
    534 
    535 	case SDP_DATA_SEQ32:
    536 		SDP_GET32(len, ptr);
    537 		break;
    538 
    539 	default:
    540 		return (-1);
    541 	}
    542 	if (ptr + len > end)
    543 		return (-1);
    544 
    545 	while (ptr < end) {
    546 		/* Descriptor */
    547 		SDP_GET8(type, ptr);
    548 		switch (type) {
    549 		case SDP_DATA_SEQ8:
    550 			if (ptr + 1 > end)
    551 				return (-1);
    552 			SDP_GET8(len, ptr);
    553 			break;
    554 
    555 		case SDP_DATA_SEQ16:
    556 			if (ptr + 2 > end)
    557 				return (-1);
    558 			SDP_GET16(len, ptr);
    559 			break;
    560 
    561 		case SDP_DATA_SEQ32:
    562 			if (ptr + 4 > end)
    563 				return (-1);
    564 			SDP_GET32(len, ptr);
    565 			break;
    566 
    567 		default:
    568 			return (-1);
    569 		}
    570 
    571 		/* Descripor type */
    572 		if (ptr + 1 > end)
    573 			return (-1);
    574 		SDP_GET8(type, ptr);
    575 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
    576 			return (-1);
    577 		SDP_GET8(descriptor_type, ptr);
    578 
    579 		/* Descriptor value */
    580 		if (ptr + 1 > end)
    581 			return (-1);
    582 		SDP_GET8(type, ptr);
    583 		switch (type) {
    584 		case SDP_DATA_STR8:
    585 			if (ptr + 1 > end)
    586 				return (-1);
    587 			SDP_GET8(len, ptr);
    588 			break;
    589 
    590 		case SDP_DATA_STR16:
    591 			if (ptr + 2 > end)
    592 				return (-1);
    593 			SDP_GET16(len, ptr);
    594 			break;
    595 
    596 		case SDP_DATA_STR32:
    597 			if (ptr + 4 > end)
    598 				return (-1);
    599 			SDP_GET32(len, ptr);
    600 			break;
    601 
    602 		default:
    603 			return (-1);
    604 		}
    605 		if (ptr + len > end)
    606 			return (-1);
    607 
    608 		if (descriptor_type == UDESC_REPORT && len > 0) {
    609 			a->value = ptr;
    610 			a->vlen = len;
    611 
    612 			return (0);
    613 		}
    614 
    615 		ptr += len;
    616 	}
    617 
    618 	return (-1);
    619 }
    620 
    621 /*
    622  * Parse boolean value
    623  *
    624  * bool8 int8
    625  */
    626 
    627 static int32_t
    628 parse_boolean(sdp_attr_t *a)
    629 {
    630 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
    631 		return (-1);
    632 
    633 	return (a->value[1]);
    634 }
    635 
    636 /*
    637  * Parse protocol descriptor list for the RFCOMM channel
    638  *
    639  * seq8 len8				2
    640  *	seq8 len8			2
    641  *		uuid16 value16		3	L2CAP
    642  *	seq8 len8			2
    643  *		uuid16 value16		3	RFCOMM
    644  *		uint8 value8		2	channel
    645  *				      ===
    646  *				       14
    647  */
    648 
    649 static int32_t
    650 parse_rfcomm_channel(sdp_attr_t *a)
    651 {
    652 	uint8_t	*ptr = a->value;
    653 	uint8_t	*end = a->value + a->vlen;
    654 	int32_t	 type, len, uuid, channel;
    655 
    656 	if (end - ptr < 14)
    657 		return (-1);
    658 
    659 	SDP_GET8(type, ptr);
    660 	switch (type) {
    661 	case SDP_DATA_SEQ8:
    662 		SDP_GET8(len, ptr);
    663 		break;
    664 
    665 	case SDP_DATA_SEQ16:
    666 		SDP_GET16(len, ptr);
    667 		break;
    668 
    669 	case SDP_DATA_SEQ32:
    670 		SDP_GET32(len, ptr);
    671 		break;
    672 
    673 	default:
    674 		return (-1);
    675 	}
    676 	if (ptr + len > end)
    677 		return (-1);
    678 
    679 	/* Protocol */
    680 	SDP_GET8(type, ptr);
    681 	switch (type) {
    682 	case SDP_DATA_SEQ8:
    683 		SDP_GET8(len, ptr);
    684 		break;
    685 
    686 	case SDP_DATA_SEQ16:
    687 		SDP_GET16(len, ptr);
    688 		break;
    689 
    690 	case SDP_DATA_SEQ32:
    691 		SDP_GET32(len, ptr);
    692 		break;
    693 
    694 	default:
    695 		return (-1);
    696 	}
    697 	if (ptr + len > end)
    698 		return (-1);
    699 
    700 	/* UUID */
    701 	if (ptr + 3 > end)
    702 		return (-1);
    703 	SDP_GET8(type, ptr);
    704 	switch (type) {
    705 	case SDP_DATA_UUID16:
    706 		SDP_GET16(uuid, ptr);
    707 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
    708 			return (-1);
    709 		break;
    710 
    711 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
    712 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
    713 	default:
    714 		return (-1);
    715 	}
    716 
    717 	/* Protocol */
    718 	SDP_GET8(type, ptr);
    719 	switch (type) {
    720 	case SDP_DATA_SEQ8:
    721 		SDP_GET8(len, ptr);
    722 		break;
    723 
    724 	case SDP_DATA_SEQ16:
    725 		SDP_GET16(len, ptr);
    726 		break;
    727 
    728 	case SDP_DATA_SEQ32:
    729 		SDP_GET32(len, ptr);
    730 		break;
    731 
    732 	default:
    733 		return (-1);
    734 	}
    735 	if (ptr + len > end)
    736 		return (-1);
    737 
    738 	/* UUID */
    739 	if (ptr + 3 > end)
    740 		return (-1);
    741 	SDP_GET8(type, ptr);
    742 	switch (type) {
    743 	case SDP_DATA_UUID16:
    744 		SDP_GET16(uuid, ptr);
    745 		if (uuid != SDP_UUID_PROTOCOL_RFCOMM)
    746 			return (-1);
    747 		break;
    748 
    749 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
    750 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
    751 	default:
    752 		return (-1);
    753 	}
    754 
    755 	/* channel */
    756 	if (ptr + 2 > end)
    757 		return (-1);
    758 
    759 	SDP_GET8(type, ptr);
    760 	if (type != SDP_DATA_UINT8)
    761 		return (-1);
    762 
    763 	SDP_GET8(channel, ptr);
    764 
    765 	return (channel);
    766 }
    767