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