xref: /btstack/src/btstack_hid_parser.c (revision 279225e6c756a3f523908834851ec949fb4f54f4)
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(uint16_t 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 = HID_REPORT_ID_UNDEFINED;
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_report_id_valid(uint16_t report_id, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
430     uint16_t current_report_id = HID_REPORT_ID_UNDEFINED;
431     btstack_hid_descriptor_iterator_t iterator;
432     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
433     bool report_id_found = false;
434     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
435         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
436         switch (item->item_type){
437             case Global:
438                 switch ((GlobalItemTag)item->item_tag){
439                     case ReportID:
440                         current_report_id = item->item_value;
441                         if (current_report_id == report_id) {
442                             report_id_found = true;
443                         }
444                     default:
445                         break;
446                 }
447                 break;
448             default:
449                 break;
450         }
451     }
452 
453     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
454         if (report_id_found){
455             return HID_REPORT_ID_VALID;
456         }
457         if ((report_id  == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) {
458             return HID_REPORT_ID_VALID;
459         }
460         return HID_REPORT_ID_UNDECLARED;
461     } else {
462         return HID_REPORT_ID_INVALID;
463     }
464 }
465 
466 bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
467     btstack_hid_descriptor_iterator_t iterator;
468     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
469     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
470         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
471         switch (item->item_type){
472             case Global:
473                 switch ((GlobalItemTag)item->item_tag){
474                     case ReportID:
475                         return true;
476                     default:
477                         break;
478                 }
479                 break;
480             default:
481                 break;
482         }
483     }
484     return false;
485 }
486 
487 // HID Descriptor Usage Iterator
488 
489 static void btstack_parser_usage_iterator_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
490     hid_pretty_print_item(parser, item);
491     int valid_field = 0;
492     uint16_t report_id_before;
493     switch ((TagType)item->item_type){
494         case Main:
495             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
496                                                                         parser->report_type);
497             break;
498         case Global:
499             report_id_before = parser->global_report_id;
500             btstack_hid_handle_global_item(parser, item);
501             // track record id for report handling, reset report position
502             if (report_id_before != parser->global_report_id){
503                 parser->report_pos_in_bit = 8u;
504             }
505             break;
506         case Local:
507         case Reserved:
508             break;
509         default:
510             btstack_assert(false);
511             break;
512     }
513     if (!valid_field) return;
514 
515     // handle constant fields used for padding
516     if (item->item_value & 1){
517         int item_bits = parser->global_report_size * parser->global_report_count;
518 #ifdef HID_PARSER_PRETTY_PRINT
519         log_info("- Skip %u constant bits", item_bits);
520 #endif
521         parser->report_pos_in_bit += item_bits;
522         return;
523     }
524     // Empty Item
525     if (parser->global_report_count == 0u) return;
526     // let's start
527     parser->required_usages = parser->global_report_count;
528 }
529 
530 static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_parser_t * parser) {
531     while (btstack_hid_descriptor_iterator_has_more(&parser->descriptor_iterator)){
532         parser->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&parser->descriptor_iterator);
533 
534         btstack_parser_usage_iterator_process_item(parser, &parser->descriptor_item);
535 
536         if (parser->required_usages){
537             hid_find_next_usage(parser);
538             if (parser->available_usages) {
539                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
540                 return;
541             } else {
542                 log_debug("no usages found");
543                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
544                 return;
545             }
546         } else {
547             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
548                 // reset usage
549                 parser->usage_pos = parser->descriptor_iterator.descriptor_pos;
550                 parser->usage_page = parser->global_usage_page;
551             }
552         }
553     }
554     // end of descriptor
555     parser->state = BTSTACK_HID_PARSER_COMPLETE;
556 }
557 
558 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){
559     memset(parser, 0, sizeof(btstack_hid_parser_t));
560 
561     parser->descriptor     = hid_descriptor;
562     parser->descriptor_len = hid_descriptor_len;
563     parser->report_type    = hid_report_type;
564     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
565     parser->global_report_id = HID_REPORT_ID_UNDEFINED;
566     btstack_hid_descriptor_iterator_init(&parser->descriptor_iterator, hid_descriptor, hid_descriptor_len);
567 
568     btstack_hid_usage_iterator_find_next_usage(parser);
569 }
570 
571 bool btstack_hid_usage_iterator_has_more(btstack_hid_parser_t * parser){
572     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
573 }
574 
575 void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_hid_usage_item_t * item){
576     // cache current values
577     memset(item, 0, sizeof(btstack_hid_usage_item_t));
578     item->size = parser->global_report_size;
579     item->report_id = parser->global_report_id;
580     item->usage_page = parser->usage_minimum >> 16;
581     item->bit_pos = parser->report_pos_in_bit;
582 
583     bool is_variable  = (parser->descriptor_item.item_value & 2) != 0;
584     if (is_variable){
585         item->usage = parser->usage_minimum & 0xffffu;
586     }
587     parser->required_usages--;
588     parser->report_pos_in_bit += parser->global_report_size;
589 
590     // cache descriptor item and
591     item->descriptor_item = parser->descriptor_item;
592     item->global_logical_minimum = parser->global_logical_minimum;
593 
594     // next usage
595     if (is_variable){
596         parser->usage_minimum++;
597         parser->available_usages--;
598         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
599             // usage min - max range smaller than report count, ignore remaining bit in report
600             log_debug("Ignoring %u items without Usage", parser->required_usages);
601             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
602             parser->required_usages = 0;
603         }
604     } else {
605         if (parser->required_usages == 0u){
606             parser->available_usages = 0;
607         }
608     }
609     if (parser->available_usages) {
610         return;
611     }
612     if (parser->required_usages == 0u){
613         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
614         btstack_hid_usage_iterator_find_next_usage(parser);
615     } else {
616         hid_find_next_usage(parser);
617         if (parser->available_usages == 0u) {
618             parser->state = BTSTACK_HID_PARSER_COMPLETE;
619         }
620     }
621 }
622 
623 
624 // HID Report Parser
625 
626 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){
627     btstack_hid_usage_iterator_init(parser, hid_descriptor, hid_descriptor_len, hid_report_type);
628     parser->report = hid_report;
629     parser->report_len = hid_report_len;
630     parser->have_report_usage_ready = false;
631 }
632 
633 /**
634  * @brief Checks if more fields are available
635  * @param parser
636  */
637 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
638     while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(parser)){
639         btstack_hid_usage_iterator_get_item(parser, &parser->descriptor__usage_item);
640         // ignore usages for other report ids
641         if (parser->descriptor__usage_item.report_id != HID_REPORT_ID_UNDEFINED){
642             if (parser->descriptor__usage_item.report_id != parser->report[0]){
643                 continue;
644             }
645         }
646         parser->have_report_usage_ready = true;
647     }
648     return parser->have_report_usage_ready;
649 }
650 
651 /**
652  * @brief Get next field
653  * @param parser
654  * @param usage_page
655  * @param usage
656  * @param value provided in HID report
657  */
658 
659 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
660 
661     // fetch data from descriptor usage item
662     uint16_t bit_pos = parser->descriptor__usage_item.bit_pos;
663     uint16_t size    = parser->descriptor__usage_item.size;
664     *usage_page      = parser->descriptor__usage_item.usage_page;
665     *usage           = parser->descriptor__usage_item.usage;
666 
667 
668     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
669     bool is_variable   = (parser->descriptor__usage_item.descriptor_item.item_value & 2) != 0;
670     bool is_signed     = parser->descriptor__usage_item.global_logical_minimum < 0;
671     int pos_start     = btstack_min(  bit_pos >> 3, parser->report_len);
672     int pos_end       = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len);
673     int bytes_to_read = pos_end - pos_start + 1;
674 
675     int i;
676     uint32_t multi_byte_value = 0;
677     for (i=0;i < bytes_to_read;i++){
678         multi_byte_value |= parser->report[pos_start+i] << (i*8);
679     }
680     uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u);
681     // 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);
682     if (is_variable){
683         if (is_signed && (unsigned_value & (1u<<(size-1u)))){
684             *value = unsigned_value - (1u<<size);
685         } else {
686             *value = unsigned_value;
687         }
688     } else {
689         *usage  = unsigned_value;
690         *value  = 1;
691     }
692 
693     parser->have_report_usage_ready = false;
694 }
695