1 1.12 andvar /* $NetBSD: sdp.c,v 1.12 2021/12/12 22:20:52 andvar Exp $ */ 2 1.1 plunky 3 1.1 plunky /*- 4 1.1 plunky * Copyright (c) 2006 Itronix Inc. 5 1.1 plunky * All rights reserved. 6 1.1 plunky * 7 1.1 plunky * Redistribution and use in source and binary forms, with or without 8 1.1 plunky * modification, are permitted provided that the following conditions 9 1.1 plunky * are met: 10 1.1 plunky * 1. Redistributions of source code must retain the above copyright 11 1.1 plunky * notice, this list of conditions and the following disclaimer. 12 1.1 plunky * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 plunky * notice, this list of conditions and the following disclaimer in the 14 1.1 plunky * documentation and/or other materials provided with the distribution. 15 1.1 plunky * 3. The name of Itronix Inc. may not be used to endorse 16 1.1 plunky * or promote products derived from this software without specific 17 1.1 plunky * prior written permission. 18 1.1 plunky * 19 1.1 plunky * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 20 1.1 plunky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 plunky * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 plunky * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 23 1.1 plunky * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 1.1 plunky * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 1.1 plunky * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 1.1 plunky * ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 plunky * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 plunky * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 plunky * POSSIBILITY OF SUCH DAMAGE. 30 1.1 plunky */ 31 1.1 plunky /* 32 1.7 plunky * Copyright (c) 2009 The NetBSD Foundation, Inc. 33 1.1 plunky * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin (at) yahoo.com> 34 1.1 plunky * All rights reserved. 35 1.1 plunky * 36 1.1 plunky * Redistribution and use in source and binary forms, with or without 37 1.1 plunky * modification, are permitted provided that the following conditions 38 1.1 plunky * are met: 39 1.1 plunky * 1. Redistributions of source code must retain the above copyright 40 1.1 plunky * notice, this list of conditions and the following disclaimer. 41 1.1 plunky * 2. Redistributions in binary form must reproduce the above copyright 42 1.1 plunky * notice, this list of conditions and the following disclaimer in the 43 1.1 plunky * documentation and/or other materials provided with the distribution. 44 1.1 plunky * 45 1.1 plunky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 1.1 plunky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 1.1 plunky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 1.1 plunky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 1.1 plunky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 1.1 plunky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 1.1 plunky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 1.1 plunky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 1.1 plunky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 1.1 plunky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 1.1 plunky * SUCH DAMAGE. 56 1.1 plunky */ 57 1.1 plunky 58 1.1 plunky #include <sys/cdefs.h> 59 1.12 andvar __RCSID("$NetBSD: sdp.c,v 1.12 2021/12/12 22:20:52 andvar Exp $"); 60 1.1 plunky 61 1.1 plunky #include <sys/types.h> 62 1.1 plunky 63 1.1 plunky #include <dev/bluetooth/btdev.h> 64 1.1 plunky #include <dev/bluetooth/bthidev.h> 65 1.1 plunky #include <dev/bluetooth/btsco.h> 66 1.1 plunky #include <dev/usb/usb.h> 67 1.1 plunky #include <dev/usb/usbhid.h> 68 1.10 bouyer #include <dev/hid/hid.h> 69 1.1 plunky 70 1.1 plunky #include <prop/proplib.h> 71 1.1 plunky 72 1.1 plunky #include <bluetooth.h> 73 1.1 plunky #include <err.h> 74 1.1 plunky #include <errno.h> 75 1.1 plunky #include <sdp.h> 76 1.1 plunky #include <stdlib.h> 77 1.7 plunky #include <strings.h> 78 1.1 plunky #include <usbhid.h> 79 1.1 plunky 80 1.1 plunky #include "btdevctl.h" 81 1.1 plunky 82 1.7 plunky static bool parse_hid_descriptor(sdp_data_t *); 83 1.7 plunky static int32_t parse_boolean(sdp_data_t *); 84 1.7 plunky static int32_t parse_pdl_param(sdp_data_t *, uint16_t); 85 1.7 plunky static int32_t parse_pdl(sdp_data_t *, uint16_t); 86 1.7 plunky static int32_t parse_apdl(sdp_data_t *, uint16_t); 87 1.7 plunky 88 1.8 plunky static int config_pnp(prop_dictionary_t, sdp_data_t *); 89 1.7 plunky static int config_hid(prop_dictionary_t, sdp_data_t *); 90 1.7 plunky static int config_hset(prop_dictionary_t, sdp_data_t *); 91 1.7 plunky static int config_hf(prop_dictionary_t, sdp_data_t *); 92 1.1 plunky 93 1.8 plunky uint16_t pnp_services[] = { 94 1.8 plunky SDP_SERVICE_CLASS_PNP_INFORMATION, 95 1.8 plunky }; 96 1.8 plunky 97 1.1 plunky uint16_t hid_services[] = { 98 1.7 plunky SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE, 99 1.1 plunky }; 100 1.1 plunky 101 1.1 plunky uint16_t hset_services[] = { 102 1.7 plunky SDP_SERVICE_CLASS_HEADSET, 103 1.1 plunky }; 104 1.1 plunky 105 1.1 plunky uint16_t hf_services[] = { 106 1.7 plunky SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY, 107 1.1 plunky }; 108 1.1 plunky 109 1.1 plunky static struct { 110 1.1 plunky const char *name; 111 1.7 plunky int (*handler)(prop_dictionary_t, sdp_data_t *); 112 1.1 plunky const char *description; 113 1.1 plunky uint16_t *services; 114 1.7 plunky size_t nservices; 115 1.1 plunky } cfgtype[] = { 116 1.1 plunky { 117 1.1 plunky "HID", config_hid, "Human Interface Device", 118 1.5 plunky hid_services, __arraycount(hid_services), 119 1.1 plunky }, 120 1.1 plunky { 121 1.1 plunky "HSET", config_hset, "Headset", 122 1.5 plunky hset_services, __arraycount(hset_services), 123 1.1 plunky }, 124 1.1 plunky { 125 1.1 plunky "HF", config_hf, "Handsfree", 126 1.5 plunky hf_services, __arraycount(hf_services), 127 1.1 plunky }, 128 1.1 plunky }; 129 1.1 plunky 130 1.7 plunky #define MAX_SSP (2 + 1 * 3) /* largest nservices is 1 */ 131 1.1 plunky 132 1.8 plunky static bool 133 1.8 plunky cfg_ssa(sdp_session_t ss, uint16_t *services, size_t nservices, sdp_data_t *rsp) 134 1.8 plunky { 135 1.8 plunky uint8_t buf[MAX_SSP]; 136 1.8 plunky sdp_data_t ssp; 137 1.8 plunky size_t i; 138 1.8 plunky 139 1.8 plunky ssp.next = buf; 140 1.8 plunky ssp.end = buf + sizeof(buf); 141 1.8 plunky 142 1.8 plunky for (i = 0; i < nservices; i++) 143 1.8 plunky sdp_put_uuid16(&ssp, services[i]); 144 1.8 plunky 145 1.8 plunky ssp.end = ssp.next; 146 1.8 plunky ssp.next = buf; 147 1.8 plunky 148 1.8 plunky return sdp_service_search_attribute(ss, &ssp, NULL, rsp); 149 1.8 plunky } 150 1.8 plunky 151 1.8 plunky static bool 152 1.8 plunky cfg_search(sdp_session_t ss, int i, prop_dictionary_t dict) 153 1.8 plunky { 154 1.8 plunky sdp_data_t rsp, rec; 155 1.8 plunky 156 1.8 plunky /* check PnP Information first */ 157 1.8 plunky if (!cfg_ssa(ss, pnp_services, __arraycount(pnp_services), &rsp)) 158 1.8 plunky return false; 159 1.8 plunky 160 1.8 plunky while (sdp_get_seq(&rsp, &rec)) { 161 1.8 plunky if (config_pnp(dict, &rec) == 0) 162 1.8 plunky break; 163 1.8 plunky } 164 1.8 plunky 165 1.8 plunky /* then requested service */ 166 1.8 plunky if (!cfg_ssa(ss, cfgtype[i].services, cfgtype[i].nservices, &rsp)) 167 1.8 plunky return false; 168 1.8 plunky 169 1.8 plunky while (sdp_get_seq(&rsp, &rec)) { 170 1.8 plunky errno = (*cfgtype[i].handler)(dict, &rec); 171 1.8 plunky if (errno == 0) 172 1.8 plunky return true; 173 1.8 plunky } 174 1.8 plunky 175 1.8 plunky return false; 176 1.8 plunky } 177 1.8 plunky 178 1.1 plunky prop_dictionary_t 179 1.1 plunky cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) 180 1.1 plunky { 181 1.1 plunky prop_dictionary_t dict; 182 1.7 plunky sdp_session_t ss; 183 1.8 plunky size_t i; 184 1.1 plunky 185 1.1 plunky dict = prop_dictionary_create(); 186 1.1 plunky if (dict == NULL) 187 1.9 plunky err(EXIT_FAILURE, "prop_dictionary_create()"); 188 1.1 plunky 189 1.5 plunky for (i = 0; i < __arraycount(cfgtype); i++) { 190 1.1 plunky if (strcasecmp(service, cfgtype[i].name) == 0) { 191 1.1 plunky ss = sdp_open(laddr, raddr); 192 1.9 plunky if (ss == NULL) 193 1.9 plunky err(EXIT_FAILURE, "SDP connection failed"); 194 1.1 plunky 195 1.9 plunky if (!cfg_search(ss, i, dict)) 196 1.9 plunky errx(EXIT_FAILURE, "service %s not found", service); 197 1.1 plunky 198 1.9 plunky sdp_close(ss); 199 1.9 plunky return dict; 200 1.1 plunky } 201 1.1 plunky } 202 1.1 plunky 203 1.1 plunky printf("Known config types:\n"); 204 1.5 plunky for (i = 0; i < __arraycount(cfgtype); i++) 205 1.1 plunky printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description); 206 1.1 plunky 207 1.1 plunky exit(EXIT_FAILURE); 208 1.1 plunky } 209 1.1 plunky 210 1.1 plunky /* 211 1.8 plunky * Configure PnP Information results 212 1.8 plunky */ 213 1.8 plunky static int 214 1.8 plunky config_pnp(prop_dictionary_t dict, sdp_data_t *rec) 215 1.8 plunky { 216 1.8 plunky sdp_data_t value; 217 1.8 plunky uintmax_t v; 218 1.8 plunky uint16_t attr; 219 1.8 plunky int vendor, product, source; 220 1.8 plunky 221 1.8 plunky vendor = -1; 222 1.8 plunky product = -1; 223 1.8 plunky source = -1; 224 1.8 plunky 225 1.8 plunky while (sdp_get_attr(rec, &attr, &value)) { 226 1.8 plunky switch (attr) { 227 1.8 plunky case 0x0201: /* Vendor ID */ 228 1.8 plunky if (sdp_get_uint(&value, &v) 229 1.8 plunky && v <= UINT16_MAX) 230 1.8 plunky vendor = (int)v; 231 1.8 plunky 232 1.8 plunky break; 233 1.8 plunky 234 1.8 plunky case 0x0202: /* Product ID */ 235 1.8 plunky if (sdp_get_uint(&value, &v) 236 1.8 plunky && v <= UINT16_MAX) 237 1.8 plunky product = (int)v; 238 1.8 plunky 239 1.8 plunky break; 240 1.8 plunky 241 1.8 plunky case 0x0205: /* Vendor ID Source */ 242 1.8 plunky if (sdp_get_uint(&value, &v) 243 1.8 plunky && v <= UINT16_MAX) 244 1.8 plunky source = (int)v; 245 1.8 plunky 246 1.8 plunky break; 247 1.8 plunky 248 1.8 plunky default: 249 1.8 plunky break; 250 1.8 plunky } 251 1.8 plunky } 252 1.8 plunky 253 1.8 plunky if (vendor == -1 || product == -1) 254 1.8 plunky return ENOATTR; 255 1.8 plunky 256 1.8 plunky if (source != 0x0002) /* "USB Implementers Forum" */ 257 1.8 plunky return ENOATTR; 258 1.8 plunky 259 1.8 plunky if (!prop_dictionary_set_uint16(dict, BTDEVvendor, (uint16_t)vendor)) 260 1.8 plunky return errno; 261 1.8 plunky 262 1.8 plunky if (!prop_dictionary_set_uint16(dict, BTDEVproduct, (uint16_t)product)) 263 1.8 plunky return errno; 264 1.8 plunky 265 1.8 plunky return 0; 266 1.8 plunky } 267 1.8 plunky 268 1.8 plunky /* 269 1.1 plunky * Configure HID results 270 1.1 plunky */ 271 1.1 plunky static int 272 1.7 plunky config_hid(prop_dictionary_t dict, sdp_data_t *rec) 273 1.1 plunky { 274 1.1 plunky prop_object_t obj; 275 1.1 plunky int32_t control_psm, interrupt_psm, 276 1.7 plunky reconnect_initiate, hid_length; 277 1.1 plunky uint8_t *hid_descriptor; 278 1.7 plunky sdp_data_t value; 279 1.3 plunky const char *mode; 280 1.7 plunky uint16_t attr; 281 1.1 plunky 282 1.1 plunky control_psm = -1; 283 1.1 plunky interrupt_psm = -1; 284 1.1 plunky reconnect_initiate = -1; 285 1.1 plunky hid_descriptor = NULL; 286 1.1 plunky hid_length = -1; 287 1.1 plunky 288 1.7 plunky while (sdp_get_attr(rec, &attr, &value)) { 289 1.7 plunky switch (attr) { 290 1.1 plunky case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 291 1.7 plunky control_psm = parse_pdl(&value, SDP_UUID_PROTOCOL_L2CAP); 292 1.1 plunky break; 293 1.1 plunky 294 1.1 plunky case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: 295 1.7 plunky interrupt_psm = parse_apdl(&value, SDP_UUID_PROTOCOL_L2CAP); 296 1.1 plunky break; 297 1.1 plunky 298 1.1 plunky case 0x0205: /* HIDReconnectInitiate */ 299 1.7 plunky reconnect_initiate = parse_boolean(&value); 300 1.1 plunky break; 301 1.1 plunky 302 1.1 plunky case 0x0206: /* HIDDescriptorList */ 303 1.7 plunky if (parse_hid_descriptor(&value)) { 304 1.7 plunky hid_descriptor = value.next; 305 1.7 plunky hid_length = value.end - value.next; 306 1.1 plunky } 307 1.1 plunky break; 308 1.1 plunky 309 1.7 plunky default: 310 1.1 plunky break; 311 1.1 plunky } 312 1.1 plunky } 313 1.1 plunky 314 1.1 plunky if (control_psm == -1 315 1.1 plunky || interrupt_psm == -1 316 1.1 plunky || reconnect_initiate == -1 317 1.1 plunky || hid_descriptor == NULL 318 1.1 plunky || hid_length == -1) 319 1.1 plunky return ENOATTR; 320 1.1 plunky 321 1.11 thorpej if (!prop_dictionary_set_string_nocopy(dict, BTDEVtype, "bthidev")) 322 1.1 plunky return errno; 323 1.1 plunky 324 1.11 thorpej if (!prop_dictionary_set_int32(dict, BTHIDEVcontrolpsm, control_psm) || 325 1.11 thorpej !prop_dictionary_set_int32(dict, BTHIDEVinterruptpsm, 326 1.11 thorpej interrupt_psm)) 327 1.1 plunky return errno; 328 1.1 plunky 329 1.11 thorpej obj = prop_data_create_copy(hid_descriptor, hid_length); 330 1.1 plunky if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj)) 331 1.1 plunky return errno; 332 1.1 plunky 333 1.3 plunky mode = hid_mode(obj); 334 1.3 plunky prop_object_release(obj); 335 1.3 plunky 336 1.11 thorpej if (!prop_dictionary_set_string_nocopy(dict, BTDEVmode, mode)) 337 1.3 plunky return errno; 338 1.3 plunky 339 1.1 plunky if (!reconnect_initiate) { 340 1.11 thorpej if (!prop_dictionary_set_bool(dict, BTHIDEVreconnect, true)) 341 1.1 plunky return errno; 342 1.1 plunky } 343 1.1 plunky 344 1.1 plunky return 0; 345 1.1 plunky } 346 1.1 plunky 347 1.1 plunky /* 348 1.1 plunky * Configure HSET results 349 1.1 plunky */ 350 1.1 plunky static int 351 1.7 plunky config_hset(prop_dictionary_t dict, sdp_data_t *rec) 352 1.1 plunky { 353 1.7 plunky sdp_data_t value; 354 1.7 plunky int32_t channel; 355 1.7 plunky uint16_t attr; 356 1.1 plunky 357 1.1 plunky channel = -1; 358 1.1 plunky 359 1.7 plunky while (sdp_get_attr(rec, &attr, &value)) { 360 1.7 plunky switch (attr) { 361 1.7 plunky case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 362 1.7 plunky channel = parse_pdl(&value, SDP_UUID_PROTOCOL_RFCOMM); 363 1.7 plunky break; 364 1.1 plunky 365 1.7 plunky default: 366 1.1 plunky break; 367 1.1 plunky } 368 1.1 plunky } 369 1.1 plunky 370 1.7 plunky if (channel == -1) 371 1.1 plunky return ENOATTR; 372 1.1 plunky 373 1.11 thorpej if (!prop_dictionary_set_string_nocopy(dict, BTDEVtype, "btsco")) 374 1.1 plunky return errno; 375 1.1 plunky 376 1.11 thorpej if (!prop_dictionary_set_int32(dict, BTSCOchannel, channel)) 377 1.1 plunky return errno; 378 1.1 plunky 379 1.1 plunky return 0; 380 1.1 plunky } 381 1.1 plunky 382 1.1 plunky /* 383 1.1 plunky * Configure HF results 384 1.1 plunky */ 385 1.1 plunky static int 386 1.7 plunky config_hf(prop_dictionary_t dict, sdp_data_t *rec) 387 1.1 plunky { 388 1.7 plunky sdp_data_t value; 389 1.7 plunky int32_t channel; 390 1.7 plunky uint16_t attr; 391 1.1 plunky 392 1.1 plunky channel = -1; 393 1.1 plunky 394 1.7 plunky while (sdp_get_attr(rec, &attr, &value)) { 395 1.7 plunky switch (attr) { 396 1.7 plunky case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 397 1.7 plunky channel = parse_pdl(&value, SDP_UUID_PROTOCOL_RFCOMM); 398 1.7 plunky break; 399 1.1 plunky 400 1.7 plunky default: 401 1.1 plunky break; 402 1.1 plunky } 403 1.1 plunky } 404 1.1 plunky 405 1.7 plunky if (channel == -1) 406 1.1 plunky return ENOATTR; 407 1.1 plunky 408 1.11 thorpej if (!prop_dictionary_set_string_nocopy(dict, BTDEVtype, "btsco")) 409 1.1 plunky return errno; 410 1.1 plunky 411 1.11 thorpej if (!prop_dictionary_set_bool(dict, BTSCOlisten, true)) 412 1.1 plunky return errno; 413 1.1 plunky 414 1.11 thorpej if (!prop_dictionary_set_int32(dict, BTSCOchannel, channel)) 415 1.1 plunky return errno; 416 1.1 plunky 417 1.1 plunky return 0; 418 1.1 plunky } 419 1.1 plunky 420 1.1 plunky /* 421 1.7 plunky * Parse HIDDescriptorList . This is a sequence of HIDDescriptors, of which 422 1.7 plunky * each is a data element sequence containing, minimally, a ClassDescriptorType 423 1.7 plunky * and ClassDescriptorData containing a byte array of data. Any extra elements 424 1.7 plunky * should be ignored. 425 1.1 plunky * 426 1.7 plunky * If a ClassDescriptorType "Report" is found, set SDP data value to the 427 1.7 plunky * ClassDescriptorData content and return true. Note that we don't need to 428 1.7 plunky * extract the actual length as the SDP data is guaranteed valid. 429 1.1 plunky */ 430 1.1 plunky 431 1.7 plunky static bool 432 1.7 plunky parse_hid_descriptor(sdp_data_t *value) 433 1.1 plunky { 434 1.7 plunky sdp_data_t list, desc; 435 1.7 plunky uintmax_t type; 436 1.7 plunky char *str; 437 1.7 plunky size_t len; 438 1.7 plunky 439 1.7 plunky if (!sdp_get_seq(value, &list)) 440 1.7 plunky return false; 441 1.7 plunky 442 1.7 plunky while (sdp_get_seq(&list, &desc)) { 443 1.7 plunky if (sdp_get_uint(&desc, &type) 444 1.7 plunky && type == UDESC_REPORT 445 1.7 plunky && sdp_get_str(&desc, &str, &len)) { 446 1.7 plunky value->next = (uint8_t *)str; 447 1.7 plunky value->end = (uint8_t *)(str + len); 448 1.7 plunky return true; 449 1.1 plunky } 450 1.1 plunky } 451 1.1 plunky 452 1.7 plunky return false; 453 1.7 plunky } 454 1.1 plunky 455 1.7 plunky static int32_t 456 1.7 plunky parse_boolean(sdp_data_t *value) 457 1.7 plunky { 458 1.7 plunky bool bv; 459 1.1 plunky 460 1.7 plunky if (!sdp_get_bool(value, &bv)) 461 1.7 plunky return -1; 462 1.1 plunky 463 1.7 plunky return bv; 464 1.1 plunky } 465 1.1 plunky 466 1.1 plunky /* 467 1.7 plunky * The ProtocolDescriptorList attribute describes one or 468 1.7 plunky * more protocol stacks that may be used to gain access to 469 1.12 andvar * the service described by the service record. 470 1.7 plunky * 471 1.7 plunky * If the ProtocolDescriptorList describes a single stack, 472 1.7 plunky * the attribute value takes the form of a data element 473 1.7 plunky * sequence in which each element of the sequence is a 474 1.7 plunky * protocol descriptor. 475 1.1 plunky * 476 1.7 plunky * seq 477 1.7 plunky * <list> 478 1.7 plunky * 479 1.7 plunky * If it is possible for more than one kind of protocol 480 1.7 plunky * stack to be used to gain access to the service, the 481 1.7 plunky * ProtocolDescriptorList takes the form of a data element 482 1.7 plunky * alternative where each member is a data element sequence 483 1.7 plunky * consisting of a list of sequences describing each protocol 484 1.7 plunky * 485 1.7 plunky * alt 486 1.7 plunky * seq 487 1.7 plunky * <list> 488 1.7 plunky * seq 489 1.7 plunky * <list> 490 1.7 plunky * 491 1.7 plunky * Each ProtocolDescriptorList is a list containing a sequence for 492 1.7 plunky * each protocol, where each sequence contains the protocol UUUID 493 1.7 plunky * and any protocol specific parameters. 494 1.7 plunky * 495 1.7 plunky * seq 496 1.7 plunky * uuid L2CAP 497 1.7 plunky * uint16 psm 498 1.7 plunky * seq 499 1.7 plunky * uuid RFCOMM 500 1.7 plunky * uint8 channel 501 1.7 plunky * 502 1.7 plunky * We want to extract the ProtocolSpecificParameter#1 for the 503 1.7 plunky * given protocol, which will be an unsigned int. 504 1.1 plunky */ 505 1.1 plunky static int32_t 506 1.7 plunky parse_pdl_param(sdp_data_t *pdl, uint16_t proto) 507 1.1 plunky { 508 1.7 plunky sdp_data_t seq; 509 1.7 plunky uintmax_t param; 510 1.7 plunky 511 1.7 plunky while (sdp_get_seq(pdl, &seq)) { 512 1.7 plunky if (!sdp_match_uuid16(&seq, proto)) 513 1.7 plunky continue; 514 1.1 plunky 515 1.7 plunky if (sdp_get_uint(&seq, ¶m)) 516 1.7 plunky return param; 517 1.1 plunky 518 1.1 plunky break; 519 1.1 plunky } 520 1.1 plunky 521 1.7 plunky return -1; 522 1.1 plunky } 523 1.1 plunky 524 1.1 plunky static int32_t 525 1.7 plunky parse_pdl(sdp_data_t *value, uint16_t proto) 526 1.1 plunky { 527 1.7 plunky sdp_data_t seq; 528 1.7 plunky int32_t param = -1; 529 1.7 plunky 530 1.7 plunky sdp_get_alt(value, value); /* strip any alt header */ 531 1.1 plunky 532 1.7 plunky while (param == -1 && sdp_get_seq(value, &seq)) 533 1.7 plunky param = parse_pdl_param(&seq, proto); 534 1.7 plunky 535 1.7 plunky return param; 536 1.1 plunky } 537 1.1 plunky 538 1.1 plunky /* 539 1.7 plunky * Parse AdditionalProtocolDescriptorList 540 1.1 plunky */ 541 1.1 plunky static int32_t 542 1.7 plunky parse_apdl(sdp_data_t *value, uint16_t proto) 543 1.1 plunky { 544 1.7 plunky sdp_data_t seq; 545 1.7 plunky int32_t param = -1; 546 1.1 plunky 547 1.7 plunky sdp_get_seq(value, value); /* strip seq header */ 548 1.1 plunky 549 1.7 plunky while (param == -1 && sdp_get_seq(value, &seq)) 550 1.7 plunky param = parse_pdl_param(&seq, proto); 551 1.1 plunky 552 1.7 plunky return param; 553 1.1 plunky } 554 1.3 plunky 555 1.3 plunky /* 556 1.3 plunky * return appropriate mode for HID descriptor 557 1.3 plunky */ 558 1.3 plunky const char * 559 1.3 plunky hid_mode(prop_data_t desc) 560 1.3 plunky { 561 1.3 plunky report_desc_t r; 562 1.3 plunky hid_data_t d; 563 1.3 plunky struct hid_item h; 564 1.3 plunky const char *mode; 565 1.3 plunky 566 1.3 plunky hid_init(NULL); 567 1.3 plunky 568 1.3 plunky mode = BTDEVauth; /* default */ 569 1.3 plunky 570 1.11 thorpej r = hid_use_report_desc(prop_data_value(desc), 571 1.3 plunky prop_data_size(desc)); 572 1.3 plunky if (r == NULL) 573 1.3 plunky err(EXIT_FAILURE, "hid_use_report_desc"); 574 1.3 plunky 575 1.3 plunky d = hid_start_parse(r, ~0, -1); 576 1.3 plunky while (hid_get_item(d, &h) > 0) { 577 1.3 plunky if (h.kind == hid_collection 578 1.3 plunky && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP 579 1.3 plunky && HID_USAGE(h.usage) == HUG_KEYBOARD) 580 1.3 plunky mode = BTDEVencrypt; 581 1.3 plunky } 582 1.3 plunky 583 1.3 plunky hid_end_parse(d); 584 1.3 plunky hid_dispose_report_desc(r); 585 1.3 plunky 586 1.3 plunky return mode; 587 1.3 plunky } 588