xref: /btstack/src/btstack_hid_parser.c (revision 8f9863439ffe34da3929cff894805410b48c6a69)
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 // #define HID_PARSER_PRETTY_PRINT
52 
53 /*
54  *  btstack_hid_parser.c
55  */
56 
57 #ifdef HID_PARSER_PRETTY_PRINT
58 
59 static const char * type_names[] = {
60     "Main",
61     "Global",
62     "Local",
63     "Reserved"
64 };
65 static const char * main_tags[] = {
66     "",
67     "",
68     "",
69     "",
70     "",
71     "",
72     "",
73     "",
74     "Input ",
75     "Output",
76     "Collection",
77     "Feature",
78     "End Collection",
79     "Reserved",
80     "Reserved",
81     "Reserved"
82 };
83 static const char * global_tags[] = {
84     "Usage Page",
85     "Logical Minimum",
86     "Logical Maximum",
87     "Physical Minimum",
88     "Physical Maximum",
89     "Unit Exponent",
90     "Unit",
91     "Report Size",
92     "Report ID",
93     "Report Count",
94     "Push",
95     "Pop",
96     "Reserved",
97     "Reserved",
98     "Reserved",
99     "Reserved"
100 };
101 static const char * local_tags[] = {
102     "Usage",
103     "Usage Minimum",
104     "Usage Maximum",
105     "Designator Index",
106     "Designator Minimum",
107     "Designator Maximum",
108     "String Index",
109     "String Minimum",
110     "String Maximum",
111     "Delimiter",
112     "Reserved",
113     "Reserved",
114     "Reserved",
115     "Reserved",
116     "Reserved",
117     "Reserved"
118 };
119 #endif
120 
121 static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
122 #ifdef HID_PARSER_PRETTY_PRINT
123     const char ** item_tag_table;
124     switch ((TagType)item->item_type){
125         case Main:
126             item_tag_table = main_tags;
127             break;
128         case Global:
129             item_tag_table = global_tags;
130             break;
131         case Local:
132             item_tag_table = local_tags;
133             break;
134         default:
135             item_tag_table = NULL;
136             break;
137     }
138     const char * item_tag_name = "Invalid";
139     if (item_tag_table){
140         item_tag_name = item_tag_table[item->item_tag];
141     }
142     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
143 #else
144     UNUSED(parser);
145     UNUSED(item);
146 #endif
147 }
148 
149 // parse descriptor item and read up to 32-bit bit value
150 bool btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
151 
152     const int hid_item_sizes[] = { 0, 1, 2, 4 };
153 
154     // parse item header
155     if (hid_descriptor_len < 1u) return false;
156     uint16_t pos = 0;
157     uint8_t item_header = hid_descriptor[pos++];
158     item->data_size = hid_item_sizes[item_header & 0x03u];
159     item->item_type = (item_header & 0x0cu) >> 2u;
160     item->item_tag  = (item_header & 0xf0u) >> 4u;
161     // long item
162     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
163         if (hid_descriptor_len < 3u) return false;
164         item->data_size = hid_descriptor[pos++];
165         item->item_tag  = hid_descriptor[pos++];
166     }
167     item->item_size =  pos + item->data_size;
168     item->item_value = 0;
169 
170     // read item value
171     if (hid_descriptor_len < item->item_size) return false;
172     if (item->data_size > 4u) return false;
173     int i;
174     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
175     int32_t value = 0;
176     uint8_t latest_byte = 0;
177     for (i=0;i<item->data_size;i++){
178         latest_byte = hid_descriptor[pos++];
179         value = (latest_byte << (8*i)) | value;
180     }
181     if (sgnd && (item->data_size > 0u)){
182         if (latest_byte & 0x80u) {
183             value -= 1u << (item->data_size*8u);
184         }
185     }
186     item->item_value = value;
187     return true;
188 }
189 
190 static bool btstack_hid_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){
191     switch (tag){
192         case Input:
193             return report_type == HID_REPORT_TYPE_INPUT;
194         case Output:
195             return report_type == HID_REPORT_TYPE_OUTPUT;
196         case Feature:
197             return report_type == HID_REPORT_TYPE_FEATURE;
198         default:
199             return false;
200     }
201 }
202 
203 static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
204     switch((GlobalItemTag)item->item_tag){
205         case UsagePage:
206             parser->global_usage_page = item->item_value;
207             break;
208         case LogicalMinimum:
209             parser->global_logical_minimum = item->item_value;
210             break;
211         case LogicalMaximum:
212             parser->global_logical_maximum = item->item_value;
213             break;
214         case ReportSize:
215             parser->global_report_size = item->item_value;
216             break;
217         case ReportID:
218             parser->global_report_id = item->item_value;
219             break;
220         case ReportCount:
221             parser->global_report_count = item->item_value;
222             break;
223 
224         // TODO handle tags
225         case PhysicalMinimum:
226         case PhysicalMaximum:
227         case UnitExponent:
228         case Unit:
229         case Push:
230         case Pop:
231             break;
232 
233         default:
234             btstack_assert(false);
235             break;
236     }
237 }
238 
239 static void hid_find_next_usage(btstack_hid_parser_t * parser){
240     bool have_usage_min = false;
241     bool have_usage_max = false;
242     parser->usage_range = false;
243     btstack_hid_descriptor_iterator_t iterator;
244     btstack_hid_descriptor_iterator_init(&iterator, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
245     while ((parser->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){
246         hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator);
247         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
248             parser->usage_page = usage_item.item_value;
249         }
250         if (usage_item.item_type == Local){
251             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
252             switch (usage_item.item_tag){
253                 case Usage:
254                     parser->available_usages = 1;
255                     parser->usage_minimum = usage_value;
256                     break;
257                 case UsageMinimum:
258                     parser->usage_minimum = usage_value;
259                     have_usage_min = true;
260                     break;
261                 case UsageMaximum:
262                     parser->usage_maximum = usage_value;
263                     have_usage_max = true;
264                     break;
265                 default:
266                     break;
267             }
268             if (have_usage_min && have_usage_max){
269                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
270                 parser->usage_range = true;
271                 if (parser->available_usages < parser->required_usages){
272                     log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages);
273                 }
274             }
275         }
276     }
277     parser->usage_pos += iterator.descriptor_pos;
278 }
279 
280 static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
281     hid_pretty_print_item(parser, item);
282     int valid_field = 0;
283     uint16_t report_id_before;
284     switch ((TagType)item->item_type){
285         case Main:
286             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
287                                                                         parser->report_type);
288             break;
289         case Global:
290             report_id_before = parser->global_report_id;
291             btstack_hid_handle_global_item(parser, item);
292             // track record id for report handling
293             if ((GlobalItemTag)item->item_tag == ReportID){
294                 if (parser->active_record && (report_id_before != item->item_value)){
295                     parser->active_record = 0;
296                 }
297             }
298             break;
299         case Local:
300         case Reserved:
301             break;
302         default:
303             btstack_assert(false);
304             break;
305     }
306     if (!valid_field) return;
307 
308     // verify record id
309     if (parser->global_report_id && !parser->active_record){
310         if (parser->report[0] != parser->global_report_id){
311             return;
312         }
313         parser->report_pos_in_bit += 8u;
314     }
315     parser->active_record = 1;
316     // handle constant fields used for padding
317     if (item->item_value & 1){
318         int item_bits = parser->global_report_size * parser->global_report_count;
319 #ifdef HID_PARSER_PRETTY_PRINT
320         log_info("- Skip %u constant bits", item_bits);
321 #endif
322         parser->report_pos_in_bit += item_bits;
323         return;
324     }
325     // Empty Item
326     if (parser->global_report_count == 0u) return;
327     // let's start
328     parser->required_usages = parser->global_report_count;
329 }
330 
331 // PUBLIC API
332 
333 // HID Descriptor Iterator
334 
335 void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
336     iterator->descriptor = hid_descriptor;
337     iterator->descriptor_pos = 0;
338     iterator->descriptor_len = hid_descriptor_len;
339     iterator->item_ready = false;
340     iterator->valid = true;
341 }
342 
343 bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
344     if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){
345         uint16_t  item_len = iterator->descriptor_len - iterator->descriptor_pos;
346         const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
347         bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len);
348         if (ok){
349             iterator->item_ready = true;
350         } else {
351             iterator->valid = false;
352         }
353     }
354     return iterator->item_ready;
355 }
356 
357 const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
358     iterator->descriptor_pos += iterator->descriptor_item.item_size;
359     iterator->item_ready = false;
360     return &iterator->descriptor_item;
361 }
362 
363 bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
364     return iterator->valid;
365 }
366 
367 
368 int btstack_hid_get_report_size_for_id(int report_id, hid_report_type_t report_type, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor) {
369     int total_report_size = 0;
370     int report_size = 0;
371     int report_count = 0;
372     int current_report_id = 0;
373 
374     btstack_hid_descriptor_iterator_t iterator;
375     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
376     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
377         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
378         int valid_report_type = 0;
379         switch (item->item_type) {
380             case Global:
381                 switch ((GlobalItemTag) item->item_tag) {
382                     case ReportID:
383                         current_report_id = item->item_value;
384                         break;
385                     case ReportCount:
386                         report_count = item->item_value;
387                         break;
388                     case ReportSize:
389                         report_size = item->item_value;
390                         break;
391                     default:
392                         break;
393                 }
394                 break;
395             case Main:
396                 if (current_report_id != report_id) break;
397                 switch ((MainItemTag) item->item_tag) {
398                     case Input:
399                         if (report_type != HID_REPORT_TYPE_INPUT) break;
400                         valid_report_type = 1;
401                         break;
402                     case Output:
403                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
404                         valid_report_type = 1;
405                         break;
406                     case Feature:
407                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
408                         valid_report_type = 1;
409                         break;
410                     default:
411                         break;
412                 }
413                 if (!valid_report_type) break;
414                 total_report_size += report_count * report_size;
415                 break;
416             default:
417                 break;
418         }
419         if (total_report_size > 0 && current_report_id != report_id) break;
420     }
421 
422     if (btstack_hid_descriptor_iterator_valid(&iterator)){
423         return (total_report_size + 7) / 8;
424     } else {
425         return 0;
426     }
427 }
428 
429 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
430     int current_report_id = -1;
431     btstack_hid_descriptor_iterator_t iterator;
432     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
433     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
434         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
435         switch (item->item_type){
436             case Global:
437                 switch ((GlobalItemTag)item->item_tag){
438                     case ReportID:
439                         current_report_id = item->item_value;
440                         if (current_report_id != report_id) break;
441                         return HID_REPORT_ID_VALID;
442                     default:
443                         break;
444                 }
445                 break;
446             default:
447                 break;
448         }
449     }
450 
451     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
452         if (current_report_id != -1) {
453             return HID_REPORT_ID_INVALID;
454         } else {
455             return HID_REPORT_ID_UNDECLARED;
456         }
457     } else {
458         return HID_REPORT_ID_INVALID;
459     }
460 }
461 
462 bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
463     btstack_hid_descriptor_iterator_t iterator;
464     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
465     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
466         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
467         switch (item->item_type){
468             case Global:
469                 switch ((GlobalItemTag)item->item_tag){
470                     case ReportID:
471                         return true;
472                     default:
473                         break;
474                 }
475                 break;
476             default:
477                 break;
478         }
479     }
480     return false;
481 }
482 
483 // HID Descriptor Usage Iterator
484 
485 static void btstack_parser_usage_iterator_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
486     hid_pretty_print_item(parser, item);
487     int valid_field = 0;
488     uint16_t report_id_before;
489     switch ((TagType)item->item_type){
490         case Main:
491             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
492                                                                         parser->report_type);
493             break;
494         case Global:
495             report_id_before = parser->global_report_id;
496             btstack_hid_handle_global_item(parser, item);
497             // track record id for report handling, reset report position
498             if (report_id_before != parser->global_report_id){
499                 parser->report_pos_in_bit = 8u;
500             }
501             break;
502         case Local:
503         case Reserved:
504             break;
505         default:
506             btstack_assert(false);
507             break;
508     }
509     if (!valid_field) return;
510 
511     // handle constant fields used for padding
512     if (item->item_value & 1){
513         int item_bits = parser->global_report_size * parser->global_report_count;
514 #ifdef HID_PARSER_PRETTY_PRINT
515         log_info("- Skip %u constant bits", item_bits);
516 #endif
517         parser->report_pos_in_bit += item_bits;
518         return;
519     }
520     // Empty Item
521     if (parser->global_report_count == 0u) return;
522     // let's start
523     parser->required_usages = parser->global_report_count;
524 }
525 
526 static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_parser_t * parser) {
527     while (btstack_hid_descriptor_iterator_has_more(&parser->descriptor_iterator)){
528         parser->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&parser->descriptor_iterator);
529 
530         btstack_parser_usage_iterator_process_item(parser, &parser->descriptor_item);
531 
532         if (parser->required_usages){
533             hid_find_next_usage(parser);
534             if (parser->available_usages) {
535                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
536                 return;
537             } else {
538                 log_debug("no usages found");
539                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
540                 return;
541             }
542         } else {
543             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
544                 // reset usage
545                 parser->usage_pos = parser->descriptor_iterator.descriptor_pos;
546                 parser->usage_page = parser->global_usage_page;
547             }
548         }
549     }
550     // end of descriptor
551     parser->state = BTSTACK_HID_PARSER_COMPLETE;
552 }
553 
554 void btstack_hid_usage_iterator_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){
555     memset(parser, 0, sizeof(btstack_hid_parser_t));
556 
557     parser->descriptor     = hid_descriptor;
558     parser->descriptor_len = hid_descriptor_len;
559     parser->report_type    = hid_report_type;
560     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
561     parser->global_report_id = 0xffff;
562     btstack_hid_descriptor_iterator_init(&parser->descriptor_iterator, hid_descriptor, hid_descriptor_len);
563 
564     btstack_hid_usage_iterator_find_next_usage(parser);
565 }
566 
567 bool btstack_hid_usage_iterator_has_more(btstack_hid_parser_t * parser){
568     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
569 }
570 
571 void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_hid_usage_item_t * item){
572     // cache current values
573     memset(item, 0, sizeof(btstack_hid_usage_item_t));
574     item->size = parser->global_report_size;
575     item->report_id = parser->global_report_id;
576     item->usage_page = parser->usage_minimum >> 16;
577     item->bit_pos = parser->report_pos_in_bit;
578 
579     bool is_variable  = (parser->descriptor_item.item_value & 2) != 0;
580     if (is_variable){
581         item->usage = parser->usage_minimum & 0xffffu;
582     }
583     parser->required_usages--;
584     parser->report_pos_in_bit += parser->global_report_size;
585 
586     // cache descriptor item and
587     item->descriptor_item = parser->descriptor_item;
588     item->global_logical_minimum = parser->global_logical_minimum;
589 
590     // next usage
591     if (is_variable){
592         parser->usage_minimum++;
593         parser->available_usages--;
594         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
595             // usage min - max range smaller than report count, ignore remaining bit in report
596             log_debug("Ignoring %u items without Usage", parser->required_usages);
597             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
598             parser->required_usages = 0;
599         }
600     } else {
601         if (parser->required_usages == 0u){
602             parser->available_usages = 0;
603         }
604     }
605     if (parser->available_usages) {
606         return;
607     }
608     if (parser->required_usages == 0u){
609         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
610         btstack_hid_usage_iterator_find_next_usage(parser);
611     } else {
612         hid_find_next_usage(parser);
613         if (parser->available_usages == 0u) {
614             parser->state = BTSTACK_HID_PARSER_COMPLETE;
615         }
616     }
617 }
618 
619 
620 // HID Report Parser
621 
622 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){
623     btstack_hid_usage_iterator_init(parser, hid_descriptor, hid_descriptor_len, hid_report_type);
624     parser->report = hid_report;
625     parser->report_len = hid_report_len;
626     parser->have_report_usage_ready = false;
627 }
628 
629 /**
630  * @brief Checks if more fields are available
631  * @param parser
632  */
633 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
634     while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(parser)){
635         btstack_hid_usage_iterator_get_item(parser, &parser->descriptor__usage_item);
636         // ignore usages for other report ids
637         if (parser->descriptor__usage_item.report_id != 0xffff){
638             if (parser->descriptor__usage_item.report_id != parser->report[0]){
639                 continue;
640             }
641         }
642         parser->have_report_usage_ready = true;
643     }
644     return parser->have_report_usage_ready;
645 }
646 
647 /**
648  * @brief Get next field
649  * @param parser
650  * @param usage_page
651  * @param usage
652  * @param value provided in HID report
653  */
654 
655 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
656 
657     // fetch data from descriptor usage item
658     uint16_t bit_pos = parser->descriptor__usage_item.bit_pos;
659     uint16_t size    = parser->descriptor__usage_item.size;
660     *usage_page      = parser->descriptor__usage_item.usage_page;
661     *usage           = parser->descriptor__usage_item.usage;
662 
663 
664     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
665     bool is_variable   = (parser->descriptor__usage_item.descriptor_item.item_value & 2) != 0;
666     bool is_signed     = parser->descriptor__usage_item.global_logical_minimum < 0;
667     int pos_start     = btstack_min(  bit_pos >> 3, parser->report_len);
668     int pos_end       = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len);
669     int bytes_to_read = pos_end - pos_start + 1;
670 
671     int i;
672     uint32_t multi_byte_value = 0;
673     for (i=0;i < bytes_to_read;i++){
674         multi_byte_value |= parser->report[pos_start+i] << (i*8);
675     }
676     uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u);
677     // 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);
678     if (is_variable){
679         if (is_signed && (unsigned_value & (1u<<(size-1u)))){
680             *value = unsigned_value - (1u<<size);
681         } else {
682             *value = unsigned_value;
683         }
684     } else {
685         *usage  = unsigned_value;
686         *value  = 1;
687     }
688 
689     parser->have_report_usage_ready = false;
690 }
691