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 * 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 = 0u; 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", iterator->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 // skip optional Report ID 495 if (parser->descriptor_usage_item.report_id != HID_REPORT_ID_UNDEFINED){ 496 bit_pos += 8; 497 } 498 499 // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 500 bool is_variable = (parser->descriptor_usage_item.descriptor_item.item_value & 2) != 0; 501 bool is_signed = parser->descriptor_usage_item.global_logical_minimum < 0; 502 int pos_start = btstack_min( bit_pos >> 3, parser->report_len); 503 int pos_end = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len); 504 int bytes_to_read = pos_end - pos_start + 1; 505 506 int i; 507 uint32_t multi_byte_value = 0; 508 for (i=0;i < bytes_to_read;i++){ 509 multi_byte_value |= parser->report[pos_start+i] << (i*8); 510 } 511 uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u); 512 // 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); 513 if (is_variable){ 514 if (is_signed && (unsigned_value & (1u<<(size-1u)))){ 515 *value = unsigned_value - (1u<<size); 516 } else { 517 *value = unsigned_value; 518 } 519 } else { 520 *usage = unsigned_value; 521 *value = 1; 522 } 523 524 parser->have_report_usage_ready = false; 525 } 526 527 528 // Utility functions 529 530 int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor, 531 uint16_t hid_descriptor_len) { 532 int total_report_size = 0; 533 int report_size = 0; 534 int report_count = 0; 535 int current_report_id = HID_REPORT_ID_UNDEFINED; 536 537 btstack_hid_descriptor_iterator_t iterator; 538 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 539 while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 540 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 541 int valid_report_type = 0; 542 switch (item->item_type) { 543 case Global: 544 switch ((GlobalItemTag) item->item_tag) { 545 case ReportID: 546 current_report_id = item->item_value; 547 break; 548 case ReportCount: 549 report_count = item->item_value; 550 break; 551 case ReportSize: 552 report_size = item->item_value; 553 break; 554 default: 555 break; 556 } 557 break; 558 case Main: 559 if (current_report_id != report_id) break; 560 switch ((MainItemTag) item->item_tag) { 561 case Input: 562 if (report_type != HID_REPORT_TYPE_INPUT) break; 563 valid_report_type = 1; 564 break; 565 case Output: 566 if (report_type != HID_REPORT_TYPE_OUTPUT) break; 567 valid_report_type = 1; 568 break; 569 case Feature: 570 if (report_type != HID_REPORT_TYPE_FEATURE) break; 571 valid_report_type = 1; 572 break; 573 default: 574 break; 575 } 576 if (!valid_report_type) break; 577 total_report_size += report_count * report_size; 578 break; 579 default: 580 break; 581 } 582 if (total_report_size > 0 && current_report_id != report_id) break; 583 } 584 585 if (btstack_hid_descriptor_iterator_valid(&iterator)){ 586 return (total_report_size + 7) / 8; 587 } else { 588 return 0; 589 } 590 } 591 592 hid_report_id_status_t btstack_hid_report_id_valid(uint16_t report_id, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 593 uint16_t current_report_id = HID_REPORT_ID_UNDEFINED; 594 btstack_hid_descriptor_iterator_t iterator; 595 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 596 bool report_id_found = false; 597 while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 598 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 599 switch (item->item_type){ 600 case Global: 601 switch ((GlobalItemTag)item->item_tag){ 602 case ReportID: 603 current_report_id = item->item_value; 604 if (current_report_id == report_id) { 605 report_id_found = true; 606 } 607 default: 608 break; 609 } 610 break; 611 default: 612 break; 613 } 614 } 615 616 if (btstack_hid_descriptor_iterator_valid(&iterator)) { 617 if (report_id_found){ 618 return HID_REPORT_ID_VALID; 619 } 620 if ((report_id == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) { 621 return HID_REPORT_ID_VALID; 622 } 623 return HID_REPORT_ID_UNDECLARED; 624 } else { 625 return HID_REPORT_ID_INVALID; 626 } 627 } 628 629 bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) { 630 btstack_hid_descriptor_iterator_t iterator; 631 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 632 while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 633 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 634 switch (item->item_type){ 635 case Global: 636 switch ((GlobalItemTag)item->item_tag){ 637 case ReportID: 638 return true; 639 default: 640 break; 641 } 642 break; 643 default: 644 break; 645 } 646 } 647 return false; 648 } 649