xref: /btstack/src/btstack_hid_parser.c (revision 6897da5c53aac5b1f90f41b5b15d0bd43d61dfff)
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 = 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", 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     // 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