112ccb71bSMatthias Ringwald /* 212ccb71bSMatthias Ringwald * Copyright (C) 2017 BlueKitchen GmbH 312ccb71bSMatthias Ringwald * 412ccb71bSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 512ccb71bSMatthias Ringwald * modification, are permitted provided that the following conditions 612ccb71bSMatthias Ringwald * are met: 712ccb71bSMatthias Ringwald * 812ccb71bSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 912ccb71bSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 1012ccb71bSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 1112ccb71bSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 1212ccb71bSMatthias Ringwald * documentation and/or other materials provided with the distribution. 1312ccb71bSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 1412ccb71bSMatthias Ringwald * contributors may be used to endorse or promote products derived 1512ccb71bSMatthias Ringwald * from this software without specific prior written permission. 1612ccb71bSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 1712ccb71bSMatthias Ringwald * personal benefit and not for any commercial purpose or for 1812ccb71bSMatthias Ringwald * monetary gain. 1912ccb71bSMatthias Ringwald * 2012ccb71bSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 2112ccb71bSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2212ccb71bSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2512ccb71bSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2612ccb71bSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2712ccb71bSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2812ccb71bSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2912ccb71bSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 3012ccb71bSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3112ccb71bSMatthias Ringwald * SUCH DAMAGE. 3212ccb71bSMatthias Ringwald * 3312ccb71bSMatthias Ringwald * Please inquire about commercial licensing options at 3412ccb71bSMatthias Ringwald * [email protected] 3512ccb71bSMatthias Ringwald * 3612ccb71bSMatthias Ringwald */ 3712ccb71bSMatthias Ringwald 38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "btstack_hid_parser.c" 3912ccb71bSMatthias Ringwald 40cb406331SDirk Helbig #include <inttypes.h> 4112ccb71bSMatthias Ringwald #include <string.h> 4212ccb71bSMatthias Ringwald 4312ccb71bSMatthias Ringwald #include "btstack_hid_parser.h" 4412ccb71bSMatthias Ringwald #include "btstack_util.h" 4512ccb71bSMatthias Ringwald #include "btstack_debug.h" 4612ccb71bSMatthias Ringwald 4712ccb71bSMatthias Ringwald // Not implemented: 4812ccb71bSMatthias Ringwald // - Support for Push/Pop 4912ccb71bSMatthias Ringwald // - Optional Pretty Print of HID Descripor 5012ccb71bSMatthias Ringwald // - Support to query descriptort for contained usages, e.g. to detect keyboard or mouse 5112ccb71bSMatthias Ringwald 5212ccb71bSMatthias Ringwald // #define HID_PARSER_PRETTY_PRINT 5312ccb71bSMatthias Ringwald 5412ccb71bSMatthias Ringwald /* 5512ccb71bSMatthias Ringwald * btstack_hid_parser.c 5612ccb71bSMatthias Ringwald */ 5712ccb71bSMatthias Ringwald 5812ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 5912ccb71bSMatthias Ringwald 6012ccb71bSMatthias Ringwald static const char * type_names[] = { 6112ccb71bSMatthias Ringwald "Main", 6212ccb71bSMatthias Ringwald "Global", 6312ccb71bSMatthias Ringwald "Local", 6412ccb71bSMatthias Ringwald "Reserved" 6512ccb71bSMatthias Ringwald }; 6612ccb71bSMatthias Ringwald static const char * main_tags[] = { 6712ccb71bSMatthias Ringwald "", 6812ccb71bSMatthias Ringwald "", 6912ccb71bSMatthias Ringwald "", 7012ccb71bSMatthias Ringwald "", 7112ccb71bSMatthias Ringwald "", 7212ccb71bSMatthias Ringwald "", 7312ccb71bSMatthias Ringwald "", 7412ccb71bSMatthias Ringwald "", 7512ccb71bSMatthias Ringwald "Input ", 7612ccb71bSMatthias Ringwald "Output", 7712ccb71bSMatthias Ringwald "Collection", 7812ccb71bSMatthias Ringwald "Feature", 7912ccb71bSMatthias Ringwald "End Collection", 8012ccb71bSMatthias Ringwald "Reserved", 8112ccb71bSMatthias Ringwald "Reserved", 8212ccb71bSMatthias Ringwald "Reserved" 8312ccb71bSMatthias Ringwald }; 8412ccb71bSMatthias Ringwald static const char * global_tags[] = { 8512ccb71bSMatthias Ringwald "Usage Page", 8612ccb71bSMatthias Ringwald "Logical Minimum", 8712ccb71bSMatthias Ringwald "Logical Maximum", 8812ccb71bSMatthias Ringwald "Physical Minimum", 8912ccb71bSMatthias Ringwald "Physical Maximum", 9012ccb71bSMatthias Ringwald "Unit Exponent", 9112ccb71bSMatthias Ringwald "Unit", 9212ccb71bSMatthias Ringwald "Report Size", 9312ccb71bSMatthias Ringwald "Report ID", 9412ccb71bSMatthias Ringwald "Report Count", 9512ccb71bSMatthias Ringwald "Push", 9612ccb71bSMatthias Ringwald "Pop", 9712ccb71bSMatthias Ringwald "Reserved", 9812ccb71bSMatthias Ringwald "Reserved", 9912ccb71bSMatthias Ringwald "Reserved", 10012ccb71bSMatthias Ringwald "Reserved" 10112ccb71bSMatthias Ringwald }; 10212ccb71bSMatthias Ringwald static const char * local_tags[] = { 10312ccb71bSMatthias Ringwald "Usage", 10412ccb71bSMatthias Ringwald "Usage Minimum", 10512ccb71bSMatthias Ringwald "Usage Maximum", 10612ccb71bSMatthias Ringwald "Designator Index", 10712ccb71bSMatthias Ringwald "Designator Minimum", 10812ccb71bSMatthias Ringwald "Designator Maximum", 10912ccb71bSMatthias Ringwald "String Index", 11012ccb71bSMatthias Ringwald "String Minimum", 11112ccb71bSMatthias Ringwald "String Maximum", 11212ccb71bSMatthias Ringwald "Delimiter", 11312ccb71bSMatthias Ringwald "Reserved", 11412ccb71bSMatthias Ringwald "Reserved", 11512ccb71bSMatthias Ringwald "Reserved", 11612ccb71bSMatthias Ringwald "Reserved", 11712ccb71bSMatthias Ringwald "Reserved", 11812ccb71bSMatthias Ringwald "Reserved" 11912ccb71bSMatthias Ringwald }; 12012ccb71bSMatthias Ringwald #endif 12112ccb71bSMatthias Ringwald 12212ccb71bSMatthias Ringwald static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 12312ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 12412ccb71bSMatthias Ringwald const char ** item_tag_table; 1257bbeb3adSMilanka Ringwald switch ((TagType)item->item_type){ 12612ccb71bSMatthias Ringwald case Main: 12712ccb71bSMatthias Ringwald item_tag_table = main_tags; 12812ccb71bSMatthias Ringwald break; 12912ccb71bSMatthias Ringwald case Global: 13012ccb71bSMatthias Ringwald item_tag_table = global_tags; 13112ccb71bSMatthias Ringwald break; 13212ccb71bSMatthias Ringwald case Local: 13312ccb71bSMatthias Ringwald item_tag_table = local_tags; 13412ccb71bSMatthias Ringwald break; 13512ccb71bSMatthias Ringwald default: 13612ccb71bSMatthias Ringwald item_tag_table = NULL; 13712ccb71bSMatthias Ringwald break; 13812ccb71bSMatthias Ringwald } 13912ccb71bSMatthias Ringwald const char * item_tag_name = "Invalid"; 14012ccb71bSMatthias Ringwald if (item_tag_table){ 14112ccb71bSMatthias Ringwald item_tag_name = item_tag_table[item->item_tag]; 14212ccb71bSMatthias Ringwald } 143ea1e21c2SMatthias Ringwald log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value); 14412ccb71bSMatthias Ringwald #else 14512ccb71bSMatthias Ringwald UNUSED(parser); 14612ccb71bSMatthias Ringwald UNUSED(item); 14712ccb71bSMatthias Ringwald #endif 14812ccb71bSMatthias Ringwald } 14912ccb71bSMatthias Ringwald 15012ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value 15176fa2448SMatthias Ringwald bool btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 1528334d3d8SMatthias Ringwald 1538334d3d8SMatthias Ringwald const int hid_item_sizes[] = { 0, 1, 2, 4 }; 1548334d3d8SMatthias Ringwald 15512ccb71bSMatthias Ringwald // parse item header 15676fa2448SMatthias Ringwald if (hid_descriptor_len < 1u) return false; 15712ccb71bSMatthias Ringwald uint16_t pos = 0; 15812ccb71bSMatthias Ringwald uint8_t item_header = hid_descriptor[pos++]; 1594ea43905SMatthias Ringwald item->data_size = hid_item_sizes[item_header & 0x03u]; 1604ea43905SMatthias Ringwald item->item_type = (item_header & 0x0cu) >> 2u; 1614ea43905SMatthias Ringwald item->item_tag = (item_header & 0xf0u) >> 4u; 16212ccb71bSMatthias Ringwald // long item 1634ea43905SMatthias Ringwald if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){ 16476fa2448SMatthias Ringwald if (hid_descriptor_len < 3u) return false; 16512ccb71bSMatthias Ringwald item->data_size = hid_descriptor[pos++]; 16612ccb71bSMatthias Ringwald item->item_tag = hid_descriptor[pos++]; 16712ccb71bSMatthias Ringwald } 16812ccb71bSMatthias Ringwald item->item_size = pos + item->data_size; 16912ccb71bSMatthias Ringwald item->item_value = 0; 17012ccb71bSMatthias Ringwald 17112ccb71bSMatthias Ringwald // read item value 17276fa2448SMatthias Ringwald if (hid_descriptor_len < item->item_size) return false; 17376fa2448SMatthias Ringwald if (item->data_size > 4u) return false; 17412ccb71bSMatthias Ringwald int i; 1754ea43905SMatthias Ringwald int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u); 17612ccb71bSMatthias Ringwald int32_t value = 0; 17712ccb71bSMatthias Ringwald uint8_t latest_byte = 0; 17812ccb71bSMatthias Ringwald for (i=0;i<item->data_size;i++){ 17912ccb71bSMatthias Ringwald latest_byte = hid_descriptor[pos++]; 18012ccb71bSMatthias Ringwald value = (latest_byte << (8*i)) | value; 18112ccb71bSMatthias Ringwald } 1824ea43905SMatthias Ringwald if (sgnd && (item->data_size > 0u)){ 1834ea43905SMatthias Ringwald if (latest_byte & 0x80u) { 1844ea43905SMatthias Ringwald value -= 1u << (item->data_size*8u); 18512ccb71bSMatthias Ringwald } 18612ccb71bSMatthias Ringwald } 18712ccb71bSMatthias Ringwald item->item_value = value; 18876fa2448SMatthias Ringwald return true; 18912ccb71bSMatthias Ringwald } 19012ccb71bSMatthias Ringwald 19112ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 1927bbeb3adSMilanka Ringwald switch((GlobalItemTag)item->item_tag){ 19312ccb71bSMatthias Ringwald case UsagePage: 19412ccb71bSMatthias Ringwald parser->global_usage_page = item->item_value; 19512ccb71bSMatthias Ringwald break; 19612ccb71bSMatthias Ringwald case LogicalMinimum: 19712ccb71bSMatthias Ringwald parser->global_logical_minimum = item->item_value; 19812ccb71bSMatthias Ringwald break; 19912ccb71bSMatthias Ringwald case LogicalMaximum: 20012ccb71bSMatthias Ringwald parser->global_logical_maximum = item->item_value; 20112ccb71bSMatthias Ringwald break; 20212ccb71bSMatthias Ringwald case ReportSize: 20312ccb71bSMatthias Ringwald parser->global_report_size = item->item_value; 20412ccb71bSMatthias Ringwald break; 20512ccb71bSMatthias Ringwald case ReportID: 20612ccb71bSMatthias Ringwald parser->global_report_id = item->item_value; 20712ccb71bSMatthias Ringwald break; 20812ccb71bSMatthias Ringwald case ReportCount: 20912ccb71bSMatthias Ringwald parser->global_report_count = item->item_value; 21012ccb71bSMatthias Ringwald break; 2117bbeb3adSMilanka Ringwald 2127bbeb3adSMilanka Ringwald // TODO handle tags 2137bbeb3adSMilanka Ringwald case PhysicalMinimum: 2147bbeb3adSMilanka Ringwald case PhysicalMaximum: 2157bbeb3adSMilanka Ringwald case UnitExponent: 2167bbeb3adSMilanka Ringwald case Unit: 2177bbeb3adSMilanka Ringwald case Push: 2187bbeb3adSMilanka Ringwald case Pop: 2197bbeb3adSMilanka Ringwald break; 2207bbeb3adSMilanka Ringwald 22112ccb71bSMatthias Ringwald default: 2227bbeb3adSMilanka Ringwald btstack_assert(false); 22312ccb71bSMatthias Ringwald break; 22412ccb71bSMatthias Ringwald } 22512ccb71bSMatthias Ringwald } 22612ccb71bSMatthias Ringwald 22712ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){ 2281a05cde1SMatthias Ringwald bool have_usage_min = false; 2291a05cde1SMatthias Ringwald bool have_usage_max = false; 2301a05cde1SMatthias Ringwald parser->usage_range = false; 2314ea43905SMatthias Ringwald while ((parser->available_usages == 0u) && (parser->usage_pos < parser->descriptor_pos)){ 23212ccb71bSMatthias Ringwald hid_descriptor_item_t usage_item; 23312ccb71bSMatthias Ringwald // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len 2340e7cc963SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos); 2350e7cc963SMatthias Ringwald if (ok == false){ 2360e7cc963SMatthias Ringwald break; 2370e7cc963SMatthias Ringwald } 2380e588213SMatthias Ringwald if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){ 23912ccb71bSMatthias Ringwald parser->usage_page = usage_item.item_value; 24012ccb71bSMatthias Ringwald } 24112ccb71bSMatthias Ringwald if (usage_item.item_type == Local){ 2424ea43905SMatthias Ringwald uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value); 24312ccb71bSMatthias Ringwald switch (usage_item.item_tag){ 24412ccb71bSMatthias Ringwald case Usage: 24512ccb71bSMatthias Ringwald parser->available_usages = 1; 24612ccb71bSMatthias Ringwald parser->usage_minimum = usage_value; 24712ccb71bSMatthias Ringwald break; 24812ccb71bSMatthias Ringwald case UsageMinimum: 24912ccb71bSMatthias Ringwald parser->usage_minimum = usage_value; 2501a05cde1SMatthias Ringwald have_usage_min = true; 25112ccb71bSMatthias Ringwald break; 25212ccb71bSMatthias Ringwald case UsageMaximum: 25312ccb71bSMatthias Ringwald parser->usage_maximum = usage_value; 2541a05cde1SMatthias Ringwald have_usage_max = true; 25512ccb71bSMatthias Ringwald break; 25612ccb71bSMatthias Ringwald default: 25712ccb71bSMatthias Ringwald break; 25812ccb71bSMatthias Ringwald } 2591a05cde1SMatthias Ringwald if (have_usage_min && have_usage_max){ 2604ea43905SMatthias Ringwald parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u; 2611a05cde1SMatthias Ringwald parser->usage_range = true; 262dfb01e77SMatthias Ringwald if (parser->available_usages < parser->required_usages){ 263cb406331SDirk Helbig log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages); 264dfb01e77SMatthias Ringwald } 26512ccb71bSMatthias Ringwald } 26612ccb71bSMatthias Ringwald } 26712ccb71bSMatthias Ringwald parser->usage_pos += usage_item.item_size; 26812ccb71bSMatthias Ringwald } 26912ccb71bSMatthias Ringwald } 27012ccb71bSMatthias Ringwald 27112ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 27212ccb71bSMatthias Ringwald hid_pretty_print_item(parser, item); 27312ccb71bSMatthias Ringwald int valid_field = 0; 274eb78fadaSMatthias Ringwald uint16_t report_id_before; 2757bbeb3adSMilanka Ringwald switch ((TagType)item->item_type){ 27612ccb71bSMatthias Ringwald case Main: 2777bbeb3adSMilanka Ringwald switch ((MainItemTag)item->item_tag){ 27812ccb71bSMatthias Ringwald case Input: 279662cddc2SMilanka Ringwald valid_field = parser->report_type == HID_REPORT_TYPE_INPUT; 28012ccb71bSMatthias Ringwald break; 28112ccb71bSMatthias Ringwald case Output: 282662cddc2SMilanka Ringwald valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT; 28312ccb71bSMatthias Ringwald break; 28412ccb71bSMatthias Ringwald case Feature: 285662cddc2SMilanka Ringwald valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE; 28612ccb71bSMatthias Ringwald break; 28712ccb71bSMatthias Ringwald default: 28812ccb71bSMatthias Ringwald break; 28912ccb71bSMatthias Ringwald } 29012ccb71bSMatthias Ringwald break; 29112ccb71bSMatthias Ringwald case Global: 292eb78fadaSMatthias Ringwald report_id_before = parser->global_report_id; 29312ccb71bSMatthias Ringwald btstack_hid_handle_global_item(parser, item); 294eb78fadaSMatthias Ringwald // track record id for report handling 295eb78fadaSMatthias Ringwald if ((GlobalItemTag)item->item_tag == ReportID){ 296eb78fadaSMatthias Ringwald if (parser->active_record && (report_id_before != item->item_value)){ 297eb78fadaSMatthias Ringwald parser->active_record = 0; 298eb78fadaSMatthias Ringwald } 299eb78fadaSMatthias Ringwald } 30012ccb71bSMatthias Ringwald break; 30112ccb71bSMatthias Ringwald case Local: 3027bbeb3adSMilanka Ringwald case Reserved: 3037bbeb3adSMilanka Ringwald break; 3047bbeb3adSMilanka Ringwald default: 3057bbeb3adSMilanka Ringwald btstack_assert(false); 30612ccb71bSMatthias Ringwald break; 30712ccb71bSMatthias Ringwald } 30812ccb71bSMatthias Ringwald if (!valid_field) return; 30912ccb71bSMatthias Ringwald 31012ccb71bSMatthias Ringwald // verify record id 31112ccb71bSMatthias Ringwald if (parser->global_report_id && !parser->active_record){ 31212ccb71bSMatthias Ringwald if (parser->report[0] != parser->global_report_id){ 31312ccb71bSMatthias Ringwald return; 31412ccb71bSMatthias Ringwald } 3154ea43905SMatthias Ringwald parser->report_pos_in_bit += 8u; 31612ccb71bSMatthias Ringwald } 31712ccb71bSMatthias Ringwald parser->active_record = 1; 31812ccb71bSMatthias Ringwald // handle constant fields used for padding 31912ccb71bSMatthias Ringwald if (item->item_value & 1){ 32012ccb71bSMatthias Ringwald int item_bits = parser->global_report_size * parser->global_report_count; 32112ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 32212ccb71bSMatthias Ringwald log_info("- Skip %u constant bits", item_bits); 32312ccb71bSMatthias Ringwald #endif 32412ccb71bSMatthias Ringwald parser->report_pos_in_bit += item_bits; 32512ccb71bSMatthias Ringwald return; 32612ccb71bSMatthias Ringwald } 32712ccb71bSMatthias Ringwald // Empty Item 3284ea43905SMatthias Ringwald if (parser->global_report_count == 0u) return; 32912ccb71bSMatthias Ringwald // let's start 33012ccb71bSMatthias Ringwald parser->required_usages = parser->global_report_count; 33112ccb71bSMatthias Ringwald } 33212ccb71bSMatthias Ringwald 33312ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){ 33412ccb71bSMatthias Ringwald while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){ 33512ccb71bSMatthias Ringwald if (parser->descriptor_pos >= parser->descriptor_len){ 33612ccb71bSMatthias Ringwald // end of descriptor 33712ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 33812ccb71bSMatthias Ringwald break; 33912ccb71bSMatthias Ringwald } 3400c4004b0SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos); 3410c4004b0SMatthias Ringwald if (ok == false){ 3420c4004b0SMatthias Ringwald // abort parsing 3430c4004b0SMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 3440c4004b0SMatthias Ringwald break; 3450c4004b0SMatthias Ringwald } 34612ccb71bSMatthias Ringwald hid_process_item(parser, &parser->descriptor_item); 34712ccb71bSMatthias Ringwald if (parser->required_usages){ 34812ccb71bSMatthias Ringwald hid_find_next_usage(parser); 34912ccb71bSMatthias Ringwald if (parser->available_usages) { 35012ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE; 35112ccb71bSMatthias Ringwald } else { 352dfb01e77SMatthias Ringwald log_debug("no usages found"); 35312ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 35412ccb71bSMatthias Ringwald } 35512ccb71bSMatthias Ringwald } else { 356*fd8e652cSMatthias Ringwald if ((TagType) (&parser->descriptor_item)->item_type == Main) { 357*fd8e652cSMatthias Ringwald // reset usage 358*fd8e652cSMatthias Ringwald parser->usage_pos = parser->descriptor_pos; 359*fd8e652cSMatthias Ringwald parser->usage_page = parser->global_usage_page; 360*fd8e652cSMatthias Ringwald } 361*fd8e652cSMatthias Ringwald parser->descriptor_pos += parser->descriptor_item.item_size; 36212ccb71bSMatthias Ringwald } 36312ccb71bSMatthias Ringwald } 36412ccb71bSMatthias Ringwald } 36512ccb71bSMatthias Ringwald 36612ccb71bSMatthias Ringwald // PUBLIC API 36712ccb71bSMatthias Ringwald 368662cddc2SMilanka 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){ 36912ccb71bSMatthias Ringwald 37012ccb71bSMatthias Ringwald memset(parser, 0, sizeof(btstack_hid_parser_t)); 37112ccb71bSMatthias Ringwald 37212ccb71bSMatthias Ringwald parser->descriptor = hid_descriptor; 37312ccb71bSMatthias Ringwald parser->descriptor_len = hid_descriptor_len; 37412ccb71bSMatthias Ringwald parser->report_type = hid_report_type; 37512ccb71bSMatthias Ringwald parser->report = hid_report; 37612ccb71bSMatthias Ringwald parser->report_len = hid_report_len; 37712ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM; 37812ccb71bSMatthias Ringwald 37912ccb71bSMatthias Ringwald btstack_hid_parser_find_next_usage(parser); 38012ccb71bSMatthias Ringwald } 38112ccb71bSMatthias Ringwald 382088c59dfSMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 38312ccb71bSMatthias Ringwald return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE; 38412ccb71bSMatthias Ringwald } 38512ccb71bSMatthias Ringwald 38612ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 38712ccb71bSMatthias Ringwald 38812ccb71bSMatthias Ringwald *usage_page = parser->usage_minimum >> 16; 38912ccb71bSMatthias Ringwald 39012ccb71bSMatthias Ringwald // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 3911979f09cSMatthias Ringwald bool is_variable = (parser->descriptor_item.item_value & 2) != 0; 3921979f09cSMatthias Ringwald bool is_signed = parser->global_logical_minimum < 0; 39312ccb71bSMatthias Ringwald int pos_start = btstack_min( parser->report_pos_in_bit >> 3, parser->report_len); 3944ea43905SMatthias Ringwald int pos_end = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len); 39512ccb71bSMatthias Ringwald int bytes_to_read = pos_end - pos_start + 1; 39612ccb71bSMatthias Ringwald int i; 39712ccb71bSMatthias Ringwald uint32_t multi_byte_value = 0; 39812ccb71bSMatthias Ringwald for (i=0;i < bytes_to_read;i++){ 39912ccb71bSMatthias Ringwald multi_byte_value |= parser->report[pos_start+i] << (i*8); 40012ccb71bSMatthias Ringwald } 4014ea43905SMatthias Ringwald uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u); 40212ccb71bSMatthias 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); 40312ccb71bSMatthias Ringwald if (is_variable){ 4044ea43905SMatthias Ringwald *usage = parser->usage_minimum & 0xffffu; 4054ea43905SMatthias Ringwald if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){ 4064ea43905SMatthias Ringwald *value = unsigned_value - (1u<<parser->global_report_size); 40712ccb71bSMatthias Ringwald } else { 40812ccb71bSMatthias Ringwald *value = unsigned_value; 40912ccb71bSMatthias Ringwald } 41012ccb71bSMatthias Ringwald } else { 41112ccb71bSMatthias Ringwald *usage = unsigned_value; 41212ccb71bSMatthias Ringwald *value = 1; 41312ccb71bSMatthias Ringwald } 41412ccb71bSMatthias Ringwald parser->required_usages--; 41512ccb71bSMatthias Ringwald parser->report_pos_in_bit += parser->global_report_size; 41612ccb71bSMatthias Ringwald 41712ccb71bSMatthias Ringwald // next usage 41812ccb71bSMatthias Ringwald if (is_variable){ 41912ccb71bSMatthias Ringwald parser->usage_minimum++; 42012ccb71bSMatthias Ringwald parser->available_usages--; 4211a05cde1SMatthias Ringwald if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){ 422dfb01e77SMatthias Ringwald // usage min - max range smaller than report count, ignore remaining bit in report 423dfb01e77SMatthias Ringwald log_debug("Ignoring %u items without Usage", parser->required_usages); 424dfb01e77SMatthias Ringwald parser->report_pos_in_bit += parser->global_report_size * parser->required_usages; 425dfb01e77SMatthias Ringwald parser->required_usages = 0; 426dfb01e77SMatthias Ringwald } 42712ccb71bSMatthias Ringwald } else { 4284ea43905SMatthias Ringwald if (parser->required_usages == 0u){ 42912ccb71bSMatthias Ringwald parser->available_usages = 0; 43012ccb71bSMatthias Ringwald } 43112ccb71bSMatthias Ringwald } 43212ccb71bSMatthias Ringwald if (parser->available_usages) { 43312ccb71bSMatthias Ringwald return; 43412ccb71bSMatthias Ringwald } 4354ea43905SMatthias Ringwald if (parser->required_usages == 0u){ 436*fd8e652cSMatthias Ringwald parser->descriptor_pos += parser->descriptor_item.item_size; 43712ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM; 43812ccb71bSMatthias Ringwald btstack_hid_parser_find_next_usage(parser); 43912ccb71bSMatthias Ringwald } else { 44012ccb71bSMatthias Ringwald hid_find_next_usage(parser); 4414ea43905SMatthias Ringwald if (parser->available_usages == 0u) { 44212ccb71bSMatthias Ringwald parser->state = BTSTACK_HID_PARSER_COMPLETE; 44312ccb71bSMatthias Ringwald } 44412ccb71bSMatthias Ringwald } 44512ccb71bSMatthias Ringwald } 446fada7179SMilanka Ringwald 4470c7e4a25SMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 4480c7e4a25SMatthias Ringwald iterator->descriptor = hid_descriptor; 4490c7e4a25SMatthias Ringwald iterator->descriptor_pos = 0; 4500c7e4a25SMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 4510c7e4a25SMatthias Ringwald iterator->item_ready = false; 4520c7e4a25SMatthias Ringwald iterator->valid = true; 4530c7e4a25SMatthias Ringwald } 4540c7e4a25SMatthias Ringwald 4550c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){ 4560c7e4a25SMatthias Ringwald if ((iterator->item_ready == false) && (iterator->descriptor_len > 0)){ 4570c7e4a25SMatthias Ringwald uint16_t item_len = iterator->descriptor_len - iterator->descriptor_pos; 4580c7e4a25SMatthias Ringwald const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos]; 4590c7e4a25SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len); 4600c7e4a25SMatthias Ringwald if (ok){ 4610c7e4a25SMatthias Ringwald iterator->item_ready = true; 4620c7e4a25SMatthias Ringwald } else { 4630c7e4a25SMatthias Ringwald iterator->valid = false; 4640c7e4a25SMatthias Ringwald } 4650c7e4a25SMatthias Ringwald } 4660c7e4a25SMatthias Ringwald return iterator->item_ready; 4670c7e4a25SMatthias Ringwald } 4680c7e4a25SMatthias Ringwald 4690c7e4a25SMatthias Ringwald const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){ 4700c7e4a25SMatthias Ringwald 4710c7e4a25SMatthias Ringwald btstack_assert(iterator->descriptor_len >= iterator->descriptor_item.item_size); 4720c7e4a25SMatthias Ringwald 4730c7e4a25SMatthias Ringwald iterator->descriptor_len -= iterator->descriptor_item.item_size; 4740c7e4a25SMatthias Ringwald iterator->descriptor += iterator->descriptor_item.item_size; 4750c7e4a25SMatthias Ringwald iterator->item_ready = false; 4760c7e4a25SMatthias Ringwald return &iterator->descriptor_item; 4770c7e4a25SMatthias Ringwald } 4780c7e4a25SMatthias Ringwald 4790c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){ 4800c7e4a25SMatthias Ringwald return iterator->valid; 4810c7e4a25SMatthias Ringwald } 4820c7e4a25SMatthias Ringwald 483662cddc2SMilanka 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) { 484fada7179SMilanka Ringwald int total_report_size = 0; 485fada7179SMilanka Ringwald int report_size = 0; 486fada7179SMilanka Ringwald int report_count = 0; 487fada7179SMilanka Ringwald int current_report_id = 0; 488fada7179SMilanka Ringwald 489960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 490960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 491960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 492960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 493fada7179SMilanka Ringwald int valid_report_type = 0; 494960622b0SMatthias Ringwald switch (item->item_type) { 495fada7179SMilanka Ringwald case Global: 496960622b0SMatthias Ringwald switch ((GlobalItemTag) item->item_tag) { 497fada7179SMilanka Ringwald case ReportID: 498960622b0SMatthias Ringwald current_report_id = item->item_value; 499fada7179SMilanka Ringwald break; 500fada7179SMilanka Ringwald case ReportCount: 501960622b0SMatthias Ringwald report_count = item->item_value; 502fada7179SMilanka Ringwald break; 503fada7179SMilanka Ringwald case ReportSize: 504960622b0SMatthias Ringwald report_size = item->item_value; 505fada7179SMilanka Ringwald break; 506fada7179SMilanka Ringwald default: 507fada7179SMilanka Ringwald break; 508fada7179SMilanka Ringwald } 509fada7179SMilanka Ringwald break; 510fada7179SMilanka Ringwald case Main: 511fada7179SMilanka Ringwald if (current_report_id != report_id) break; 512960622b0SMatthias Ringwald switch ((MainItemTag) item->item_tag) { 513fada7179SMilanka Ringwald case Input: 514662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_INPUT) break; 515fada7179SMilanka Ringwald valid_report_type = 1; 516fada7179SMilanka Ringwald break; 517fada7179SMilanka Ringwald case Output: 518662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_OUTPUT) break; 519fada7179SMilanka Ringwald valid_report_type = 1; 520fada7179SMilanka Ringwald break; 521fada7179SMilanka Ringwald case Feature: 522662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_FEATURE) break; 523fada7179SMilanka Ringwald valid_report_type = 1; 524fada7179SMilanka Ringwald break; 525fada7179SMilanka Ringwald default: 526fada7179SMilanka Ringwald break; 527fada7179SMilanka Ringwald } 528fada7179SMilanka Ringwald if (!valid_report_type) break; 529fada7179SMilanka Ringwald total_report_size += report_count * report_size; 530fada7179SMilanka Ringwald break; 531fada7179SMilanka Ringwald default: 532fada7179SMilanka Ringwald break; 533fada7179SMilanka Ringwald } 53415cf8612Sx0rloser if (total_report_size > 0 && current_report_id != report_id) break; 535fada7179SMilanka Ringwald } 536960622b0SMatthias Ringwald 537960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)){ 538fada7179SMilanka Ringwald return (total_report_size + 7) / 8; 539960622b0SMatthias Ringwald } else { 540960622b0SMatthias Ringwald return 0; 541960622b0SMatthias Ringwald } 542fada7179SMilanka Ringwald } 543dbcaefc7SMilanka Ringwald 544dbcaefc7SMilanka Ringwald hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 545960622b0SMatthias Ringwald int current_report_id = -1; 546960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 547960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 548960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 549960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 550960622b0SMatthias Ringwald switch (item->item_type){ 551dbcaefc7SMilanka Ringwald case Global: 552960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 553dbcaefc7SMilanka Ringwald case ReportID: 554960622b0SMatthias Ringwald current_report_id = item->item_value; 555dbcaefc7SMilanka Ringwald if (current_report_id != report_id) break; 556dbcaefc7SMilanka Ringwald return HID_REPORT_ID_VALID; 557dbcaefc7SMilanka Ringwald default: 558dbcaefc7SMilanka Ringwald break; 559dbcaefc7SMilanka Ringwald } 560dbcaefc7SMilanka Ringwald break; 561dbcaefc7SMilanka Ringwald default: 562dbcaefc7SMilanka Ringwald break; 563dbcaefc7SMilanka Ringwald } 564dbcaefc7SMilanka Ringwald } 565960622b0SMatthias Ringwald 566960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)) { 567960622b0SMatthias Ringwald if (current_report_id != -1) { 568960622b0SMatthias Ringwald return HID_REPORT_ID_INVALID; 569960622b0SMatthias Ringwald } else { 570dbcaefc7SMilanka Ringwald return HID_REPORT_ID_UNDECLARED; 571dbcaefc7SMilanka Ringwald } 572960622b0SMatthias Ringwald } else { 573960622b0SMatthias Ringwald return HID_REPORT_ID_INVALID; 574960622b0SMatthias Ringwald } 575960622b0SMatthias Ringwald } 576dbcaefc7SMilanka Ringwald 577c17118d0SMatthias Ringwald bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 578960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 579960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 580960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 581960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 582960622b0SMatthias Ringwald switch (item->item_type){ 583dbcaefc7SMilanka Ringwald case Global: 584960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 585dbcaefc7SMilanka Ringwald case ReportID: 586c17118d0SMatthias Ringwald return true; 587dbcaefc7SMilanka Ringwald default: 588dbcaefc7SMilanka Ringwald break; 589dbcaefc7SMilanka Ringwald } 590dbcaefc7SMilanka Ringwald break; 591dbcaefc7SMilanka Ringwald default: 592dbcaefc7SMilanka Ringwald break; 593dbcaefc7SMilanka Ringwald } 594dbcaefc7SMilanka Ringwald } 595c17118d0SMatthias Ringwald return false; 596dbcaefc7SMilanka Ringwald } 597