1 /* $NetBSD: sdp.c,v 1.5 2008/04/20 19:34:23 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.5 2008/04/20 19:34:23 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 static struct { 124 const char *name; 125 int (*handler)(prop_dictionary_t); 126 const char *description; 127 uint16_t *services; 128 int nservices; 129 uint32_t *attrs; 130 int nattrs; 131 } cfgtype[] = { 132 { 133 "HID", config_hid, "Human Interface Device", 134 hid_services, __arraycount(hid_services), 135 hid_attrs, __arraycount(hid_attrs), 136 }, 137 { 138 "HSET", config_hset, "Headset", 139 hset_services, __arraycount(hset_services), 140 hset_attrs, __arraycount(hset_attrs), 141 }, 142 { 143 "HF", config_hf, "Handsfree", 144 hf_services, __arraycount(hf_services), 145 hf_attrs, __arraycount(hf_attrs), 146 }, 147 }; 148 149 static sdp_attr_t values[8]; 150 static uint8_t buffer[__arraycount(values)][512]; 151 152 prop_dictionary_t 153 cfg_query(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) 154 { 155 prop_dictionary_t dict; 156 void *ss; 157 int rv, i; 158 159 dict = prop_dictionary_create(); 160 if (dict == NULL) 161 return NULL; 162 163 for (i = 0; i < __arraycount(values); i++) { 164 values[i].flags = SDP_ATTR_INVALID; 165 values[i].attr = 0; 166 values[i].vlen = sizeof(buffer[i]); 167 values[i].value = buffer[i]; 168 } 169 170 for (i = 0; i < __arraycount(cfgtype); i++) { 171 if (strcasecmp(service, cfgtype[i].name) == 0) { 172 ss = sdp_open(laddr, raddr); 173 174 if (ss == NULL || (errno = sdp_error(ss)) != 0) 175 return NULL; 176 177 rv = sdp_search(ss, 178 cfgtype[i].nservices, cfgtype[i].services, 179 cfgtype[i].nattrs, cfgtype[i].attrs, 180 __arraycount(values), values); 181 182 if (rv != 0) { 183 errno = sdp_error(ss); 184 return NULL; 185 } 186 sdp_close(ss); 187 188 rv = (*cfgtype[i].handler)(dict); 189 if (rv != 0) 190 return NULL; 191 192 return dict; 193 } 194 } 195 196 printf("Known config types:\n"); 197 for (i = 0; i < __arraycount(cfgtype); i++) 198 printf("\t%s\t%s\n", cfgtype[i].name, cfgtype[i].description); 199 200 exit(EXIT_FAILURE); 201 } 202 203 /* 204 * Configure HID results 205 */ 206 static int 207 config_hid(prop_dictionary_t dict) 208 { 209 prop_object_t obj; 210 int32_t control_psm, interrupt_psm, 211 reconnect_initiate, battery_power, 212 normally_connectable, hid_length; 213 uint8_t *hid_descriptor; 214 const char *mode; 215 int i; 216 217 control_psm = -1; 218 interrupt_psm = -1; 219 reconnect_initiate = -1; 220 normally_connectable = 0; 221 battery_power = 0; 222 hid_descriptor = NULL; 223 hid_length = -1; 224 225 for (i = 0; i < __arraycount(values); i++) { 226 if (values[i].flags != SDP_ATTR_OK) 227 continue; 228 229 switch (values[i].attr) { 230 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 231 control_psm = parse_l2cap_psm(&values[i]); 232 break; 233 234 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: 235 interrupt_psm = parse_l2cap_psm(&values[i]); 236 break; 237 238 case 0x0205: /* HIDReconnectInitiate */ 239 reconnect_initiate = parse_boolean(&values[i]); 240 break; 241 242 case 0x0206: /* HIDDescriptorList */ 243 if (parse_hid_descriptor(&values[i]) == 0) { 244 hid_descriptor = values[i].value; 245 hid_length = values[i].vlen; 246 } 247 break; 248 249 case 0x0209: /* HIDBatteryPower */ 250 battery_power = parse_boolean(&values[i]); 251 break; 252 253 case 0x020d: /* HIDNormallyConnectable */ 254 normally_connectable = parse_boolean(&values[i]); 255 break; 256 } 257 } 258 259 if (control_psm == -1 260 || interrupt_psm == -1 261 || reconnect_initiate == -1 262 || hid_descriptor == NULL 263 || hid_length == -1) 264 return ENOATTR; 265 266 obj = prop_string_create_cstring_nocopy("bthidev"); 267 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj)) 268 return errno; 269 270 prop_object_release(obj); 271 272 obj = prop_number_create_integer(control_psm); 273 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVcontrolpsm, obj)) 274 return errno; 275 276 prop_object_release(obj); 277 278 obj = prop_number_create_integer(interrupt_psm); 279 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVinterruptpsm, obj)) 280 return errno; 281 282 prop_object_release(obj); 283 284 obj = prop_data_create_data(hid_descriptor, hid_length); 285 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVdescriptor, obj)) 286 return errno; 287 288 mode = hid_mode(obj); 289 prop_object_release(obj); 290 291 obj = prop_string_create_cstring_nocopy(mode); 292 if (obj == NULL || !prop_dictionary_set(dict, BTDEVmode, obj)) 293 return errno; 294 295 prop_object_release(obj); 296 297 if (!reconnect_initiate) { 298 obj = prop_bool_create(true); 299 if (obj == NULL || !prop_dictionary_set(dict, BTHIDEVreconnect, obj)) 300 return errno; 301 302 prop_object_release(obj); 303 } 304 305 return 0; 306 } 307 308 /* 309 * Configure HSET results 310 */ 311 static int 312 config_hset(prop_dictionary_t dict) 313 { 314 prop_object_t obj; 315 uint32_t channel; 316 int i; 317 318 channel = -1; 319 320 for (i = 0; i < __arraycount(values); i++) { 321 if (values[i].flags != SDP_ATTR_OK) 322 continue; 323 324 switch (values[i].attr) { 325 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 326 channel = parse_rfcomm_channel(&values[i]); 327 break; 328 } 329 } 330 331 if (channel == -1) 332 return ENOATTR; 333 334 obj = prop_string_create_cstring_nocopy("btsco"); 335 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj)) 336 return errno; 337 338 prop_object_release(obj); 339 340 obj = prop_number_create_integer(channel); 341 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj)) 342 return errno; 343 344 prop_object_release(obj); 345 346 return 0; 347 } 348 349 /* 350 * Configure HF results 351 */ 352 static int 353 config_hf(prop_dictionary_t dict) 354 { 355 prop_object_t obj; 356 uint32_t channel; 357 int i; 358 359 channel = -1; 360 361 for (i = 0; i < __arraycount(values); i++) { 362 if (values[i].flags != SDP_ATTR_OK) 363 continue; 364 365 switch (values[i].attr) { 366 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: 367 channel = parse_rfcomm_channel(&values[i]); 368 break; 369 } 370 } 371 372 if (channel == -1) 373 return ENOATTR; 374 375 obj = prop_string_create_cstring_nocopy("btsco"); 376 if (obj == NULL || !prop_dictionary_set(dict, BTDEVtype, obj)) 377 return errno; 378 379 prop_object_release(obj); 380 381 obj = prop_bool_create(true); 382 if (obj == NULL || !prop_dictionary_set(dict, BTSCOlisten, obj)) 383 return errno; 384 385 prop_object_release(obj); 386 387 obj = prop_number_create_integer(channel); 388 if (obj == NULL || !prop_dictionary_set(dict, BTSCOchannel, obj)) 389 return errno; 390 391 prop_object_release(obj); 392 393 return 0; 394 } 395 396 /* 397 * Parse [additional] protocol descriptor list for L2CAP PSM 398 * 399 * seq8 len8 2 400 * seq8 len8 2 401 * uuid16 value16 3 L2CAP 402 * uint16 value16 3 PSM 403 * seq8 len8 2 404 * uuid16 value16 3 HID Protocol 405 * === 406 * 15 407 */ 408 409 static int32_t 410 parse_l2cap_psm(sdp_attr_t *a) 411 { 412 uint8_t *ptr = a->value; 413 uint8_t *end = a->value + a->vlen; 414 int32_t type, len, uuid, psm; 415 416 if (end - ptr < 15) 417 return (-1); 418 419 if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) { 420 SDP_GET8(type, ptr); 421 switch (type) { 422 case SDP_DATA_SEQ8: 423 SDP_GET8(len, ptr); 424 break; 425 426 case SDP_DATA_SEQ16: 427 SDP_GET16(len, ptr); 428 break; 429 430 case SDP_DATA_SEQ32: 431 SDP_GET32(len, ptr); 432 break; 433 434 default: 435 return (-1); 436 } 437 if (ptr + len > end) 438 return (-1); 439 } 440 441 SDP_GET8(type, ptr); 442 switch (type) { 443 case SDP_DATA_SEQ8: 444 SDP_GET8(len, ptr); 445 break; 446 447 case SDP_DATA_SEQ16: 448 SDP_GET16(len, ptr); 449 break; 450 451 case SDP_DATA_SEQ32: 452 SDP_GET32(len, ptr); 453 break; 454 455 default: 456 return (-1); 457 } 458 if (ptr + len > end) 459 return (-1); 460 461 /* Protocol */ 462 SDP_GET8(type, ptr); 463 switch (type) { 464 case SDP_DATA_SEQ8: 465 SDP_GET8(len, ptr); 466 break; 467 468 case SDP_DATA_SEQ16: 469 SDP_GET16(len, ptr); 470 break; 471 472 case SDP_DATA_SEQ32: 473 SDP_GET32(len, ptr); 474 break; 475 476 default: 477 return (-1); 478 } 479 if (ptr + len > end) 480 return (-1); 481 482 /* UUID */ 483 if (ptr + 3 > end) 484 return (-1); 485 SDP_GET8(type, ptr); 486 switch (type) { 487 case SDP_DATA_UUID16: 488 SDP_GET16(uuid, ptr); 489 if (uuid != SDP_UUID_PROTOCOL_L2CAP) 490 return (-1); 491 break; 492 493 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ 494 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ 495 default: 496 return (-1); 497 } 498 499 /* PSM */ 500 if (ptr + 3 > end) 501 return (-1); 502 SDP_GET8(type, ptr); 503 if (type != SDP_DATA_UINT16) 504 return (-1); 505 SDP_GET16(psm, ptr); 506 507 return (psm); 508 } 509 510 /* 511 * Parse HID descriptor string 512 * 513 * seq8 len8 2 514 * seq8 len8 2 515 * uint8 value8 2 516 * str value 3 517 * === 518 * 9 519 */ 520 521 static int32_t 522 parse_hid_descriptor(sdp_attr_t *a) 523 { 524 uint8_t *ptr = a->value; 525 uint8_t *end = a->value + a->vlen; 526 int32_t type, len, descriptor_type; 527 528 if (end - ptr < 9) 529 return (-1); 530 531 SDP_GET8(type, ptr); 532 switch (type) { 533 case SDP_DATA_SEQ8: 534 SDP_GET8(len, ptr); 535 break; 536 537 case SDP_DATA_SEQ16: 538 SDP_GET16(len, ptr); 539 break; 540 541 case SDP_DATA_SEQ32: 542 SDP_GET32(len, ptr); 543 break; 544 545 default: 546 return (-1); 547 } 548 if (ptr + len > end) 549 return (-1); 550 551 while (ptr < end) { 552 /* Descriptor */ 553 SDP_GET8(type, ptr); 554 switch (type) { 555 case SDP_DATA_SEQ8: 556 if (ptr + 1 > end) 557 return (-1); 558 SDP_GET8(len, ptr); 559 break; 560 561 case SDP_DATA_SEQ16: 562 if (ptr + 2 > end) 563 return (-1); 564 SDP_GET16(len, ptr); 565 break; 566 567 case SDP_DATA_SEQ32: 568 if (ptr + 4 > end) 569 return (-1); 570 SDP_GET32(len, ptr); 571 break; 572 573 default: 574 return (-1); 575 } 576 577 /* Descripor type */ 578 if (ptr + 1 > end) 579 return (-1); 580 SDP_GET8(type, ptr); 581 if (type != SDP_DATA_UINT8 || ptr + 1 > end) 582 return (-1); 583 SDP_GET8(descriptor_type, ptr); 584 585 /* Descriptor value */ 586 if (ptr + 1 > end) 587 return (-1); 588 SDP_GET8(type, ptr); 589 switch (type) { 590 case SDP_DATA_STR8: 591 if (ptr + 1 > end) 592 return (-1); 593 SDP_GET8(len, ptr); 594 break; 595 596 case SDP_DATA_STR16: 597 if (ptr + 2 > end) 598 return (-1); 599 SDP_GET16(len, ptr); 600 break; 601 602 case SDP_DATA_STR32: 603 if (ptr + 4 > end) 604 return (-1); 605 SDP_GET32(len, ptr); 606 break; 607 608 default: 609 return (-1); 610 } 611 if (ptr + len > end) 612 return (-1); 613 614 if (descriptor_type == UDESC_REPORT && len > 0) { 615 a->value = ptr; 616 a->vlen = len; 617 618 return (0); 619 } 620 621 ptr += len; 622 } 623 624 return (-1); 625 } 626 627 /* 628 * Parse boolean value 629 * 630 * bool8 int8 631 */ 632 633 static int32_t 634 parse_boolean(sdp_attr_t *a) 635 { 636 if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL) 637 return (-1); 638 639 return (a->value[1]); 640 } 641 642 /* 643 * Parse protocol descriptor list for the RFCOMM channel 644 * 645 * seq8 len8 2 646 * seq8 len8 2 647 * uuid16 value16 3 L2CAP 648 * seq8 len8 2 649 * uuid16 value16 3 RFCOMM 650 * uint8 value8 2 channel 651 * === 652 * 14 653 */ 654 655 static int32_t 656 parse_rfcomm_channel(sdp_attr_t *a) 657 { 658 uint8_t *ptr = a->value; 659 uint8_t *end = a->value + a->vlen; 660 int32_t type, len, uuid, channel; 661 662 if (end - ptr < 14) 663 return (-1); 664 665 SDP_GET8(type, ptr); 666 switch (type) { 667 case SDP_DATA_SEQ8: 668 SDP_GET8(len, ptr); 669 break; 670 671 case SDP_DATA_SEQ16: 672 SDP_GET16(len, ptr); 673 break; 674 675 case SDP_DATA_SEQ32: 676 SDP_GET32(len, ptr); 677 break; 678 679 default: 680 return (-1); 681 } 682 if (ptr + len > end) 683 return (-1); 684 685 /* Protocol */ 686 SDP_GET8(type, ptr); 687 switch (type) { 688 case SDP_DATA_SEQ8: 689 SDP_GET8(len, ptr); 690 break; 691 692 case SDP_DATA_SEQ16: 693 SDP_GET16(len, ptr); 694 break; 695 696 case SDP_DATA_SEQ32: 697 SDP_GET32(len, ptr); 698 break; 699 700 default: 701 return (-1); 702 } 703 if (ptr + len > end) 704 return (-1); 705 706 /* UUID */ 707 if (ptr + 3 > end) 708 return (-1); 709 SDP_GET8(type, ptr); 710 switch (type) { 711 case SDP_DATA_UUID16: 712 SDP_GET16(uuid, ptr); 713 if (uuid != SDP_UUID_PROTOCOL_L2CAP) 714 return (-1); 715 break; 716 717 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ 718 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ 719 default: 720 return (-1); 721 } 722 723 /* Protocol */ 724 SDP_GET8(type, ptr); 725 switch (type) { 726 case SDP_DATA_SEQ8: 727 SDP_GET8(len, ptr); 728 break; 729 730 case SDP_DATA_SEQ16: 731 SDP_GET16(len, ptr); 732 break; 733 734 case SDP_DATA_SEQ32: 735 SDP_GET32(len, ptr); 736 break; 737 738 default: 739 return (-1); 740 } 741 if (ptr + len > end) 742 return (-1); 743 744 /* UUID */ 745 if (ptr + 3 > end) 746 return (-1); 747 SDP_GET8(type, ptr); 748 switch (type) { 749 case SDP_DATA_UUID16: 750 SDP_GET16(uuid, ptr); 751 if (uuid != SDP_UUID_PROTOCOL_RFCOMM) 752 return (-1); 753 break; 754 755 case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ 756 case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ 757 default: 758 return (-1); 759 } 760 761 /* channel */ 762 if (ptr + 2 > end) 763 return (-1); 764 765 SDP_GET8(type, ptr); 766 if (type != SDP_DATA_UINT8) 767 return (-1); 768 769 SDP_GET8(channel, ptr); 770 771 return (channel); 772 } 773 774 /* 775 * return appropriate mode for HID descriptor 776 */ 777 const char * 778 hid_mode(prop_data_t desc) 779 { 780 report_desc_t r; 781 hid_data_t d; 782 struct hid_item h; 783 const char *mode; 784 785 hid_init(NULL); 786 787 mode = BTDEVauth; /* default */ 788 789 r = hid_use_report_desc(prop_data_data_nocopy(desc), 790 prop_data_size(desc)); 791 if (r == NULL) 792 err(EXIT_FAILURE, "hid_use_report_desc"); 793 794 d = hid_start_parse(r, ~0, -1); 795 while (hid_get_item(d, &h) > 0) { 796 if (h.kind == hid_collection 797 && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP 798 && HID_USAGE(h.usage) == HUG_KEYBOARD) 799 mode = BTDEVencrypt; 800 } 801 802 hid_end_parse(d); 803 hid_dispose_report_desc(r); 804 805 return mode; 806 } 807