xref: /btstack/src/btstack_hid_parser.c (revision cdb96ae56a3e1cf8f717b5b9363b5402275fc879)
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
4912ccb71bSMatthias Ringwald // - Optional Pretty Print of HID Descripor
5012ccb71bSMatthias Ringwald // - Support to query descriptort for contained usages, e.g. to detect keyboard or mouse
5112ccb71bSMatthias Ringwald 
5212ccb71bSMatthias Ringwald // #define HID_PARSER_PRETTY_PRINT
5312ccb71bSMatthias Ringwald 
5412ccb71bSMatthias Ringwald /*
5512ccb71bSMatthias Ringwald  *  btstack_hid_parser.c
5612ccb71bSMatthias Ringwald  */
5712ccb71bSMatthias Ringwald 
5812ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
5912ccb71bSMatthias Ringwald 
6012ccb71bSMatthias Ringwald static const char * type_names[] = {
6112ccb71bSMatthias Ringwald     "Main",
6212ccb71bSMatthias Ringwald     "Global",
6312ccb71bSMatthias Ringwald     "Local",
6412ccb71bSMatthias Ringwald     "Reserved"
6512ccb71bSMatthias Ringwald };
6612ccb71bSMatthias Ringwald static const char * main_tags[] = {
6712ccb71bSMatthias Ringwald     "",
6812ccb71bSMatthias Ringwald     "",
6912ccb71bSMatthias Ringwald     "",
7012ccb71bSMatthias Ringwald     "",
7112ccb71bSMatthias Ringwald     "",
7212ccb71bSMatthias Ringwald     "",
7312ccb71bSMatthias Ringwald     "",
7412ccb71bSMatthias Ringwald     "",
7512ccb71bSMatthias Ringwald     "Input ",
7612ccb71bSMatthias Ringwald     "Output",
7712ccb71bSMatthias Ringwald     "Collection",
7812ccb71bSMatthias Ringwald     "Feature",
7912ccb71bSMatthias Ringwald     "End Collection",
8012ccb71bSMatthias Ringwald     "Reserved",
8112ccb71bSMatthias Ringwald     "Reserved",
8212ccb71bSMatthias Ringwald     "Reserved"
8312ccb71bSMatthias Ringwald };
8412ccb71bSMatthias Ringwald static const char * global_tags[] = {
8512ccb71bSMatthias Ringwald     "Usage Page",
8612ccb71bSMatthias Ringwald     "Logical Minimum",
8712ccb71bSMatthias Ringwald     "Logical Maximum",
8812ccb71bSMatthias Ringwald     "Physical Minimum",
8912ccb71bSMatthias Ringwald     "Physical Maximum",
9012ccb71bSMatthias Ringwald     "Unit Exponent",
9112ccb71bSMatthias Ringwald     "Unit",
9212ccb71bSMatthias Ringwald     "Report Size",
9312ccb71bSMatthias Ringwald     "Report ID",
9412ccb71bSMatthias Ringwald     "Report Count",
9512ccb71bSMatthias Ringwald     "Push",
9612ccb71bSMatthias Ringwald     "Pop",
9712ccb71bSMatthias Ringwald     "Reserved",
9812ccb71bSMatthias Ringwald     "Reserved",
9912ccb71bSMatthias Ringwald     "Reserved",
10012ccb71bSMatthias Ringwald     "Reserved"
10112ccb71bSMatthias Ringwald };
10212ccb71bSMatthias Ringwald static const char * local_tags[] = {
10312ccb71bSMatthias Ringwald     "Usage",
10412ccb71bSMatthias Ringwald     "Usage Minimum",
10512ccb71bSMatthias Ringwald     "Usage Maximum",
10612ccb71bSMatthias Ringwald     "Designator Index",
10712ccb71bSMatthias Ringwald     "Designator Minimum",
10812ccb71bSMatthias Ringwald     "Designator Maximum",
10912ccb71bSMatthias Ringwald     "String Index",
11012ccb71bSMatthias Ringwald     "String Minimum",
11112ccb71bSMatthias Ringwald     "String Maximum",
11212ccb71bSMatthias Ringwald     "Delimiter",
11312ccb71bSMatthias Ringwald     "Reserved",
11412ccb71bSMatthias Ringwald     "Reserved",
11512ccb71bSMatthias Ringwald     "Reserved",
11612ccb71bSMatthias Ringwald     "Reserved",
11712ccb71bSMatthias Ringwald     "Reserved",
11812ccb71bSMatthias Ringwald     "Reserved"
11912ccb71bSMatthias Ringwald };
12012ccb71bSMatthias Ringwald #endif
12112ccb71bSMatthias Ringwald 
12212ccb71bSMatthias Ringwald static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
12312ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
12412ccb71bSMatthias Ringwald     const char ** item_tag_table;
1257bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
12612ccb71bSMatthias Ringwald         case Main:
12712ccb71bSMatthias Ringwald             item_tag_table = main_tags;
12812ccb71bSMatthias Ringwald             break;
12912ccb71bSMatthias Ringwald         case Global:
13012ccb71bSMatthias Ringwald             item_tag_table = global_tags;
13112ccb71bSMatthias Ringwald             break;
13212ccb71bSMatthias Ringwald         case Local:
13312ccb71bSMatthias Ringwald             item_tag_table = local_tags;
13412ccb71bSMatthias Ringwald             break;
13512ccb71bSMatthias Ringwald         default:
13612ccb71bSMatthias Ringwald             item_tag_table = NULL;
13712ccb71bSMatthias Ringwald             break;
13812ccb71bSMatthias Ringwald     }
13912ccb71bSMatthias Ringwald     const char * item_tag_name = "Invalid";
14012ccb71bSMatthias Ringwald     if (item_tag_table){
14112ccb71bSMatthias Ringwald         item_tag_name = item_tag_table[item->item_tag];
14212ccb71bSMatthias Ringwald     }
143ea1e21c2SMatthias Ringwald     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
14412ccb71bSMatthias Ringwald #else
14512ccb71bSMatthias Ringwald     UNUSED(parser);
14612ccb71bSMatthias Ringwald     UNUSED(item);
14712ccb71bSMatthias Ringwald #endif
14812ccb71bSMatthias Ringwald }
14912ccb71bSMatthias Ringwald 
15012ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value
15176fa2448SMatthias Ringwald bool btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
1528334d3d8SMatthias Ringwald 
1538334d3d8SMatthias Ringwald     const int hid_item_sizes[] = { 0, 1, 2, 4 };
1548334d3d8SMatthias Ringwald 
15512ccb71bSMatthias Ringwald     // parse item header
15676fa2448SMatthias Ringwald     if (hid_descriptor_len < 1u) return false;
15712ccb71bSMatthias Ringwald     uint16_t pos = 0;
15812ccb71bSMatthias Ringwald     uint8_t item_header = hid_descriptor[pos++];
1594ea43905SMatthias Ringwald     item->data_size = hid_item_sizes[item_header & 0x03u];
1604ea43905SMatthias Ringwald     item->item_type = (item_header & 0x0cu) >> 2u;
1614ea43905SMatthias Ringwald     item->item_tag  = (item_header & 0xf0u) >> 4u;
16212ccb71bSMatthias Ringwald     // long item
1634ea43905SMatthias Ringwald     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
16476fa2448SMatthias Ringwald         if (hid_descriptor_len < 3u) return false;
16512ccb71bSMatthias Ringwald         item->data_size = hid_descriptor[pos++];
16612ccb71bSMatthias Ringwald         item->item_tag  = hid_descriptor[pos++];
16712ccb71bSMatthias Ringwald     }
16812ccb71bSMatthias Ringwald     item->item_size =  pos + item->data_size;
16912ccb71bSMatthias Ringwald     item->item_value = 0;
17012ccb71bSMatthias Ringwald 
17112ccb71bSMatthias Ringwald     // read item value
17276fa2448SMatthias Ringwald     if (hid_descriptor_len < item->item_size) return false;
17376fa2448SMatthias Ringwald     if (item->data_size > 4u) return false;
17412ccb71bSMatthias Ringwald     int i;
1754ea43905SMatthias Ringwald     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
17612ccb71bSMatthias Ringwald     int32_t value = 0;
17712ccb71bSMatthias Ringwald     uint8_t latest_byte = 0;
17812ccb71bSMatthias Ringwald     for (i=0;i<item->data_size;i++){
17912ccb71bSMatthias Ringwald         latest_byte = hid_descriptor[pos++];
18012ccb71bSMatthias Ringwald         value = (latest_byte << (8*i)) | value;
18112ccb71bSMatthias Ringwald     }
1824ea43905SMatthias Ringwald     if (sgnd && (item->data_size > 0u)){
1834ea43905SMatthias Ringwald         if (latest_byte & 0x80u) {
1844ea43905SMatthias Ringwald             value -= 1u << (item->data_size*8u);
18512ccb71bSMatthias Ringwald         }
18612ccb71bSMatthias Ringwald     }
18712ccb71bSMatthias Ringwald     item->item_value = value;
18876fa2448SMatthias Ringwald     return true;
18912ccb71bSMatthias Ringwald }
19012ccb71bSMatthias Ringwald 
1910a4fc5fbSMatthias Ringwald static bool btstack_hid_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){
1920a4fc5fbSMatthias Ringwald     switch (tag){
1930a4fc5fbSMatthias Ringwald         case Input:
1940a4fc5fbSMatthias Ringwald             return report_type == HID_REPORT_TYPE_INPUT;
1950a4fc5fbSMatthias Ringwald         case Output:
1960a4fc5fbSMatthias Ringwald             return report_type == HID_REPORT_TYPE_OUTPUT;
1970a4fc5fbSMatthias Ringwald         case Feature:
1980a4fc5fbSMatthias Ringwald             return report_type == HID_REPORT_TYPE_FEATURE;
1990a4fc5fbSMatthias Ringwald         default:
2000a4fc5fbSMatthias Ringwald             return false;
2010a4fc5fbSMatthias Ringwald     }
2020a4fc5fbSMatthias Ringwald }
2030a4fc5fbSMatthias Ringwald 
20412ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
2057bbeb3adSMilanka Ringwald     switch((GlobalItemTag)item->item_tag){
20612ccb71bSMatthias Ringwald         case UsagePage:
20712ccb71bSMatthias Ringwald             parser->global_usage_page = item->item_value;
20812ccb71bSMatthias Ringwald             break;
20912ccb71bSMatthias Ringwald         case LogicalMinimum:
21012ccb71bSMatthias Ringwald             parser->global_logical_minimum = item->item_value;
21112ccb71bSMatthias Ringwald             break;
21212ccb71bSMatthias Ringwald         case LogicalMaximum:
21312ccb71bSMatthias Ringwald             parser->global_logical_maximum = item->item_value;
21412ccb71bSMatthias Ringwald             break;
21512ccb71bSMatthias Ringwald         case ReportSize:
21612ccb71bSMatthias Ringwald             parser->global_report_size = item->item_value;
21712ccb71bSMatthias Ringwald             break;
21812ccb71bSMatthias Ringwald         case ReportID:
21912ccb71bSMatthias Ringwald             parser->global_report_id = item->item_value;
22012ccb71bSMatthias Ringwald             break;
22112ccb71bSMatthias Ringwald         case ReportCount:
22212ccb71bSMatthias Ringwald             parser->global_report_count = item->item_value;
22312ccb71bSMatthias Ringwald             break;
2247bbeb3adSMilanka Ringwald 
2257bbeb3adSMilanka Ringwald         // TODO handle tags
2267bbeb3adSMilanka Ringwald         case PhysicalMinimum:
2277bbeb3adSMilanka Ringwald         case PhysicalMaximum:
2287bbeb3adSMilanka Ringwald         case UnitExponent:
2297bbeb3adSMilanka Ringwald         case Unit:
2307bbeb3adSMilanka Ringwald         case Push:
2317bbeb3adSMilanka Ringwald         case Pop:
2327bbeb3adSMilanka Ringwald             break;
2337bbeb3adSMilanka Ringwald 
23412ccb71bSMatthias Ringwald         default:
2357bbeb3adSMilanka Ringwald             btstack_assert(false);
23612ccb71bSMatthias Ringwald             break;
23712ccb71bSMatthias Ringwald     }
23812ccb71bSMatthias Ringwald }
23912ccb71bSMatthias Ringwald 
24012ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){
2411a05cde1SMatthias Ringwald     bool have_usage_min = false;
2421a05cde1SMatthias Ringwald     bool have_usage_max = false;
2431a05cde1SMatthias Ringwald     parser->usage_range = false;
24441afcabaSMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
24541afcabaSMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
24641afcabaSMatthias Ringwald     while ((parser->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){
24741afcabaSMatthias Ringwald         hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator);
2480e588213SMatthias Ringwald         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
24912ccb71bSMatthias Ringwald             parser->usage_page = usage_item.item_value;
25012ccb71bSMatthias Ringwald         }
25112ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
2524ea43905SMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
25312ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
25412ccb71bSMatthias Ringwald                 case Usage:
25512ccb71bSMatthias Ringwald                     parser->available_usages = 1;
25612ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
25712ccb71bSMatthias Ringwald                     break;
25812ccb71bSMatthias Ringwald                 case UsageMinimum:
25912ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
2601a05cde1SMatthias Ringwald                     have_usage_min = true;
26112ccb71bSMatthias Ringwald                     break;
26212ccb71bSMatthias Ringwald                 case UsageMaximum:
26312ccb71bSMatthias Ringwald                     parser->usage_maximum = usage_value;
2641a05cde1SMatthias Ringwald                     have_usage_max = true;
26512ccb71bSMatthias Ringwald                     break;
26612ccb71bSMatthias Ringwald                 default:
26712ccb71bSMatthias Ringwald                     break;
26812ccb71bSMatthias Ringwald             }
2691a05cde1SMatthias Ringwald             if (have_usage_min && have_usage_max){
2704ea43905SMatthias Ringwald                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
2711a05cde1SMatthias Ringwald                 parser->usage_range = true;
272dfb01e77SMatthias Ringwald                 if (parser->available_usages < parser->required_usages){
273cb406331SDirk Helbig                     log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages);
274dfb01e77SMatthias Ringwald                 }
27512ccb71bSMatthias Ringwald             }
27612ccb71bSMatthias Ringwald         }
27712ccb71bSMatthias Ringwald     }
27841afcabaSMatthias Ringwald     parser->usage_pos += iterator.descriptor_pos;
27912ccb71bSMatthias Ringwald }
28012ccb71bSMatthias Ringwald 
28112ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
28212ccb71bSMatthias Ringwald     hid_pretty_print_item(parser, item);
28312ccb71bSMatthias Ringwald     int valid_field = 0;
284eb78fadaSMatthias Ringwald     uint16_t report_id_before;
2857bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
28612ccb71bSMatthias Ringwald         case Main:
2870a4fc5fbSMatthias Ringwald             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
2880a4fc5fbSMatthias Ringwald                                                                         parser->report_type);
28912ccb71bSMatthias Ringwald             break;
29012ccb71bSMatthias Ringwald         case Global:
291eb78fadaSMatthias Ringwald             report_id_before = parser->global_report_id;
29212ccb71bSMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
293eb78fadaSMatthias Ringwald             // track record id for report handling
294eb78fadaSMatthias Ringwald             if ((GlobalItemTag)item->item_tag == ReportID){
295eb78fadaSMatthias Ringwald                 if (parser->active_record && (report_id_before != item->item_value)){
296eb78fadaSMatthias Ringwald                     parser->active_record = 0;
297eb78fadaSMatthias Ringwald                 }
298eb78fadaSMatthias Ringwald             }
29912ccb71bSMatthias Ringwald             break;
30012ccb71bSMatthias Ringwald         case Local:
3017bbeb3adSMilanka Ringwald         case Reserved:
3027bbeb3adSMilanka Ringwald             break;
3037bbeb3adSMilanka Ringwald         default:
3047bbeb3adSMilanka Ringwald             btstack_assert(false);
30512ccb71bSMatthias Ringwald             break;
30612ccb71bSMatthias Ringwald     }
30712ccb71bSMatthias Ringwald     if (!valid_field) return;
30812ccb71bSMatthias Ringwald 
30912ccb71bSMatthias Ringwald     // verify record id
31012ccb71bSMatthias Ringwald     if (parser->global_report_id && !parser->active_record){
31112ccb71bSMatthias Ringwald         if (parser->report[0] != parser->global_report_id){
31212ccb71bSMatthias Ringwald             return;
31312ccb71bSMatthias Ringwald         }
3144ea43905SMatthias Ringwald         parser->report_pos_in_bit += 8u;
31512ccb71bSMatthias Ringwald     }
31612ccb71bSMatthias Ringwald     parser->active_record = 1;
31712ccb71bSMatthias Ringwald     // handle constant fields used for padding
31812ccb71bSMatthias Ringwald     if (item->item_value & 1){
31912ccb71bSMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
32012ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
32112ccb71bSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
32212ccb71bSMatthias Ringwald #endif
32312ccb71bSMatthias Ringwald         parser->report_pos_in_bit += item_bits;
32412ccb71bSMatthias Ringwald         return;
32512ccb71bSMatthias Ringwald     }
32612ccb71bSMatthias Ringwald     // Empty Item
3274ea43905SMatthias Ringwald     if (parser->global_report_count == 0u) return;
32812ccb71bSMatthias Ringwald     // let's start
32912ccb71bSMatthias Ringwald     parser->required_usages = parser->global_report_count;
33012ccb71bSMatthias Ringwald }
33112ccb71bSMatthias Ringwald 
33212ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
333c45181feSMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&parser->descriptor_iterator)){
334c45181feSMatthias Ringwald         parser->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&parser->descriptor_iterator);
33512ccb71bSMatthias Ringwald         hid_process_item(parser, &parser->descriptor_item);
33612ccb71bSMatthias Ringwald         if (parser->required_usages){
33712ccb71bSMatthias Ringwald             hid_find_next_usage(parser);
33812ccb71bSMatthias Ringwald             if (parser->available_usages) {
33912ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
34088088c37SMatthias Ringwald                 return;
34112ccb71bSMatthias Ringwald             } else {
342dfb01e77SMatthias Ringwald                 log_debug("no usages found");
34312ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
34488088c37SMatthias Ringwald                 return;
34512ccb71bSMatthias Ringwald             }
34612ccb71bSMatthias Ringwald         } else {
347fd8e652cSMatthias Ringwald             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
348fd8e652cSMatthias Ringwald                 // reset usage
349c45181feSMatthias Ringwald                 parser->usage_pos = parser->descriptor_iterator.descriptor_pos;
350fd8e652cSMatthias Ringwald                 parser->usage_page = parser->global_usage_page;
351fd8e652cSMatthias Ringwald             }
35212ccb71bSMatthias Ringwald         }
35312ccb71bSMatthias Ringwald     }
354c45181feSMatthias Ringwald     // end of descriptor
355c45181feSMatthias Ringwald     parser->state = BTSTACK_HID_PARSER_COMPLETE;
35612ccb71bSMatthias Ringwald }
35712ccb71bSMatthias Ringwald 
35812ccb71bSMatthias Ringwald // PUBLIC API
35912ccb71bSMatthias Ringwald 
360662cddc2SMilanka 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){
36112ccb71bSMatthias Ringwald 
36212ccb71bSMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
36312ccb71bSMatthias Ringwald 
36412ccb71bSMatthias Ringwald     parser->descriptor     = hid_descriptor;
36512ccb71bSMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
36612ccb71bSMatthias Ringwald     parser->report_type    = hid_report_type;
36712ccb71bSMatthias Ringwald     parser->report         = hid_report;
36812ccb71bSMatthias Ringwald     parser->report_len     = hid_report_len;
36912ccb71bSMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
37012ccb71bSMatthias Ringwald 
371c45181feSMatthias Ringwald     btstack_hid_descriptor_iterator_init(&parser->descriptor_iterator, hid_descriptor, hid_descriptor_len);
372c45181feSMatthias Ringwald 
37312ccb71bSMatthias Ringwald     btstack_hid_parser_find_next_usage(parser);
37412ccb71bSMatthias Ringwald }
37512ccb71bSMatthias Ringwald 
376088c59dfSMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
37712ccb71bSMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
37812ccb71bSMatthias Ringwald }
37912ccb71bSMatthias Ringwald 
38012ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
38112ccb71bSMatthias Ringwald 
38212ccb71bSMatthias Ringwald     *usage_page = parser->usage_minimum >> 16;
38312ccb71bSMatthias Ringwald 
38412ccb71bSMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
3851979f09cSMatthias Ringwald     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
3861979f09cSMatthias Ringwald     bool is_signed     = parser->global_logical_minimum < 0;
38712ccb71bSMatthias Ringwald     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
3884ea43905SMatthias Ringwald     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
38912ccb71bSMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
39012ccb71bSMatthias Ringwald     int i;
39112ccb71bSMatthias Ringwald     uint32_t multi_byte_value = 0;
39212ccb71bSMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
39312ccb71bSMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
39412ccb71bSMatthias Ringwald     }
3954ea43905SMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
39612ccb71bSMatthias 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);
39712ccb71bSMatthias Ringwald     if (is_variable){
3984ea43905SMatthias Ringwald         *usage      = parser->usage_minimum & 0xffffu;
3994ea43905SMatthias Ringwald         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
4004ea43905SMatthias Ringwald             *value = unsigned_value - (1u<<parser->global_report_size);
40112ccb71bSMatthias Ringwald         } else {
40212ccb71bSMatthias Ringwald             *value = unsigned_value;
40312ccb71bSMatthias Ringwald         }
40412ccb71bSMatthias Ringwald     } else {
40512ccb71bSMatthias Ringwald         *usage  = unsigned_value;
40612ccb71bSMatthias Ringwald         *value  = 1;
40712ccb71bSMatthias Ringwald     }
40812ccb71bSMatthias Ringwald     parser->required_usages--;
40912ccb71bSMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
41012ccb71bSMatthias Ringwald 
41112ccb71bSMatthias Ringwald     // next usage
41212ccb71bSMatthias Ringwald     if (is_variable){
41312ccb71bSMatthias Ringwald         parser->usage_minimum++;
41412ccb71bSMatthias Ringwald         parser->available_usages--;
4151a05cde1SMatthias Ringwald         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
416dfb01e77SMatthias Ringwald             // usage min - max range smaller than report count, ignore remaining bit in report
417dfb01e77SMatthias Ringwald             log_debug("Ignoring %u items without Usage", parser->required_usages);
418dfb01e77SMatthias Ringwald             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
419dfb01e77SMatthias Ringwald             parser->required_usages = 0;
420dfb01e77SMatthias Ringwald         }
42112ccb71bSMatthias Ringwald     } else {
4224ea43905SMatthias Ringwald         if (parser->required_usages == 0u){
42312ccb71bSMatthias Ringwald             parser->available_usages = 0;
42412ccb71bSMatthias Ringwald         }
42512ccb71bSMatthias Ringwald     }
42612ccb71bSMatthias Ringwald     if (parser->available_usages) {
42712ccb71bSMatthias Ringwald         return;
42812ccb71bSMatthias Ringwald     }
4294ea43905SMatthias Ringwald     if (parser->required_usages == 0u){
43012ccb71bSMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
43112ccb71bSMatthias Ringwald         btstack_hid_parser_find_next_usage(parser);
43212ccb71bSMatthias Ringwald     } else {
43312ccb71bSMatthias Ringwald         hid_find_next_usage(parser);
4344ea43905SMatthias Ringwald         if (parser->available_usages == 0u) {
43512ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
43612ccb71bSMatthias Ringwald         }
43712ccb71bSMatthias Ringwald     }
43812ccb71bSMatthias Ringwald }
439fada7179SMilanka Ringwald 
4400c7e4a25SMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
4410c7e4a25SMatthias Ringwald     iterator->descriptor = hid_descriptor;
4420c7e4a25SMatthias Ringwald     iterator->descriptor_pos = 0;
4430c7e4a25SMatthias Ringwald     iterator->descriptor_len = hid_descriptor_len;
4440c7e4a25SMatthias Ringwald     iterator->item_ready = false;
4450c7e4a25SMatthias Ringwald     iterator->valid = true;
4460c7e4a25SMatthias Ringwald }
4470c7e4a25SMatthias Ringwald 
4480c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
4493cc55c4aSMatthias Ringwald     if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){
4500c7e4a25SMatthias Ringwald         uint16_t  item_len = iterator->descriptor_len - iterator->descriptor_pos;
4510c7e4a25SMatthias Ringwald         const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
4520c7e4a25SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len);
4530c7e4a25SMatthias Ringwald         if (ok){
4540c7e4a25SMatthias Ringwald             iterator->item_ready = true;
4550c7e4a25SMatthias Ringwald         } else {
4560c7e4a25SMatthias Ringwald             iterator->valid = false;
4570c7e4a25SMatthias Ringwald         }
4580c7e4a25SMatthias Ringwald     }
4590c7e4a25SMatthias Ringwald     return iterator->item_ready;
4600c7e4a25SMatthias Ringwald }
4610c7e4a25SMatthias Ringwald 
4620c7e4a25SMatthias Ringwald const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
4633cc55c4aSMatthias Ringwald     iterator->descriptor_pos += iterator->descriptor_item.item_size;
4640c7e4a25SMatthias Ringwald     iterator->item_ready = false;
4650c7e4a25SMatthias Ringwald     return &iterator->descriptor_item;
4660c7e4a25SMatthias Ringwald }
4670c7e4a25SMatthias Ringwald 
4680c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
4690c7e4a25SMatthias Ringwald     return iterator->valid;
4700c7e4a25SMatthias Ringwald }
4710c7e4a25SMatthias Ringwald 
472662cddc2SMilanka Ringwald 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) {
473fada7179SMilanka Ringwald     int total_report_size = 0;
474fada7179SMilanka Ringwald     int report_size = 0;
475fada7179SMilanka Ringwald     int report_count = 0;
476fada7179SMilanka Ringwald     int current_report_id = 0;
477fada7179SMilanka Ringwald 
478960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
479960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
480960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
481960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
482fada7179SMilanka Ringwald         int valid_report_type = 0;
483960622b0SMatthias Ringwald         switch (item->item_type) {
484fada7179SMilanka Ringwald             case Global:
485960622b0SMatthias Ringwald                 switch ((GlobalItemTag) item->item_tag) {
486fada7179SMilanka Ringwald                     case ReportID:
487960622b0SMatthias Ringwald                         current_report_id = item->item_value;
488fada7179SMilanka Ringwald                         break;
489fada7179SMilanka Ringwald                     case ReportCount:
490960622b0SMatthias Ringwald                         report_count = item->item_value;
491fada7179SMilanka Ringwald                         break;
492fada7179SMilanka Ringwald                     case ReportSize:
493960622b0SMatthias Ringwald                         report_size = item->item_value;
494fada7179SMilanka Ringwald                         break;
495fada7179SMilanka Ringwald                     default:
496fada7179SMilanka Ringwald                         break;
497fada7179SMilanka Ringwald                 }
498fada7179SMilanka Ringwald                 break;
499fada7179SMilanka Ringwald             case Main:
500fada7179SMilanka Ringwald                 if (current_report_id != report_id) break;
501960622b0SMatthias Ringwald                 switch ((MainItemTag) item->item_tag) {
502fada7179SMilanka Ringwald                     case Input:
503662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_INPUT) break;
504fada7179SMilanka Ringwald                         valid_report_type = 1;
505fada7179SMilanka Ringwald                         break;
506fada7179SMilanka Ringwald                     case Output:
507662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
508fada7179SMilanka Ringwald                         valid_report_type = 1;
509fada7179SMilanka Ringwald                         break;
510fada7179SMilanka Ringwald                     case Feature:
511662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
512fada7179SMilanka Ringwald                         valid_report_type = 1;
513fada7179SMilanka Ringwald                         break;
514fada7179SMilanka Ringwald                     default:
515fada7179SMilanka Ringwald                         break;
516fada7179SMilanka Ringwald                 }
517fada7179SMilanka Ringwald                 if (!valid_report_type) break;
518fada7179SMilanka Ringwald                 total_report_size += report_count * report_size;
519fada7179SMilanka Ringwald                 break;
520fada7179SMilanka Ringwald             default:
521fada7179SMilanka Ringwald                 break;
522fada7179SMilanka Ringwald         }
52315cf8612Sx0rloser         if (total_report_size > 0 && current_report_id != report_id) break;
524fada7179SMilanka Ringwald     }
525960622b0SMatthias Ringwald 
526960622b0SMatthias Ringwald     if (btstack_hid_descriptor_iterator_valid(&iterator)){
527fada7179SMilanka Ringwald         return (total_report_size + 7) / 8;
528960622b0SMatthias Ringwald     } else {
529960622b0SMatthias Ringwald         return 0;
530960622b0SMatthias Ringwald     }
531fada7179SMilanka Ringwald }
532dbcaefc7SMilanka Ringwald 
533dbcaefc7SMilanka Ringwald hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
534960622b0SMatthias Ringwald     int current_report_id = -1;
535960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
536960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
537960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
538960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
539960622b0SMatthias Ringwald         switch (item->item_type){
540dbcaefc7SMilanka Ringwald             case Global:
541960622b0SMatthias Ringwald                 switch ((GlobalItemTag)item->item_tag){
542dbcaefc7SMilanka Ringwald                     case ReportID:
543960622b0SMatthias Ringwald                         current_report_id = item->item_value;
544dbcaefc7SMilanka Ringwald                         if (current_report_id != report_id) break;
545dbcaefc7SMilanka Ringwald                         return HID_REPORT_ID_VALID;
546dbcaefc7SMilanka Ringwald                     default:
547dbcaefc7SMilanka Ringwald                         break;
548dbcaefc7SMilanka Ringwald                 }
549dbcaefc7SMilanka Ringwald                 break;
550dbcaefc7SMilanka Ringwald             default:
551dbcaefc7SMilanka Ringwald                 break;
552dbcaefc7SMilanka Ringwald         }
553dbcaefc7SMilanka Ringwald     }
554960622b0SMatthias Ringwald 
555960622b0SMatthias Ringwald     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
556960622b0SMatthias Ringwald         if (current_report_id != -1) {
557960622b0SMatthias Ringwald             return HID_REPORT_ID_INVALID;
558960622b0SMatthias Ringwald         } else {
559dbcaefc7SMilanka Ringwald             return HID_REPORT_ID_UNDECLARED;
560dbcaefc7SMilanka Ringwald         }
561960622b0SMatthias Ringwald     } else {
562960622b0SMatthias Ringwald         return HID_REPORT_ID_INVALID;
563960622b0SMatthias Ringwald     }
564960622b0SMatthias Ringwald }
565dbcaefc7SMilanka Ringwald 
566c17118d0SMatthias Ringwald bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
567960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
568960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
569960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
570960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
571960622b0SMatthias Ringwald         switch (item->item_type){
572dbcaefc7SMilanka Ringwald             case Global:
573960622b0SMatthias Ringwald                 switch ((GlobalItemTag)item->item_tag){
574dbcaefc7SMilanka Ringwald                     case ReportID:
575c17118d0SMatthias Ringwald                         return true;
576dbcaefc7SMilanka Ringwald                     default:
577dbcaefc7SMilanka Ringwald                         break;
578dbcaefc7SMilanka Ringwald                 }
579dbcaefc7SMilanka Ringwald                 break;
580dbcaefc7SMilanka Ringwald             default:
581dbcaefc7SMilanka Ringwald                 break;
582dbcaefc7SMilanka Ringwald         }
583dbcaefc7SMilanka Ringwald     }
584c17118d0SMatthias Ringwald     return false;
585dbcaefc7SMilanka Ringwald }
586*cdb96ae5SMatthias Ringwald 
587*cdb96ae5SMatthias Ringwald static void btstack_parser_usage_iterator_process_item2(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
588*cdb96ae5SMatthias Ringwald     hid_pretty_print_item(parser, item);
589*cdb96ae5SMatthias Ringwald     int valid_field = 0;
590*cdb96ae5SMatthias Ringwald     uint16_t report_id_before;
591*cdb96ae5SMatthias Ringwald     switch ((TagType)item->item_type){
592*cdb96ae5SMatthias Ringwald         case Main:
593*cdb96ae5SMatthias Ringwald             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
594*cdb96ae5SMatthias Ringwald                                                                         parser->report_type);
595*cdb96ae5SMatthias Ringwald             break;
596*cdb96ae5SMatthias Ringwald         case Global:
597*cdb96ae5SMatthias Ringwald             report_id_before = parser->global_report_id;
598*cdb96ae5SMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
599*cdb96ae5SMatthias Ringwald             // track record id for report handling, reset report position
600*cdb96ae5SMatthias Ringwald             if (report_id_before != parser->global_report_id){
601*cdb96ae5SMatthias Ringwald                 parser->report_pos_in_bit = 8u;
602*cdb96ae5SMatthias Ringwald             }
603*cdb96ae5SMatthias Ringwald             break;
604*cdb96ae5SMatthias Ringwald         case Local:
605*cdb96ae5SMatthias Ringwald         case Reserved:
606*cdb96ae5SMatthias Ringwald             break;
607*cdb96ae5SMatthias Ringwald         default:
608*cdb96ae5SMatthias Ringwald             btstack_assert(false);
609*cdb96ae5SMatthias Ringwald             break;
610*cdb96ae5SMatthias Ringwald     }
611*cdb96ae5SMatthias Ringwald     if (!valid_field) return;
612*cdb96ae5SMatthias Ringwald 
613*cdb96ae5SMatthias Ringwald     // handle constant fields used for padding
614*cdb96ae5SMatthias Ringwald     if (item->item_value & 1){
615*cdb96ae5SMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
616*cdb96ae5SMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
617*cdb96ae5SMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
618*cdb96ae5SMatthias Ringwald #endif
619*cdb96ae5SMatthias Ringwald         parser->report_pos_in_bit += item_bits;
620*cdb96ae5SMatthias Ringwald         return;
621*cdb96ae5SMatthias Ringwald     }
622*cdb96ae5SMatthias Ringwald     // Empty Item
623*cdb96ae5SMatthias Ringwald     if (parser->global_report_count == 0u) return;
624*cdb96ae5SMatthias Ringwald     // let's start
625*cdb96ae5SMatthias Ringwald     parser->required_usages = parser->global_report_count;
626*cdb96ae5SMatthias Ringwald }
627*cdb96ae5SMatthias Ringwald 
628*cdb96ae5SMatthias Ringwald static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_parser_t * parser) {
629*cdb96ae5SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&parser->descriptor_iterator)){
630*cdb96ae5SMatthias Ringwald         parser->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&parser->descriptor_iterator);
631*cdb96ae5SMatthias Ringwald 
632*cdb96ae5SMatthias Ringwald         btstack_parser_usage_iterator_process_item2(parser, &parser->descriptor_item);
633*cdb96ae5SMatthias Ringwald 
634*cdb96ae5SMatthias Ringwald         if (parser->required_usages){
635*cdb96ae5SMatthias Ringwald             hid_find_next_usage(parser);
636*cdb96ae5SMatthias Ringwald             if (parser->available_usages) {
637*cdb96ae5SMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
638*cdb96ae5SMatthias Ringwald                 return;
639*cdb96ae5SMatthias Ringwald             } else {
640*cdb96ae5SMatthias Ringwald                 log_debug("no usages found");
641*cdb96ae5SMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
642*cdb96ae5SMatthias Ringwald                 return;
643*cdb96ae5SMatthias Ringwald             }
644*cdb96ae5SMatthias Ringwald         } else {
645*cdb96ae5SMatthias Ringwald             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
646*cdb96ae5SMatthias Ringwald                 // reset usage
647*cdb96ae5SMatthias Ringwald                 parser->usage_pos = parser->descriptor_iterator.descriptor_pos;
648*cdb96ae5SMatthias Ringwald                 parser->usage_page = parser->global_usage_page;
649*cdb96ae5SMatthias Ringwald             }
650*cdb96ae5SMatthias Ringwald         }
651*cdb96ae5SMatthias Ringwald     }
652*cdb96ae5SMatthias Ringwald     // end of descriptor
653*cdb96ae5SMatthias Ringwald     parser->state = BTSTACK_HID_PARSER_COMPLETE;
654*cdb96ae5SMatthias Ringwald }
655*cdb96ae5SMatthias Ringwald 
656*cdb96ae5SMatthias Ringwald 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){
657*cdb96ae5SMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
658*cdb96ae5SMatthias Ringwald 
659*cdb96ae5SMatthias Ringwald     parser->descriptor     = hid_descriptor;
660*cdb96ae5SMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
661*cdb96ae5SMatthias Ringwald     parser->report_type    = hid_report_type;
662*cdb96ae5SMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
663*cdb96ae5SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&parser->descriptor_iterator, hid_descriptor, hid_descriptor_len);
664*cdb96ae5SMatthias Ringwald 
665*cdb96ae5SMatthias Ringwald     btstack_hid_usage_iterator_find_next_usage(parser);
666*cdb96ae5SMatthias Ringwald }
667*cdb96ae5SMatthias Ringwald 
668*cdb96ae5SMatthias Ringwald bool btstack_hid_usage_iterator_has_more(btstack_hid_parser_t * parser){
669*cdb96ae5SMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
670*cdb96ae5SMatthias Ringwald }
671*cdb96ae5SMatthias Ringwald 
672*cdb96ae5SMatthias Ringwald void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_hid_usage_item_t * item){
673*cdb96ae5SMatthias Ringwald     // cache current values
674*cdb96ae5SMatthias Ringwald     memset(item, 0, sizeof(btstack_hid_usage_item_t));
675*cdb96ae5SMatthias Ringwald     item->size = parser->global_report_size;
676*cdb96ae5SMatthias Ringwald     item->report_id = parser->global_report_id;
677*cdb96ae5SMatthias Ringwald     item->usage_page = parser->usage_minimum >> 16;
678*cdb96ae5SMatthias Ringwald     item->bit_pos = parser->report_pos_in_bit;
679*cdb96ae5SMatthias Ringwald 
680*cdb96ae5SMatthias Ringwald     bool is_variable  = (parser->descriptor_item.item_value & 2) != 0;
681*cdb96ae5SMatthias Ringwald     if (is_variable){
682*cdb96ae5SMatthias Ringwald         item->usage = parser->usage_minimum & 0xffffu;
683*cdb96ae5SMatthias Ringwald     }
684*cdb96ae5SMatthias Ringwald     parser->required_usages--;
685*cdb96ae5SMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
686*cdb96ae5SMatthias Ringwald 
687*cdb96ae5SMatthias Ringwald     // next usage
688*cdb96ae5SMatthias Ringwald     if (is_variable){
689*cdb96ae5SMatthias Ringwald         parser->usage_minimum++;
690*cdb96ae5SMatthias Ringwald         parser->available_usages--;
691*cdb96ae5SMatthias Ringwald         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
692*cdb96ae5SMatthias Ringwald             // usage min - max range smaller than report count, ignore remaining bit in report
693*cdb96ae5SMatthias Ringwald             log_debug("Ignoring %u items without Usage", parser->required_usages);
694*cdb96ae5SMatthias Ringwald             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
695*cdb96ae5SMatthias Ringwald             parser->required_usages = 0;
696*cdb96ae5SMatthias Ringwald         }
697*cdb96ae5SMatthias Ringwald     } else {
698*cdb96ae5SMatthias Ringwald         if (parser->required_usages == 0u){
699*cdb96ae5SMatthias Ringwald             parser->available_usages = 0;
700*cdb96ae5SMatthias Ringwald         }
701*cdb96ae5SMatthias Ringwald     }
702*cdb96ae5SMatthias Ringwald     if (parser->available_usages) {
703*cdb96ae5SMatthias Ringwald         return;
704*cdb96ae5SMatthias Ringwald     }
705*cdb96ae5SMatthias Ringwald     if (parser->required_usages == 0u){
706*cdb96ae5SMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
707*cdb96ae5SMatthias Ringwald         btstack_hid_usage_iterator_find_next_usage(parser);
708*cdb96ae5SMatthias Ringwald     } else {
709*cdb96ae5SMatthias Ringwald         hid_find_next_usage(parser);
710*cdb96ae5SMatthias Ringwald         if (parser->available_usages == 0u) {
711*cdb96ae5SMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
712*cdb96ae5SMatthias Ringwald         }
713*cdb96ae5SMatthias Ringwald     }
714*cdb96ae5SMatthias Ringwald }
715*cdb96ae5SMatthias Ringwald 
716*cdb96ae5SMatthias Ringwald //