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