1 /*- 2 * sdp.c 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $ 31 */ 32 33 #include <sys/param.h> 34 #include <sys/queue.h> 35 #include <sys/sysctl.h> 36 #define L2CAP_SOCKET_CHECKED 37 #include <bluetooth.h> 38 #include <dev/usb/usb.h> 39 #include <dev/usb/usbhid.h> 40 #include <errno.h> 41 #include <sdp.h> 42 #include <stdio.h> 43 #include <string.h> 44 #include <usbhid.h> 45 #include "bthid_config.h" 46 #include "bthidcontrol.h" 47 48 static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error); 49 static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a); 50 static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a); 51 static int32_t hid_sdp_parse_boolean (sdp_attr_p a); 52 53 /* 54 * Hard coded attribute IDs taken from the 55 * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12 56 */ 57 58 #define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID 0x0201 59 #define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202 60 #define SDP_ATTR_DEVICE_ID_SERVICE_VERSION 0x0203 61 #define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \ 62 SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION ) 63 64 static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE; 65 static uint16_t service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION; 66 static uint32_t attrs_devid = SDP_ATTR_DEVICE_ID_RANGE; 67 68 static uint32_t attrs[] = { 69 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, 70 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), 71 SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS, 72 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS), 73 SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */ 74 0x0205), 75 SDP_ATTR_RANGE( 0x0206, /* HIDDescriptorList */ 76 0x0206), 77 SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */ 78 0x0209), 79 SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */ 80 0x020d) 81 }; 82 #define nattrs nitems(attrs) 83 84 static sdp_attr_t values[8]; 85 #define nvalues nitems(values) 86 87 static uint8_t buffer[nvalues][512]; 88 89 /* 90 * Query remote device 91 */ 92 93 #undef hid_sdp_query_exit 94 #define hid_sdp_query_exit(e) { \ 95 if (error != NULL) \ 96 *error = (e); \ 97 if (ss != NULL) { \ 98 sdp_close(ss); \ 99 ss = NULL; \ 100 } \ 101 return (((e) == 0)? 0 : -1); \ 102 } 103 104 static void 105 hid_init_return_values() { 106 int i; 107 for (i = 0; i < nvalues; i ++) { 108 values[i].flags = SDP_ATTR_INVALID; 109 values[i].attr = 0; 110 values[i].vlen = sizeof(buffer[i]); 111 values[i].value = buffer[i]; 112 } 113 } 114 115 static int32_t 116 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error) 117 { 118 void *ss = NULL; 119 uint8_t *hid_descriptor = NULL, *v; 120 int32_t i, control_psm = -1, interrupt_psm = -1, 121 reconnect_initiate = -1, 122 normally_connectable = 0, battery_power = 0, 123 hid_descriptor_length = -1, type; 124 int16_t vendor_id = 0, product_id = 0, version = 0; 125 bdaddr_t sdp_local; 126 char devname[HCI_DEVNAME_SIZE]; 127 128 if (local == NULL) 129 local = NG_HCI_BDADDR_ANY; 130 if (hd == NULL) 131 hid_sdp_query_exit(EINVAL); 132 133 hid_init_return_values(); 134 135 if ((ss = sdp_open(local, &hd->bdaddr)) == NULL) 136 hid_sdp_query_exit(ENOMEM); 137 if (sdp_error(ss) != 0) 138 hid_sdp_query_exit(sdp_error(ss)); 139 if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0) 140 hid_sdp_query_exit(sdp_error(ss)); 141 142 for (i = 0; i < nvalues; i ++) { 143 if (values[i].flags != SDP_ATTR_OK) 144 continue; 145 146 switch (values[i].attr) { 147 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 148 control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); 149 break; 150 151 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: 152 interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); 153 break; 154 155 case 0x0205: /* HIDReconnectInitiate */ 156 reconnect_initiate = hid_sdp_parse_boolean(&values[i]); 157 break; 158 159 case 0x0206: /* HIDDescriptorList */ 160 if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) { 161 hid_descriptor = values[i].value; 162 hid_descriptor_length = values[i].vlen; 163 } 164 break; 165 166 case 0x0209: /* HIDBatteryPower */ 167 battery_power = hid_sdp_parse_boolean(&values[i]); 168 break; 169 170 case 0x020d: /* HIDNormallyConnectable */ 171 normally_connectable = hid_sdp_parse_boolean(&values[i]); 172 break; 173 } 174 } 175 176 hid_init_return_values(); 177 178 if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0) 179 hid_sdp_query_exit(sdp_error(ss)); 180 181 /* Try extract HCI bdaddr from opened SDP session */ 182 if (sdp_get_lcaddr(ss, &sdp_local) != 0 || 183 bt_devname(devname, &sdp_local) == 0) 184 hid_sdp_query_exit(ENOATTR); 185 186 sdp_close(ss); 187 ss = NULL; 188 189 /* If search is successful, scan through return vals */ 190 for (i = 0; i < 3; i ++ ) { 191 if (values[i].flags == SDP_ATTR_INVALID ) 192 continue; 193 194 /* Expecting tag + uint16_t on all 3 attributes */ 195 if (values[i].vlen != 3) 196 continue; 197 198 /* Make sure, we're reading a uint16_t */ 199 v = values[i].value; 200 SDP_GET8(type, v); 201 if (type != SDP_DATA_UINT16 ) 202 continue; 203 204 switch (values[i].attr) { 205 case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID: 206 SDP_GET16(vendor_id, v); 207 break; 208 case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID: 209 SDP_GET16(product_id, v); 210 break; 211 case SDP_ATTR_DEVICE_ID_SERVICE_VERSION: 212 SDP_GET16(version, v); 213 break; 214 default: 215 break; 216 } 217 } 218 219 if (control_psm == -1 || interrupt_psm == -1 || 220 reconnect_initiate == -1 || 221 hid_descriptor == NULL || hid_descriptor_length == -1) 222 hid_sdp_query_exit(ENOATTR); 223 hd->name = bt_devremote_name_gen(devname, &hd->bdaddr); 224 hd->vendor_id = vendor_id; 225 hd->product_id = product_id; 226 hd->version = version; 227 hd->control_psm = control_psm; 228 hd->interrupt_psm = interrupt_psm; 229 hd->reconnect_initiate = reconnect_initiate? 1 : 0; 230 hd->battery_power = battery_power? 1 : 0; 231 hd->normally_connectable = normally_connectable? 1 : 0; 232 hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length); 233 if (hd->desc == NULL) 234 hid_sdp_query_exit(ENOMEM); 235 236 return (0); 237 } 238 239 /* 240 * seq len 2 241 * seq len 2 242 * uuid value 3 243 * uint16 value 3 244 * seq len 2 245 * uuid value 3 246 */ 247 248 static int32_t 249 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a) 250 { 251 uint8_t *ptr = a->value; 252 uint8_t *end = a->value + a->vlen; 253 int32_t type, len, uuid, psm; 254 255 if (end - ptr < 15) 256 return (-1); 257 258 if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) { 259 SDP_GET8(type, ptr); 260 switch (type) { 261 case SDP_DATA_SEQ8: 262 SDP_GET8(len, ptr); 263 break; 264 265 case SDP_DATA_SEQ16: 266 SDP_GET16(len, ptr); 267 break; 268 269 case SDP_DATA_SEQ32: 270 SDP_GET32(len, ptr); 271 break; 272 273 default: 274 return (-1); 275 } 276 if (ptr + len > end) 277 return (-1); 278 } 279 280 SDP_GET8(type, ptr); 281 switch (type) { 282 case SDP_DATA_SEQ8: 283 SDP_GET8(len, ptr); 284 break; 285 286 case SDP_DATA_SEQ16: 287 SDP_GET16(len, ptr); 288 break; 289 290 case SDP_DATA_SEQ32: 291 SDP_GET32(len, ptr); 292 break; 293 294 default: 295 return (-1); 296 } 297 if (ptr + len > end) 298 return (-1); 299 300 /* Protocol */ 301 SDP_GET8(type, ptr); 302 switch (type) { 303 case SDP_DATA_SEQ8: 304 SDP_GET8(len, ptr); 305 break; 306 307 case SDP_DATA_SEQ16: 308 SDP_GET16(len, ptr); 309 break; 310 311 case SDP_DATA_SEQ32: 312 SDP_GET32(len, ptr); 313 break; 314 315 default: 316 return (-1); 317 } 318 if (ptr + len > end) 319 return (-1); 320 321 /* UUID */ 322 if (ptr + 3 > end) 323 return (-1); 324 SDP_GET8(type, ptr); 325 switch (type) { 326 case SDP_DATA_UUID16: 327 SDP_GET16(uuid, ptr); 328 if (uuid != SDP_UUID_PROTOCOL_L2CAP) 329 return (-1); 330 break; 331 332 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ 333 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ 334 default: 335 return (-1); 336 } 337 338 /* PSM */ 339 if (ptr + 3 > end) 340 return (-1); 341 SDP_GET8(type, ptr); 342 if (type != SDP_DATA_UINT16) 343 return (-1); 344 SDP_GET16(psm, ptr); 345 346 return (psm); 347 } 348 349 /* 350 * seq len 2 351 * seq len 2 352 * uint8 value8 2 353 * str value 3 354 */ 355 356 static int32_t 357 hid_sdp_parse_hid_descriptor(sdp_attr_p a) 358 { 359 uint8_t *ptr = a->value; 360 uint8_t *end = a->value + a->vlen; 361 int32_t type, len, descriptor_type; 362 363 if (end - ptr < 9) 364 return (-1); 365 366 SDP_GET8(type, ptr); 367 switch (type) { 368 case SDP_DATA_SEQ8: 369 SDP_GET8(len, ptr); 370 break; 371 372 case SDP_DATA_SEQ16: 373 SDP_GET16(len, ptr); 374 break; 375 376 case SDP_DATA_SEQ32: 377 SDP_GET32(len, ptr); 378 break; 379 380 default: 381 return (-1); 382 } 383 if (ptr + len > end) 384 return (-1); 385 386 while (ptr < end) { 387 /* Descriptor */ 388 SDP_GET8(type, ptr); 389 switch (type) { 390 case SDP_DATA_SEQ8: 391 if (ptr + 1 > end) 392 return (-1); 393 SDP_GET8(len, ptr); 394 break; 395 396 case SDP_DATA_SEQ16: 397 if (ptr + 2 > end) 398 return (-1); 399 SDP_GET16(len, ptr); 400 break; 401 402 case SDP_DATA_SEQ32: 403 if (ptr + 4 > end) 404 return (-1); 405 SDP_GET32(len, ptr); 406 break; 407 408 default: 409 return (-1); 410 } 411 412 /* Descripor type */ 413 if (ptr + 1 > end) 414 return (-1); 415 SDP_GET8(type, ptr); 416 if (type != SDP_DATA_UINT8 || ptr + 1 > end) 417 return (-1); 418 SDP_GET8(descriptor_type, ptr); 419 420 /* Descriptor value */ 421 if (ptr + 1 > end) 422 return (-1); 423 SDP_GET8(type, ptr); 424 switch (type) { 425 case SDP_DATA_STR8: 426 if (ptr + 1 > end) 427 return (-1); 428 SDP_GET8(len, ptr); 429 break; 430 431 case SDP_DATA_STR16: 432 if (ptr + 2 > end) 433 return (-1); 434 SDP_GET16(len, ptr); 435 break; 436 437 case SDP_DATA_STR32: 438 if (ptr + 4 > end) 439 return (-1); 440 SDP_GET32(len, ptr); 441 break; 442 443 default: 444 return (-1); 445 } 446 if (ptr + len > end) 447 return (-1); 448 449 if (descriptor_type == UDESC_REPORT && len > 0) { 450 a->value = ptr; 451 a->vlen = len; 452 453 return (0); 454 } 455 456 ptr += len; 457 } 458 459 return (-1); 460 } 461 462 /* bool8 int8 */ 463 static int32_t 464 hid_sdp_parse_boolean(sdp_attr_p a) 465 { 466 if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL) 467 return (-1); 468 469 return (a->value[1]); 470 } 471 472 /* Perform SDP query */ 473 static int32_t 474 hid_query(bdaddr_t *bdaddr, int argc, char **argv) 475 { 476 struct hid_device hd; 477 int e; 478 479 memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr)); 480 if (hid_sdp_query(NULL, &hd, &e) < 0) { 481 fprintf(stderr, "Could not perform SDP query on the " \ 482 "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL), 483 strerror(e), e); 484 return (FAILED); 485 } 486 487 print_hid_device(&hd, stdout); 488 489 return (OK); 490 } 491 492 struct bthid_command sdp_commands[] = 493 { 494 { 495 "Query", 496 "Perform SDP query to the specified device and print HID configuration entry\n"\ 497 "for the device. The configuration entry should be appended to the Bluetooth\n"\ 498 "HID daemon configuration file and the daemon should be restarted.\n", 499 hid_query 500 }, 501 { NULL, NULL, NULL } 502 }; 503 504