1 /* 2 * Copyright (C) 2017 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "btstack_hid_parser.c" 39 40 #include <inttypes.h> 41 #include <string.h> 42 43 #include "btstack_hid_parser.h" 44 #include "btstack_util.h" 45 #include "btstack_debug.h" 46 47 // Not implemented: 48 // - Support for Push/Pop 49 // - Optional Pretty Print of HID Descriptor 50 51 /* 52 * btstack_hid_parser.c 53 */ 54 55 #ifdef HID_PARSER_PRETTY_PRINT 56 57 static const char * type_names[] = { 58 "Main", 59 "Global", 60 "Local", 61 "Reserved" 62 }; 63 static const char * main_tags[] = { 64 "", 65 "", 66 "", 67 "", 68 "", 69 "", 70 "", 71 "", 72 "Input ", 73 "Output", 74 "Collection", 75 "Feature", 76 "End Collection", 77 "Reserved", 78 "Reserved", 79 "Reserved" 80 }; 81 static const char * global_tags[] = { 82 "Usage Page", 83 "Logical Minimum", 84 "Logical Maximum", 85 "Physical Minimum", 86 "Physical Maximum", 87 "Unit Exponent", 88 "Unit", 89 "Report Size", 90 "Report ID", 91 "Report Count", 92 "Push", 93 "Pop", 94 "Reserved", 95 "Reserved", 96 "Reserved", 97 "Reserved" 98 }; 99 static const char * local_tags[] = { 100 "Usage", 101 "Usage Minimum", 102 "Usage Maximum", 103 "Designator Index", 104 "Designator Minimum", 105 "Designator Maximum", 106 "String Index", 107 "String Minimum", 108 "String Maximum", 109 "Delimiter", 110 "Reserved", 111 "Reserved", 112 "Reserved", 113 "Reserved", 114 "Reserved", 115 "Reserved" 116 }; 117 #endif 118 119 // HID Descriptor Iterator 120 121 // parse descriptor item and read up to 32-bit bit value 122 static bool btstack_hid_descriptor_parse_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 123 124 const int hid_item_sizes[] = { 0, 1, 2, 4 }; 125 126 // parse item header 127 if (hid_descriptor_len < 1u) return false; 128 uint16_t pos = 0; 129 uint8_t item_header = hid_descriptor[pos++]; 130 item->data_size = hid_item_sizes[item_header & 0x03u]; 131 item->item_type = (item_header & 0x0cu) >> 2u; 132 item->item_tag = (item_header & 0xf0u) >> 4u; 133 // long item 134 if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){ 135 if (hid_descriptor_len < 3u) return false; 136 item->data_size = hid_descriptor[pos++]; 137 item->item_tag = hid_descriptor[pos++]; 138 } 139 item->item_size = pos + item->data_size; 140 item->item_value = 0; 141 142 // read item value 143 if (hid_descriptor_len < item->item_size) return false; 144 if (item->data_size > 4u) return false; 145 int i; 146 int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u); 147 int32_t value = 0; 148 uint8_t latest_byte = 0; 149 for (i=0;i<item->data_size;i++){ 150 latest_byte = hid_descriptor[pos++]; 151 value = (latest_byte << (8*i)) | value; 152 } 153 if (sgnd && (item->data_size > 0u)){ 154 if (latest_byte & 0x80u) { 155 value -= 1u << (item->data_size*8u); 156 } 157 } 158 item->item_value = value; 159 return true; 160 } 161 162 static void btstack_hid_descriptor_iterator_pretty_print_item(btstack_hid_descriptor_iterator_t * iterator, hid_descriptor_item_t * item){ 163 #ifdef HID_PARSER_PRETTY_PRINT 164 const char ** item_tag_table; 165 switch ((TagType)item->item_type){ 166 case Main: 167 item_tag_table = main_tags; 168 break; 169 case Global: 170 item_tag_table = global_tags; 171 break; 172 case Local: 173 item_tag_table = local_tags; 174 break; 175 default: 176 item_tag_table = NULL; 177 break; 178 } 179 const char * item_tag_name = "Invalid"; 180 if (item_tag_table){ 181 item_tag_name = item_tag_table[item->item_tag]; 182 } 183 log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 184 printf("%-15s (%-6s) // %02x 0x%0008x\n", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 185 #else 186 UNUSED(iterator); 187 UNUSED(item); 188 #endif 189 } 190 191 void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 192 iterator->descriptor = hid_descriptor; 193 iterator->descriptor_pos = 0; 194 iterator->descriptor_len = hid_descriptor_len; 195 iterator->item_ready = false; 196 iterator->valid = true; 197 } 198 199 bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){ 200 if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){ 201 uint16_t item_len = iterator->descriptor_len - iterator->descriptor_pos; 202 const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos]; 203 bool ok = btstack_hid_descriptor_parse_item(&iterator->descriptor_item, item_data, item_len); 204 btstack_hid_descriptor_iterator_pretty_print_item(iterator, &iterator->descriptor_item); 205 if (ok){ 206 iterator->item_ready = true; 207 } else { 208 iterator->valid = false; 209 } 210 } 211 return iterator->item_ready; 212 } 213 214 const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){ 215 iterator->descriptor_pos += iterator->descriptor_item.item_size; 216 iterator->item_ready = false; 217 return &iterator->descriptor_item; 218 } 219 220 bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){ 221 return iterator->valid; 222 } 223 224 // HID Descriptor Usage Iterator 225 226 static bool btstack_hid_usage_iterator_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){ 227 switch (tag){ 228 case Input: 229 return report_type == HID_REPORT_TYPE_INPUT; 230 case Output: 231 return report_type == HID_REPORT_TYPE_OUTPUT; 232 case Feature: 233 return report_type == HID_REPORT_TYPE_FEATURE; 234 default: 235 return false; 236 } 237 } 238 239 static void btstack_hid_usage_iterator_handle_global_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 240 switch((GlobalItemTag)item->item_tag){ 241 case UsagePage: 242 iterator->global_usage_page = item->item_value; 243 break; 244 case LogicalMinimum: 245 iterator->global_logical_minimum = item->item_value; 246 break; 247 case LogicalMaximum: 248 iterator->global_logical_maximum = item->item_value; 249 break; 250 case ReportSize: 251 iterator->global_report_size = item->item_value; 252 break; 253 case ReportID: 254 iterator->global_report_id = item->item_value; 255 break; 256 case ReportCount: 257 iterator->global_report_count = item->item_value; 258 break; 259 260 // TODO handle tags 261 case PhysicalMinimum: 262 case PhysicalMaximum: 263 case UnitExponent: 264 case Unit: 265 case Push: 266 case Pop: 267 break; 268 269 default: 270 btstack_assert(false); 271 break; 272 } 273 } 274 275 static void btstack_usage_iterator_hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator){ 276 bool have_usage_min = false; 277 bool have_usage_max = false; 278 main_iterator->usage_range = false; 279 btstack_hid_descriptor_iterator_t iterator; 280 btstack_hid_descriptor_iterator_init(&iterator, &main_iterator->descriptor[main_iterator->usage_pos], main_iterator->descriptor_len - main_iterator->usage_pos); 281 while ((main_iterator->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){ 282 hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator); 283 if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){ 284 main_iterator->usage_page = usage_item.item_value; 285 } 286 if (usage_item.item_type == Local){ 287 uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((main_iterator->usage_page << 16u) | usage_item.item_value); 288 switch (usage_item.item_tag){ 289 case Usage: 290 main_iterator->available_usages = 1; 291 main_iterator->usage_minimum = usage_value; 292 break; 293 case UsageMinimum: 294 main_iterator->usage_minimum = usage_value; 295 have_usage_min = true; 296 break; 297 case UsageMaximum: 298 main_iterator->usage_maximum = usage_value; 299 have_usage_max = true; 300 break; 301 default: 302 break; 303 } 304 if (have_usage_min && have_usage_max){ 305 main_iterator->available_usages = main_iterator->usage_maximum - main_iterator->usage_minimum + 1u; 306 main_iterator->usage_range = true; 307 if (main_iterator->available_usages < main_iterator->required_usages){ 308 log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", main_iterator->usage_minimum & 0xffff, main_iterator->usage_maximum & 0xffff, main_iterator->required_usages); 309 } 310 } 311 } 312 } 313 main_iterator->usage_pos += iterator.descriptor_pos; 314 } 315 316 static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 317 int valid_field = 0; 318 uint16_t report_id_before; 319 switch ((TagType)item->item_type){ 320 case Main: 321 valid_field = btstack_hid_usage_iterator_main_item_tag_matches_report_type((MainItemTag) item->item_tag, 322 iterator->report_type); 323 break; 324 case Global: 325 report_id_before = iterator->global_report_id; 326 btstack_hid_usage_iterator_handle_global_item(iterator, item); 327 // track record id for report handling, reset report position 328 if (report_id_before != iterator->global_report_id){ 329 iterator->report_pos_in_bit = 8u; 330 } 331 break; 332 case Local: 333 case Reserved: 334 break; 335 default: 336 btstack_assert(false); 337 break; 338 } 339 if (!valid_field) return; 340 341 // handle constant fields used for padding 342 if (item->item_value & 1){ 343 int item_bits = iterator->global_report_size * iterator->global_report_count; 344 #ifdef HID_PARSER_PRETTY_PRINT 345 log_info("- Skip %u constant bits", item_bits); 346 #endif 347 iterator->report_pos_in_bit += item_bits; 348 return; 349 } 350 // Empty Item 351 if (iterator->global_report_count == 0u) return; 352 // let's start 353 iterator->required_usages = iterator->global_report_count; 354 } 355 356 static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) { 357 while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){ 358 iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator); 359 360 btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item); 361 362 if (iterator->required_usages){ 363 btstack_usage_iterator_hid_find_next_usage(iterator); 364 if (iterator->available_usages) { 365 iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 366 return; 367 } else { 368 log_debug("no usages found"); 369 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 370 return; 371 } 372 } else { 373 if ((TagType) (&iterator->descriptor_item)->item_type == Main) { 374 // reset usage 375 iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos; 376 iterator->usage_page = iterator->global_usage_page; 377 } 378 } 379 } 380 // end of descriptor 381 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 382 } 383 384 void btstack_hid_usage_iterator_init(btstack_hid_usage_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){ 385 memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t)); 386 387 iterator->descriptor = hid_descriptor; 388 iterator->descriptor_len = hid_descriptor_len; 389 iterator->report_type = hid_report_type; 390 iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 391 iterator->global_report_id = HID_REPORT_ID_UNDEFINED; 392 btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len); 393 } 394 395 bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){ 396 while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){ 397 btstack_hid_usage_iterator_find_next_usage(iterator); 398 } 399 return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 400 } 401 402 void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){ 403 // cache current values 404 memset(item, 0, sizeof(btstack_hid_usage_item_t)); 405 item->size = iterator->global_report_size; 406 item->report_id = iterator->global_report_id; 407 item->usage_page = iterator->usage_minimum >> 16; 408 item->bit_pos = iterator->report_pos_in_bit; 409 410 bool is_variable = (iterator->descriptor_item.item_value & 2) != 0; 411 if (is_variable){ 412 item->usage = iterator->usage_minimum & 0xffffu; 413 } 414 iterator->required_usages--; 415 iterator->report_pos_in_bit += iterator->global_report_size; 416 417 // cache descriptor item and 418 item->descriptor_item = iterator->descriptor_item; 419 item->global_logical_minimum = iterator->global_logical_minimum; 420 421 // next usage 422 if (is_variable){ 423 iterator->usage_minimum++; 424 iterator->available_usages--; 425 if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){ 426 // usage min - max range smaller than report count, ignore remaining bit in report 427 log_debug("Ignoring %u items without Usage", parser->required_usages); 428 iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages; 429 iterator->required_usages = 0; 430 } 431 } else { 432 if (iterator->required_usages == 0u){ 433 iterator->available_usages = 0; 434 } 435 } 436 437 if (iterator->available_usages) { 438 return; 439 } 440 if (iterator->required_usages == 0u){ 441 iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 442 } else { 443 btstack_usage_iterator_hid_find_next_usage(iterator); 444 if (iterator->available_usages == 0u) { 445 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 446 } 447 } 448 } 449 450 451 // HID Report Parser 452 453 void btstack_hid_parser_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type, const uint8_t * hid_report, uint16_t hid_report_len){ 454 btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type); 455 parser->report = hid_report; 456 parser->report_len = hid_report_len; 457 parser->have_report_usage_ready = false; 458 } 459 460 /** 461 * @brief Checks if more fields are available 462 * @param parser 463 */ 464 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 465 while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){ 466 btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor_usage_item); 467 // ignore usages for other report ids 468 if (parser->descriptor_usage_item.report_id != HID_REPORT_ID_UNDEFINED){ 469 if (parser->descriptor_usage_item.report_id != parser->report[0]){ 470 continue; 471 } 472 } 473 parser->have_report_usage_ready = true; 474 } 475 return parser->have_report_usage_ready; 476 } 477 478 /** 479 * @brief Get next field 480 * @param parser 481 * @param usage_page 482 * @param usage 483 * @param value provided in HID report 484 */ 485 486 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 487 488 // fetch data from descriptor usage item 489 uint16_t bit_pos = parser->descriptor_usage_item.bit_pos; 490 uint16_t size = parser->descriptor_usage_item.size; 491 *usage_page = parser->descriptor_usage_item.usage_page; 492 *usage = parser->descriptor_usage_item.usage; 493 494 495 // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 496 bool is_variable = (parser->descriptor_usage_item.descriptor_item.item_value & 2) != 0; 497 bool is_signed = parser->descriptor_usage_item.global_logical_minimum < 0; 498 int pos_start = btstack_min( bit_pos >> 3, parser->report_len); 499 int pos_end = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len); 500 int bytes_to_read = pos_end - pos_start + 1; 501 502 int i; 503 uint32_t multi_byte_value = 0; 504 for (i=0;i < bytes_to_read;i++){ 505 multi_byte_value |= parser->report[pos_start+i] << (i*8); 506 } 507 uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u); 508 // log_debug("bit pos %2u, report size %u, start %u, end %u, len %u;; unsigned value %08x", parser->report_pos_in_bit, parser->global_report_size, pos_start, pos_end, parser->report_len, unsigned_value); 509 if (is_variable){ 510 if (is_signed && (unsigned_value & (1u<<(size-1u)))){ 511 *value = unsigned_value - (1u<<size); 512 } else { 513 *value = unsigned_value; 514 } 515 } else { 516 *usage = unsigned_value; 517 *value = 1; 518 } 519 520 parser->have_report_usage_ready = false; 521 } 522 523 524 // Utility functions 525 526 int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor, 527 uint16_t hid_descriptor_len) { 528 int total_report_size = 0; 529 int report_size = 0; 530 int report_count = 0; 531 int current_report_id = HID_REPORT_ID_UNDEFINED; 532 533 btstack_hid_descriptor_iterator_t iterator; 534 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 535 while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 536 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 537 int valid_report_type = 0; 538 switch (item->item_type) { 539 case Global: 540 switch ((GlobalItemTag) item->item_tag) { 541 case ReportID: 542 current_report_id = item->item_value; 543 break; 544 case ReportCount: 545 report_count = item->item_value; 546 break; 547 case ReportSize: 548 report_size = item->item_value; 549 break; 550 default: 551 break; 552 } 553 break; 554 case Main: 555 if (current_report_id != report_id) break; 556 switch ((MainItemTag) item->item_tag) { 557 case Input: 558 if (report_type != HID_REPORT_TYPE_INPUT) break; 559 valid_report_type = 1; 560 break; 561 case Output: 562 if (report_type != HID_REPORT_TYPE_OUTPUT) break; 563 valid_report_type = 1; 564 break; 565 case Feature: 566 if (report_type != HID_REPORT_TYPE_FEATURE) break; 567 valid_report_type = 1; 568 break; 569 default: 570 break; 571 } 572 if (!valid_report_type) break; 573 total_report_size += report_count * report_size; 574 break; 575 default: 576 break; 577 } 578 if (total_report_size > 0 && current_report_id != report_id) break; 579 } 580 581 if (btstack_hid_descriptor_iterator_valid(&iterator)){ 582 return (total_report_size + 7) / 8; 583 } else { 584 return 0; 585 } 586 } 587 588 hid_report_id_status_t btstack_hid_report_id_valid(uint16_t report_id, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 589 uint16_t current_report_id = HID_REPORT_ID_UNDEFINED; 590 btstack_hid_descriptor_iterator_t iterator; 591 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 592 bool report_id_found = false; 593 while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 594 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 595 switch (item->item_type){ 596 case Global: 597 switch ((GlobalItemTag)item->item_tag){ 598 case ReportID: 599 current_report_id = item->item_value; 600 if (current_report_id == report_id) { 601 report_id_found = true; 602 } 603 default: 604 break; 605 } 606 break; 607 default: 608 break; 609 } 610 } 611 612 if (btstack_hid_descriptor_iterator_valid(&iterator)) { 613 if (report_id_found){ 614 return HID_REPORT_ID_VALID; 615 } 616 if ((report_id == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) { 617 return HID_REPORT_ID_VALID; 618 } 619 return HID_REPORT_ID_UNDECLARED; 620 } else { 621 return HID_REPORT_ID_INVALID; 622 } 623 } 624 625 bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) { 626 btstack_hid_descriptor_iterator_t iterator; 627 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 628 while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 629 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 630 switch (item->item_type){ 631 case Global: 632 switch ((GlobalItemTag)item->item_tag){ 633 case ReportID: 634 return true; 635 default: 636 break; 637 } 638 break; 639 default: 640 break; 641 } 642 } 643 return false; 644 } 645