xref: /netbsd-src/usr.sbin/btdevctl/sdp.c (revision 1cb7819f041295655a920be44fda6df07e7f28fb)
1*1cb7819fSandvar /*	$NetBSD: sdp.c,v 1.12 2021/12/12 22:20:52 andvar Exp $	*/
24f1cbddcSplunky 
34f1cbddcSplunky /*-
44f1cbddcSplunky  * Copyright (c) 2006 Itronix Inc.
54f1cbddcSplunky  * All rights reserved.
64f1cbddcSplunky  *
74f1cbddcSplunky  * Redistribution and use in source and binary forms, with or without
84f1cbddcSplunky  * modification, are permitted provided that the following conditions
94f1cbddcSplunky  * are met:
104f1cbddcSplunky  * 1. Redistributions of source code must retain the above copyright
114f1cbddcSplunky  *    notice, this list of conditions and the following disclaimer.
124f1cbddcSplunky  * 2. Redistributions in binary form must reproduce the above copyright
134f1cbddcSplunky  *    notice, this list of conditions and the following disclaimer in the
144f1cbddcSplunky  *    documentation and/or other materials provided with the distribution.
154f1cbddcSplunky  * 3. The name of Itronix Inc. may not be used to endorse
164f1cbddcSplunky  *    or promote products derived from this software without specific
174f1cbddcSplunky  *    prior written permission.
184f1cbddcSplunky  *
194f1cbddcSplunky  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
204f1cbddcSplunky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
214f1cbddcSplunky  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
224f1cbddcSplunky  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
234f1cbddcSplunky  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
244f1cbddcSplunky  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
254f1cbddcSplunky  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
264f1cbddcSplunky  * ON ANY THEORY OF LIABILITY, WHETHER IN
274f1cbddcSplunky  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
284f1cbddcSplunky  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
294f1cbddcSplunky  * POSSIBILITY OF SUCH DAMAGE.
304f1cbddcSplunky  */
314f1cbddcSplunky /*
32170631f4Splunky  * Copyright (c) 2009 The NetBSD Foundation, Inc.
334f1cbddcSplunky  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
344f1cbddcSplunky  * All rights reserved.
354f1cbddcSplunky  *
364f1cbddcSplunky  * Redistribution and use in source and binary forms, with or without
374f1cbddcSplunky  * modification, are permitted provided that the following conditions
384f1cbddcSplunky  * are met:
394f1cbddcSplunky  * 1. Redistributions of source code must retain the above copyright
404f1cbddcSplunky  *    notice, this list of conditions and the following disclaimer.
414f1cbddcSplunky  * 2. Redistributions in binary form must reproduce the above copyright
424f1cbddcSplunky  *    notice, this list of conditions and the following disclaimer in the
434f1cbddcSplunky  *    documentation and/or other materials provided with the distribution.
444f1cbddcSplunky  *
454f1cbddcSplunky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
464f1cbddcSplunky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
474f1cbddcSplunky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
484f1cbddcSplunky  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
494f1cbddcSplunky  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
504f1cbddcSplunky  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
514f1cbddcSplunky  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
524f1cbddcSplunky  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
534f1cbddcSplunky  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
544f1cbddcSplunky  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
554f1cbddcSplunky  * SUCH DAMAGE.
564f1cbddcSplunky  */
574f1cbddcSplunky 
584f1cbddcSplunky #include <sys/cdefs.h>
59*1cb7819fSandvar __RCSID("$NetBSD: sdp.c,v 1.12 2021/12/12 22:20:52 andvar Exp $");
604f1cbddcSplunky 
614f1cbddcSplunky #include <sys/types.h>
624f1cbddcSplunky 
634f1cbddcSplunky #include <dev/bluetooth/btdev.h>
644f1cbddcSplunky #include <dev/bluetooth/bthidev.h>
654f1cbddcSplunky #include <dev/bluetooth/btsco.h>
664f1cbddcSplunky #include <dev/usb/usb.h>
674f1cbddcSplunky #include <dev/usb/usbhid.h>
68878cb1cfSbouyer #include <dev/hid/hid.h>
694f1cbddcSplunky 
704f1cbddcSplunky #include <prop/proplib.h>
714f1cbddcSplunky 
724f1cbddcSplunky #include <bluetooth.h>
734f1cbddcSplunky #include <err.h>
744f1cbddcSplunky #include <errno.h>
754f1cbddcSplunky #include <sdp.h>
764f1cbddcSplunky #include <stdlib.h>
77170631f4Splunky #include <strings.h>
784f1cbddcSplunky #include <usbhid.h>
794f1cbddcSplunky 
804f1cbddcSplunky #include "btdevctl.h"
814f1cbddcSplunky 
82170631f4Splunky static bool parse_hid_descriptor(sdp_data_t *);
83170631f4Splunky static int32_t parse_boolean(sdp_data_t *);
84170631f4Splunky static int32_t parse_pdl_param(sdp_data_t *, uint16_t);
85170631f4Splunky static int32_t parse_pdl(sdp_data_t *, uint16_t);
86170631f4Splunky static int32_t parse_apdl(sdp_data_t *, uint16_t);
874f1cbddcSplunky 
88366a13e7Splunky static int config_pnp(prop_dictionary_t, sdp_data_t *);
89170631f4Splunky static int config_hid(prop_dictionary_t, sdp_data_t *);
90170631f4Splunky static int config_hset(prop_dictionary_t, sdp_data_t *);
91170631f4Splunky static int config_hf(prop_dictionary_t, sdp_data_t *);
924f1cbddcSplunky 
93366a13e7Splunky uint16_t pnp_services[] = {
94366a13e7Splunky 	SDP_SERVICE_CLASS_PNP_INFORMATION,
95366a13e7Splunky };
96366a13e7Splunky 
974f1cbddcSplunky uint16_t hid_services[] = {
98170631f4Splunky 	SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE,
994f1cbddcSplunky };
1004f1cbddcSplunky 
1014f1cbddcSplunky uint16_t hset_services[] = {
102170631f4Splunky 	SDP_SERVICE_CLASS_HEADSET,
1034f1cbddcSplunky };
1044f1cbddcSplunky 
1054f1cbddcSplunky uint16_t hf_services[] = {
106170631f4Splunky 	SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY,
1074f1cbddcSplunky };
1084f1cbddcSplunky 
1094f1cbddcSplunky static struct {
1104f1cbddcSplunky 	const char		*name;
111170631f4Splunky 	int			(*handler)(prop_dictionary_t, sdp_data_t *);
1124f1cbddcSplunky 	const char		*description;
1134f1cbddcSplunky 	uint16_t		*services;
114170631f4Splunky 	size_t			nservices;
1154f1cbddcSplunky } cfgtype[] = {
1164f1cbddcSplunky     {
1174f1cbddcSplunky 	"HID",		config_hid,	"Human Interface Device",
1189abc85a7Splunky 	hid_services,	__arraycount(hid_services),
1194f1cbddcSplunky     },
1204f1cbddcSplunky     {
1214f1cbddcSplunky 	"HSET",		config_hset,	"Headset",
1229abc85a7Splunky 	hset_services,	__arraycount(hset_services),
1234f1cbddcSplunky     },
1244f1cbddcSplunky     {
1254f1cbddcSplunky 	"HF",		config_hf,	"Handsfree",
1269abc85a7Splunky 	hf_services,	__arraycount(hf_services),
1274f1cbddcSplunky     },
1284f1cbddcSplunky };
1294f1cbddcSplunky 
130170631f4Splunky #define MAX_SSP		(2 + 1 * 3)	/* largest nservices is 1 */
1314f1cbddcSplunky 
132366a13e7Splunky static bool
cfg_ssa(sdp_session_t ss,uint16_t * services,size_t nservices,sdp_data_t * rsp)133366a13e7Splunky cfg_ssa(sdp_session_t ss, uint16_t *services, size_t nservices, sdp_data_t *rsp)
134366a13e7Splunky {
135366a13e7Splunky 	uint8_t buf[MAX_SSP];
136366a13e7Splunky 	sdp_data_t ssp;
137366a13e7Splunky 	size_t i;
138366a13e7Splunky 
139366a13e7Splunky 	ssp.next = buf;
140366a13e7Splunky 	ssp.end = buf + sizeof(buf);
141366a13e7Splunky 
142366a13e7Splunky 	for (i = 0; i < nservices; i++)
143366a13e7Splunky 		sdp_put_uuid16(&ssp, services[i]);
144366a13e7Splunky 
145366a13e7Splunky 	ssp.end = ssp.next;
146366a13e7Splunky 	ssp.next = buf;
147366a13e7Splunky 
148366a13e7Splunky 	return sdp_service_search_attribute(ss, &ssp, NULL, rsp);
149366a13e7Splunky }
150366a13e7Splunky 
151366a13e7Splunky static bool
cfg_search(sdp_session_t ss,int i,prop_dictionary_t dict)152366a13e7Splunky cfg_search(sdp_session_t ss, int i, prop_dictionary_t dict)
153366a13e7Splunky {
154366a13e7Splunky 	sdp_data_t rsp, rec;
155366a13e7Splunky 
156366a13e7Splunky 	/* check PnP Information first */
157366a13e7Splunky 	if (!cfg_ssa(ss, pnp_services, __arraycount(pnp_services), &rsp))
158366a13e7Splunky 		return false;
159366a13e7Splunky 
160366a13e7Splunky 	while (sdp_get_seq(&rsp, &rec)) {
161366a13e7Splunky 		if (config_pnp(dict, &rec) == 0)
162366a13e7Splunky 			break;
163366a13e7Splunky 	}
164366a13e7Splunky 
165366a13e7Splunky 	/* then requested service */
166366a13e7Splunky 	if (!cfg_ssa(ss, cfgtype[i].services, cfgtype[i].nservices, &rsp))
167366a13e7Splunky 		return false;
168366a13e7Splunky 
169366a13e7Splunky 	while (sdp_get_seq(&rsp, &rec)) {
170366a13e7Splunky 		errno = (*cfgtype[i].handler)(dict, &rec);
171366a13e7Splunky 		if (errno == 0)
172366a13e7Splunky 			return true;
173366a13e7Splunky 	}
174366a13e7Splunky 
175366a13e7Splunky 	return false;
176366a13e7Splunky }
177366a13e7Splunky 
1784f1cbddcSplunky prop_dictionary_t
cfg_query(bdaddr_t * laddr,bdaddr_t * raddr,const char * service)1794f1cbddcSplunky cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
1804f1cbddcSplunky {
1814f1cbddcSplunky 	prop_dictionary_t dict;
182170631f4Splunky 	sdp_session_t ss;
183366a13e7Splunky 	size_t i;
1844f1cbddcSplunky 
1854f1cbddcSplunky 	dict = prop_dictionary_create();
1864f1cbddcSplunky 	if (dict == NULL)
1870e26bab8Splunky 		err(EXIT_FAILURE, "prop_dictionary_create()");
1884f1cbddcSplunky 
1899abc85a7Splunky 	for (i = 0; i < __arraycount(cfgtype); i++) {
1904f1cbddcSplunky 		if (strcasecmp(service, cfgtype[i].name) == 0) {
1914f1cbddcSplunky 			ss = sdp_open(laddr, raddr);
1920e26bab8Splunky 			if (ss == NULL)
1930e26bab8Splunky 				err(EXIT_FAILURE, "SDP connection failed");
1940e26bab8Splunky 
1950e26bab8Splunky 			if (!cfg_search(ss, i, dict))
1960e26bab8Splunky 				errx(EXIT_FAILURE, "service %s not found", service);
1974f1cbddcSplunky 
198170631f4Splunky 			sdp_close(ss);
1994f1cbddcSplunky 			return dict;
2004f1cbddcSplunky 		}
201170631f4Splunky 	}
202170631f4Splunky 
2034f1cbddcSplunky 	printf("Known config types:\n");
2049abc85a7Splunky 	for (i = 0; i < __arraycount(cfgtype); i++)
2054f1cbddcSplunky 		printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description);
2064f1cbddcSplunky 
2074f1cbddcSplunky 	exit(EXIT_FAILURE);
2084f1cbddcSplunky }
2094f1cbddcSplunky 
2104f1cbddcSplunky /*
211366a13e7Splunky  * Configure PnP Information results
212366a13e7Splunky  */
213366a13e7Splunky static int
config_pnp(prop_dictionary_t dict,sdp_data_t * rec)214366a13e7Splunky config_pnp(prop_dictionary_t dict, sdp_data_t *rec)
215366a13e7Splunky {
216366a13e7Splunky 	sdp_data_t value;
217366a13e7Splunky 	uintmax_t v;
218366a13e7Splunky 	uint16_t attr;
219366a13e7Splunky 	int vendor, product, source;
220366a13e7Splunky 
221366a13e7Splunky 	vendor = -1;
222366a13e7Splunky 	product = -1;
223366a13e7Splunky 	source = -1;
224366a13e7Splunky 
225366a13e7Splunky 	while (sdp_get_attr(rec, &attr, &value)) {
226366a13e7Splunky 		switch (attr) {
227366a13e7Splunky 		case 0x0201:	/* Vendor ID */
228366a13e7Splunky 			if (sdp_get_uint(&value, &v)
229366a13e7Splunky 			    && v <= UINT16_MAX)
230366a13e7Splunky 				vendor = (int)v;
231366a13e7Splunky 
232366a13e7Splunky 			break;
233366a13e7Splunky 
234366a13e7Splunky 		case 0x0202:	/* Product ID */
235366a13e7Splunky 			if (sdp_get_uint(&value, &v)
236366a13e7Splunky 			    && v <= UINT16_MAX)
237366a13e7Splunky 				product = (int)v;
238366a13e7Splunky 
239366a13e7Splunky 			break;
240366a13e7Splunky 
241366a13e7Splunky 		case 0x0205:	/* Vendor ID Source */
242366a13e7Splunky 			if (sdp_get_uint(&value, &v)
243366a13e7Splunky 			    && v <= UINT16_MAX)
244366a13e7Splunky 				source = (int)v;
245366a13e7Splunky 
246366a13e7Splunky 			break;
247366a13e7Splunky 
248366a13e7Splunky 		default:
249366a13e7Splunky 			break;
250366a13e7Splunky 		}
251366a13e7Splunky 	}
252366a13e7Splunky 
253366a13e7Splunky 	if (vendor == -1 || product == -1)
254366a13e7Splunky 		return ENOATTR;
255366a13e7Splunky 
256366a13e7Splunky 	if (source != 0x0002)	/* "USB Implementers Forum" */
257366a13e7Splunky 		return ENOATTR;
258366a13e7Splunky 
259366a13e7Splunky 	if (!prop_dictionary_set_uint16(dict, BTDEVvendor, (uint16_t)vendor))
260366a13e7Splunky 		return errno;
261366a13e7Splunky 
262366a13e7Splunky 	if (!prop_dictionary_set_uint16(dict, BTDEVproduct, (uint16_t)product))
263366a13e7Splunky 		return errno;
264366a13e7Splunky 
265366a13e7Splunky 	return 0;
266366a13e7Splunky }
267366a13e7Splunky 
268366a13e7Splunky /*
2694f1cbddcSplunky  * Configure HID results
2704f1cbddcSplunky  */
2714f1cbddcSplunky static int
config_hid(prop_dictionary_t dict,sdp_data_t * rec)272170631f4Splunky config_hid(prop_dictionary_t dict, sdp_data_t *rec)
2734f1cbddcSplunky {
2744f1cbddcSplunky 	prop_object_t obj;
2754f1cbddcSplunky 	int32_t control_psm, interrupt_psm,
276170631f4Splunky 		reconnect_initiate, hid_length;
2774f1cbddcSplunky 	uint8_t *hid_descriptor;
278170631f4Splunky 	sdp_data_t value;
279f5db72e7Splunky 	const char *mode;
280170631f4Splunky 	uint16_t attr;
2814f1cbddcSplunky 
2824f1cbddcSplunky 	control_psm = -1;
2834f1cbddcSplunky 	interrupt_psm = -1;
2844f1cbddcSplunky 	reconnect_initiate = -1;
2854f1cbddcSplunky 	hid_descriptor = NULL;
2864f1cbddcSplunky 	hid_length = -1;
2874f1cbddcSplunky 
288170631f4Splunky 	while (sdp_get_attr(rec, &attr, &value)) {
289170631f4Splunky 		switch (attr) {
2904f1cbddcSplunky 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
291170631f4Splunky 			control_psm = parse_pdl(&value, SDP_UUID_PROTOCOL_L2CAP);
2924f1cbddcSplunky 			break;
2934f1cbddcSplunky 
2944f1cbddcSplunky 		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
295170631f4Splunky 			interrupt_psm = parse_apdl(&value, SDP_UUID_PROTOCOL_L2CAP);
2964f1cbddcSplunky 			break;
2974f1cbddcSplunky 
2984f1cbddcSplunky 		case 0x0205: /* HIDReconnectInitiate */
299170631f4Splunky 			reconnect_initiate = parse_boolean(&value);
3004f1cbddcSplunky 			break;
3014f1cbddcSplunky 
3024f1cbddcSplunky 		case 0x0206: /* HIDDescriptorList */
303170631f4Splunky 			if (parse_hid_descriptor(&value)) {
304170631f4Splunky 				hid_descriptor = value.next;
305170631f4Splunky 				hid_length = value.end - value.next;
3064f1cbddcSplunky 			}
3074f1cbddcSplunky 			break;
3084f1cbddcSplunky 
309170631f4Splunky 		default:
3104f1cbddcSplunky 			break;
3114f1cbddcSplunky 		}
3124f1cbddcSplunky 	}
3134f1cbddcSplunky 
3144f1cbddcSplunky 	if (control_psm == -1
3154f1cbddcSplunky 	    || interrupt_psm == -1
3164f1cbddcSplunky 	    || reconnect_initiate == -1
3174f1cbddcSplunky 	    || hid_descriptor == NULL
3184f1cbddcSplunky 	    || hid_length == -1)
3194f1cbddcSplunky 		return ENOATTR;
3204f1cbddcSplunky 
321bcd1eb15Sthorpej 	if (!prop_dictionary_set_string_nocopy(dict, BTDEVtype, "bthidev"))
3224f1cbddcSplunky 		return errno;
3234f1cbddcSplunky 
324bcd1eb15Sthorpej 	if (!prop_dictionary_set_int32(dict, BTHIDEVcontrolpsm, control_psm) ||
325bcd1eb15Sthorpej 	    !prop_dictionary_set_int32(dict, BTHIDEVinterruptpsm,
326bcd1eb15Sthorpej 	    			       interrupt_psm))
3274f1cbddcSplunky 		return errno;
3284f1cbddcSplunky 
329bcd1eb15Sthorpej 	obj = prop_data_create_copy(hid_descriptor, hid_length);
3304f1cbddcSplunky 	if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj))
3314f1cbddcSplunky 		return errno;
3324f1cbddcSplunky 
333f5db72e7Splunky 	mode = hid_mode(obj);
334f5db72e7Splunky 	prop_object_release(obj);
335f5db72e7Splunky 
336bcd1eb15Sthorpej 	if (!prop_dictionary_set_string_nocopy(dict, BTDEVmode, mode))
337f5db72e7Splunky 		return errno;
338f5db72e7Splunky 
3394f1cbddcSplunky 	if (!reconnect_initiate) {
340bcd1eb15Sthorpej 		if (!prop_dictionary_set_bool(dict, BTHIDEVreconnect, true))
3414f1cbddcSplunky 			return errno;
3424f1cbddcSplunky 	}
3434f1cbddcSplunky 
3444f1cbddcSplunky 	return 0;
3454f1cbddcSplunky }
3464f1cbddcSplunky 
3474f1cbddcSplunky /*
3484f1cbddcSplunky  * Configure HSET results
3494f1cbddcSplunky  */
3504f1cbddcSplunky static int
config_hset(prop_dictionary_t dict,sdp_data_t * rec)351170631f4Splunky config_hset(prop_dictionary_t dict, sdp_data_t *rec)
3524f1cbddcSplunky {
353170631f4Splunky 	sdp_data_t value;
354170631f4Splunky 	int32_t channel;
355170631f4Splunky 	uint16_t attr;
3564f1cbddcSplunky 
3574f1cbddcSplunky 	channel = -1;
3584f1cbddcSplunky 
359170631f4Splunky 	while (sdp_get_attr(rec, &attr, &value)) {
360170631f4Splunky 		switch (attr) {
3614f1cbddcSplunky 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
362170631f4Splunky 			channel = parse_pdl(&value, SDP_UUID_PROTOCOL_RFCOMM);
363170631f4Splunky 			break;
364170631f4Splunky 
365170631f4Splunky 		default:
3664f1cbddcSplunky 			break;
3674f1cbddcSplunky 		}
3684f1cbddcSplunky 	}
3694f1cbddcSplunky 
370170631f4Splunky 	if (channel == -1)
3714f1cbddcSplunky 		return ENOATTR;
3724f1cbddcSplunky 
373bcd1eb15Sthorpej 	if (!prop_dictionary_set_string_nocopy(dict, BTDEVtype, "btsco"))
3744f1cbddcSplunky 		return errno;
3754f1cbddcSplunky 
376bcd1eb15Sthorpej 	if (!prop_dictionary_set_int32(dict, BTSCOchannel, channel))
3774f1cbddcSplunky 		return errno;
3784f1cbddcSplunky 
3794f1cbddcSplunky 	return 0;
3804f1cbddcSplunky }
3814f1cbddcSplunky 
3824f1cbddcSplunky /*
3834f1cbddcSplunky  * Configure HF results
3844f1cbddcSplunky  */
3854f1cbddcSplunky static int
config_hf(prop_dictionary_t dict,sdp_data_t * rec)386170631f4Splunky config_hf(prop_dictionary_t dict, sdp_data_t *rec)
3874f1cbddcSplunky {
388170631f4Splunky 	sdp_data_t value;
389170631f4Splunky 	int32_t channel;
390170631f4Splunky 	uint16_t attr;
3914f1cbddcSplunky 
3924f1cbddcSplunky 	channel = -1;
3934f1cbddcSplunky 
394170631f4Splunky 	while (sdp_get_attr(rec, &attr, &value)) {
395170631f4Splunky 		switch (attr) {
3964f1cbddcSplunky 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
397170631f4Splunky 			channel = parse_pdl(&value, SDP_UUID_PROTOCOL_RFCOMM);
398170631f4Splunky 			break;
399170631f4Splunky 
400170631f4Splunky 		default:
4014f1cbddcSplunky 			break;
4024f1cbddcSplunky 		}
4034f1cbddcSplunky 	}
4044f1cbddcSplunky 
405170631f4Splunky 	if (channel == -1)
4064f1cbddcSplunky 		return ENOATTR;
4074f1cbddcSplunky 
408bcd1eb15Sthorpej 	if (!prop_dictionary_set_string_nocopy(dict, BTDEVtype, "btsco"))
4094f1cbddcSplunky 		return errno;
4104f1cbddcSplunky 
411bcd1eb15Sthorpej 	if (!prop_dictionary_set_bool(dict, BTSCOlisten, true))
4124f1cbddcSplunky 		return errno;
4134f1cbddcSplunky 
414bcd1eb15Sthorpej 	if (!prop_dictionary_set_int32(dict, BTSCOchannel, channel))
4154f1cbddcSplunky 		return errno;
4164f1cbddcSplunky 
4174f1cbddcSplunky 	return 0;
4184f1cbddcSplunky }
4194f1cbddcSplunky 
4204f1cbddcSplunky /*
421170631f4Splunky  * Parse HIDDescriptorList . This is a sequence of HIDDescriptors, of which
422170631f4Splunky  * each is a data element sequence containing, minimally, a ClassDescriptorType
423170631f4Splunky  * and ClassDescriptorData containing a byte array of data. Any extra elements
424170631f4Splunky  * should be ignored.
4254f1cbddcSplunky  *
426170631f4Splunky  * If a ClassDescriptorType "Report" is found, set SDP data value to the
427170631f4Splunky  * ClassDescriptorData content and return true. Note that we don't need to
428170631f4Splunky  * extract the actual length as the SDP data is guaranteed valid.
4294f1cbddcSplunky  */
4304f1cbddcSplunky 
431170631f4Splunky static bool
parse_hid_descriptor(sdp_data_t * value)432170631f4Splunky parse_hid_descriptor(sdp_data_t *value)
4334f1cbddcSplunky {
434170631f4Splunky 	sdp_data_t list, desc;
435170631f4Splunky 	uintmax_t type;
436170631f4Splunky 	char *str;
437170631f4Splunky 	size_t len;
4384f1cbddcSplunky 
439170631f4Splunky 	if (!sdp_get_seq(value, &list))
440170631f4Splunky 		return false;
4414f1cbddcSplunky 
442170631f4Splunky 	while (sdp_get_seq(&list, &desc)) {
443170631f4Splunky 		if (sdp_get_uint(&desc, &type)
444170631f4Splunky 		    && type == UDESC_REPORT
445170631f4Splunky 		    && sdp_get_str(&desc, &str, &len)) {
446170631f4Splunky 			value->next = (uint8_t *)str;
447170631f4Splunky 			value->end = (uint8_t *)(str + len);
448170631f4Splunky 			return true;
4494f1cbddcSplunky 		}
4504f1cbddcSplunky 	}
4514f1cbddcSplunky 
452170631f4Splunky 	return false;
4534f1cbddcSplunky }
4544f1cbddcSplunky 
455170631f4Splunky static int32_t
parse_boolean(sdp_data_t * value)456170631f4Splunky parse_boolean(sdp_data_t *value)
457170631f4Splunky {
458170631f4Splunky 	bool bv;
4594f1cbddcSplunky 
460170631f4Splunky 	if (!sdp_get_bool(value, &bv))
461170631f4Splunky 		return -1;
462170631f4Splunky 
463170631f4Splunky 	return bv;
4644f1cbddcSplunky }
4654f1cbddcSplunky 
4664f1cbddcSplunky /*
467170631f4Splunky  * The ProtocolDescriptorList attribute describes one or
468170631f4Splunky  * more protocol stacks that may be used to gain access to
469*1cb7819fSandvar  * the service described by the service record.
4704f1cbddcSplunky  *
471170631f4Splunky  * If the ProtocolDescriptorList describes a single stack,
472170631f4Splunky  * the attribute value takes the form of a data element
473170631f4Splunky  * sequence in which each element of the sequence is a
474170631f4Splunky  * protocol descriptor.
475170631f4Splunky  *
476170631f4Splunky  *	seq
477170631f4Splunky  *	  <list>
478170631f4Splunky  *
479170631f4Splunky  * If it is possible for more than one kind of protocol
480170631f4Splunky  * stack to be used to gain access to the service, the
481170631f4Splunky  * ProtocolDescriptorList takes the form of a data element
482170631f4Splunky  * alternative where each member is a data element sequence
483170631f4Splunky  * consisting of a list of sequences describing each protocol
484170631f4Splunky  *
485170631f4Splunky  *	alt
486170631f4Splunky  *	  seq
487170631f4Splunky  *	    <list>
488170631f4Splunky  *	  seq
489170631f4Splunky  *	    <list>
490170631f4Splunky  *
491170631f4Splunky  * Each ProtocolDescriptorList is a list containing a sequence for
492170631f4Splunky  * each protocol, where each sequence contains the protocol UUUID
493170631f4Splunky  * and any protocol specific parameters.
494170631f4Splunky  *
495170631f4Splunky  *	seq
496170631f4Splunky  *	  uuid		L2CAP
497170631f4Splunky  *	  uint16	psm
498170631f4Splunky  *	seq
499170631f4Splunky  *	  uuid		RFCOMM
500170631f4Splunky  *	  uint8		channel
501170631f4Splunky  *
502170631f4Splunky  * We want to extract the ProtocolSpecificParameter#1 for the
503170631f4Splunky  * given protocol, which will be an unsigned int.
5044f1cbddcSplunky  */
505170631f4Splunky static int32_t
parse_pdl_param(sdp_data_t * pdl,uint16_t proto)506170631f4Splunky parse_pdl_param(sdp_data_t *pdl, uint16_t proto)
507170631f4Splunky {
508170631f4Splunky 	sdp_data_t seq;
509170631f4Splunky 	uintmax_t param;
510170631f4Splunky 
511170631f4Splunky 	while (sdp_get_seq(pdl, &seq)) {
512170631f4Splunky 		if (!sdp_match_uuid16(&seq, proto))
513170631f4Splunky 			continue;
514170631f4Splunky 
515170631f4Splunky 		if (sdp_get_uint(&seq, &param))
516170631f4Splunky 			return param;
517170631f4Splunky 
518170631f4Splunky 		break;
519170631f4Splunky 	}
520170631f4Splunky 
521170631f4Splunky 	return -1;
522170631f4Splunky }
5234f1cbddcSplunky 
5244f1cbddcSplunky static int32_t
parse_pdl(sdp_data_t * value,uint16_t proto)525170631f4Splunky parse_pdl(sdp_data_t *value, uint16_t proto)
5264f1cbddcSplunky {
527170631f4Splunky 	sdp_data_t seq;
528170631f4Splunky 	int32_t param = -1;
5294f1cbddcSplunky 
530170631f4Splunky 	sdp_get_alt(value, value);	/* strip any alt header */
5314f1cbddcSplunky 
532170631f4Splunky 	while (param == -1 && sdp_get_seq(value, &seq))
533170631f4Splunky 		param = parse_pdl_param(&seq, proto);
5344f1cbddcSplunky 
535170631f4Splunky 	return param;
5364f1cbddcSplunky }
5374f1cbddcSplunky 
5384f1cbddcSplunky /*
539170631f4Splunky  * Parse AdditionalProtocolDescriptorList
5404f1cbddcSplunky  */
5414f1cbddcSplunky static int32_t
parse_apdl(sdp_data_t * value,uint16_t proto)542170631f4Splunky parse_apdl(sdp_data_t *value, uint16_t proto)
5434f1cbddcSplunky {
544170631f4Splunky 	sdp_data_t seq;
545170631f4Splunky 	int32_t param = -1;
5464f1cbddcSplunky 
547170631f4Splunky 	sdp_get_seq(value, value);	/* strip seq header */
5484f1cbddcSplunky 
549170631f4Splunky 	while (param == -1 && sdp_get_seq(value, &seq))
550170631f4Splunky 		param = parse_pdl_param(&seq, proto);
5514f1cbddcSplunky 
552170631f4Splunky 	return param;
5534f1cbddcSplunky }
554f5db72e7Splunky 
555f5db72e7Splunky /*
556f5db72e7Splunky  * return appropriate mode for HID descriptor
557f5db72e7Splunky  */
558f5db72e7Splunky const char *
hid_mode(prop_data_t desc)559f5db72e7Splunky hid_mode(prop_data_t desc)
560f5db72e7Splunky {
561f5db72e7Splunky 	report_desc_t r;
562f5db72e7Splunky 	hid_data_t d;
563f5db72e7Splunky 	struct hid_item h;
564f5db72e7Splunky 	const char *mode;
565f5db72e7Splunky 
566f5db72e7Splunky 	hid_init(NULL);
567f5db72e7Splunky 
568f5db72e7Splunky 	mode = BTDEVauth;	/* default */
569f5db72e7Splunky 
570bcd1eb15Sthorpej 	r = hid_use_report_desc(prop_data_value(desc),
571f5db72e7Splunky 				prop_data_size(desc));
572f5db72e7Splunky 	if (r == NULL)
573f5db72e7Splunky 		err(EXIT_FAILURE, "hid_use_report_desc");
574f5db72e7Splunky 
575f5db72e7Splunky 	d = hid_start_parse(r, ~0, -1);
576f5db72e7Splunky 	while (hid_get_item(d, &h) > 0) {
577f5db72e7Splunky 		if (h.kind == hid_collection
578f5db72e7Splunky 		    && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP
579f5db72e7Splunky 		    && HID_USAGE(h.usage) == HUG_KEYBOARD)
580f5db72e7Splunky 			mode = BTDEVencrypt;
581f5db72e7Splunky 	}
582f5db72e7Splunky 
583f5db72e7Splunky 	hid_end_parse(d);
584f5db72e7Splunky 	hid_dispose_report_desc(r);
585f5db72e7Splunky 
586f5db72e7Splunky 	return mode;
587f5db72e7Splunky }
588