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 15076fa2448SMatthias Ringwald bool 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 15576fa2448SMatthias Ringwald if (hid_descriptor_len < 1u) return false; 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)){ 16376fa2448SMatthias Ringwald if (hid_descriptor_len < 3u) return false; 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 17176fa2448SMatthias Ringwald if (hid_descriptor_len < item->item_size) return false; 17276fa2448SMatthias Ringwald if (item->data_size > 4u) return false; 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; 18776fa2448SMatthias Ringwald return true; 18812ccb71bSMatthias Ringwald } 18912ccb71bSMatthias Ringwald 19012ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 1917bbeb3adSMilanka Ringwald switch((GlobalItemTag)item->item_tag){ 19212ccb71bSMatthias Ringwald case UsagePage: 19312ccb71bSMatthias Ringwald parser->global_usage_page = item->item_value; 19412ccb71bSMatthias Ringwald break; 19512ccb71bSMatthias Ringwald case LogicalMinimum: 19612ccb71bSMatthias Ringwald parser->global_logical_minimum = item->item_value; 19712ccb71bSMatthias Ringwald break; 19812ccb71bSMatthias Ringwald case LogicalMaximum: 19912ccb71bSMatthias Ringwald parser->global_logical_maximum = item->item_value; 20012ccb71bSMatthias Ringwald break; 20112ccb71bSMatthias Ringwald case ReportSize: 20212ccb71bSMatthias Ringwald parser->global_report_size = item->item_value; 20312ccb71bSMatthias Ringwald break; 20412ccb71bSMatthias Ringwald case ReportID: 205c1ab6cc1SMatthias Ringwald if (parser->active_record && (parser->global_report_id != item->item_value)){ 20612ccb71bSMatthias Ringwald parser->active_record = 0; 20712ccb71bSMatthias Ringwald } 20812ccb71bSMatthias Ringwald parser->global_report_id = item->item_value; 20912ccb71bSMatthias Ringwald break; 21012ccb71bSMatthias Ringwald case ReportCount: 21112ccb71bSMatthias Ringwald parser->global_report_count = item->item_value; 21212ccb71bSMatthias Ringwald break; 2137bbeb3adSMilanka Ringwald 2147bbeb3adSMilanka Ringwald // TODO handle tags 2157bbeb3adSMilanka Ringwald case PhysicalMinimum: 2167bbeb3adSMilanka Ringwald case PhysicalMaximum: 2177bbeb3adSMilanka Ringwald case UnitExponent: 2187bbeb3adSMilanka Ringwald case Unit: 2197bbeb3adSMilanka Ringwald case Push: 2207bbeb3adSMilanka Ringwald case Pop: 2217bbeb3adSMilanka Ringwald break; 2227bbeb3adSMilanka Ringwald 22312ccb71bSMatthias Ringwald default: 2247bbeb3adSMilanka Ringwald btstack_assert(false); 22512ccb71bSMatthias Ringwald break; 22612ccb71bSMatthias Ringwald } 22712ccb71bSMatthias Ringwald } 22812ccb71bSMatthias Ringwald 22912ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){ 2301a05cde1SMatthias Ringwald bool have_usage_min = false; 2311a05cde1SMatthias Ringwald bool have_usage_max = false; 2321a05cde1SMatthias Ringwald parser->usage_range = false; 2334ea43905SMatthias Ringwald while ((parser->available_usages == 0u) && (parser->usage_pos < parser->descriptor_pos)){ 23412ccb71bSMatthias Ringwald hid_descriptor_item_t usage_item; 23512ccb71bSMatthias Ringwald // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len 2360e7cc963SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos); 2370e7cc963SMatthias Ringwald if (ok == false){ 2380e7cc963SMatthias Ringwald break; 2390e7cc963SMatthias Ringwald } 2400e588213SMatthias Ringwald if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){ 24112ccb71bSMatthias Ringwald parser->usage_page = usage_item.item_value; 24212ccb71bSMatthias Ringwald } 24312ccb71bSMatthias Ringwald if (usage_item.item_type == Local){ 2444ea43905SMatthias Ringwald uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value); 24512ccb71bSMatthias Ringwald switch (usage_item.item_tag){ 24612ccb71bSMatthias Ringwald case Usage: 24712ccb71bSMatthias Ringwald parser->available_usages = 1; 24812ccb71bSMatthias Ringwald parser->usage_minimum = usage_value; 24912ccb71bSMatthias Ringwald break; 25012ccb71bSMatthias Ringwald case UsageMinimum: 25112ccb71bSMatthias Ringwald parser->usage_minimum = usage_value; 2521a05cde1SMatthias Ringwald have_usage_min = true; 25312ccb71bSMatthias Ringwald break; 25412ccb71bSMatthias Ringwald case UsageMaximum: 25512ccb71bSMatthias Ringwald parser->usage_maximum = usage_value; 2561a05cde1SMatthias Ringwald have_usage_max = true; 25712ccb71bSMatthias Ringwald break; 25812ccb71bSMatthias Ringwald default: 25912ccb71bSMatthias Ringwald break; 26012ccb71bSMatthias Ringwald } 2611a05cde1SMatthias Ringwald if (have_usage_min && have_usage_max){ 2624ea43905SMatthias Ringwald parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u; 2631a05cde1SMatthias Ringwald parser->usage_range = true; 264dfb01e77SMatthias Ringwald if (parser->available_usages < parser->required_usages){ 265d30de501SMatthias Ringwald log_debug("Usage Min - Usage Max [%04x..%04x] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages); 266dfb01e77SMatthias Ringwald } 26712ccb71bSMatthias Ringwald } 26812ccb71bSMatthias Ringwald } 26912ccb71bSMatthias Ringwald parser->usage_pos += usage_item.item_size; 27012ccb71bSMatthias Ringwald } 27112ccb71bSMatthias Ringwald } 27212ccb71bSMatthias Ringwald 27312ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 27412ccb71bSMatthias Ringwald hid_pretty_print_item(parser, item); 27512ccb71bSMatthias Ringwald int valid_field = 0; 2767bbeb3adSMilanka Ringwald switch ((TagType)item->item_type){ 27712ccb71bSMatthias Ringwald case Main: 2787bbeb3adSMilanka Ringwald switch ((MainItemTag)item->item_tag){ 27912ccb71bSMatthias Ringwald case Input: 280662cddc2SMilanka Ringwald valid_field = parser->report_type == HID_REPORT_TYPE_INPUT; 28112ccb71bSMatthias Ringwald break; 28212ccb71bSMatthias Ringwald case Output: 283662cddc2SMilanka Ringwald valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT; 28412ccb71bSMatthias Ringwald break; 28512ccb71bSMatthias Ringwald case Feature: 286662cddc2SMilanka Ringwald valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE; 28712ccb71bSMatthias Ringwald break; 28812ccb71bSMatthias Ringwald default: 28912ccb71bSMatthias Ringwald break; 29012ccb71bSMatthias Ringwald } 29112ccb71bSMatthias Ringwald break; 29212ccb71bSMatthias Ringwald case Global: 29312ccb71bSMatthias Ringwald btstack_hid_handle_global_item(parser, item); 29412ccb71bSMatthias Ringwald break; 29512ccb71bSMatthias Ringwald case Local: 2967bbeb3adSMilanka Ringwald case Reserved: 2977bbeb3adSMilanka Ringwald break; 2987bbeb3adSMilanka Ringwald default: 2997bbeb3adSMilanka Ringwald btstack_assert(false); 30012ccb71bSMatthias Ringwald break; 30112ccb71bSMatthias Ringwald } 30212ccb71bSMatthias Ringwald if (!valid_field) return; 30312ccb71bSMatthias Ringwald 30412ccb71bSMatthias Ringwald // verify record id 30512ccb71bSMatthias Ringwald if (parser->global_report_id && !parser->active_record){ 30612ccb71bSMatthias Ringwald if (parser->report[0] != parser->global_report_id){ 30712ccb71bSMatthias Ringwald return; 30812ccb71bSMatthias Ringwald } 3094ea43905SMatthias Ringwald parser->report_pos_in_bit += 8u; 31012ccb71bSMatthias Ringwald } 31112ccb71bSMatthias Ringwald parser->active_record = 1; 31212ccb71bSMatthias Ringwald // handle constant fields used for padding 31312ccb71bSMatthias Ringwald if (item->item_value & 1){ 31412ccb71bSMatthias Ringwald int item_bits = parser->global_report_size * parser->global_report_count; 31512ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 31612ccb71bSMatthias Ringwald log_info("- Skip %u constant bits", item_bits); 31712ccb71bSMatthias Ringwald #endif 31812ccb71bSMatthias Ringwald parser->report_pos_in_bit += item_bits; 31912ccb71bSMatthias Ringwald return; 32012ccb71bSMatthias Ringwald } 32112ccb71bSMatthias Ringwald // Empty Item 3224ea43905SMatthias Ringwald if (parser->global_report_count == 0u) return; 32312ccb71bSMatthias Ringwald // let's start 32412ccb71bSMatthias Ringwald parser->required_usages = parser->global_report_count; 32512ccb71bSMatthias Ringwald } 32612ccb71bSMatthias Ringwald 32712ccb71bSMatthias Ringwald static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 3287bbeb3adSMilanka Ringwald if ((TagType)item->item_type == Main){ 32912ccb71bSMatthias Ringwald // reset usage 33012ccb71bSMatthias Ringwald parser->usage_pos = parser->descriptor_pos; 33112ccb71bSMatthias Ringwald parser->usage_page = parser->global_usage_page; 33212ccb71bSMatthias Ringwald } 33312ccb71bSMatthias Ringwald parser->descriptor_pos += item->item_size; 33412ccb71bSMatthias Ringwald } 33512ccb71bSMatthias Ringwald 33612ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){ 33712ccb71bSMatthias Ringwald while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){ 33812ccb71bSMatthias Ringwald if (parser->descriptor_pos >= parser->descriptor_len){ 33912ccb71bSMatthias Ringwald // end of descriptor 34012ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 34112ccb71bSMatthias Ringwald break; 34212ccb71bSMatthias Ringwald } 343*0c4004b0SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos); 344*0c4004b0SMatthias Ringwald if (ok == false){ 345*0c4004b0SMatthias Ringwald // abort parsing 346*0c4004b0SMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 347*0c4004b0SMatthias Ringwald break; 348*0c4004b0SMatthias Ringwald } 34912ccb71bSMatthias Ringwald hid_process_item(parser, &parser->descriptor_item); 35012ccb71bSMatthias Ringwald if (parser->required_usages){ 35112ccb71bSMatthias Ringwald hid_find_next_usage(parser); 35212ccb71bSMatthias Ringwald if (parser->available_usages) { 35312ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE; 35412ccb71bSMatthias Ringwald } else { 355dfb01e77SMatthias Ringwald log_debug("no usages found"); 35612ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 35712ccb71bSMatthias Ringwald } 35812ccb71bSMatthias Ringwald } else { 35912ccb71bSMatthias Ringwald hid_post_process_item(parser, &parser->descriptor_item); 36012ccb71bSMatthias Ringwald } 36112ccb71bSMatthias Ringwald } 36212ccb71bSMatthias Ringwald } 36312ccb71bSMatthias Ringwald 36412ccb71bSMatthias Ringwald // PUBLIC API 36512ccb71bSMatthias Ringwald 366662cddc2SMilanka 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){ 36712ccb71bSMatthias Ringwald 36812ccb71bSMatthias Ringwald memset(parser, 0, sizeof(btstack_hid_parser_t)); 36912ccb71bSMatthias Ringwald 37012ccb71bSMatthias Ringwald parser->descriptor = hid_descriptor; 37112ccb71bSMatthias Ringwald parser->descriptor_len = hid_descriptor_len; 37212ccb71bSMatthias Ringwald parser->report_type = hid_report_type; 37312ccb71bSMatthias Ringwald parser->report = hid_report; 37412ccb71bSMatthias Ringwald parser->report_len = hid_report_len; 37512ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM; 37612ccb71bSMatthias Ringwald 37712ccb71bSMatthias Ringwald btstack_hid_parser_find_next_usage(parser); 37812ccb71bSMatthias Ringwald } 37912ccb71bSMatthias Ringwald 38012ccb71bSMatthias Ringwald int btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 38112ccb71bSMatthias Ringwald return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE; 38212ccb71bSMatthias Ringwald } 38312ccb71bSMatthias Ringwald 38412ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 38512ccb71bSMatthias Ringwald 38612ccb71bSMatthias Ringwald *usage_page = parser->usage_minimum >> 16; 38712ccb71bSMatthias Ringwald 38812ccb71bSMatthias Ringwald // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 3891979f09cSMatthias Ringwald bool is_variable = (parser->descriptor_item.item_value & 2) != 0; 3901979f09cSMatthias Ringwald bool is_signed = parser->global_logical_minimum < 0; 39112ccb71bSMatthias Ringwald int pos_start = btstack_min( parser->report_pos_in_bit >> 3, parser->report_len); 3924ea43905SMatthias Ringwald int pos_end = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len); 39312ccb71bSMatthias Ringwald int bytes_to_read = pos_end - pos_start + 1; 39412ccb71bSMatthias Ringwald int i; 39512ccb71bSMatthias Ringwald uint32_t multi_byte_value = 0; 39612ccb71bSMatthias Ringwald for (i=0;i < bytes_to_read;i++){ 39712ccb71bSMatthias Ringwald multi_byte_value |= parser->report[pos_start+i] << (i*8); 39812ccb71bSMatthias Ringwald } 3994ea43905SMatthias Ringwald uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u); 40012ccb71bSMatthias 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); 40112ccb71bSMatthias Ringwald if (is_variable){ 4024ea43905SMatthias Ringwald *usage = parser->usage_minimum & 0xffffu; 4034ea43905SMatthias Ringwald if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){ 4044ea43905SMatthias Ringwald *value = unsigned_value - (1u<<parser->global_report_size); 40512ccb71bSMatthias Ringwald } else { 40612ccb71bSMatthias Ringwald *value = unsigned_value; 40712ccb71bSMatthias Ringwald } 40812ccb71bSMatthias Ringwald } else { 40912ccb71bSMatthias Ringwald *usage = unsigned_value; 41012ccb71bSMatthias Ringwald *value = 1; 41112ccb71bSMatthias Ringwald } 41212ccb71bSMatthias Ringwald parser->required_usages--; 41312ccb71bSMatthias Ringwald parser->report_pos_in_bit += parser->global_report_size; 41412ccb71bSMatthias Ringwald 41512ccb71bSMatthias Ringwald // next usage 41612ccb71bSMatthias Ringwald if (is_variable){ 41712ccb71bSMatthias Ringwald parser->usage_minimum++; 41812ccb71bSMatthias Ringwald parser->available_usages--; 4191a05cde1SMatthias Ringwald if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){ 420dfb01e77SMatthias Ringwald // usage min - max range smaller than report count, ignore remaining bit in report 421dfb01e77SMatthias Ringwald log_debug("Ignoring %u items without Usage", parser->required_usages); 422dfb01e77SMatthias Ringwald parser->report_pos_in_bit += parser->global_report_size * parser->required_usages; 423dfb01e77SMatthias Ringwald parser->required_usages = 0; 424dfb01e77SMatthias Ringwald } 42512ccb71bSMatthias Ringwald } else { 4264ea43905SMatthias Ringwald if (parser->required_usages == 0u){ 42712ccb71bSMatthias Ringwald parser->available_usages = 0; 42812ccb71bSMatthias Ringwald } 42912ccb71bSMatthias Ringwald } 43012ccb71bSMatthias Ringwald if (parser->available_usages) { 43112ccb71bSMatthias Ringwald return; 43212ccb71bSMatthias Ringwald } 4334ea43905SMatthias Ringwald if (parser->required_usages == 0u){ 43412ccb71bSMatthias Ringwald hid_post_process_item(parser, &parser->descriptor_item); 43512ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM; 43612ccb71bSMatthias Ringwald btstack_hid_parser_find_next_usage(parser); 43712ccb71bSMatthias Ringwald } else { 43812ccb71bSMatthias Ringwald hid_find_next_usage(parser); 4394ea43905SMatthias Ringwald if (parser->available_usages == 0u) { 44012ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 44112ccb71bSMatthias Ringwald } 44212ccb71bSMatthias Ringwald } 44312ccb71bSMatthias Ringwald } 444fada7179SMilanka Ringwald 445662cddc2SMilanka 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){ 446fada7179SMilanka Ringwald int total_report_size = 0; 447fada7179SMilanka Ringwald int report_size = 0; 448fada7179SMilanka Ringwald int report_count = 0; 449fada7179SMilanka Ringwald int current_report_id = 0; 450fada7179SMilanka Ringwald 451fada7179SMilanka Ringwald while (hid_descriptor_len){ 452fada7179SMilanka Ringwald int valid_report_type = 0; 453fada7179SMilanka Ringwald hid_descriptor_item_t item; 4543a1d5296SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 4553a1d5296SMatthias Ringwald if (ok == false) { 4563a1d5296SMatthias Ringwald return 0; 4573a1d5296SMatthias Ringwald } 458fada7179SMilanka Ringwald switch (item.item_type){ 459fada7179SMilanka Ringwald case Global: 460fada7179SMilanka Ringwald switch ((GlobalItemTag)item.item_tag){ 461fada7179SMilanka Ringwald case ReportID: 462fada7179SMilanka Ringwald current_report_id = item.item_value; 463fada7179SMilanka Ringwald break; 464fada7179SMilanka Ringwald case ReportCount: 465fada7179SMilanka Ringwald report_count = item.item_value; 466fada7179SMilanka Ringwald break; 467fada7179SMilanka Ringwald case ReportSize: 468fada7179SMilanka Ringwald report_size = item.item_value; 469fada7179SMilanka Ringwald break; 470fada7179SMilanka Ringwald default: 471fada7179SMilanka Ringwald break; 472fada7179SMilanka Ringwald } 473fada7179SMilanka Ringwald break; 474fada7179SMilanka Ringwald case Main: 475fada7179SMilanka Ringwald if (current_report_id != report_id) break; 476fada7179SMilanka Ringwald switch ((MainItemTag)item.item_tag){ 477fada7179SMilanka Ringwald case Input: 478662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_INPUT) break; 479fada7179SMilanka Ringwald valid_report_type = 1; 480fada7179SMilanka Ringwald break; 481fada7179SMilanka Ringwald case Output: 482662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_OUTPUT) break; 483fada7179SMilanka Ringwald valid_report_type = 1; 484fada7179SMilanka Ringwald break; 485fada7179SMilanka Ringwald case Feature: 486662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_FEATURE) break; 487fada7179SMilanka Ringwald valid_report_type = 1; 488fada7179SMilanka Ringwald break; 489fada7179SMilanka Ringwald default: 490fada7179SMilanka Ringwald break; 491fada7179SMilanka Ringwald } 492fada7179SMilanka Ringwald if (!valid_report_type) break; 493fada7179SMilanka Ringwald total_report_size += report_count * report_size; 494fada7179SMilanka Ringwald break; 495fada7179SMilanka Ringwald default: 496fada7179SMilanka Ringwald break; 497fada7179SMilanka Ringwald } 49815cf8612Sx0rloser if (total_report_size > 0 && current_report_id != report_id) break; 499fada7179SMilanka Ringwald hid_descriptor_len -= item.item_size; 500fada7179SMilanka Ringwald hid_descriptor += item.item_size; 501fada7179SMilanka Ringwald } 502fada7179SMilanka Ringwald return (total_report_size + 7)/8; 503fada7179SMilanka Ringwald } 504dbcaefc7SMilanka Ringwald 505dbcaefc7SMilanka Ringwald hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 506dbcaefc7SMilanka Ringwald int current_report_id = 0; 507dbcaefc7SMilanka Ringwald while (hid_descriptor_len){ 508dbcaefc7SMilanka Ringwald hid_descriptor_item_t item; 5090e7cc963SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 5100e7cc963SMatthias Ringwald if (ok == false){ 5110e7cc963SMatthias Ringwald return HID_REPORT_ID_INVALID; 5120e7cc963SMatthias Ringwald } 513dbcaefc7SMilanka Ringwald switch (item.item_type){ 514dbcaefc7SMilanka Ringwald case Global: 515dbcaefc7SMilanka Ringwald switch ((GlobalItemTag)item.item_tag){ 516dbcaefc7SMilanka Ringwald case ReportID: 517dbcaefc7SMilanka Ringwald current_report_id = item.item_value; 518dbcaefc7SMilanka Ringwald if (current_report_id != report_id) break; 519dbcaefc7SMilanka Ringwald return HID_REPORT_ID_VALID; 520dbcaefc7SMilanka Ringwald default: 521dbcaefc7SMilanka Ringwald break; 522dbcaefc7SMilanka Ringwald } 523dbcaefc7SMilanka Ringwald break; 524dbcaefc7SMilanka Ringwald default: 525dbcaefc7SMilanka Ringwald break; 526dbcaefc7SMilanka Ringwald } 527dbcaefc7SMilanka Ringwald hid_descriptor_len -= item.item_size; 528dbcaefc7SMilanka Ringwald hid_descriptor += item.item_size; 529dbcaefc7SMilanka Ringwald } 530dbcaefc7SMilanka Ringwald if (current_report_id != 0) return HID_REPORT_ID_INVALID; 531dbcaefc7SMilanka Ringwald return HID_REPORT_ID_UNDECLARED; 532dbcaefc7SMilanka Ringwald } 533dbcaefc7SMilanka Ringwald 534dbcaefc7SMilanka Ringwald int btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 535dbcaefc7SMilanka Ringwald while (hid_descriptor_len){ 536dbcaefc7SMilanka Ringwald hid_descriptor_item_t item; 537dbcaefc7SMilanka Ringwald btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 538dbcaefc7SMilanka Ringwald switch (item.item_type){ 539dbcaefc7SMilanka Ringwald case Global: 540dbcaefc7SMilanka Ringwald switch ((GlobalItemTag)item.item_tag){ 541dbcaefc7SMilanka Ringwald case ReportID: 542dbcaefc7SMilanka Ringwald return 1; 543dbcaefc7SMilanka Ringwald default: 544dbcaefc7SMilanka Ringwald break; 545dbcaefc7SMilanka Ringwald } 546dbcaefc7SMilanka Ringwald break; 547dbcaefc7SMilanka Ringwald default: 548dbcaefc7SMilanka Ringwald break; 549dbcaefc7SMilanka Ringwald } 550dbcaefc7SMilanka Ringwald hid_descriptor_len -= item.item_size; 551dbcaefc7SMilanka Ringwald hid_descriptor += item.item_size; 552dbcaefc7SMilanka Ringwald } 553dbcaefc7SMilanka Ringwald return 0; 554dbcaefc7SMilanka Ringwald } 555