xref: /btstack/src/btstack_hid_parser.c (revision cb4063318d6c90b52d682d5b3cbadc20b0ccda77)
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 
40*cb406331SDirk 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 
19112ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
1927bbeb3adSMilanka Ringwald     switch((GlobalItemTag)item->item_tag){
19312ccb71bSMatthias Ringwald         case UsagePage:
19412ccb71bSMatthias Ringwald             parser->global_usage_page = item->item_value;
19512ccb71bSMatthias Ringwald             break;
19612ccb71bSMatthias Ringwald         case LogicalMinimum:
19712ccb71bSMatthias Ringwald             parser->global_logical_minimum = item->item_value;
19812ccb71bSMatthias Ringwald             break;
19912ccb71bSMatthias Ringwald         case LogicalMaximum:
20012ccb71bSMatthias Ringwald             parser->global_logical_maximum = item->item_value;
20112ccb71bSMatthias Ringwald             break;
20212ccb71bSMatthias Ringwald         case ReportSize:
20312ccb71bSMatthias Ringwald             parser->global_report_size = item->item_value;
20412ccb71bSMatthias Ringwald             break;
20512ccb71bSMatthias Ringwald         case ReportID:
206c1ab6cc1SMatthias Ringwald             if (parser->active_record && (parser->global_report_id != item->item_value)){
20712ccb71bSMatthias Ringwald                 parser->active_record = 0;
20812ccb71bSMatthias Ringwald             }
20912ccb71bSMatthias Ringwald             parser->global_report_id = item->item_value;
21012ccb71bSMatthias Ringwald             break;
21112ccb71bSMatthias Ringwald         case ReportCount:
21212ccb71bSMatthias Ringwald             parser->global_report_count = item->item_value;
21312ccb71bSMatthias Ringwald             break;
2147bbeb3adSMilanka Ringwald 
2157bbeb3adSMilanka Ringwald         // TODO handle tags
2167bbeb3adSMilanka Ringwald         case PhysicalMinimum:
2177bbeb3adSMilanka Ringwald         case PhysicalMaximum:
2187bbeb3adSMilanka Ringwald         case UnitExponent:
2197bbeb3adSMilanka Ringwald         case Unit:
2207bbeb3adSMilanka Ringwald         case Push:
2217bbeb3adSMilanka Ringwald         case Pop:
2227bbeb3adSMilanka Ringwald             break;
2237bbeb3adSMilanka Ringwald 
22412ccb71bSMatthias Ringwald         default:
2257bbeb3adSMilanka Ringwald             btstack_assert(false);
22612ccb71bSMatthias Ringwald             break;
22712ccb71bSMatthias Ringwald     }
22812ccb71bSMatthias Ringwald }
22912ccb71bSMatthias Ringwald 
23012ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){
2311a05cde1SMatthias Ringwald     bool have_usage_min = false;
2321a05cde1SMatthias Ringwald     bool have_usage_max = false;
2331a05cde1SMatthias Ringwald     parser->usage_range = false;
2344ea43905SMatthias Ringwald     while ((parser->available_usages == 0u) && (parser->usage_pos < parser->descriptor_pos)){
23512ccb71bSMatthias Ringwald         hid_descriptor_item_t usage_item;
23612ccb71bSMatthias Ringwald         // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len
2370e7cc963SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
2380e7cc963SMatthias Ringwald         if (ok == false){
2390e7cc963SMatthias Ringwald             break;
2400e7cc963SMatthias Ringwald         }
2410e588213SMatthias Ringwald         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
24212ccb71bSMatthias Ringwald             parser->usage_page = usage_item.item_value;
24312ccb71bSMatthias Ringwald         }
24412ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
2454ea43905SMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
24612ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
24712ccb71bSMatthias Ringwald                 case Usage:
24812ccb71bSMatthias Ringwald                     parser->available_usages = 1;
24912ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
25012ccb71bSMatthias Ringwald                     break;
25112ccb71bSMatthias Ringwald                 case UsageMinimum:
25212ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
2531a05cde1SMatthias Ringwald                     have_usage_min = true;
25412ccb71bSMatthias Ringwald                     break;
25512ccb71bSMatthias Ringwald                 case UsageMaximum:
25612ccb71bSMatthias Ringwald                     parser->usage_maximum = usage_value;
2571a05cde1SMatthias Ringwald                     have_usage_max = true;
25812ccb71bSMatthias Ringwald                     break;
25912ccb71bSMatthias Ringwald                 default:
26012ccb71bSMatthias Ringwald                     break;
26112ccb71bSMatthias Ringwald             }
2621a05cde1SMatthias Ringwald             if (have_usage_min && have_usage_max){
2634ea43905SMatthias Ringwald                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
2641a05cde1SMatthias Ringwald                 parser->usage_range = true;
265dfb01e77SMatthias Ringwald                 if (parser->available_usages < parser->required_usages){
266*cb406331SDirk 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);
267dfb01e77SMatthias Ringwald                 }
26812ccb71bSMatthias Ringwald             }
26912ccb71bSMatthias Ringwald         }
27012ccb71bSMatthias Ringwald         parser->usage_pos += usage_item.item_size;
27112ccb71bSMatthias Ringwald     }
27212ccb71bSMatthias Ringwald }
27312ccb71bSMatthias Ringwald 
27412ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
27512ccb71bSMatthias Ringwald     hid_pretty_print_item(parser, item);
27612ccb71bSMatthias Ringwald     int valid_field = 0;
2777bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
27812ccb71bSMatthias Ringwald         case Main:
2797bbeb3adSMilanka Ringwald             switch ((MainItemTag)item->item_tag){
28012ccb71bSMatthias Ringwald                 case Input:
281662cddc2SMilanka Ringwald                     valid_field = parser->report_type == HID_REPORT_TYPE_INPUT;
28212ccb71bSMatthias Ringwald                     break;
28312ccb71bSMatthias Ringwald                 case Output:
284662cddc2SMilanka Ringwald                     valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT;
28512ccb71bSMatthias Ringwald                     break;
28612ccb71bSMatthias Ringwald                 case Feature:
287662cddc2SMilanka Ringwald                     valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE;
28812ccb71bSMatthias Ringwald                     break;
28912ccb71bSMatthias Ringwald                 default:
29012ccb71bSMatthias Ringwald                     break;
29112ccb71bSMatthias Ringwald             }
29212ccb71bSMatthias Ringwald             break;
29312ccb71bSMatthias Ringwald         case Global:
29412ccb71bSMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
29512ccb71bSMatthias Ringwald             break;
29612ccb71bSMatthias Ringwald         case Local:
2977bbeb3adSMilanka Ringwald         case Reserved:
2987bbeb3adSMilanka Ringwald             break;
2997bbeb3adSMilanka Ringwald         default:
3007bbeb3adSMilanka Ringwald             btstack_assert(false);
30112ccb71bSMatthias Ringwald             break;
30212ccb71bSMatthias Ringwald     }
30312ccb71bSMatthias Ringwald     if (!valid_field) return;
30412ccb71bSMatthias Ringwald 
30512ccb71bSMatthias Ringwald     // verify record id
30612ccb71bSMatthias Ringwald     if (parser->global_report_id && !parser->active_record){
30712ccb71bSMatthias Ringwald         if (parser->report[0] != parser->global_report_id){
30812ccb71bSMatthias Ringwald             return;
30912ccb71bSMatthias Ringwald         }
3104ea43905SMatthias Ringwald         parser->report_pos_in_bit += 8u;
31112ccb71bSMatthias Ringwald     }
31212ccb71bSMatthias Ringwald     parser->active_record = 1;
31312ccb71bSMatthias Ringwald     // handle constant fields used for padding
31412ccb71bSMatthias Ringwald     if (item->item_value & 1){
31512ccb71bSMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
31612ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
31712ccb71bSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
31812ccb71bSMatthias Ringwald #endif
31912ccb71bSMatthias Ringwald         parser->report_pos_in_bit += item_bits;
32012ccb71bSMatthias Ringwald         return;
32112ccb71bSMatthias Ringwald     }
32212ccb71bSMatthias Ringwald     // Empty Item
3234ea43905SMatthias Ringwald     if (parser->global_report_count == 0u) return;
32412ccb71bSMatthias Ringwald     // let's start
32512ccb71bSMatthias Ringwald     parser->required_usages = parser->global_report_count;
32612ccb71bSMatthias Ringwald }
32712ccb71bSMatthias Ringwald 
32812ccb71bSMatthias Ringwald static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
3297bbeb3adSMilanka Ringwald     if ((TagType)item->item_type == Main){
33012ccb71bSMatthias Ringwald         // reset usage
33112ccb71bSMatthias Ringwald         parser->usage_pos  = parser->descriptor_pos;
33212ccb71bSMatthias Ringwald         parser->usage_page = parser->global_usage_page;
33312ccb71bSMatthias Ringwald     }
33412ccb71bSMatthias Ringwald     parser->descriptor_pos += item->item_size;
33512ccb71bSMatthias Ringwald }
33612ccb71bSMatthias Ringwald 
33712ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
33812ccb71bSMatthias Ringwald     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
33912ccb71bSMatthias Ringwald         if (parser->descriptor_pos >= parser->descriptor_len){
34012ccb71bSMatthias Ringwald             // end of descriptor
34112ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
34212ccb71bSMatthias Ringwald             break;
34312ccb71bSMatthias Ringwald         }
3440c4004b0SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
3450c4004b0SMatthias Ringwald         if (ok == false){
3460c4004b0SMatthias Ringwald             // abort parsing
3470c4004b0SMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
3480c4004b0SMatthias Ringwald             break;
3490c4004b0SMatthias Ringwald         }
35012ccb71bSMatthias Ringwald         hid_process_item(parser, &parser->descriptor_item);
35112ccb71bSMatthias Ringwald         if (parser->required_usages){
35212ccb71bSMatthias Ringwald             hid_find_next_usage(parser);
35312ccb71bSMatthias Ringwald             if (parser->available_usages) {
35412ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
35512ccb71bSMatthias Ringwald             } else {
356dfb01e77SMatthias Ringwald                 log_debug("no usages found");
35712ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
35812ccb71bSMatthias Ringwald             }
35912ccb71bSMatthias Ringwald         } else {
36012ccb71bSMatthias Ringwald             hid_post_process_item(parser, &parser->descriptor_item);
36112ccb71bSMatthias Ringwald         }
36212ccb71bSMatthias Ringwald     }
36312ccb71bSMatthias Ringwald }
36412ccb71bSMatthias Ringwald 
36512ccb71bSMatthias Ringwald // PUBLIC API
36612ccb71bSMatthias Ringwald 
367662cddc2SMilanka 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){
36812ccb71bSMatthias Ringwald 
36912ccb71bSMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
37012ccb71bSMatthias Ringwald 
37112ccb71bSMatthias Ringwald     parser->descriptor     = hid_descriptor;
37212ccb71bSMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
37312ccb71bSMatthias Ringwald     parser->report_type    = hid_report_type;
37412ccb71bSMatthias Ringwald     parser->report         = hid_report;
37512ccb71bSMatthias Ringwald     parser->report_len     = hid_report_len;
37612ccb71bSMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
37712ccb71bSMatthias Ringwald 
37812ccb71bSMatthias Ringwald     btstack_hid_parser_find_next_usage(parser);
37912ccb71bSMatthias Ringwald }
38012ccb71bSMatthias Ringwald 
381088c59dfSMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
38212ccb71bSMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
38312ccb71bSMatthias Ringwald }
38412ccb71bSMatthias Ringwald 
38512ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
38612ccb71bSMatthias Ringwald 
38712ccb71bSMatthias Ringwald     *usage_page = parser->usage_minimum >> 16;
38812ccb71bSMatthias Ringwald 
38912ccb71bSMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
3901979f09cSMatthias Ringwald     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
3911979f09cSMatthias Ringwald     bool is_signed     = parser->global_logical_minimum < 0;
39212ccb71bSMatthias Ringwald     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
3934ea43905SMatthias Ringwald     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
39412ccb71bSMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
39512ccb71bSMatthias Ringwald     int i;
39612ccb71bSMatthias Ringwald     uint32_t multi_byte_value = 0;
39712ccb71bSMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
39812ccb71bSMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
39912ccb71bSMatthias Ringwald     }
4004ea43905SMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
40112ccb71bSMatthias 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);
40212ccb71bSMatthias Ringwald     if (is_variable){
4034ea43905SMatthias Ringwald         *usage      = parser->usage_minimum & 0xffffu;
4044ea43905SMatthias Ringwald         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
4054ea43905SMatthias Ringwald             *value = unsigned_value - (1u<<parser->global_report_size);
40612ccb71bSMatthias Ringwald         } else {
40712ccb71bSMatthias Ringwald             *value = unsigned_value;
40812ccb71bSMatthias Ringwald         }
40912ccb71bSMatthias Ringwald     } else {
41012ccb71bSMatthias Ringwald         *usage  = unsigned_value;
41112ccb71bSMatthias Ringwald         *value  = 1;
41212ccb71bSMatthias Ringwald     }
41312ccb71bSMatthias Ringwald     parser->required_usages--;
41412ccb71bSMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
41512ccb71bSMatthias Ringwald 
41612ccb71bSMatthias Ringwald     // next usage
41712ccb71bSMatthias Ringwald     if (is_variable){
41812ccb71bSMatthias Ringwald         parser->usage_minimum++;
41912ccb71bSMatthias Ringwald         parser->available_usages--;
4201a05cde1SMatthias Ringwald         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
421dfb01e77SMatthias Ringwald             // usage min - max range smaller than report count, ignore remaining bit in report
422dfb01e77SMatthias Ringwald             log_debug("Ignoring %u items without Usage", parser->required_usages);
423dfb01e77SMatthias Ringwald             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
424dfb01e77SMatthias Ringwald             parser->required_usages = 0;
425dfb01e77SMatthias Ringwald         }
42612ccb71bSMatthias Ringwald     } else {
4274ea43905SMatthias Ringwald         if (parser->required_usages == 0u){
42812ccb71bSMatthias Ringwald             parser->available_usages = 0;
42912ccb71bSMatthias Ringwald         }
43012ccb71bSMatthias Ringwald     }
43112ccb71bSMatthias Ringwald     if (parser->available_usages) {
43212ccb71bSMatthias Ringwald         return;
43312ccb71bSMatthias Ringwald     }
4344ea43905SMatthias Ringwald     if (parser->required_usages == 0u){
43512ccb71bSMatthias Ringwald         hid_post_process_item(parser, &parser->descriptor_item);
43612ccb71bSMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
43712ccb71bSMatthias Ringwald         btstack_hid_parser_find_next_usage(parser);
43812ccb71bSMatthias Ringwald     } else {
43912ccb71bSMatthias Ringwald         hid_find_next_usage(parser);
4404ea43905SMatthias Ringwald         if (parser->available_usages == 0u) {
44112ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
44212ccb71bSMatthias Ringwald         }
44312ccb71bSMatthias Ringwald     }
44412ccb71bSMatthias Ringwald }
445fada7179SMilanka Ringwald 
446662cddc2SMilanka 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){
447fada7179SMilanka Ringwald     int total_report_size = 0;
448fada7179SMilanka Ringwald     int report_size = 0;
449fada7179SMilanka Ringwald     int report_count = 0;
450fada7179SMilanka Ringwald     int current_report_id = 0;
451fada7179SMilanka Ringwald 
452fada7179SMilanka Ringwald     while (hid_descriptor_len){
453fada7179SMilanka Ringwald         int valid_report_type = 0;
454fada7179SMilanka Ringwald         hid_descriptor_item_t item;
4553a1d5296SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
4563a1d5296SMatthias Ringwald         if (ok == false) {
4573a1d5296SMatthias Ringwald             return 0;
4583a1d5296SMatthias Ringwald         }
459fada7179SMilanka Ringwald         switch (item.item_type){
460fada7179SMilanka Ringwald             case Global:
461fada7179SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
462fada7179SMilanka Ringwald                     case ReportID:
463fada7179SMilanka Ringwald                         current_report_id = item.item_value;
464fada7179SMilanka Ringwald                         break;
465fada7179SMilanka Ringwald                     case ReportCount:
466fada7179SMilanka Ringwald                         report_count = item.item_value;
467fada7179SMilanka Ringwald                         break;
468fada7179SMilanka Ringwald                     case ReportSize:
469fada7179SMilanka Ringwald                         report_size = item.item_value;
470fada7179SMilanka Ringwald                         break;
471fada7179SMilanka Ringwald                     default:
472fada7179SMilanka Ringwald                         break;
473fada7179SMilanka Ringwald                 }
474fada7179SMilanka Ringwald                 break;
475fada7179SMilanka Ringwald             case Main:
476fada7179SMilanka Ringwald                 if (current_report_id != report_id) break;
477fada7179SMilanka Ringwald                 switch ((MainItemTag)item.item_tag){
478fada7179SMilanka Ringwald                     case Input:
479662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_INPUT) break;
480fada7179SMilanka Ringwald                         valid_report_type = 1;
481fada7179SMilanka Ringwald                         break;
482fada7179SMilanka Ringwald                     case Output:
483662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
484fada7179SMilanka Ringwald                         valid_report_type = 1;
485fada7179SMilanka Ringwald                         break;
486fada7179SMilanka Ringwald                     case Feature:
487662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
488fada7179SMilanka Ringwald                         valid_report_type = 1;
489fada7179SMilanka Ringwald                         break;
490fada7179SMilanka Ringwald                     default:
491fada7179SMilanka Ringwald                         break;
492fada7179SMilanka Ringwald                 }
493fada7179SMilanka Ringwald                 if (!valid_report_type) break;
494fada7179SMilanka Ringwald                 total_report_size += report_count * report_size;
495fada7179SMilanka Ringwald                 break;
496fada7179SMilanka Ringwald             default:
497fada7179SMilanka Ringwald                 break;
498fada7179SMilanka Ringwald         }
49915cf8612Sx0rloser 		if (total_report_size > 0 && current_report_id != report_id) break;
500fada7179SMilanka Ringwald         hid_descriptor_len -= item.item_size;
501fada7179SMilanka Ringwald         hid_descriptor += item.item_size;
502fada7179SMilanka Ringwald     }
503fada7179SMilanka Ringwald     return (total_report_size + 7)/8;
504fada7179SMilanka Ringwald }
505dbcaefc7SMilanka Ringwald 
506dbcaefc7SMilanka Ringwald hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
507dbcaefc7SMilanka Ringwald     int current_report_id = 0;
508dbcaefc7SMilanka Ringwald     while (hid_descriptor_len){
509dbcaefc7SMilanka Ringwald         hid_descriptor_item_t item;
5100e7cc963SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
5110e7cc963SMatthias Ringwald         if (ok == false){
5120e7cc963SMatthias Ringwald             return HID_REPORT_ID_INVALID;
5130e7cc963SMatthias Ringwald         }
514dbcaefc7SMilanka Ringwald         switch (item.item_type){
515dbcaefc7SMilanka Ringwald             case Global:
516dbcaefc7SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
517dbcaefc7SMilanka Ringwald                     case ReportID:
518dbcaefc7SMilanka Ringwald                         current_report_id = item.item_value;
519dbcaefc7SMilanka Ringwald                         if (current_report_id != report_id) break;
520dbcaefc7SMilanka Ringwald                         return HID_REPORT_ID_VALID;
521dbcaefc7SMilanka Ringwald                     default:
522dbcaefc7SMilanka Ringwald                         break;
523dbcaefc7SMilanka Ringwald                 }
524dbcaefc7SMilanka Ringwald                 break;
525dbcaefc7SMilanka Ringwald             default:
526dbcaefc7SMilanka Ringwald                 break;
527dbcaefc7SMilanka Ringwald         }
528dbcaefc7SMilanka Ringwald         hid_descriptor_len -= item.item_size;
529dbcaefc7SMilanka Ringwald         hid_descriptor += item.item_size;
530dbcaefc7SMilanka Ringwald     }
531dbcaefc7SMilanka Ringwald     if (current_report_id != 0) return HID_REPORT_ID_INVALID;
532dbcaefc7SMilanka Ringwald     return HID_REPORT_ID_UNDECLARED;
533dbcaefc7SMilanka Ringwald }
534dbcaefc7SMilanka Ringwald 
535c17118d0SMatthias Ringwald bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
536dbcaefc7SMilanka Ringwald     while (hid_descriptor_len){
537dbcaefc7SMilanka Ringwald         hid_descriptor_item_t item;
53864ce1065SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
53964ce1065SMatthias Ringwald         if (ok == false){
54064ce1065SMatthias Ringwald             break;
54164ce1065SMatthias Ringwald         }
542dbcaefc7SMilanka Ringwald         switch (item.item_type){
543dbcaefc7SMilanka Ringwald             case Global:
544dbcaefc7SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
545dbcaefc7SMilanka Ringwald                     case ReportID:
546c17118d0SMatthias Ringwald                         return true;
547dbcaefc7SMilanka Ringwald                     default:
548dbcaefc7SMilanka Ringwald                         break;
549dbcaefc7SMilanka Ringwald                 }
550dbcaefc7SMilanka Ringwald                 break;
551dbcaefc7SMilanka Ringwald             default:
552dbcaefc7SMilanka Ringwald                 break;
553dbcaefc7SMilanka Ringwald         }
554dbcaefc7SMilanka Ringwald         hid_descriptor_len -= item.item_size;
555dbcaefc7SMilanka Ringwald         hid_descriptor += item.item_size;
556dbcaefc7SMilanka Ringwald     }
557c17118d0SMatthias Ringwald     return false;
558dbcaefc7SMilanka Ringwald }
559