xref: /btstack/src/btstack_hid_parser.c (revision a05e6df3cc4a3f040a8383ac398c2ac1e51c3b9e)
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