xref: /netbsd-src/usr.sbin/btdevctl/sdp.c (revision b7ae68fde0d8ef1c03714e8bbb1ee7c6118ea93b)
1 /*	$NetBSD: sdp.c,v 1.1 2006/09/10 15:45:56 plunky Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006 Itronix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of Itronix Inc. may not be used to endorse
16  *    or promote products derived from this software without specific
17  *    prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
33  * All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  *
44  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54  * SUCH DAMAGE.
55  */
56 
57 #include <sys/cdefs.h>
58 __RCSID("$NetBSD: sdp.c,v 1.1 2006/09/10 15:45:56 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 	obj = prop_number_create_integer(control_psm);
272 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVcontrolpsm, obj))
273 		return errno;
274 
275 	obj = prop_number_create_integer(interrupt_psm);
276 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVinterruptpsm, obj))
277 		return errno;
278 
279 	obj = prop_data_create_data(hid_descriptor, hid_length);
280 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj))
281 		return errno;
282 
283 	if (!reconnect_initiate) {
284 		obj = prop_bool_create(TRUE);
285 		if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj))
286 			return errno;
287 	}
288 
289 	return 0;
290 }
291 
292 /*
293  * Configure HSET results
294  */
295 static int
296 config_hset(prop_dictionary_t dict)
297 {
298 	prop_object_t obj;
299 	uint32_t channel;
300 	int i;
301 
302 	channel = -1;
303 
304 	for (i = 0; i < NUM(values) ; i++) {
305 		if (values[i].flags != SDP_ATTR_OK)
306 			continue;
307 
308 		switch (values[i].attr) {
309 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
310 			channel = parse_rfcomm_channel(&values[i]);
311 			break;
312 		}
313 	}
314 
315 	if (channel == -1)
316 		return ENOATTR;
317 
318 	obj = prop_string_create_cstring_nocopy("btsco");
319 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
320 		return errno;
321 
322 	obj = prop_number_create_integer(channel);
323 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
324 		return errno;
325 
326 	return 0;
327 }
328 
329 /*
330  * Configure HF results
331  */
332 static int
333 config_hf(prop_dictionary_t dict)
334 {
335 	prop_object_t obj;
336 	uint32_t channel;
337 	int i;
338 
339 	channel = -1;
340 
341 	for (i = 0 ; i < NUM(values) ; i++) {
342 		if (values[i].flags != SDP_ATTR_OK)
343 			continue;
344 
345 		switch (values[i].attr) {
346 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
347 			channel = parse_rfcomm_channel(&values[i]);
348 			break;
349 		}
350 	}
351 
352 	if (channel == -1)
353 		return ENOATTR;
354 
355 	obj = prop_string_create_cstring_nocopy("btsco");
356 	if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj))
357 		return errno;
358 
359 	obj = prop_bool_create(TRUE);
360 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj))
361 		return errno;
362 
363 	obj = prop_number_create_integer(channel);
364 	if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj))
365 		return errno;
366 
367 	return 0;
368 }
369 
370 /*
371  * Parse [additional] protocol descriptor list for L2CAP PSM
372  *
373  * seq8 len8				2
374  *	seq8 len8			2
375  *		uuid16 value16		3	L2CAP
376  *		uint16 value16		3	PSM
377  *	seq8 len8			2
378  *		uuid16 value16		3	HID Protocol
379  *				      ===
380  *				       15
381  */
382 
383 static int32_t
384 parse_l2cap_psm(sdp_attr_t *a)
385 {
386 	uint8_t	*ptr = a->value;
387 	uint8_t	*end = a->value + a->vlen;
388 	int32_t	 type, len, uuid, psm;
389 
390 	if (end - ptr < 15)
391 		return (-1);
392 
393 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
394 		SDP_GET8(type, ptr);
395 		switch (type) {
396 		case SDP_DATA_SEQ8:
397 			SDP_GET8(len, ptr);
398 			break;
399 
400 		case SDP_DATA_SEQ16:
401 			SDP_GET16(len, ptr);
402 			break;
403 
404 		case SDP_DATA_SEQ32:
405 			SDP_GET32(len, ptr);
406 			break;
407 
408 		default:
409 			return (-1);
410 		}
411 		if (ptr + len > end)
412 			return (-1);
413 	}
414 
415 	SDP_GET8(type, ptr);
416 	switch (type) {
417 	case SDP_DATA_SEQ8:
418 		SDP_GET8(len, ptr);
419 		break;
420 
421 	case SDP_DATA_SEQ16:
422 		SDP_GET16(len, ptr);
423 		break;
424 
425 	case SDP_DATA_SEQ32:
426 		SDP_GET32(len, ptr);
427 		break;
428 
429 	default:
430 		return (-1);
431 	}
432 	if (ptr + len > end)
433 		return (-1);
434 
435 	/* Protocol */
436 	SDP_GET8(type, ptr);
437 	switch (type) {
438 	case SDP_DATA_SEQ8:
439 		SDP_GET8(len, ptr);
440 		break;
441 
442 	case SDP_DATA_SEQ16:
443 		SDP_GET16(len, ptr);
444 		break;
445 
446 	case SDP_DATA_SEQ32:
447 		SDP_GET32(len, ptr);
448 		break;
449 
450 	default:
451 		return (-1);
452 	}
453 	if (ptr + len > end)
454 		return (-1);
455 
456 	/* UUID */
457 	if (ptr + 3 > end)
458 		return (-1);
459 	SDP_GET8(type, ptr);
460 	switch (type) {
461 	case SDP_DATA_UUID16:
462 		SDP_GET16(uuid, ptr);
463 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
464 			return (-1);
465 		break;
466 
467 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
468 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
469 	default:
470 		return (-1);
471 	}
472 
473 	/* PSM */
474 	if (ptr + 3 > end)
475 		return (-1);
476 	SDP_GET8(type, ptr);
477 	if (type != SDP_DATA_UINT16)
478 		return (-1);
479 	SDP_GET16(psm, ptr);
480 
481 	return (psm);
482 }
483 
484 /*
485  * Parse HID descriptor string
486  *
487  * seq8 len8			2
488  *	seq8 len8		2
489  *		uint8 value8	2
490  *		str value	3
491  *			      ===
492  *			        9
493  */
494 
495 static int32_t
496 parse_hid_descriptor(sdp_attr_t *a)
497 {
498 	uint8_t	*ptr = a->value;
499 	uint8_t	*end = a->value + a->vlen;
500 	int32_t	 type, len, descriptor_type;
501 
502 	if (end - ptr < 9)
503 		return (-1);
504 
505 	SDP_GET8(type, ptr);
506 	switch (type) {
507 	case SDP_DATA_SEQ8:
508 		SDP_GET8(len, ptr);
509 		break;
510 
511 	case SDP_DATA_SEQ16:
512 		SDP_GET16(len, ptr);
513 		break;
514 
515 	case SDP_DATA_SEQ32:
516 		SDP_GET32(len, ptr);
517 		break;
518 
519 	default:
520 		return (-1);
521 	}
522 	if (ptr + len > end)
523 		return (-1);
524 
525 	while (ptr < end) {
526 		/* Descriptor */
527 		SDP_GET8(type, ptr);
528 		switch (type) {
529 		case SDP_DATA_SEQ8:
530 			if (ptr + 1 > end)
531 				return (-1);
532 			SDP_GET8(len, ptr);
533 			break;
534 
535 		case SDP_DATA_SEQ16:
536 			if (ptr + 2 > end)
537 				return (-1);
538 			SDP_GET16(len, ptr);
539 			break;
540 
541 		case SDP_DATA_SEQ32:
542 			if (ptr + 4 > end)
543 				return (-1);
544 			SDP_GET32(len, ptr);
545 			break;
546 
547 		default:
548 			return (-1);
549 		}
550 
551 		/* Descripor type */
552 		if (ptr + 1 > end)
553 			return (-1);
554 		SDP_GET8(type, ptr);
555 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
556 			return (-1);
557 		SDP_GET8(descriptor_type, ptr);
558 
559 		/* Descriptor value */
560 		if (ptr + 1 > end)
561 			return (-1);
562 		SDP_GET8(type, ptr);
563 		switch (type) {
564 		case SDP_DATA_STR8:
565 			if (ptr + 1 > end)
566 				return (-1);
567 			SDP_GET8(len, ptr);
568 			break;
569 
570 		case SDP_DATA_STR16:
571 			if (ptr + 2 > end)
572 				return (-1);
573 			SDP_GET16(len, ptr);
574 			break;
575 
576 		case SDP_DATA_STR32:
577 			if (ptr + 4 > end)
578 				return (-1);
579 			SDP_GET32(len, ptr);
580 			break;
581 
582 		default:
583 			return (-1);
584 		}
585 		if (ptr + len > end)
586 			return (-1);
587 
588 		if (descriptor_type == UDESC_REPORT && len > 0) {
589 			a->value = ptr;
590 			a->vlen = len;
591 
592 			return (0);
593 		}
594 
595 		ptr += len;
596 	}
597 
598 	return (-1);
599 }
600 
601 /*
602  * Parse boolean value
603  *
604  * bool8 int8
605  */
606 
607 static int32_t
608 parse_boolean(sdp_attr_t *a)
609 {
610 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
611 		return (-1);
612 
613 	return (a->value[1]);
614 }
615 
616 /*
617  * Parse protocol descriptor list for the RFCOMM channel
618  *
619  * seq8 len8				2
620  *	seq8 len8			2
621  *		uuid16 value16		3	L2CAP
622  *	seq8 len8			2
623  *		uuid16 value16		3	RFCOMM
624  *		uint8 value8		2	channel
625  *				      ===
626  *				       14
627  */
628 
629 static int32_t
630 parse_rfcomm_channel(sdp_attr_t *a)
631 {
632 	uint8_t	*ptr = a->value;
633 	uint8_t	*end = a->value + a->vlen;
634 	int32_t	 type, len, uuid, channel;
635 
636 	if (end - ptr < 14)
637 		return (-1);
638 
639 	SDP_GET8(type, ptr);
640 	switch (type) {
641 	case SDP_DATA_SEQ8:
642 		SDP_GET8(len, ptr);
643 		break;
644 
645 	case SDP_DATA_SEQ16:
646 		SDP_GET16(len, ptr);
647 		break;
648 
649 	case SDP_DATA_SEQ32:
650 		SDP_GET32(len, ptr);
651 		break;
652 
653 	default:
654 		return (-1);
655 	}
656 	if (ptr + len > end)
657 		return (-1);
658 
659 	/* Protocol */
660 	SDP_GET8(type, ptr);
661 	switch (type) {
662 	case SDP_DATA_SEQ8:
663 		SDP_GET8(len, ptr);
664 		break;
665 
666 	case SDP_DATA_SEQ16:
667 		SDP_GET16(len, ptr);
668 		break;
669 
670 	case SDP_DATA_SEQ32:
671 		SDP_GET32(len, ptr);
672 		break;
673 
674 	default:
675 		return (-1);
676 	}
677 	if (ptr + len > end)
678 		return (-1);
679 
680 	/* UUID */
681 	if (ptr + 3 > end)
682 		return (-1);
683 	SDP_GET8(type, ptr);
684 	switch (type) {
685 	case SDP_DATA_UUID16:
686 		SDP_GET16(uuid, ptr);
687 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
688 			return (-1);
689 		break;
690 
691 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
692 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
693 	default:
694 		return (-1);
695 	}
696 
697 	/* Protocol */
698 	SDP_GET8(type, ptr);
699 	switch (type) {
700 	case SDP_DATA_SEQ8:
701 		SDP_GET8(len, ptr);
702 		break;
703 
704 	case SDP_DATA_SEQ16:
705 		SDP_GET16(len, ptr);
706 		break;
707 
708 	case SDP_DATA_SEQ32:
709 		SDP_GET32(len, ptr);
710 		break;
711 
712 	default:
713 		return (-1);
714 	}
715 	if (ptr + len > end)
716 		return (-1);
717 
718 	/* UUID */
719 	if (ptr + 3 > end)
720 		return (-1);
721 	SDP_GET8(type, ptr);
722 	switch (type) {
723 	case SDP_DATA_UUID16:
724 		SDP_GET16(uuid, ptr);
725 		if (uuid != SDP_UUID_PROTOCOL_RFCOMM)
726 			return (-1);
727 		break;
728 
729 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
730 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
731 	default:
732 		return (-1);
733 	}
734 
735 	/* channel */
736 	if (ptr + 2 > end)
737 		return (-1);
738 
739 	SDP_GET8(type, ptr);
740 	if (type != SDP_DATA_UINT8)
741 		return (-1);
742 
743 	SDP_GET8(channel, ptr);
744 
745 	return (channel);
746 }
747