xref: /btstack/src/btstack_hid_parser.c (revision dfb01e777425a3bc67a37a54368ece74b1630f47)
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 
4012ccb71bSMatthias Ringwald #include <string.h>
4112ccb71bSMatthias Ringwald 
4212ccb71bSMatthias Ringwald #include "btstack_hid_parser.h"
4312ccb71bSMatthias Ringwald #include "btstack_util.h"
4412ccb71bSMatthias Ringwald #include "btstack_debug.h"
4512ccb71bSMatthias Ringwald 
4612ccb71bSMatthias Ringwald // Not implemented:
4712ccb71bSMatthias Ringwald // - Support for Push/Pop
4812ccb71bSMatthias Ringwald // - Optional Pretty Print of HID Descripor
4912ccb71bSMatthias Ringwald // - Support to query descriptort for contained usages, e.g. to detect keyboard or mouse
5012ccb71bSMatthias Ringwald 
5112ccb71bSMatthias Ringwald // #define HID_PARSER_PRETTY_PRINT
5212ccb71bSMatthias Ringwald 
5312ccb71bSMatthias Ringwald /*
5412ccb71bSMatthias Ringwald  *  btstack_hid_parser.c
5512ccb71bSMatthias Ringwald  */
5612ccb71bSMatthias Ringwald 
5712ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
5812ccb71bSMatthias Ringwald 
5912ccb71bSMatthias Ringwald static const char * type_names[] = {
6012ccb71bSMatthias Ringwald     "Main",
6112ccb71bSMatthias Ringwald     "Global",
6212ccb71bSMatthias Ringwald     "Local",
6312ccb71bSMatthias Ringwald     "Reserved"
6412ccb71bSMatthias Ringwald };
6512ccb71bSMatthias Ringwald static const char * main_tags[] = {
6612ccb71bSMatthias Ringwald     "",
6712ccb71bSMatthias Ringwald     "",
6812ccb71bSMatthias Ringwald     "",
6912ccb71bSMatthias Ringwald     "",
7012ccb71bSMatthias Ringwald     "",
7112ccb71bSMatthias Ringwald     "",
7212ccb71bSMatthias Ringwald     "",
7312ccb71bSMatthias Ringwald     "",
7412ccb71bSMatthias Ringwald     "Input ",
7512ccb71bSMatthias Ringwald     "Output",
7612ccb71bSMatthias Ringwald     "Collection",
7712ccb71bSMatthias Ringwald     "Feature",
7812ccb71bSMatthias Ringwald     "End Collection",
7912ccb71bSMatthias Ringwald     "Reserved",
8012ccb71bSMatthias Ringwald     "Reserved",
8112ccb71bSMatthias Ringwald     "Reserved"
8212ccb71bSMatthias Ringwald };
8312ccb71bSMatthias Ringwald static const char * global_tags[] = {
8412ccb71bSMatthias Ringwald     "Usage Page",
8512ccb71bSMatthias Ringwald     "Logical Minimum",
8612ccb71bSMatthias Ringwald     "Logical Maximum",
8712ccb71bSMatthias Ringwald     "Physical Minimum",
8812ccb71bSMatthias Ringwald     "Physical Maximum",
8912ccb71bSMatthias Ringwald     "Unit Exponent",
9012ccb71bSMatthias Ringwald     "Unit",
9112ccb71bSMatthias Ringwald     "Report Size",
9212ccb71bSMatthias Ringwald     "Report ID",
9312ccb71bSMatthias Ringwald     "Report Count",
9412ccb71bSMatthias Ringwald     "Push",
9512ccb71bSMatthias Ringwald     "Pop",
9612ccb71bSMatthias Ringwald     "Reserved",
9712ccb71bSMatthias Ringwald     "Reserved",
9812ccb71bSMatthias Ringwald     "Reserved",
9912ccb71bSMatthias Ringwald     "Reserved"
10012ccb71bSMatthias Ringwald };
10112ccb71bSMatthias Ringwald static const char * local_tags[] = {
10212ccb71bSMatthias Ringwald     "Usage",
10312ccb71bSMatthias Ringwald     "Usage Minimum",
10412ccb71bSMatthias Ringwald     "Usage Maximum",
10512ccb71bSMatthias Ringwald     "Designator Index",
10612ccb71bSMatthias Ringwald     "Designator Minimum",
10712ccb71bSMatthias Ringwald     "Designator Maximum",
10812ccb71bSMatthias Ringwald     "String Index",
10912ccb71bSMatthias Ringwald     "String Minimum",
11012ccb71bSMatthias Ringwald     "String Maximum",
11112ccb71bSMatthias Ringwald     "Delimiter",
11212ccb71bSMatthias Ringwald     "Reserved",
11312ccb71bSMatthias Ringwald     "Reserved",
11412ccb71bSMatthias Ringwald     "Reserved",
11512ccb71bSMatthias Ringwald     "Reserved",
11612ccb71bSMatthias Ringwald     "Reserved",
11712ccb71bSMatthias Ringwald     "Reserved"
11812ccb71bSMatthias Ringwald };
11912ccb71bSMatthias Ringwald #endif
12012ccb71bSMatthias Ringwald 
12112ccb71bSMatthias Ringwald static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
12212ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
12312ccb71bSMatthias Ringwald     const char ** item_tag_table;
1247bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
12512ccb71bSMatthias Ringwald         case Main:
12612ccb71bSMatthias Ringwald             item_tag_table = main_tags;
12712ccb71bSMatthias Ringwald             break;
12812ccb71bSMatthias Ringwald         case Global:
12912ccb71bSMatthias Ringwald             item_tag_table = global_tags;
13012ccb71bSMatthias Ringwald             break;
13112ccb71bSMatthias Ringwald         case Local:
13212ccb71bSMatthias Ringwald             item_tag_table = local_tags;
13312ccb71bSMatthias Ringwald             break;
13412ccb71bSMatthias Ringwald         default:
13512ccb71bSMatthias Ringwald             item_tag_table = NULL;
13612ccb71bSMatthias Ringwald             break;
13712ccb71bSMatthias Ringwald     }
13812ccb71bSMatthias Ringwald     const char * item_tag_name = "Invalid";
13912ccb71bSMatthias Ringwald     if (item_tag_table){
14012ccb71bSMatthias Ringwald         item_tag_name = item_tag_table[item->item_tag];
14112ccb71bSMatthias Ringwald     }
142ea1e21c2SMatthias Ringwald     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
14312ccb71bSMatthias Ringwald #else
14412ccb71bSMatthias Ringwald     UNUSED(parser);
14512ccb71bSMatthias Ringwald     UNUSED(item);
14612ccb71bSMatthias Ringwald #endif
14712ccb71bSMatthias Ringwald }
14812ccb71bSMatthias Ringwald 
14912ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value
150fada7179SMilanka Ringwald void btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
1518334d3d8SMatthias Ringwald 
1528334d3d8SMatthias Ringwald     const int hid_item_sizes[] = { 0, 1, 2, 4 };
1538334d3d8SMatthias Ringwald 
15412ccb71bSMatthias Ringwald     // parse item header
1554ea43905SMatthias Ringwald     if (hid_descriptor_len < 1u) return;
15612ccb71bSMatthias Ringwald     uint16_t pos = 0;
15712ccb71bSMatthias Ringwald     uint8_t item_header = hid_descriptor[pos++];
1584ea43905SMatthias Ringwald     item->data_size = hid_item_sizes[item_header & 0x03u];
1594ea43905SMatthias Ringwald     item->item_type = (item_header & 0x0cu) >> 2u;
1604ea43905SMatthias Ringwald     item->item_tag  = (item_header & 0xf0u) >> 4u;
16112ccb71bSMatthias Ringwald     // long item
1624ea43905SMatthias Ringwald     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
1634ea43905SMatthias Ringwald         if (hid_descriptor_len < 3u) return;
16412ccb71bSMatthias Ringwald         item->data_size = hid_descriptor[pos++];
16512ccb71bSMatthias Ringwald         item->item_tag  = hid_descriptor[pos++];
16612ccb71bSMatthias Ringwald     }
16712ccb71bSMatthias Ringwald     item->item_size =  pos + item->data_size;
16812ccb71bSMatthias Ringwald     item->item_value = 0;
16912ccb71bSMatthias Ringwald 
17012ccb71bSMatthias Ringwald     // read item value
17112ccb71bSMatthias Ringwald     if (hid_descriptor_len < item->item_size) return;
1724ea43905SMatthias Ringwald     if (item->data_size > 4u) return;
17312ccb71bSMatthias Ringwald     int i;
1744ea43905SMatthias Ringwald     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
17512ccb71bSMatthias Ringwald     int32_t value = 0;
17612ccb71bSMatthias Ringwald     uint8_t latest_byte = 0;
17712ccb71bSMatthias Ringwald     for (i=0;i<item->data_size;i++){
17812ccb71bSMatthias Ringwald         latest_byte = hid_descriptor[pos++];
17912ccb71bSMatthias Ringwald         value = (latest_byte << (8*i)) | value;
18012ccb71bSMatthias Ringwald     }
1814ea43905SMatthias Ringwald     if (sgnd && (item->data_size > 0u)){
1824ea43905SMatthias Ringwald         if (latest_byte & 0x80u) {
1834ea43905SMatthias Ringwald             value -= 1u << (item->data_size*8u);
18412ccb71bSMatthias Ringwald         }
18512ccb71bSMatthias Ringwald     }
18612ccb71bSMatthias Ringwald     item->item_value = value;
18712ccb71bSMatthias Ringwald }
18812ccb71bSMatthias Ringwald 
18912ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
1907bbeb3adSMilanka Ringwald     switch((GlobalItemTag)item->item_tag){
19112ccb71bSMatthias Ringwald         case UsagePage:
19212ccb71bSMatthias Ringwald             parser->global_usage_page = item->item_value;
19312ccb71bSMatthias Ringwald             break;
19412ccb71bSMatthias Ringwald         case LogicalMinimum:
19512ccb71bSMatthias Ringwald             parser->global_logical_minimum = item->item_value;
19612ccb71bSMatthias Ringwald             break;
19712ccb71bSMatthias Ringwald         case LogicalMaximum:
19812ccb71bSMatthias Ringwald             parser->global_logical_maximum = item->item_value;
19912ccb71bSMatthias Ringwald             break;
20012ccb71bSMatthias Ringwald         case ReportSize:
20112ccb71bSMatthias Ringwald             parser->global_report_size = item->item_value;
20212ccb71bSMatthias Ringwald             break;
20312ccb71bSMatthias Ringwald         case ReportID:
204c1ab6cc1SMatthias Ringwald             if (parser->active_record && (parser->global_report_id != item->item_value)){
20512ccb71bSMatthias Ringwald                 parser->active_record = 0;
20612ccb71bSMatthias Ringwald             }
20712ccb71bSMatthias Ringwald             parser->global_report_id = item->item_value;
20812ccb71bSMatthias Ringwald             break;
20912ccb71bSMatthias Ringwald         case ReportCount:
21012ccb71bSMatthias Ringwald             parser->global_report_count = item->item_value;
21112ccb71bSMatthias Ringwald             break;
2127bbeb3adSMilanka Ringwald 
2137bbeb3adSMilanka Ringwald         // TODO handle tags
2147bbeb3adSMilanka Ringwald         case PhysicalMinimum:
2157bbeb3adSMilanka Ringwald         case PhysicalMaximum:
2167bbeb3adSMilanka Ringwald         case UnitExponent:
2177bbeb3adSMilanka Ringwald         case Unit:
2187bbeb3adSMilanka Ringwald         case Push:
2197bbeb3adSMilanka Ringwald         case Pop:
2207bbeb3adSMilanka Ringwald             break;
2217bbeb3adSMilanka Ringwald 
22212ccb71bSMatthias Ringwald         default:
2237bbeb3adSMilanka Ringwald             btstack_assert(false);
22412ccb71bSMatthias Ringwald             break;
22512ccb71bSMatthias Ringwald     }
22612ccb71bSMatthias Ringwald }
22712ccb71bSMatthias Ringwald 
22812ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){
2294ea43905SMatthias Ringwald     while ((parser->available_usages == 0u) && (parser->usage_pos < parser->descriptor_pos)){
23012ccb71bSMatthias Ringwald         hid_descriptor_item_t usage_item;
23112ccb71bSMatthias Ringwald         // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len
23212ccb71bSMatthias Ringwald         btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
2330e588213SMatthias Ringwald         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
23412ccb71bSMatthias Ringwald             parser->usage_page = usage_item.item_value;
23512ccb71bSMatthias Ringwald         }
23612ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
2374ea43905SMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
23812ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
23912ccb71bSMatthias Ringwald                 case Usage:
24012ccb71bSMatthias Ringwald                     parser->available_usages = 1;
24112ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
24212ccb71bSMatthias Ringwald                     break;
24312ccb71bSMatthias Ringwald                 case UsageMinimum:
24412ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
24512ccb71bSMatthias Ringwald                     parser->have_usage_min = 1;
24612ccb71bSMatthias Ringwald                     break;
24712ccb71bSMatthias Ringwald                 case UsageMaximum:
24812ccb71bSMatthias Ringwald                     parser->usage_maximum = usage_value;
24912ccb71bSMatthias Ringwald                     parser->have_usage_max = 1;
25012ccb71bSMatthias Ringwald                     break;
25112ccb71bSMatthias Ringwald                 default:
25212ccb71bSMatthias Ringwald                     break;
25312ccb71bSMatthias Ringwald             }
25412ccb71bSMatthias Ringwald             if (parser->have_usage_min && parser->have_usage_max){
2554ea43905SMatthias Ringwald                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
256*dfb01e77SMatthias Ringwald                 if (parser->available_usages < parser->required_usages){
257*dfb01e77SMatthias Ringwald                     log_debug("Usage Min - Usage Max [%04x..%04x] < Report Count %u", parser->usage_minimum, parser->usage_maximum, parser->required_usages);
258*dfb01e77SMatthias Ringwald                 }
25912ccb71bSMatthias Ringwald                 parser->have_usage_min = 0;
26012ccb71bSMatthias Ringwald                 parser->have_usage_max = 0;
26112ccb71bSMatthias Ringwald             }
26212ccb71bSMatthias Ringwald         }
26312ccb71bSMatthias Ringwald         parser->usage_pos += usage_item.item_size;
26412ccb71bSMatthias Ringwald     }
26512ccb71bSMatthias Ringwald }
26612ccb71bSMatthias Ringwald 
26712ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
26812ccb71bSMatthias Ringwald     hid_pretty_print_item(parser, item);
26912ccb71bSMatthias Ringwald     int valid_field = 0;
2707bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
27112ccb71bSMatthias Ringwald         case Main:
2727bbeb3adSMilanka Ringwald             switch ((MainItemTag)item->item_tag){
27312ccb71bSMatthias Ringwald                 case Input:
274662cddc2SMilanka Ringwald                     valid_field = parser->report_type == HID_REPORT_TYPE_INPUT;
27512ccb71bSMatthias Ringwald                     break;
27612ccb71bSMatthias Ringwald                 case Output:
277662cddc2SMilanka Ringwald                     valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT;
27812ccb71bSMatthias Ringwald                     break;
27912ccb71bSMatthias Ringwald                 case Feature:
280662cddc2SMilanka Ringwald                     valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE;
28112ccb71bSMatthias Ringwald                     break;
28212ccb71bSMatthias Ringwald                 default:
28312ccb71bSMatthias Ringwald                     break;
28412ccb71bSMatthias Ringwald             }
28512ccb71bSMatthias Ringwald             break;
28612ccb71bSMatthias Ringwald         case Global:
28712ccb71bSMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
28812ccb71bSMatthias Ringwald             break;
28912ccb71bSMatthias Ringwald         case Local:
2907bbeb3adSMilanka Ringwald         case Reserved:
2917bbeb3adSMilanka Ringwald             break;
2927bbeb3adSMilanka Ringwald         default:
2937bbeb3adSMilanka Ringwald             btstack_assert(false);
29412ccb71bSMatthias Ringwald             break;
29512ccb71bSMatthias Ringwald     }
29612ccb71bSMatthias Ringwald     if (!valid_field) return;
29712ccb71bSMatthias Ringwald 
29812ccb71bSMatthias Ringwald     // verify record id
29912ccb71bSMatthias Ringwald     if (parser->global_report_id && !parser->active_record){
30012ccb71bSMatthias Ringwald         if (parser->report[0] != parser->global_report_id){
30112ccb71bSMatthias Ringwald             return;
30212ccb71bSMatthias Ringwald         }
3034ea43905SMatthias Ringwald         parser->report_pos_in_bit += 8u;
30412ccb71bSMatthias Ringwald     }
30512ccb71bSMatthias Ringwald     parser->active_record = 1;
30612ccb71bSMatthias Ringwald     // handle constant fields used for padding
30712ccb71bSMatthias Ringwald     if (item->item_value & 1){
30812ccb71bSMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
30912ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
31012ccb71bSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
31112ccb71bSMatthias Ringwald #endif
31212ccb71bSMatthias Ringwald         parser->report_pos_in_bit += item_bits;
31312ccb71bSMatthias Ringwald         return;
31412ccb71bSMatthias Ringwald     }
31512ccb71bSMatthias Ringwald     // Empty Item
3164ea43905SMatthias Ringwald     if (parser->global_report_count == 0u) return;
31712ccb71bSMatthias Ringwald     // let's start
31812ccb71bSMatthias Ringwald     parser->required_usages = parser->global_report_count;
31912ccb71bSMatthias Ringwald }
32012ccb71bSMatthias Ringwald 
32112ccb71bSMatthias Ringwald static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
3227bbeb3adSMilanka Ringwald     if ((TagType)item->item_type == Main){
32312ccb71bSMatthias Ringwald         // reset usage
32412ccb71bSMatthias Ringwald         parser->usage_pos  = parser->descriptor_pos;
32512ccb71bSMatthias Ringwald         parser->usage_page = parser->global_usage_page;
32612ccb71bSMatthias Ringwald     }
32712ccb71bSMatthias Ringwald     parser->descriptor_pos += item->item_size;
32812ccb71bSMatthias Ringwald }
32912ccb71bSMatthias Ringwald 
33012ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
33112ccb71bSMatthias Ringwald     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
33212ccb71bSMatthias Ringwald         if (parser->descriptor_pos >= parser->descriptor_len){
33312ccb71bSMatthias Ringwald             // end of descriptor
33412ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
33512ccb71bSMatthias Ringwald             break;
33612ccb71bSMatthias Ringwald         }
33712ccb71bSMatthias Ringwald         btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
33812ccb71bSMatthias Ringwald         hid_process_item(parser, &parser->descriptor_item);
33912ccb71bSMatthias Ringwald         if (parser->required_usages){
34012ccb71bSMatthias Ringwald             hid_find_next_usage(parser);
34112ccb71bSMatthias Ringwald             if (parser->available_usages) {
34212ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
34312ccb71bSMatthias Ringwald             } else {
344*dfb01e77SMatthias Ringwald                 log_debug("no usages found");
34512ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
34612ccb71bSMatthias Ringwald             }
34712ccb71bSMatthias Ringwald         } else {
34812ccb71bSMatthias Ringwald             hid_post_process_item(parser, &parser->descriptor_item);
34912ccb71bSMatthias Ringwald         }
35012ccb71bSMatthias Ringwald     }
35112ccb71bSMatthias Ringwald }
35212ccb71bSMatthias Ringwald 
35312ccb71bSMatthias Ringwald // PUBLIC API
35412ccb71bSMatthias Ringwald 
355662cddc2SMilanka 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){
35612ccb71bSMatthias Ringwald 
35712ccb71bSMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
35812ccb71bSMatthias Ringwald 
35912ccb71bSMatthias Ringwald     parser->descriptor     = hid_descriptor;
36012ccb71bSMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
36112ccb71bSMatthias Ringwald     parser->report_type    = hid_report_type;
36212ccb71bSMatthias Ringwald     parser->report         = hid_report;
36312ccb71bSMatthias Ringwald     parser->report_len     = hid_report_len;
36412ccb71bSMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
36512ccb71bSMatthias Ringwald 
36612ccb71bSMatthias Ringwald     btstack_hid_parser_find_next_usage(parser);
36712ccb71bSMatthias Ringwald }
36812ccb71bSMatthias Ringwald 
36912ccb71bSMatthias Ringwald int  btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
37012ccb71bSMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
37112ccb71bSMatthias Ringwald }
37212ccb71bSMatthias Ringwald 
37312ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
37412ccb71bSMatthias Ringwald 
37512ccb71bSMatthias Ringwald     *usage_page = parser->usage_minimum >> 16;
37612ccb71bSMatthias Ringwald 
37712ccb71bSMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
3781979f09cSMatthias Ringwald     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
3791979f09cSMatthias Ringwald     bool is_signed     = parser->global_logical_minimum < 0;
38012ccb71bSMatthias Ringwald     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
3814ea43905SMatthias Ringwald     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
38212ccb71bSMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
38312ccb71bSMatthias Ringwald     int i;
38412ccb71bSMatthias Ringwald     uint32_t multi_byte_value = 0;
38512ccb71bSMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
38612ccb71bSMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
38712ccb71bSMatthias Ringwald     }
3884ea43905SMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
38912ccb71bSMatthias 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);
39012ccb71bSMatthias Ringwald     if (is_variable){
3914ea43905SMatthias Ringwald         *usage      = parser->usage_minimum & 0xffffu;
3924ea43905SMatthias Ringwald         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
3934ea43905SMatthias Ringwald             *value = unsigned_value - (1u<<parser->global_report_size);
39412ccb71bSMatthias Ringwald         } else {
39512ccb71bSMatthias Ringwald             *value = unsigned_value;
39612ccb71bSMatthias Ringwald         }
39712ccb71bSMatthias Ringwald     } else {
39812ccb71bSMatthias Ringwald         *usage  = unsigned_value;
39912ccb71bSMatthias Ringwald         *value  = 1;
40012ccb71bSMatthias Ringwald     }
40112ccb71bSMatthias Ringwald     parser->required_usages--;
40212ccb71bSMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
40312ccb71bSMatthias Ringwald 
40412ccb71bSMatthias Ringwald     // next usage
40512ccb71bSMatthias Ringwald     if (is_variable){
40612ccb71bSMatthias Ringwald         parser->usage_minimum++;
40712ccb71bSMatthias Ringwald         parser->available_usages--;
408*dfb01e77SMatthias Ringwald         if (parser->usage_minimum > parser->usage_maximum){
409*dfb01e77SMatthias Ringwald             // usage min - max range smaller than report count, ignore remaining bit in report
410*dfb01e77SMatthias Ringwald             log_debug("Ignoring %u items without Usage", parser->required_usages);
411*dfb01e77SMatthias Ringwald             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
412*dfb01e77SMatthias Ringwald             parser->required_usages = 0;
413*dfb01e77SMatthias Ringwald         }
41412ccb71bSMatthias Ringwald     } else {
4154ea43905SMatthias Ringwald         if (parser->required_usages == 0u){
41612ccb71bSMatthias Ringwald             parser->available_usages = 0;
41712ccb71bSMatthias Ringwald         }
41812ccb71bSMatthias Ringwald     }
41912ccb71bSMatthias Ringwald     if (parser->available_usages) {
42012ccb71bSMatthias Ringwald         return;
42112ccb71bSMatthias Ringwald     }
4224ea43905SMatthias Ringwald     if (parser->required_usages == 0u){
42312ccb71bSMatthias Ringwald         hid_post_process_item(parser, &parser->descriptor_item);
42412ccb71bSMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
42512ccb71bSMatthias Ringwald         btstack_hid_parser_find_next_usage(parser);
42612ccb71bSMatthias Ringwald     } else {
42712ccb71bSMatthias Ringwald         hid_find_next_usage(parser);
4284ea43905SMatthias Ringwald         if (parser->available_usages == 0u) {
42912ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
43012ccb71bSMatthias Ringwald         }
43112ccb71bSMatthias Ringwald     }
43212ccb71bSMatthias Ringwald }
433fada7179SMilanka Ringwald 
434662cddc2SMilanka 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){
435fada7179SMilanka Ringwald     int total_report_size = 0;
436fada7179SMilanka Ringwald     int report_size = 0;
437fada7179SMilanka Ringwald     int report_count = 0;
438fada7179SMilanka Ringwald     int current_report_id = 0;
439fada7179SMilanka Ringwald 
440fada7179SMilanka Ringwald     while (hid_descriptor_len){
441fada7179SMilanka Ringwald         int valid_report_type = 0;
442fada7179SMilanka Ringwald         hid_descriptor_item_t item;
443fada7179SMilanka Ringwald         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
444fada7179SMilanka Ringwald         switch (item.item_type){
445fada7179SMilanka Ringwald             case Global:
446fada7179SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
447fada7179SMilanka Ringwald                     case ReportID:
448fada7179SMilanka Ringwald                         current_report_id = item.item_value;
449fada7179SMilanka Ringwald                         break;
450fada7179SMilanka Ringwald                     case ReportCount:
451fada7179SMilanka Ringwald                         report_count = item.item_value;
452fada7179SMilanka Ringwald                         break;
453fada7179SMilanka Ringwald                     case ReportSize:
454fada7179SMilanka Ringwald                         report_size = item.item_value;
455fada7179SMilanka Ringwald                         break;
456fada7179SMilanka Ringwald                     default:
457fada7179SMilanka Ringwald                         break;
458fada7179SMilanka Ringwald                 }
459fada7179SMilanka Ringwald                 break;
460fada7179SMilanka Ringwald             case Main:
461fada7179SMilanka Ringwald                 if (current_report_id != report_id) break;
462fada7179SMilanka Ringwald                 switch ((MainItemTag)item.item_tag){
463fada7179SMilanka Ringwald                     case Input:
464662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_INPUT) break;
465fada7179SMilanka Ringwald                         valid_report_type = 1;
466fada7179SMilanka Ringwald                         break;
467fada7179SMilanka Ringwald                     case Output:
468662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
469fada7179SMilanka Ringwald                         valid_report_type = 1;
470fada7179SMilanka Ringwald                         break;
471fada7179SMilanka Ringwald                     case Feature:
472662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
473fada7179SMilanka Ringwald                         valid_report_type = 1;
474fada7179SMilanka Ringwald                         break;
475fada7179SMilanka Ringwald                     default:
476fada7179SMilanka Ringwald                         break;
477fada7179SMilanka Ringwald                 }
478fada7179SMilanka Ringwald                 if (!valid_report_type) break;
479fada7179SMilanka Ringwald                 total_report_size += report_count * report_size;
480fada7179SMilanka Ringwald                 break;
481fada7179SMilanka Ringwald             default:
482fada7179SMilanka Ringwald                 break;
483fada7179SMilanka Ringwald         }
48415cf8612Sx0rloser 		if (total_report_size > 0 && current_report_id != report_id) break;
485fada7179SMilanka Ringwald         hid_descriptor_len -= item.item_size;
486fada7179SMilanka Ringwald         hid_descriptor += item.item_size;
487fada7179SMilanka Ringwald     }
488fada7179SMilanka Ringwald     return (total_report_size + 7)/8;
489fada7179SMilanka Ringwald }
490dbcaefc7SMilanka Ringwald 
491dbcaefc7SMilanka Ringwald hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
492dbcaefc7SMilanka Ringwald     int current_report_id = 0;
493dbcaefc7SMilanka Ringwald     while (hid_descriptor_len){
494dbcaefc7SMilanka Ringwald         hid_descriptor_item_t item;
495dbcaefc7SMilanka Ringwald         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
496dbcaefc7SMilanka Ringwald         switch (item.item_type){
497dbcaefc7SMilanka Ringwald             case Global:
498dbcaefc7SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
499dbcaefc7SMilanka Ringwald                     case ReportID:
500dbcaefc7SMilanka Ringwald                         current_report_id = item.item_value;
501dbcaefc7SMilanka Ringwald                         if (current_report_id != report_id) break;
502dbcaefc7SMilanka Ringwald                         return HID_REPORT_ID_VALID;
503dbcaefc7SMilanka Ringwald                     default:
504dbcaefc7SMilanka Ringwald                         break;
505dbcaefc7SMilanka Ringwald                 }
506dbcaefc7SMilanka Ringwald                 break;
507dbcaefc7SMilanka Ringwald             default:
508dbcaefc7SMilanka Ringwald                 break;
509dbcaefc7SMilanka Ringwald         }
510dbcaefc7SMilanka Ringwald         hid_descriptor_len -= item.item_size;
511dbcaefc7SMilanka Ringwald         hid_descriptor += item.item_size;
512dbcaefc7SMilanka Ringwald     }
513dbcaefc7SMilanka Ringwald     if (current_report_id != 0) return HID_REPORT_ID_INVALID;
514dbcaefc7SMilanka Ringwald     return HID_REPORT_ID_UNDECLARED;
515dbcaefc7SMilanka Ringwald }
516dbcaefc7SMilanka Ringwald 
517dbcaefc7SMilanka Ringwald int btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
518dbcaefc7SMilanka Ringwald     while (hid_descriptor_len){
519dbcaefc7SMilanka Ringwald         hid_descriptor_item_t item;
520dbcaefc7SMilanka Ringwald         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
521dbcaefc7SMilanka Ringwald         switch (item.item_type){
522dbcaefc7SMilanka Ringwald             case Global:
523dbcaefc7SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
524dbcaefc7SMilanka Ringwald                     case ReportID:
525dbcaefc7SMilanka Ringwald                         return 1;
526dbcaefc7SMilanka Ringwald                     default:
527dbcaefc7SMilanka Ringwald                         break;
528dbcaefc7SMilanka Ringwald                 }
529dbcaefc7SMilanka Ringwald                 break;
530dbcaefc7SMilanka Ringwald             default:
531dbcaefc7SMilanka Ringwald                 break;
532dbcaefc7SMilanka Ringwald         }
533dbcaefc7SMilanka Ringwald         hid_descriptor_len -= item.item_size;
534dbcaefc7SMilanka Ringwald         hid_descriptor += item.item_size;
535dbcaefc7SMilanka Ringwald     }
536dbcaefc7SMilanka Ringwald     return 0;
537dbcaefc7SMilanka Ringwald }
538