xref: /btstack/src/btstack_hid_parser.c (revision 5dd091fdb91b5afc30cc5e35582e0400243e6fe9)
112ccb71bSMatthias Ringwald /*
212ccb71bSMatthias Ringwald  * Copyright (C) 2017 BlueKitchen GmbH
312ccb71bSMatthias Ringwald  *
412ccb71bSMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
512ccb71bSMatthias Ringwald  * modification, are permitted provided that the following conditions
612ccb71bSMatthias Ringwald  * are met:
712ccb71bSMatthias Ringwald  *
812ccb71bSMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
912ccb71bSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
1012ccb71bSMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1112ccb71bSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
1212ccb71bSMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
1312ccb71bSMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
1412ccb71bSMatthias Ringwald  *    contributors may be used to endorse or promote products derived
1512ccb71bSMatthias Ringwald  *    from this software without specific prior written permission.
1612ccb71bSMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
1712ccb71bSMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
1812ccb71bSMatthias Ringwald  *    monetary gain.
1912ccb71bSMatthias Ringwald  *
2012ccb71bSMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2112ccb71bSMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2212ccb71bSMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2512ccb71bSMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2612ccb71bSMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2712ccb71bSMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2812ccb71bSMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2912ccb71bSMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3012ccb71bSMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3112ccb71bSMatthias Ringwald  * SUCH DAMAGE.
3212ccb71bSMatthias Ringwald  *
3312ccb71bSMatthias Ringwald  * Please inquire about commercial licensing options at
3412ccb71bSMatthias Ringwald  * [email protected]
3512ccb71bSMatthias Ringwald  *
3612ccb71bSMatthias Ringwald  */
3712ccb71bSMatthias Ringwald 
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "btstack_hid_parser.c"
3912ccb71bSMatthias Ringwald 
40cb406331SDirk Helbig #include <inttypes.h>
4112ccb71bSMatthias Ringwald #include <string.h>
4212ccb71bSMatthias Ringwald 
4312ccb71bSMatthias Ringwald #include "btstack_hid_parser.h"
4412ccb71bSMatthias Ringwald #include "btstack_util.h"
4512ccb71bSMatthias Ringwald #include "btstack_debug.h"
4612ccb71bSMatthias Ringwald 
4712ccb71bSMatthias Ringwald // Not implemented:
4812ccb71bSMatthias Ringwald // - Support for Push/Pop
490ba748f2SMatthias Ringwald // - Optional Pretty Print of HID Descriptor
5012ccb71bSMatthias Ringwald 
5112ccb71bSMatthias Ringwald /*
5212ccb71bSMatthias Ringwald  *  btstack_hid_parser.c
5312ccb71bSMatthias Ringwald  */
5412ccb71bSMatthias Ringwald 
5512ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
5612ccb71bSMatthias Ringwald 
5712ccb71bSMatthias Ringwald static const char * type_names[] = {
5812ccb71bSMatthias Ringwald     "Main",
5912ccb71bSMatthias Ringwald     "Global",
6012ccb71bSMatthias Ringwald     "Local",
6112ccb71bSMatthias Ringwald     "Reserved"
6212ccb71bSMatthias Ringwald };
6312ccb71bSMatthias Ringwald static const char * main_tags[] = {
6412ccb71bSMatthias Ringwald     "",
6512ccb71bSMatthias Ringwald     "",
6612ccb71bSMatthias Ringwald     "",
6712ccb71bSMatthias Ringwald     "",
6812ccb71bSMatthias Ringwald     "",
6912ccb71bSMatthias Ringwald     "",
7012ccb71bSMatthias Ringwald     "",
7112ccb71bSMatthias Ringwald     "",
7212ccb71bSMatthias Ringwald     "Input ",
7312ccb71bSMatthias Ringwald     "Output",
7412ccb71bSMatthias Ringwald     "Collection",
7512ccb71bSMatthias Ringwald     "Feature",
7612ccb71bSMatthias Ringwald     "End Collection",
7712ccb71bSMatthias Ringwald     "Reserved",
7812ccb71bSMatthias Ringwald     "Reserved",
7912ccb71bSMatthias Ringwald     "Reserved"
8012ccb71bSMatthias Ringwald };
8112ccb71bSMatthias Ringwald static const char * global_tags[] = {
8212ccb71bSMatthias Ringwald     "Usage Page",
8312ccb71bSMatthias Ringwald     "Logical Minimum",
8412ccb71bSMatthias Ringwald     "Logical Maximum",
8512ccb71bSMatthias Ringwald     "Physical Minimum",
8612ccb71bSMatthias Ringwald     "Physical Maximum",
8712ccb71bSMatthias Ringwald     "Unit Exponent",
8812ccb71bSMatthias Ringwald     "Unit",
8912ccb71bSMatthias Ringwald     "Report Size",
9012ccb71bSMatthias Ringwald     "Report ID",
9112ccb71bSMatthias Ringwald     "Report Count",
9212ccb71bSMatthias Ringwald     "Push",
9312ccb71bSMatthias Ringwald     "Pop",
9412ccb71bSMatthias Ringwald     "Reserved",
9512ccb71bSMatthias Ringwald     "Reserved",
9612ccb71bSMatthias Ringwald     "Reserved",
9712ccb71bSMatthias Ringwald     "Reserved"
9812ccb71bSMatthias Ringwald };
9912ccb71bSMatthias Ringwald static const char * local_tags[] = {
10012ccb71bSMatthias Ringwald     "Usage",
10112ccb71bSMatthias Ringwald     "Usage Minimum",
10212ccb71bSMatthias Ringwald     "Usage Maximum",
10312ccb71bSMatthias Ringwald     "Designator Index",
10412ccb71bSMatthias Ringwald     "Designator Minimum",
10512ccb71bSMatthias Ringwald     "Designator Maximum",
10612ccb71bSMatthias Ringwald     "String Index",
10712ccb71bSMatthias Ringwald     "String Minimum",
10812ccb71bSMatthias Ringwald     "String Maximum",
10912ccb71bSMatthias Ringwald     "Delimiter",
11012ccb71bSMatthias Ringwald     "Reserved",
11112ccb71bSMatthias Ringwald     "Reserved",
11212ccb71bSMatthias Ringwald     "Reserved",
11312ccb71bSMatthias Ringwald     "Reserved",
11412ccb71bSMatthias Ringwald     "Reserved",
11512ccb71bSMatthias Ringwald     "Reserved"
11612ccb71bSMatthias Ringwald };
11712ccb71bSMatthias Ringwald #endif
11812ccb71bSMatthias Ringwald 
119*5dd091fdSMatthias Ringwald // HID Descriptor Iterator
12012ccb71bSMatthias Ringwald 
12112ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value
122*5dd091fdSMatthias Ringwald static bool btstack_hid_descriptor_parse_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
1238334d3d8SMatthias Ringwald 
1248334d3d8SMatthias Ringwald     const int hid_item_sizes[] = { 0, 1, 2, 4 };
1258334d3d8SMatthias Ringwald 
12612ccb71bSMatthias Ringwald     // parse item header
12776fa2448SMatthias Ringwald     if (hid_descriptor_len < 1u) return false;
12812ccb71bSMatthias Ringwald     uint16_t pos = 0;
12912ccb71bSMatthias Ringwald     uint8_t item_header = hid_descriptor[pos++];
1304ea43905SMatthias Ringwald     item->data_size = hid_item_sizes[item_header & 0x03u];
1314ea43905SMatthias Ringwald     item->item_type = (item_header & 0x0cu) >> 2u;
1324ea43905SMatthias Ringwald     item->item_tag  = (item_header & 0xf0u) >> 4u;
13312ccb71bSMatthias Ringwald     // long item
1344ea43905SMatthias Ringwald     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
13576fa2448SMatthias Ringwald         if (hid_descriptor_len < 3u) return false;
13612ccb71bSMatthias Ringwald         item->data_size = hid_descriptor[pos++];
13712ccb71bSMatthias Ringwald         item->item_tag  = hid_descriptor[pos++];
13812ccb71bSMatthias Ringwald     }
13912ccb71bSMatthias Ringwald     item->item_size =  pos + item->data_size;
14012ccb71bSMatthias Ringwald     item->item_value = 0;
14112ccb71bSMatthias Ringwald 
14212ccb71bSMatthias Ringwald     // read item value
14376fa2448SMatthias Ringwald     if (hid_descriptor_len < item->item_size) return false;
14476fa2448SMatthias Ringwald     if (item->data_size > 4u) return false;
14512ccb71bSMatthias Ringwald     int i;
1464ea43905SMatthias Ringwald     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
14712ccb71bSMatthias Ringwald     int32_t value = 0;
14812ccb71bSMatthias Ringwald     uint8_t latest_byte = 0;
14912ccb71bSMatthias Ringwald     for (i=0;i<item->data_size;i++){
15012ccb71bSMatthias Ringwald         latest_byte = hid_descriptor[pos++];
15112ccb71bSMatthias Ringwald         value = (latest_byte << (8*i)) | value;
15212ccb71bSMatthias Ringwald     }
1534ea43905SMatthias Ringwald     if (sgnd && (item->data_size > 0u)){
1544ea43905SMatthias Ringwald         if (latest_byte & 0x80u) {
1554ea43905SMatthias Ringwald             value -= 1u << (item->data_size*8u);
15612ccb71bSMatthias Ringwald         }
15712ccb71bSMatthias Ringwald     }
15812ccb71bSMatthias Ringwald     item->item_value = value;
15976fa2448SMatthias Ringwald     return true;
16012ccb71bSMatthias Ringwald }
16112ccb71bSMatthias Ringwald 
162*5dd091fdSMatthias Ringwald static void btstack_hid_descriptor_iterator_pretty_print_item(btstack_hid_descriptor_iterator_t * iterator, hid_descriptor_item_t * item){
163*5dd091fdSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
164*5dd091fdSMatthias Ringwald     const char ** item_tag_table;
165*5dd091fdSMatthias Ringwald     switch ((TagType)item->item_type){
166*5dd091fdSMatthias Ringwald         case Main:
167*5dd091fdSMatthias Ringwald             item_tag_table = main_tags;
168*5dd091fdSMatthias Ringwald             break;
169*5dd091fdSMatthias Ringwald         case Global:
170*5dd091fdSMatthias Ringwald             item_tag_table = global_tags;
171*5dd091fdSMatthias Ringwald             break;
172*5dd091fdSMatthias Ringwald         case Local:
173*5dd091fdSMatthias Ringwald             item_tag_table = local_tags;
174*5dd091fdSMatthias Ringwald             break;
175*5dd091fdSMatthias Ringwald         default:
176*5dd091fdSMatthias Ringwald             item_tag_table = NULL;
177*5dd091fdSMatthias Ringwald             break;
178*5dd091fdSMatthias Ringwald     }
179*5dd091fdSMatthias Ringwald     const char * item_tag_name = "Invalid";
180*5dd091fdSMatthias Ringwald     if (item_tag_table){
181*5dd091fdSMatthias Ringwald         item_tag_name = item_tag_table[item->item_tag];
182*5dd091fdSMatthias Ringwald     }
183*5dd091fdSMatthias Ringwald     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value);
184*5dd091fdSMatthias Ringwald     printf("%-15s (%-6s) // %02x 0x%0008x\n", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value);
185*5dd091fdSMatthias Ringwald #else
186*5dd091fdSMatthias Ringwald     UNUSED(iterator);
187*5dd091fdSMatthias Ringwald     UNUSED(item);
188*5dd091fdSMatthias Ringwald #endif
189*5dd091fdSMatthias Ringwald }
190*5dd091fdSMatthias Ringwald 
191*5dd091fdSMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
192*5dd091fdSMatthias Ringwald     iterator->descriptor = hid_descriptor;
193*5dd091fdSMatthias Ringwald     iterator->descriptor_pos = 0;
194*5dd091fdSMatthias Ringwald     iterator->descriptor_len = hid_descriptor_len;
195*5dd091fdSMatthias Ringwald     iterator->item_ready = false;
196*5dd091fdSMatthias Ringwald     iterator->valid = true;
197*5dd091fdSMatthias Ringwald }
198*5dd091fdSMatthias Ringwald 
199*5dd091fdSMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
200*5dd091fdSMatthias Ringwald     if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){
201*5dd091fdSMatthias Ringwald         uint16_t  item_len = iterator->descriptor_len - iterator->descriptor_pos;
202*5dd091fdSMatthias Ringwald         const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
203*5dd091fdSMatthias Ringwald         bool ok = btstack_hid_descriptor_parse_item(&iterator->descriptor_item, item_data, item_len);
204*5dd091fdSMatthias Ringwald         btstack_hid_descriptor_iterator_pretty_print_item(iterator, &iterator->descriptor_item);
205*5dd091fdSMatthias Ringwald         if (ok){
206*5dd091fdSMatthias Ringwald             iterator->item_ready = true;
207*5dd091fdSMatthias Ringwald         } else {
208*5dd091fdSMatthias Ringwald             iterator->valid = false;
209*5dd091fdSMatthias Ringwald         }
210*5dd091fdSMatthias Ringwald     }
211*5dd091fdSMatthias Ringwald     return iterator->item_ready;
212*5dd091fdSMatthias Ringwald }
213*5dd091fdSMatthias Ringwald 
214*5dd091fdSMatthias Ringwald const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
215*5dd091fdSMatthias Ringwald     iterator->descriptor_pos += iterator->descriptor_item.item_size;
216*5dd091fdSMatthias Ringwald     iterator->item_ready = false;
217*5dd091fdSMatthias Ringwald     return &iterator->descriptor_item;
218*5dd091fdSMatthias Ringwald }
219*5dd091fdSMatthias Ringwald 
220*5dd091fdSMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
221*5dd091fdSMatthias Ringwald     return iterator->valid;
222*5dd091fdSMatthias Ringwald }
223*5dd091fdSMatthias Ringwald 
224*5dd091fdSMatthias Ringwald // HID Descriptor Usage Iterator
225*5dd091fdSMatthias Ringwald 
226*5dd091fdSMatthias Ringwald static bool btstack_hid_usage_iterator_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){
2270a4fc5fbSMatthias Ringwald     switch (tag){
2280a4fc5fbSMatthias Ringwald         case Input:
2290a4fc5fbSMatthias Ringwald             return report_type == HID_REPORT_TYPE_INPUT;
2300a4fc5fbSMatthias Ringwald         case Output:
2310a4fc5fbSMatthias Ringwald             return report_type == HID_REPORT_TYPE_OUTPUT;
2320a4fc5fbSMatthias Ringwald         case Feature:
2330a4fc5fbSMatthias Ringwald             return report_type == HID_REPORT_TYPE_FEATURE;
2340a4fc5fbSMatthias Ringwald         default:
2350a4fc5fbSMatthias Ringwald             return false;
2360a4fc5fbSMatthias Ringwald     }
2370a4fc5fbSMatthias Ringwald }
2380a4fc5fbSMatthias Ringwald 
239*5dd091fdSMatthias Ringwald static void btstack_hid_usage_iterator_handle_global_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
2407bbeb3adSMilanka Ringwald     switch((GlobalItemTag)item->item_tag){
24112ccb71bSMatthias Ringwald         case UsagePage:
242c4241e61SMatthias Ringwald             iterator->global_usage_page = item->item_value;
24312ccb71bSMatthias Ringwald             break;
24412ccb71bSMatthias Ringwald         case LogicalMinimum:
245c4241e61SMatthias Ringwald             iterator->global_logical_minimum = item->item_value;
24612ccb71bSMatthias Ringwald             break;
24712ccb71bSMatthias Ringwald         case LogicalMaximum:
248c4241e61SMatthias Ringwald             iterator->global_logical_maximum = item->item_value;
24912ccb71bSMatthias Ringwald             break;
25012ccb71bSMatthias Ringwald         case ReportSize:
251c4241e61SMatthias Ringwald             iterator->global_report_size = item->item_value;
25212ccb71bSMatthias Ringwald             break;
25312ccb71bSMatthias Ringwald         case ReportID:
254c4241e61SMatthias Ringwald             iterator->global_report_id = item->item_value;
25512ccb71bSMatthias Ringwald             break;
25612ccb71bSMatthias Ringwald         case ReportCount:
257c4241e61SMatthias Ringwald             iterator->global_report_count = item->item_value;
25812ccb71bSMatthias Ringwald             break;
2597bbeb3adSMilanka Ringwald 
2607bbeb3adSMilanka Ringwald         // TODO handle tags
2617bbeb3adSMilanka Ringwald         case PhysicalMinimum:
2627bbeb3adSMilanka Ringwald         case PhysicalMaximum:
2637bbeb3adSMilanka Ringwald         case UnitExponent:
2647bbeb3adSMilanka Ringwald         case Unit:
2657bbeb3adSMilanka Ringwald         case Push:
2667bbeb3adSMilanka Ringwald         case Pop:
2677bbeb3adSMilanka Ringwald             break;
2687bbeb3adSMilanka Ringwald 
26912ccb71bSMatthias Ringwald         default:
2707bbeb3adSMilanka Ringwald             btstack_assert(false);
27112ccb71bSMatthias Ringwald             break;
27212ccb71bSMatthias Ringwald     }
27312ccb71bSMatthias Ringwald }
27412ccb71bSMatthias Ringwald 
275*5dd091fdSMatthias Ringwald static void btstack_usage_iterator_hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator){
2761a05cde1SMatthias Ringwald     bool have_usage_min = false;
2771a05cde1SMatthias Ringwald     bool have_usage_max = false;
278c4241e61SMatthias Ringwald     main_iterator->usage_range = false;
27941afcabaSMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
280c4241e61SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, &main_iterator->descriptor[main_iterator->usage_pos], main_iterator->descriptor_len - main_iterator->usage_pos);
281c4241e61SMatthias Ringwald     while ((main_iterator->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){
28241afcabaSMatthias Ringwald         hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator);
2830e588213SMatthias Ringwald         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
284c4241e61SMatthias Ringwald             main_iterator->usage_page = usage_item.item_value;
28512ccb71bSMatthias Ringwald         }
28612ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
287c4241e61SMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((main_iterator->usage_page << 16u) | usage_item.item_value);
28812ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
28912ccb71bSMatthias Ringwald                 case Usage:
290c4241e61SMatthias Ringwald                     main_iterator->available_usages = 1;
291c4241e61SMatthias Ringwald                     main_iterator->usage_minimum = usage_value;
29212ccb71bSMatthias Ringwald                     break;
29312ccb71bSMatthias Ringwald                 case UsageMinimum:
294c4241e61SMatthias Ringwald                     main_iterator->usage_minimum = usage_value;
2951a05cde1SMatthias Ringwald                     have_usage_min = true;
29612ccb71bSMatthias Ringwald                     break;
29712ccb71bSMatthias Ringwald                 case UsageMaximum:
298c4241e61SMatthias Ringwald                     main_iterator->usage_maximum = usage_value;
2991a05cde1SMatthias Ringwald                     have_usage_max = true;
30012ccb71bSMatthias Ringwald                     break;
30112ccb71bSMatthias Ringwald                 default:
30212ccb71bSMatthias Ringwald                     break;
30312ccb71bSMatthias Ringwald             }
3041a05cde1SMatthias Ringwald             if (have_usage_min && have_usage_max){
305c4241e61SMatthias Ringwald                 main_iterator->available_usages = main_iterator->usage_maximum - main_iterator->usage_minimum + 1u;
306c4241e61SMatthias Ringwald                 main_iterator->usage_range = true;
307c4241e61SMatthias Ringwald                 if (main_iterator->available_usages < main_iterator->required_usages){
308c4241e61SMatthias Ringwald                     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);
309dfb01e77SMatthias Ringwald                 }
31012ccb71bSMatthias Ringwald             }
31112ccb71bSMatthias Ringwald         }
31212ccb71bSMatthias Ringwald     }
313c4241e61SMatthias Ringwald     main_iterator->usage_pos += iterator.descriptor_pos;
31412ccb71bSMatthias Ringwald }
31512ccb71bSMatthias Ringwald 
316*5dd091fdSMatthias Ringwald static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
317*5dd091fdSMatthias Ringwald     int valid_field = 0;
318*5dd091fdSMatthias Ringwald     uint16_t report_id_before;
319*5dd091fdSMatthias Ringwald     switch ((TagType)item->item_type){
320*5dd091fdSMatthias Ringwald         case Main:
321*5dd091fdSMatthias Ringwald             valid_field = btstack_hid_usage_iterator_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
322*5dd091fdSMatthias Ringwald                                                                                        iterator->report_type);
323*5dd091fdSMatthias Ringwald             break;
324*5dd091fdSMatthias Ringwald         case Global:
325*5dd091fdSMatthias Ringwald             report_id_before = iterator->global_report_id;
326*5dd091fdSMatthias Ringwald             btstack_hid_usage_iterator_handle_global_item(iterator, item);
327*5dd091fdSMatthias Ringwald             // track record id for report handling, reset report position
328*5dd091fdSMatthias Ringwald             if (report_id_before != iterator->global_report_id){
329*5dd091fdSMatthias Ringwald                 iterator->report_pos_in_bit = 8u;
330*5dd091fdSMatthias Ringwald             }
331*5dd091fdSMatthias Ringwald             break;
332*5dd091fdSMatthias Ringwald         case Local:
333*5dd091fdSMatthias Ringwald         case Reserved:
334*5dd091fdSMatthias Ringwald             break;
335*5dd091fdSMatthias Ringwald         default:
336*5dd091fdSMatthias Ringwald             btstack_assert(false);
337*5dd091fdSMatthias Ringwald             break;
338*5dd091fdSMatthias Ringwald     }
339*5dd091fdSMatthias Ringwald     if (!valid_field) return;
34012ccb71bSMatthias Ringwald 
341*5dd091fdSMatthias Ringwald     // handle constant fields used for padding
342*5dd091fdSMatthias Ringwald     if (item->item_value & 1){
343*5dd091fdSMatthias Ringwald         int item_bits = iterator->global_report_size * iterator->global_report_count;
344*5dd091fdSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
345*5dd091fdSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
346*5dd091fdSMatthias Ringwald #endif
347*5dd091fdSMatthias Ringwald         iterator->report_pos_in_bit += item_bits;
348*5dd091fdSMatthias Ringwald         return;
349*5dd091fdSMatthias Ringwald     }
350*5dd091fdSMatthias Ringwald     // Empty Item
351*5dd091fdSMatthias Ringwald     if (iterator->global_report_count == 0u) return;
352*5dd091fdSMatthias Ringwald     // let's start
353*5dd091fdSMatthias Ringwald     iterator->required_usages = iterator->global_report_count;
3540c7e4a25SMatthias Ringwald }
3550c7e4a25SMatthias Ringwald 
356*5dd091fdSMatthias Ringwald static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) {
357*5dd091fdSMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){
358*5dd091fdSMatthias Ringwald         iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator);
359*5dd091fdSMatthias Ringwald 
360*5dd091fdSMatthias Ringwald         btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item);
361*5dd091fdSMatthias Ringwald 
362*5dd091fdSMatthias Ringwald         if (iterator->required_usages){
363*5dd091fdSMatthias Ringwald             btstack_usage_iterator_hid_find_next_usage(iterator);
364*5dd091fdSMatthias Ringwald             if (iterator->available_usages) {
365*5dd091fdSMatthias Ringwald                 iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
366*5dd091fdSMatthias Ringwald                 return;
3670c7e4a25SMatthias Ringwald             } else {
368*5dd091fdSMatthias Ringwald                 log_debug("no usages found");
369*5dd091fdSMatthias Ringwald                 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
370*5dd091fdSMatthias Ringwald                 return;
371*5dd091fdSMatthias Ringwald             }
372*5dd091fdSMatthias Ringwald         } else {
373*5dd091fdSMatthias Ringwald             if ((TagType) (&iterator->descriptor_item)->item_type == Main) {
374*5dd091fdSMatthias Ringwald                 // reset usage
375*5dd091fdSMatthias Ringwald                 iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos;
376*5dd091fdSMatthias Ringwald                 iterator->usage_page = iterator->global_usage_page;
3770c7e4a25SMatthias Ringwald             }
3780c7e4a25SMatthias Ringwald         }
379*5dd091fdSMatthias Ringwald     }
380*5dd091fdSMatthias Ringwald     // end of descriptor
381*5dd091fdSMatthias Ringwald     iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
3820c7e4a25SMatthias Ringwald }
3830c7e4a25SMatthias Ringwald 
384*5dd091fdSMatthias Ringwald 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*5dd091fdSMatthias Ringwald     memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t));
386*5dd091fdSMatthias Ringwald 
387*5dd091fdSMatthias Ringwald     iterator->descriptor     = hid_descriptor;
388*5dd091fdSMatthias Ringwald     iterator->descriptor_len = hid_descriptor_len;
389*5dd091fdSMatthias Ringwald     iterator->report_type    = hid_report_type;
390*5dd091fdSMatthias Ringwald     iterator->state          = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
391*5dd091fdSMatthias Ringwald     iterator->global_report_id = HID_REPORT_ID_UNDEFINED;
392*5dd091fdSMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len);
3930c7e4a25SMatthias Ringwald }
3940c7e4a25SMatthias Ringwald 
395*5dd091fdSMatthias Ringwald bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){
396*5dd091fdSMatthias Ringwald     while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){
397*5dd091fdSMatthias Ringwald         btstack_hid_usage_iterator_find_next_usage(iterator);
398*5dd091fdSMatthias Ringwald     }
399*5dd091fdSMatthias Ringwald     return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
4000c7e4a25SMatthias Ringwald }
4010c7e4a25SMatthias Ringwald 
402*5dd091fdSMatthias Ringwald void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){
403*5dd091fdSMatthias Ringwald     // cache current values
404*5dd091fdSMatthias Ringwald     memset(item, 0, sizeof(btstack_hid_usage_item_t));
405*5dd091fdSMatthias Ringwald     item->size = iterator->global_report_size;
406*5dd091fdSMatthias Ringwald     item->report_id = iterator->global_report_id;
407*5dd091fdSMatthias Ringwald     item->usage_page = iterator->usage_minimum >> 16;
408*5dd091fdSMatthias Ringwald     item->bit_pos = iterator->report_pos_in_bit;
409*5dd091fdSMatthias Ringwald 
410*5dd091fdSMatthias Ringwald     bool is_variable  = (iterator->descriptor_item.item_value & 2) != 0;
411*5dd091fdSMatthias Ringwald     if (is_variable){
412*5dd091fdSMatthias Ringwald         item->usage = iterator->usage_minimum & 0xffffu;
413*5dd091fdSMatthias Ringwald     }
414*5dd091fdSMatthias Ringwald     iterator->required_usages--;
415*5dd091fdSMatthias Ringwald     iterator->report_pos_in_bit += iterator->global_report_size;
416*5dd091fdSMatthias Ringwald 
417*5dd091fdSMatthias Ringwald     // cache descriptor item and
418*5dd091fdSMatthias Ringwald     item->descriptor_item = iterator->descriptor_item;
419*5dd091fdSMatthias Ringwald     item->global_logical_minimum = iterator->global_logical_minimum;
420*5dd091fdSMatthias Ringwald 
421*5dd091fdSMatthias Ringwald     // next usage
422*5dd091fdSMatthias Ringwald     if (is_variable){
423*5dd091fdSMatthias Ringwald         iterator->usage_minimum++;
424*5dd091fdSMatthias Ringwald         iterator->available_usages--;
425*5dd091fdSMatthias Ringwald         if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){
426*5dd091fdSMatthias Ringwald             // usage min - max range smaller than report count, ignore remaining bit in report
427*5dd091fdSMatthias Ringwald             log_debug("Ignoring %u items without Usage", parser->required_usages);
428*5dd091fdSMatthias Ringwald             iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages;
429*5dd091fdSMatthias Ringwald             iterator->required_usages = 0;
430*5dd091fdSMatthias Ringwald         }
431*5dd091fdSMatthias Ringwald     } else {
432*5dd091fdSMatthias Ringwald         if (iterator->required_usages == 0u){
433*5dd091fdSMatthias Ringwald             iterator->available_usages = 0;
434*5dd091fdSMatthias Ringwald         }
435*5dd091fdSMatthias Ringwald     }
436*5dd091fdSMatthias Ringwald 
437*5dd091fdSMatthias Ringwald     if (iterator->available_usages) {
438*5dd091fdSMatthias Ringwald         return;
439*5dd091fdSMatthias Ringwald     }
440*5dd091fdSMatthias Ringwald     if (iterator->required_usages == 0u){
441*5dd091fdSMatthias Ringwald         iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
442*5dd091fdSMatthias Ringwald     } else {
443*5dd091fdSMatthias Ringwald         btstack_usage_iterator_hid_find_next_usage(iterator);
444*5dd091fdSMatthias Ringwald         if (iterator->available_usages == 0u) {
445*5dd091fdSMatthias Ringwald             iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
446*5dd091fdSMatthias Ringwald         }
447*5dd091fdSMatthias Ringwald     }
448*5dd091fdSMatthias Ringwald }
449*5dd091fdSMatthias Ringwald 
450*5dd091fdSMatthias Ringwald 
451*5dd091fdSMatthias Ringwald // HID Report Parser
452*5dd091fdSMatthias Ringwald 
453*5dd091fdSMatthias Ringwald 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*5dd091fdSMatthias Ringwald     btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type);
455*5dd091fdSMatthias Ringwald     parser->report = hid_report;
456*5dd091fdSMatthias Ringwald     parser->report_len = hid_report_len;
457*5dd091fdSMatthias Ringwald     parser->have_report_usage_ready = false;
458*5dd091fdSMatthias Ringwald }
459*5dd091fdSMatthias Ringwald 
460*5dd091fdSMatthias Ringwald /**
461*5dd091fdSMatthias Ringwald  * @brief Checks if more fields are available
462*5dd091fdSMatthias Ringwald  * @param parser
463*5dd091fdSMatthias Ringwald  */
464*5dd091fdSMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
465*5dd091fdSMatthias Ringwald     while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){
466*5dd091fdSMatthias Ringwald         btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor__usage_item);
467*5dd091fdSMatthias Ringwald         // ignore usages for other report ids
468*5dd091fdSMatthias Ringwald         if (parser->descriptor__usage_item.report_id != HID_REPORT_ID_UNDEFINED){
469*5dd091fdSMatthias Ringwald             if (parser->descriptor__usage_item.report_id != parser->report[0]){
470*5dd091fdSMatthias Ringwald                 continue;
471*5dd091fdSMatthias Ringwald             }
472*5dd091fdSMatthias Ringwald         }
473*5dd091fdSMatthias Ringwald         parser->have_report_usage_ready = true;
474*5dd091fdSMatthias Ringwald     }
475*5dd091fdSMatthias Ringwald     return parser->have_report_usage_ready;
476*5dd091fdSMatthias Ringwald }
477*5dd091fdSMatthias Ringwald 
478*5dd091fdSMatthias Ringwald /**
479*5dd091fdSMatthias Ringwald  * @brief Get next field
480*5dd091fdSMatthias Ringwald  * @param parser
481*5dd091fdSMatthias Ringwald  * @param usage_page
482*5dd091fdSMatthias Ringwald  * @param usage
483*5dd091fdSMatthias Ringwald  * @param value provided in HID report
484*5dd091fdSMatthias Ringwald  */
485*5dd091fdSMatthias Ringwald 
486*5dd091fdSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
487*5dd091fdSMatthias Ringwald 
488*5dd091fdSMatthias Ringwald     // fetch data from descriptor usage item
489*5dd091fdSMatthias Ringwald     uint16_t bit_pos = parser->descriptor__usage_item.bit_pos;
490*5dd091fdSMatthias Ringwald     uint16_t size    = parser->descriptor__usage_item.size;
491*5dd091fdSMatthias Ringwald     *usage_page      = parser->descriptor__usage_item.usage_page;
492*5dd091fdSMatthias Ringwald     *usage           = parser->descriptor__usage_item.usage;
493*5dd091fdSMatthias Ringwald 
494*5dd091fdSMatthias Ringwald 
495*5dd091fdSMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
496*5dd091fdSMatthias Ringwald     bool is_variable   = (parser->descriptor__usage_item.descriptor_item.item_value & 2) != 0;
497*5dd091fdSMatthias Ringwald     bool is_signed     = parser->descriptor__usage_item.global_logical_minimum < 0;
498*5dd091fdSMatthias Ringwald     int pos_start     = btstack_min(  bit_pos >> 3, parser->report_len);
499*5dd091fdSMatthias Ringwald     int pos_end       = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len);
500*5dd091fdSMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
501*5dd091fdSMatthias Ringwald 
502*5dd091fdSMatthias Ringwald     int i;
503*5dd091fdSMatthias Ringwald     uint32_t multi_byte_value = 0;
504*5dd091fdSMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
505*5dd091fdSMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
506*5dd091fdSMatthias Ringwald     }
507*5dd091fdSMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u);
508*5dd091fdSMatthias Ringwald     // 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*5dd091fdSMatthias Ringwald     if (is_variable){
510*5dd091fdSMatthias Ringwald         if (is_signed && (unsigned_value & (1u<<(size-1u)))){
511*5dd091fdSMatthias Ringwald             *value = unsigned_value - (1u<<size);
512*5dd091fdSMatthias Ringwald         } else {
513*5dd091fdSMatthias Ringwald             *value = unsigned_value;
514*5dd091fdSMatthias Ringwald         }
515*5dd091fdSMatthias Ringwald     } else {
516*5dd091fdSMatthias Ringwald         *usage  = unsigned_value;
517*5dd091fdSMatthias Ringwald         *value  = 1;
518*5dd091fdSMatthias Ringwald     }
519*5dd091fdSMatthias Ringwald 
520*5dd091fdSMatthias Ringwald     parser->have_report_usage_ready = false;
521*5dd091fdSMatthias Ringwald }
522*5dd091fdSMatthias Ringwald 
523*5dd091fdSMatthias Ringwald 
524*5dd091fdSMatthias Ringwald // Utility functions
5250ba748f2SMatthias Ringwald 
5262cca3b08SMatthias Ringwald int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor,
5272cca3b08SMatthias Ringwald                                        uint16_t hid_descriptor_len) {
528fada7179SMilanka Ringwald     int total_report_size = 0;
529fada7179SMilanka Ringwald     int report_size = 0;
530fada7179SMilanka Ringwald     int report_count = 0;
531279225e6SMatthias Ringwald     int current_report_id = HID_REPORT_ID_UNDEFINED;
532fada7179SMilanka Ringwald 
533960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
534960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
535960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
536960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
537fada7179SMilanka Ringwald         int valid_report_type = 0;
538960622b0SMatthias Ringwald         switch (item->item_type) {
539fada7179SMilanka Ringwald             case Global:
540960622b0SMatthias Ringwald                 switch ((GlobalItemTag) item->item_tag) {
541fada7179SMilanka Ringwald                     case ReportID:
542960622b0SMatthias Ringwald                         current_report_id = item->item_value;
543fada7179SMilanka Ringwald                         break;
544fada7179SMilanka Ringwald                     case ReportCount:
545960622b0SMatthias Ringwald                         report_count = item->item_value;
546fada7179SMilanka Ringwald                         break;
547fada7179SMilanka Ringwald                     case ReportSize:
548960622b0SMatthias Ringwald                         report_size = item->item_value;
549fada7179SMilanka Ringwald                         break;
550fada7179SMilanka Ringwald                     default:
551fada7179SMilanka Ringwald                         break;
552fada7179SMilanka Ringwald                 }
553fada7179SMilanka Ringwald                 break;
554fada7179SMilanka Ringwald             case Main:
555fada7179SMilanka Ringwald                 if (current_report_id != report_id) break;
556960622b0SMatthias Ringwald                 switch ((MainItemTag) item->item_tag) {
557fada7179SMilanka Ringwald                     case Input:
558662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_INPUT) break;
559fada7179SMilanka Ringwald                         valid_report_type = 1;
560fada7179SMilanka Ringwald                         break;
561fada7179SMilanka Ringwald                     case Output:
562662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
563fada7179SMilanka Ringwald                         valid_report_type = 1;
564fada7179SMilanka Ringwald                         break;
565fada7179SMilanka Ringwald                     case Feature:
566662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
567fada7179SMilanka Ringwald                         valid_report_type = 1;
568fada7179SMilanka Ringwald                         break;
569fada7179SMilanka Ringwald                     default:
570fada7179SMilanka Ringwald                         break;
571fada7179SMilanka Ringwald                 }
572fada7179SMilanka Ringwald                 if (!valid_report_type) break;
573fada7179SMilanka Ringwald                 total_report_size += report_count * report_size;
574fada7179SMilanka Ringwald                 break;
575fada7179SMilanka Ringwald             default:
576fada7179SMilanka Ringwald                 break;
577fada7179SMilanka Ringwald         }
57815cf8612Sx0rloser         if (total_report_size > 0 && current_report_id != report_id) break;
579fada7179SMilanka Ringwald     }
580960622b0SMatthias Ringwald 
581960622b0SMatthias Ringwald     if (btstack_hid_descriptor_iterator_valid(&iterator)){
582fada7179SMilanka Ringwald         return (total_report_size + 7) / 8;
583960622b0SMatthias Ringwald     } else {
584960622b0SMatthias Ringwald         return 0;
585960622b0SMatthias Ringwald     }
586fada7179SMilanka Ringwald }
587dbcaefc7SMilanka Ringwald 
588279225e6SMatthias Ringwald hid_report_id_status_t btstack_hid_report_id_valid(uint16_t report_id, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
589279225e6SMatthias Ringwald     uint16_t current_report_id = HID_REPORT_ID_UNDEFINED;
590960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
591960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
592279225e6SMatthias Ringwald     bool report_id_found = false;
593960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
594960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
595960622b0SMatthias Ringwald         switch (item->item_type){
596dbcaefc7SMilanka Ringwald             case Global:
597960622b0SMatthias Ringwald                 switch ((GlobalItemTag)item->item_tag){
598dbcaefc7SMilanka Ringwald                     case ReportID:
599960622b0SMatthias Ringwald                         current_report_id = item->item_value;
600279225e6SMatthias Ringwald                         if (current_report_id == report_id) {
601279225e6SMatthias Ringwald                             report_id_found = true;
602279225e6SMatthias Ringwald                         }
603dbcaefc7SMilanka Ringwald                     default:
604dbcaefc7SMilanka Ringwald                         break;
605dbcaefc7SMilanka Ringwald                 }
606dbcaefc7SMilanka Ringwald                 break;
607dbcaefc7SMilanka Ringwald             default:
608dbcaefc7SMilanka Ringwald                 break;
609dbcaefc7SMilanka Ringwald         }
610dbcaefc7SMilanka Ringwald     }
611960622b0SMatthias Ringwald 
612960622b0SMatthias Ringwald     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
613279225e6SMatthias Ringwald         if (report_id_found){
614279225e6SMatthias Ringwald             return HID_REPORT_ID_VALID;
615dbcaefc7SMilanka Ringwald         }
616279225e6SMatthias Ringwald         if ((report_id  == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) {
617279225e6SMatthias Ringwald             return HID_REPORT_ID_VALID;
618279225e6SMatthias Ringwald         }
619279225e6SMatthias Ringwald         return HID_REPORT_ID_UNDECLARED;
620960622b0SMatthias Ringwald     } else {
621960622b0SMatthias Ringwald         return HID_REPORT_ID_INVALID;
622960622b0SMatthias Ringwald     }
623960622b0SMatthias Ringwald }
624dbcaefc7SMilanka Ringwald 
6252cca3b08SMatthias Ringwald bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) {
626960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
627960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
628960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
629960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
630960622b0SMatthias Ringwald         switch (item->item_type){
631dbcaefc7SMilanka Ringwald             case Global:
632960622b0SMatthias Ringwald                 switch ((GlobalItemTag)item->item_tag){
633dbcaefc7SMilanka Ringwald                     case ReportID:
634c17118d0SMatthias Ringwald                         return true;
635dbcaefc7SMilanka Ringwald                     default:
636dbcaefc7SMilanka Ringwald                         break;
637dbcaefc7SMilanka Ringwald                 }
638dbcaefc7SMilanka Ringwald                 break;
639dbcaefc7SMilanka Ringwald             default:
640dbcaefc7SMilanka Ringwald                 break;
641dbcaefc7SMilanka Ringwald         }
642dbcaefc7SMilanka Ringwald     }
643c17118d0SMatthias Ringwald     return false;
644dbcaefc7SMilanka Ringwald }
645