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, ¶m))
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