xref: /btstack/src/btstack_hid_parser.c (revision fada717989a35f4df29d97c95f5973153ee9e7d2)
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
2312ccb71bSMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
2412ccb71bSMatthias Ringwald  * RINGWALD 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 
3812ccb71bSMatthias 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 const int hid_item_sizes[] = { 0, 1, 2, 4 };
5812ccb71bSMatthias Ringwald 
5912ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
6012ccb71bSMatthias Ringwald 
6112ccb71bSMatthias Ringwald static const char * type_names[] = {
6212ccb71bSMatthias Ringwald     "Main",
6312ccb71bSMatthias Ringwald     "Global",
6412ccb71bSMatthias Ringwald     "Local",
6512ccb71bSMatthias Ringwald     "Reserved"
6612ccb71bSMatthias Ringwald };
6712ccb71bSMatthias Ringwald static const char * main_tags[] = {
6812ccb71bSMatthias Ringwald     "",
6912ccb71bSMatthias Ringwald     "",
7012ccb71bSMatthias Ringwald     "",
7112ccb71bSMatthias Ringwald     "",
7212ccb71bSMatthias Ringwald     "",
7312ccb71bSMatthias Ringwald     "",
7412ccb71bSMatthias Ringwald     "",
7512ccb71bSMatthias Ringwald     "",
7612ccb71bSMatthias Ringwald     "Input ",
7712ccb71bSMatthias Ringwald     "Output",
7812ccb71bSMatthias Ringwald     "Collection",
7912ccb71bSMatthias Ringwald     "Feature",
8012ccb71bSMatthias Ringwald     "End Collection",
8112ccb71bSMatthias Ringwald     "Reserved",
8212ccb71bSMatthias Ringwald     "Reserved",
8312ccb71bSMatthias Ringwald     "Reserved"
8412ccb71bSMatthias Ringwald };
8512ccb71bSMatthias Ringwald static const char * global_tags[] = {
8612ccb71bSMatthias Ringwald     "Usage Page",
8712ccb71bSMatthias Ringwald     "Logical Minimum",
8812ccb71bSMatthias Ringwald     "Logical Maximum",
8912ccb71bSMatthias Ringwald     "Physical Minimum",
9012ccb71bSMatthias Ringwald     "Physical Maximum",
9112ccb71bSMatthias Ringwald     "Unit Exponent",
9212ccb71bSMatthias Ringwald     "Unit",
9312ccb71bSMatthias Ringwald     "Report Size",
9412ccb71bSMatthias Ringwald     "Report ID",
9512ccb71bSMatthias Ringwald     "Report Count",
9612ccb71bSMatthias Ringwald     "Push",
9712ccb71bSMatthias Ringwald     "Pop",
9812ccb71bSMatthias Ringwald     "Reserved",
9912ccb71bSMatthias Ringwald     "Reserved",
10012ccb71bSMatthias Ringwald     "Reserved",
10112ccb71bSMatthias Ringwald     "Reserved"
10212ccb71bSMatthias Ringwald };
10312ccb71bSMatthias Ringwald static const char * local_tags[] = {
10412ccb71bSMatthias Ringwald     "Usage",
10512ccb71bSMatthias Ringwald     "Usage Minimum",
10612ccb71bSMatthias Ringwald     "Usage Maximum",
10712ccb71bSMatthias Ringwald     "Designator Index",
10812ccb71bSMatthias Ringwald     "Designator Minimum",
10912ccb71bSMatthias Ringwald     "Designator Maximum",
11012ccb71bSMatthias Ringwald     "String Index",
11112ccb71bSMatthias Ringwald     "String Minimum",
11212ccb71bSMatthias Ringwald     "String Maximum",
11312ccb71bSMatthias Ringwald     "Delimiter",
11412ccb71bSMatthias Ringwald     "Reserved",
11512ccb71bSMatthias Ringwald     "Reserved",
11612ccb71bSMatthias Ringwald     "Reserved",
11712ccb71bSMatthias Ringwald     "Reserved",
11812ccb71bSMatthias Ringwald     "Reserved",
11912ccb71bSMatthias Ringwald     "Reserved"
12012ccb71bSMatthias Ringwald };
12112ccb71bSMatthias Ringwald #endif
12212ccb71bSMatthias Ringwald 
12312ccb71bSMatthias Ringwald static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
12412ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
12512ccb71bSMatthias Ringwald     const char ** item_tag_table;
12612ccb71bSMatthias Ringwald     switch (item->item_type){
12712ccb71bSMatthias Ringwald         case Main:
12812ccb71bSMatthias Ringwald             item_tag_table = main_tags;
12912ccb71bSMatthias Ringwald             break;
13012ccb71bSMatthias Ringwald         case Global:
13112ccb71bSMatthias Ringwald             item_tag_table = global_tags;
13212ccb71bSMatthias Ringwald             break;
13312ccb71bSMatthias Ringwald         case Local:
13412ccb71bSMatthias Ringwald             item_tag_table = local_tags;
13512ccb71bSMatthias Ringwald             break;
13612ccb71bSMatthias Ringwald         default:
13712ccb71bSMatthias Ringwald             item_tag_table = NULL;
13812ccb71bSMatthias Ringwald             break;
13912ccb71bSMatthias Ringwald     }
14012ccb71bSMatthias Ringwald     const char * item_tag_name = "Invalid";
14112ccb71bSMatthias Ringwald     if (item_tag_table){
14212ccb71bSMatthias Ringwald         item_tag_name = item_tag_table[item->item_tag];
14312ccb71bSMatthias Ringwald     }
14412ccb71bSMatthias Ringwald     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_table[item->item_tag], type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
14512ccb71bSMatthias Ringwald #else
14612ccb71bSMatthias Ringwald     UNUSED(parser);
14712ccb71bSMatthias Ringwald     UNUSED(item);
14812ccb71bSMatthias Ringwald #endif
14912ccb71bSMatthias Ringwald }
15012ccb71bSMatthias Ringwald 
15112ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value
152*fada7179SMilanka Ringwald void btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
15312ccb71bSMatthias Ringwald     // parse item header
15412ccb71bSMatthias Ringwald     if (hid_descriptor_len < 1) return;
15512ccb71bSMatthias Ringwald     uint16_t pos = 0;
15612ccb71bSMatthias Ringwald     uint8_t item_header = hid_descriptor[pos++];
15712ccb71bSMatthias Ringwald     item->data_size = hid_item_sizes[item_header & 0x03];
15812ccb71bSMatthias Ringwald     item->item_type = (item_header & 0x0c) >> 2;
15912ccb71bSMatthias Ringwald     item->item_tag  = (item_header & 0xf0) >> 4;
16012ccb71bSMatthias Ringwald     // long item
16112ccb71bSMatthias Ringwald     if (item->data_size == 2 && item->item_tag == 0x0f && item->item_type == 3){
16212ccb71bSMatthias Ringwald         if (hid_descriptor_len < 3) return;
16312ccb71bSMatthias Ringwald         item->data_size = hid_descriptor[pos++];
16412ccb71bSMatthias Ringwald         item->item_tag  = hid_descriptor[pos++];
16512ccb71bSMatthias Ringwald     }
16612ccb71bSMatthias Ringwald     item->item_size =  pos + item->data_size;
16712ccb71bSMatthias Ringwald     item->item_value = 0;
16812ccb71bSMatthias Ringwald 
16912ccb71bSMatthias Ringwald     // read item value
17012ccb71bSMatthias Ringwald     if (hid_descriptor_len < item->item_size) return;
17112ccb71bSMatthias Ringwald     if (item->data_size > 4) return;
17212ccb71bSMatthias Ringwald     int i;
17312ccb71bSMatthias Ringwald     int sgnd = item->item_type == Global && item->item_tag > 0 && item->item_tag < 5;
17412ccb71bSMatthias Ringwald     int32_t value = 0;
17512ccb71bSMatthias Ringwald     uint8_t latest_byte = 0;
17612ccb71bSMatthias Ringwald     for (i=0;i<item->data_size;i++){
17712ccb71bSMatthias Ringwald         latest_byte = hid_descriptor[pos++];
17812ccb71bSMatthias Ringwald         value = (latest_byte << (8*i)) | value;
17912ccb71bSMatthias Ringwald     }
18012ccb71bSMatthias Ringwald     if (sgnd && item->data_size > 0){
18112ccb71bSMatthias Ringwald         if (latest_byte & 0x80) {
18212ccb71bSMatthias Ringwald             value -= 1 << (item->data_size*8);
18312ccb71bSMatthias Ringwald         }
18412ccb71bSMatthias Ringwald     }
18512ccb71bSMatthias Ringwald     item->item_value = value;
18612ccb71bSMatthias Ringwald }
18712ccb71bSMatthias Ringwald 
18812ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
18912ccb71bSMatthias Ringwald     switch(item->item_tag){
19012ccb71bSMatthias Ringwald         case UsagePage:
19112ccb71bSMatthias Ringwald             parser->global_usage_page = item->item_value;
19212ccb71bSMatthias Ringwald             break;
19312ccb71bSMatthias Ringwald         case LogicalMinimum:
19412ccb71bSMatthias Ringwald             parser->global_logical_minimum = item->item_value;
19512ccb71bSMatthias Ringwald             break;
19612ccb71bSMatthias Ringwald         case LogicalMaximum:
19712ccb71bSMatthias Ringwald             parser->global_logical_maximum = item->item_value;
19812ccb71bSMatthias Ringwald             break;
19912ccb71bSMatthias Ringwald         case ReportSize:
20012ccb71bSMatthias Ringwald             parser->global_report_size = item->item_value;
20112ccb71bSMatthias Ringwald             break;
20212ccb71bSMatthias Ringwald         case ReportID:
20312ccb71bSMatthias Ringwald             if (parser->active_record && parser->global_report_id != item->item_value){
20412ccb71bSMatthias Ringwald                 // log_debug("New report, don't match anymore");
20512ccb71bSMatthias Ringwald                 parser->active_record = 0;
20612ccb71bSMatthias Ringwald             }
20712ccb71bSMatthias Ringwald             parser->global_report_id = item->item_value;
20812ccb71bSMatthias Ringwald             // log_info("- Report ID: %02x", parser->global_report_id);
20912ccb71bSMatthias Ringwald             break;
21012ccb71bSMatthias Ringwald         case ReportCount:
21112ccb71bSMatthias Ringwald             parser->global_report_count = item->item_value;
21212ccb71bSMatthias Ringwald             break;
21312ccb71bSMatthias Ringwald         default:
21412ccb71bSMatthias Ringwald             break;
21512ccb71bSMatthias Ringwald     }
21612ccb71bSMatthias Ringwald }
21712ccb71bSMatthias Ringwald 
21812ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){
21912ccb71bSMatthias Ringwald     while (parser->available_usages == 0 && parser->usage_pos < parser->descriptor_pos){
22012ccb71bSMatthias Ringwald         hid_descriptor_item_t usage_item;
22112ccb71bSMatthias Ringwald         // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len
22212ccb71bSMatthias Ringwald         btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
22312ccb71bSMatthias Ringwald         if (usage_item.item_type == Global && usage_item.item_tag == UsagePage){
22412ccb71bSMatthias Ringwald             parser->usage_page = usage_item.item_value;
22512ccb71bSMatthias Ringwald         }
22612ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
22712ccb71bSMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2) ? usage_item.item_value : (parser->usage_page << 16) | usage_item.item_value;
22812ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
22912ccb71bSMatthias Ringwald                 case Usage:
23012ccb71bSMatthias Ringwald                     parser->available_usages = 1;
23112ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
23212ccb71bSMatthias Ringwald                     break;
23312ccb71bSMatthias Ringwald                 case UsageMinimum:
23412ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
23512ccb71bSMatthias Ringwald                     parser->have_usage_min = 1;
23612ccb71bSMatthias Ringwald                     break;
23712ccb71bSMatthias Ringwald                 case UsageMaximum:
23812ccb71bSMatthias Ringwald                     parser->usage_maximum = usage_value;
23912ccb71bSMatthias Ringwald                     parser->have_usage_max = 1;
24012ccb71bSMatthias Ringwald                     break;
24112ccb71bSMatthias Ringwald                 default:
24212ccb71bSMatthias Ringwald                     break;
24312ccb71bSMatthias Ringwald             }
24412ccb71bSMatthias Ringwald             if (parser->have_usage_min && parser->have_usage_max){
24512ccb71bSMatthias Ringwald                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1;
24612ccb71bSMatthias Ringwald                 parser->have_usage_min = 0;
24712ccb71bSMatthias Ringwald                 parser->have_usage_max = 0;
24812ccb71bSMatthias Ringwald             }
24912ccb71bSMatthias Ringwald         }
25012ccb71bSMatthias Ringwald         parser->usage_pos += usage_item.item_size;
25112ccb71bSMatthias Ringwald     }
25212ccb71bSMatthias Ringwald }
25312ccb71bSMatthias Ringwald 
25412ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
25512ccb71bSMatthias Ringwald     hid_pretty_print_item(parser, item);
25612ccb71bSMatthias Ringwald     int valid_field = 0;
25712ccb71bSMatthias Ringwald     switch (item->item_type){
25812ccb71bSMatthias Ringwald         case Main:
25912ccb71bSMatthias Ringwald             switch (item->item_tag){
26012ccb71bSMatthias Ringwald                 case Input:
26112ccb71bSMatthias Ringwald                     valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_INPUT;
26212ccb71bSMatthias Ringwald                     break;
26312ccb71bSMatthias Ringwald                 case Output:
26412ccb71bSMatthias Ringwald                     valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_OUTPUT;
26512ccb71bSMatthias Ringwald                     break;
26612ccb71bSMatthias Ringwald                 case Feature:
26712ccb71bSMatthias Ringwald                     valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_FEATURE;
26812ccb71bSMatthias Ringwald                     break;
26912ccb71bSMatthias Ringwald                 default:
27012ccb71bSMatthias Ringwald                     break;
27112ccb71bSMatthias Ringwald             }
27212ccb71bSMatthias Ringwald             break;
27312ccb71bSMatthias Ringwald         case Global:
27412ccb71bSMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
27512ccb71bSMatthias Ringwald             break;
27612ccb71bSMatthias Ringwald         case Local:
27712ccb71bSMatthias Ringwald             break;
27812ccb71bSMatthias Ringwald     }
27912ccb71bSMatthias Ringwald     if (!valid_field) return;
28012ccb71bSMatthias Ringwald 
28112ccb71bSMatthias Ringwald     // verify record id
28212ccb71bSMatthias Ringwald     if (parser->global_report_id && !parser->active_record){
28312ccb71bSMatthias Ringwald         if (parser->report[0] != parser->global_report_id){
28412ccb71bSMatthias Ringwald             return;
28512ccb71bSMatthias Ringwald         }
28612ccb71bSMatthias Ringwald         parser->report_pos_in_bit += 8;
28712ccb71bSMatthias Ringwald     }
28812ccb71bSMatthias Ringwald     parser->active_record = 1;
28912ccb71bSMatthias Ringwald     // handle constant fields used for padding
29012ccb71bSMatthias Ringwald     if (item->item_value & 1){
29112ccb71bSMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
29212ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
29312ccb71bSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
29412ccb71bSMatthias Ringwald #endif
29512ccb71bSMatthias Ringwald         parser->report_pos_in_bit += item_bits;
29612ccb71bSMatthias Ringwald         return;
29712ccb71bSMatthias Ringwald     }
29812ccb71bSMatthias Ringwald     // Empty Item
29912ccb71bSMatthias Ringwald     if (parser->global_report_count == 0) return;
30012ccb71bSMatthias Ringwald     // let's start
30112ccb71bSMatthias Ringwald     parser->required_usages = parser->global_report_count;
30212ccb71bSMatthias Ringwald }
30312ccb71bSMatthias Ringwald 
30412ccb71bSMatthias Ringwald static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
30512ccb71bSMatthias Ringwald     if (item->item_type == Main){
30612ccb71bSMatthias Ringwald         // reset usage
30712ccb71bSMatthias Ringwald         parser->usage_pos  = parser->descriptor_pos;
30812ccb71bSMatthias Ringwald         parser->usage_page = parser->global_usage_page;
30912ccb71bSMatthias Ringwald     }
31012ccb71bSMatthias Ringwald     parser->descriptor_pos += item->item_size;
31112ccb71bSMatthias Ringwald }
31212ccb71bSMatthias Ringwald 
31312ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
31412ccb71bSMatthias Ringwald     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
31512ccb71bSMatthias Ringwald         if (parser->descriptor_pos >= parser->descriptor_len){
31612ccb71bSMatthias Ringwald             // end of descriptor
31712ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
31812ccb71bSMatthias Ringwald             break;
31912ccb71bSMatthias Ringwald         }
32012ccb71bSMatthias Ringwald         btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
32112ccb71bSMatthias Ringwald         hid_process_item(parser, &parser->descriptor_item);
32212ccb71bSMatthias Ringwald         if (parser->required_usages){
32312ccb71bSMatthias Ringwald             hid_find_next_usage(parser);
32412ccb71bSMatthias Ringwald             if (parser->available_usages) {
32512ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
32612ccb71bSMatthias Ringwald             } else {
32712ccb71bSMatthias Ringwald                 log_error("no usages found");
32812ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
32912ccb71bSMatthias Ringwald             }
33012ccb71bSMatthias Ringwald         } else {
33112ccb71bSMatthias Ringwald             hid_post_process_item(parser, &parser->descriptor_item);
33212ccb71bSMatthias Ringwald         }
33312ccb71bSMatthias Ringwald     }
33412ccb71bSMatthias Ringwald }
33512ccb71bSMatthias Ringwald 
33612ccb71bSMatthias Ringwald // PUBLIC API
33712ccb71bSMatthias Ringwald 
33812ccb71bSMatthias Ringwald void btstack_hid_parser_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, btstack_hid_report_type_t hid_report_type, const uint8_t * hid_report, uint16_t hid_report_len){
33912ccb71bSMatthias Ringwald 
34012ccb71bSMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
34112ccb71bSMatthias Ringwald 
34212ccb71bSMatthias Ringwald     parser->descriptor     = hid_descriptor;
34312ccb71bSMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
34412ccb71bSMatthias Ringwald     parser->report_type    = hid_report_type;
34512ccb71bSMatthias Ringwald     parser->report         = hid_report;
34612ccb71bSMatthias Ringwald     parser->report_len     = hid_report_len;
34712ccb71bSMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
34812ccb71bSMatthias Ringwald 
34912ccb71bSMatthias Ringwald     btstack_hid_parser_find_next_usage(parser);
35012ccb71bSMatthias Ringwald }
35112ccb71bSMatthias Ringwald 
35212ccb71bSMatthias Ringwald int  btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
35312ccb71bSMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
35412ccb71bSMatthias Ringwald }
35512ccb71bSMatthias Ringwald 
35612ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
35712ccb71bSMatthias Ringwald 
35812ccb71bSMatthias Ringwald     *usage_page = parser->usage_minimum >> 16;
35912ccb71bSMatthias Ringwald 
36012ccb71bSMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
36112ccb71bSMatthias Ringwald     int is_variable   = parser->descriptor_item.item_value & 2;
36212ccb71bSMatthias Ringwald     int is_signed     = parser->global_logical_minimum < 0;
36312ccb71bSMatthias Ringwald     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
36412ccb71bSMatthias Ringwald     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1) >> 3, parser->report_len);
36512ccb71bSMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
36612ccb71bSMatthias Ringwald     int i;
36712ccb71bSMatthias Ringwald     uint32_t multi_byte_value = 0;
36812ccb71bSMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
36912ccb71bSMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
37012ccb71bSMatthias Ringwald     }
37112ccb71bSMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07)) & ((1<<parser->global_report_size)-1);
37212ccb71bSMatthias 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);
37312ccb71bSMatthias Ringwald     if (is_variable){
37412ccb71bSMatthias Ringwald         *usage      = parser->usage_minimum & 0xffff;
37512ccb71bSMatthias Ringwald         if (is_signed && (unsigned_value & (1<<(parser->global_report_size-1)))){
37612ccb71bSMatthias Ringwald             *value = unsigned_value - (1<<parser->global_report_size);
37712ccb71bSMatthias Ringwald         } else {
37812ccb71bSMatthias Ringwald             *value = unsigned_value;
37912ccb71bSMatthias Ringwald         }
38012ccb71bSMatthias Ringwald     } else {
38112ccb71bSMatthias Ringwald         *usage  = unsigned_value;
38212ccb71bSMatthias Ringwald         *value  = 1;
38312ccb71bSMatthias Ringwald     }
38412ccb71bSMatthias Ringwald     parser->required_usages--;
38512ccb71bSMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
38612ccb71bSMatthias Ringwald 
38712ccb71bSMatthias Ringwald     // next usage
38812ccb71bSMatthias Ringwald     if (is_variable){
38912ccb71bSMatthias Ringwald         parser->usage_minimum++;
39012ccb71bSMatthias Ringwald         parser->available_usages--;
39112ccb71bSMatthias Ringwald     } else {
39212ccb71bSMatthias Ringwald         if (parser->required_usages == 0){
39312ccb71bSMatthias Ringwald             parser->available_usages = 0;
39412ccb71bSMatthias Ringwald         }
39512ccb71bSMatthias Ringwald     }
39612ccb71bSMatthias Ringwald     if (parser->available_usages) {
39712ccb71bSMatthias Ringwald         return;
39812ccb71bSMatthias Ringwald     }
39912ccb71bSMatthias Ringwald     if (parser->required_usages == 0){
40012ccb71bSMatthias Ringwald         hid_post_process_item(parser, &parser->descriptor_item);
40112ccb71bSMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
40212ccb71bSMatthias Ringwald         btstack_hid_parser_find_next_usage(parser);
40312ccb71bSMatthias Ringwald     } else {
40412ccb71bSMatthias Ringwald         hid_find_next_usage(parser);
40512ccb71bSMatthias Ringwald         if (parser->available_usages == 0) {
40612ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
40712ccb71bSMatthias Ringwald         }
40812ccb71bSMatthias Ringwald     }
40912ccb71bSMatthias Ringwald }
410*fada7179SMilanka Ringwald 
411*fada7179SMilanka Ringwald int btstack_hid_get_report_size_for_id(int report_id, btstack_hid_report_type_t report_type, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
412*fada7179SMilanka Ringwald     int total_report_size = 0;
413*fada7179SMilanka Ringwald     int report_size = 0;
414*fada7179SMilanka Ringwald     int report_count = 0;
415*fada7179SMilanka Ringwald     int current_report_id = 0;
416*fada7179SMilanka Ringwald 
417*fada7179SMilanka Ringwald     while (hid_descriptor_len){
418*fada7179SMilanka Ringwald         int valid_report_type = 0;
419*fada7179SMilanka Ringwald         hid_descriptor_item_t item;
420*fada7179SMilanka Ringwald         // printf("item: 0x%02x (%p)\n", *hid_descriptor, hid_descriptor);
421*fada7179SMilanka Ringwald         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
422*fada7179SMilanka Ringwald         switch (item.item_type){
423*fada7179SMilanka Ringwald             case Global:
424*fada7179SMilanka Ringwald                 switch ((GlobalItemTag)item.item_tag){
425*fada7179SMilanka Ringwald                     case ReportID:
426*fada7179SMilanka Ringwald                         current_report_id = item.item_value;
427*fada7179SMilanka Ringwald                         break;
428*fada7179SMilanka Ringwald                     case ReportCount:
429*fada7179SMilanka Ringwald                         if (current_report_id != report_id) break;
430*fada7179SMilanka Ringwald                         report_count = item.item_value;
431*fada7179SMilanka Ringwald                         break;
432*fada7179SMilanka Ringwald                     case ReportSize:
433*fada7179SMilanka Ringwald                         if (current_report_id != report_id) break;
434*fada7179SMilanka Ringwald                         report_size = item.item_value;
435*fada7179SMilanka Ringwald                         break;
436*fada7179SMilanka Ringwald                     default:
437*fada7179SMilanka Ringwald                         break;
438*fada7179SMilanka Ringwald                 }
439*fada7179SMilanka Ringwald                 break;
440*fada7179SMilanka Ringwald             case Main:
441*fada7179SMilanka Ringwald                 if (current_report_id != report_id) break;
442*fada7179SMilanka Ringwald                 // printf("tag %d, report_type %d\n", item.item_tag, report_type);
443*fada7179SMilanka Ringwald                 switch ((MainItemTag)item.item_tag){
444*fada7179SMilanka Ringwald                     case Input:
445*fada7179SMilanka Ringwald                         if (report_type != BTSTACK_HID_REPORT_TYPE_INPUT) break;
446*fada7179SMilanka Ringwald                         valid_report_type = 1;
447*fada7179SMilanka Ringwald                         break;
448*fada7179SMilanka Ringwald                     case Output:
449*fada7179SMilanka Ringwald                         if (report_type != BTSTACK_HID_REPORT_TYPE_OUTPUT) break;
450*fada7179SMilanka Ringwald                         valid_report_type = 1;
451*fada7179SMilanka Ringwald                         break;
452*fada7179SMilanka Ringwald                     case Feature:
453*fada7179SMilanka Ringwald                         if (report_type != BTSTACK_HID_REPORT_TYPE_FEATURE) break;
454*fada7179SMilanka Ringwald                         valid_report_type = 1;
455*fada7179SMilanka Ringwald                         break;
456*fada7179SMilanka Ringwald                     default:
457*fada7179SMilanka Ringwald                         break;
458*fada7179SMilanka Ringwald                 }
459*fada7179SMilanka Ringwald                 if (!valid_report_type) break;
460*fada7179SMilanka Ringwald                 total_report_size += report_count * report_size;
461*fada7179SMilanka Ringwald                 report_size = 0;
462*fada7179SMilanka Ringwald                 report_count = 0;
463*fada7179SMilanka Ringwald                 break;
464*fada7179SMilanka Ringwald             default:
465*fada7179SMilanka Ringwald                 break;
466*fada7179SMilanka Ringwald         }
467*fada7179SMilanka Ringwald         hid_descriptor_len -= item.item_size;
468*fada7179SMilanka Ringwald         hid_descriptor += item.item_size;
469*fada7179SMilanka Ringwald     }
470*fada7179SMilanka Ringwald     return (total_report_size + 7)/8;
471*fada7179SMilanka Ringwald }