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 490ba748f2SMatthias Ringwald // - Optional Pretty Print of HID Descriptor 5012ccb71bSMatthias Ringwald 5112ccb71bSMatthias Ringwald /* 5212ccb71bSMatthias Ringwald * btstack_hid_parser.c 5312ccb71bSMatthias Ringwald */ 5412ccb71bSMatthias Ringwald 5512ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 5612ccb71bSMatthias Ringwald 5712ccb71bSMatthias Ringwald static const char * type_names[] = { 5812ccb71bSMatthias Ringwald "Main", 5912ccb71bSMatthias Ringwald "Global", 6012ccb71bSMatthias Ringwald "Local", 6112ccb71bSMatthias Ringwald "Reserved" 6212ccb71bSMatthias Ringwald }; 6312ccb71bSMatthias Ringwald static const char * main_tags[] = { 6412ccb71bSMatthias Ringwald "", 6512ccb71bSMatthias Ringwald "", 6612ccb71bSMatthias Ringwald "", 6712ccb71bSMatthias Ringwald "", 6812ccb71bSMatthias Ringwald "", 6912ccb71bSMatthias Ringwald "", 7012ccb71bSMatthias Ringwald "", 7112ccb71bSMatthias Ringwald "", 7212ccb71bSMatthias Ringwald "Input ", 7312ccb71bSMatthias Ringwald "Output", 7412ccb71bSMatthias Ringwald "Collection", 7512ccb71bSMatthias Ringwald "Feature", 7612ccb71bSMatthias Ringwald "End Collection", 7712ccb71bSMatthias Ringwald "Reserved", 7812ccb71bSMatthias Ringwald "Reserved", 7912ccb71bSMatthias Ringwald "Reserved" 8012ccb71bSMatthias Ringwald }; 8112ccb71bSMatthias Ringwald static const char * global_tags[] = { 8212ccb71bSMatthias Ringwald "Usage Page", 8312ccb71bSMatthias Ringwald "Logical Minimum", 8412ccb71bSMatthias Ringwald "Logical Maximum", 8512ccb71bSMatthias Ringwald "Physical Minimum", 8612ccb71bSMatthias Ringwald "Physical Maximum", 8712ccb71bSMatthias Ringwald "Unit Exponent", 8812ccb71bSMatthias Ringwald "Unit", 8912ccb71bSMatthias Ringwald "Report Size", 9012ccb71bSMatthias Ringwald "Report ID", 9112ccb71bSMatthias Ringwald "Report Count", 9212ccb71bSMatthias Ringwald "Push", 9312ccb71bSMatthias Ringwald "Pop", 9412ccb71bSMatthias Ringwald "Reserved", 9512ccb71bSMatthias Ringwald "Reserved", 9612ccb71bSMatthias Ringwald "Reserved", 9712ccb71bSMatthias Ringwald "Reserved" 9812ccb71bSMatthias Ringwald }; 9912ccb71bSMatthias Ringwald static const char * local_tags[] = { 10012ccb71bSMatthias Ringwald "Usage", 10112ccb71bSMatthias Ringwald "Usage Minimum", 10212ccb71bSMatthias Ringwald "Usage Maximum", 10312ccb71bSMatthias Ringwald "Designator Index", 10412ccb71bSMatthias Ringwald "Designator Minimum", 10512ccb71bSMatthias Ringwald "Designator Maximum", 10612ccb71bSMatthias Ringwald "String Index", 10712ccb71bSMatthias Ringwald "String Minimum", 10812ccb71bSMatthias Ringwald "String Maximum", 10912ccb71bSMatthias Ringwald "Delimiter", 11012ccb71bSMatthias Ringwald "Reserved", 11112ccb71bSMatthias Ringwald "Reserved", 11212ccb71bSMatthias Ringwald "Reserved", 11312ccb71bSMatthias Ringwald "Reserved", 11412ccb71bSMatthias Ringwald "Reserved", 11512ccb71bSMatthias Ringwald "Reserved" 11612ccb71bSMatthias Ringwald }; 11712ccb71bSMatthias Ringwald #endif 11812ccb71bSMatthias Ringwald 1195dd091fdSMatthias Ringwald // HID Descriptor Iterator 12012ccb71bSMatthias Ringwald 12112ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value 1225dd091fdSMatthias Ringwald static bool btstack_hid_descriptor_parse_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 1238334d3d8SMatthias Ringwald 1248334d3d8SMatthias Ringwald const int hid_item_sizes[] = { 0, 1, 2, 4 }; 1258334d3d8SMatthias Ringwald 12612ccb71bSMatthias Ringwald // parse item header 12776fa2448SMatthias Ringwald if (hid_descriptor_len < 1u) return false; 12812ccb71bSMatthias Ringwald uint16_t pos = 0; 12912ccb71bSMatthias Ringwald uint8_t item_header = hid_descriptor[pos++]; 1304ea43905SMatthias Ringwald item->data_size = hid_item_sizes[item_header & 0x03u]; 1314ea43905SMatthias Ringwald item->item_type = (item_header & 0x0cu) >> 2u; 1324ea43905SMatthias Ringwald item->item_tag = (item_header & 0xf0u) >> 4u; 13312ccb71bSMatthias Ringwald // long item 1344ea43905SMatthias Ringwald if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){ 13576fa2448SMatthias Ringwald if (hid_descriptor_len < 3u) return false; 13612ccb71bSMatthias Ringwald item->data_size = hid_descriptor[pos++]; 13712ccb71bSMatthias Ringwald item->item_tag = hid_descriptor[pos++]; 13812ccb71bSMatthias Ringwald } 13912ccb71bSMatthias Ringwald item->item_size = pos + item->data_size; 14012ccb71bSMatthias Ringwald item->item_value = 0; 14112ccb71bSMatthias Ringwald 14212ccb71bSMatthias Ringwald // read item value 14376fa2448SMatthias Ringwald if (hid_descriptor_len < item->item_size) return false; 14476fa2448SMatthias Ringwald if (item->data_size > 4u) return false; 14512ccb71bSMatthias Ringwald int i; 1464ea43905SMatthias Ringwald int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u); 14712ccb71bSMatthias Ringwald int32_t value = 0; 14812ccb71bSMatthias Ringwald uint8_t latest_byte = 0; 14912ccb71bSMatthias Ringwald for (i=0;i<item->data_size;i++){ 15012ccb71bSMatthias Ringwald latest_byte = hid_descriptor[pos++]; 15112ccb71bSMatthias Ringwald value = (latest_byte << (8*i)) | value; 15212ccb71bSMatthias Ringwald } 1534ea43905SMatthias Ringwald if (sgnd && (item->data_size > 0u)){ 1544ea43905SMatthias Ringwald if (latest_byte & 0x80u) { 1554ea43905SMatthias Ringwald value -= 1u << (item->data_size*8u); 15612ccb71bSMatthias Ringwald } 15712ccb71bSMatthias Ringwald } 15812ccb71bSMatthias Ringwald item->item_value = value; 15976fa2448SMatthias Ringwald return true; 16012ccb71bSMatthias Ringwald } 16112ccb71bSMatthias Ringwald 1625dd091fdSMatthias Ringwald static void btstack_hid_descriptor_iterator_pretty_print_item(btstack_hid_descriptor_iterator_t * iterator, hid_descriptor_item_t * item){ 1635dd091fdSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 1645dd091fdSMatthias Ringwald const char ** item_tag_table; 1655dd091fdSMatthias Ringwald switch ((TagType)item->item_type){ 1665dd091fdSMatthias Ringwald case Main: 1675dd091fdSMatthias Ringwald item_tag_table = main_tags; 1685dd091fdSMatthias Ringwald break; 1695dd091fdSMatthias Ringwald case Global: 1705dd091fdSMatthias Ringwald item_tag_table = global_tags; 1715dd091fdSMatthias Ringwald break; 1725dd091fdSMatthias Ringwald case Local: 1735dd091fdSMatthias Ringwald item_tag_table = local_tags; 1745dd091fdSMatthias Ringwald break; 1755dd091fdSMatthias Ringwald default: 1765dd091fdSMatthias Ringwald item_tag_table = NULL; 1775dd091fdSMatthias Ringwald break; 1785dd091fdSMatthias Ringwald } 1795dd091fdSMatthias Ringwald const char * item_tag_name = "Invalid"; 1805dd091fdSMatthias Ringwald if (item_tag_table){ 1815dd091fdSMatthias Ringwald item_tag_name = item_tag_table[item->item_tag]; 1825dd091fdSMatthias Ringwald } 1835dd091fdSMatthias Ringwald log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 1845dd091fdSMatthias Ringwald printf("%-15s (%-6s) // %02x 0x%0008x\n", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 1855dd091fdSMatthias Ringwald #else 1865dd091fdSMatthias Ringwald UNUSED(iterator); 1875dd091fdSMatthias Ringwald UNUSED(item); 1885dd091fdSMatthias Ringwald #endif 1895dd091fdSMatthias Ringwald } 1905dd091fdSMatthias Ringwald 1915dd091fdSMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 1925dd091fdSMatthias Ringwald iterator->descriptor = hid_descriptor; 1935dd091fdSMatthias Ringwald iterator->descriptor_pos = 0; 1945dd091fdSMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 1955dd091fdSMatthias Ringwald iterator->item_ready = false; 1965dd091fdSMatthias Ringwald iterator->valid = true; 1975dd091fdSMatthias Ringwald } 1985dd091fdSMatthias Ringwald 1995dd091fdSMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){ 2005dd091fdSMatthias Ringwald if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){ 2015dd091fdSMatthias Ringwald uint16_t item_len = iterator->descriptor_len - iterator->descriptor_pos; 2025dd091fdSMatthias Ringwald const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos]; 2035dd091fdSMatthias Ringwald bool ok = btstack_hid_descriptor_parse_item(&iterator->descriptor_item, item_data, item_len); 2045dd091fdSMatthias Ringwald btstack_hid_descriptor_iterator_pretty_print_item(iterator, &iterator->descriptor_item); 2055dd091fdSMatthias Ringwald if (ok){ 2065dd091fdSMatthias Ringwald iterator->item_ready = true; 2075dd091fdSMatthias Ringwald } else { 2085dd091fdSMatthias Ringwald iterator->valid = false; 2095dd091fdSMatthias Ringwald } 2105dd091fdSMatthias Ringwald } 2115dd091fdSMatthias Ringwald return iterator->item_ready; 2125dd091fdSMatthias Ringwald } 2135dd091fdSMatthias Ringwald 214*3465b19cSDirk Helbig const hid_descriptor_item_t * btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){ 2155dd091fdSMatthias Ringwald iterator->descriptor_pos += iterator->descriptor_item.item_size; 2165dd091fdSMatthias Ringwald iterator->item_ready = false; 2175dd091fdSMatthias Ringwald return &iterator->descriptor_item; 2185dd091fdSMatthias Ringwald } 2195dd091fdSMatthias Ringwald 2205dd091fdSMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){ 2215dd091fdSMatthias Ringwald return iterator->valid; 2225dd091fdSMatthias Ringwald } 2235dd091fdSMatthias Ringwald 2245dd091fdSMatthias Ringwald // HID Descriptor Usage Iterator 2255dd091fdSMatthias Ringwald 2265dd091fdSMatthias Ringwald static bool btstack_hid_usage_iterator_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){ 2270a4fc5fbSMatthias Ringwald switch (tag){ 2280a4fc5fbSMatthias Ringwald case Input: 2290a4fc5fbSMatthias Ringwald return report_type == HID_REPORT_TYPE_INPUT; 2300a4fc5fbSMatthias Ringwald case Output: 2310a4fc5fbSMatthias Ringwald return report_type == HID_REPORT_TYPE_OUTPUT; 2320a4fc5fbSMatthias Ringwald case Feature: 2330a4fc5fbSMatthias Ringwald return report_type == HID_REPORT_TYPE_FEATURE; 2340a4fc5fbSMatthias Ringwald default: 2350a4fc5fbSMatthias Ringwald return false; 2360a4fc5fbSMatthias Ringwald } 2370a4fc5fbSMatthias Ringwald } 2380a4fc5fbSMatthias Ringwald 2395dd091fdSMatthias Ringwald static void btstack_hid_usage_iterator_handle_global_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 2407bbeb3adSMilanka Ringwald switch((GlobalItemTag)item->item_tag){ 24112ccb71bSMatthias Ringwald case UsagePage: 242c4241e61SMatthias Ringwald iterator->global_usage_page = item->item_value; 24312ccb71bSMatthias Ringwald break; 24412ccb71bSMatthias Ringwald case LogicalMinimum: 245c4241e61SMatthias Ringwald iterator->global_logical_minimum = item->item_value; 24612ccb71bSMatthias Ringwald break; 24712ccb71bSMatthias Ringwald case LogicalMaximum: 248c4241e61SMatthias Ringwald iterator->global_logical_maximum = item->item_value; 24912ccb71bSMatthias Ringwald break; 25012ccb71bSMatthias Ringwald case ReportSize: 251c4241e61SMatthias Ringwald iterator->global_report_size = item->item_value; 25212ccb71bSMatthias Ringwald break; 25312ccb71bSMatthias Ringwald case ReportID: 254c4241e61SMatthias Ringwald iterator->global_report_id = item->item_value; 25512ccb71bSMatthias Ringwald break; 25612ccb71bSMatthias Ringwald case ReportCount: 257c4241e61SMatthias Ringwald iterator->global_report_count = item->item_value; 25812ccb71bSMatthias Ringwald break; 2597bbeb3adSMilanka Ringwald 2607bbeb3adSMilanka Ringwald // TODO handle tags 2617bbeb3adSMilanka Ringwald case PhysicalMinimum: 2627bbeb3adSMilanka Ringwald case PhysicalMaximum: 2637bbeb3adSMilanka Ringwald case UnitExponent: 2647bbeb3adSMilanka Ringwald case Unit: 2657bbeb3adSMilanka Ringwald case Push: 2667bbeb3adSMilanka Ringwald case Pop: 2677bbeb3adSMilanka Ringwald break; 2687bbeb3adSMilanka Ringwald 26912ccb71bSMatthias Ringwald default: 2707bbeb3adSMilanka Ringwald btstack_assert(false); 27112ccb71bSMatthias Ringwald break; 27212ccb71bSMatthias Ringwald } 27312ccb71bSMatthias Ringwald } 27412ccb71bSMatthias Ringwald 2755dd091fdSMatthias Ringwald static void btstack_usage_iterator_hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator){ 2761a05cde1SMatthias Ringwald bool have_usage_min = false; 2771a05cde1SMatthias Ringwald bool have_usage_max = false; 278c4241e61SMatthias Ringwald main_iterator->usage_range = false; 27941afcabaSMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 280c4241e61SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, &main_iterator->descriptor[main_iterator->usage_pos], main_iterator->descriptor_len - main_iterator->usage_pos); 281c4241e61SMatthias Ringwald while ((main_iterator->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){ 28241afcabaSMatthias Ringwald hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator); 2830e588213SMatthias Ringwald if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){ 284c4241e61SMatthias Ringwald main_iterator->usage_page = usage_item.item_value; 28512ccb71bSMatthias Ringwald } 28612ccb71bSMatthias Ringwald if (usage_item.item_type == Local){ 287c4241e61SMatthias Ringwald uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((main_iterator->usage_page << 16u) | usage_item.item_value); 28812ccb71bSMatthias Ringwald switch (usage_item.item_tag){ 28912ccb71bSMatthias Ringwald case Usage: 290c4241e61SMatthias Ringwald main_iterator->available_usages = 1; 291c4241e61SMatthias Ringwald main_iterator->usage_minimum = usage_value; 29212ccb71bSMatthias Ringwald break; 29312ccb71bSMatthias Ringwald case UsageMinimum: 294c4241e61SMatthias Ringwald main_iterator->usage_minimum = usage_value; 2951a05cde1SMatthias Ringwald have_usage_min = true; 29612ccb71bSMatthias Ringwald break; 29712ccb71bSMatthias Ringwald case UsageMaximum: 298c4241e61SMatthias Ringwald main_iterator->usage_maximum = usage_value; 2991a05cde1SMatthias Ringwald have_usage_max = true; 30012ccb71bSMatthias Ringwald break; 30112ccb71bSMatthias Ringwald default: 30212ccb71bSMatthias Ringwald break; 30312ccb71bSMatthias Ringwald } 3041a05cde1SMatthias Ringwald if (have_usage_min && have_usage_max){ 305c4241e61SMatthias Ringwald main_iterator->available_usages = main_iterator->usage_maximum - main_iterator->usage_minimum + 1u; 306c4241e61SMatthias Ringwald main_iterator->usage_range = true; 307c4241e61SMatthias Ringwald if (main_iterator->available_usages < main_iterator->required_usages){ 308c4241e61SMatthias Ringwald log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", main_iterator->usage_minimum & 0xffff, main_iterator->usage_maximum & 0xffff, main_iterator->required_usages); 309dfb01e77SMatthias Ringwald } 31012ccb71bSMatthias Ringwald } 31112ccb71bSMatthias Ringwald } 31212ccb71bSMatthias Ringwald } 313c4241e61SMatthias Ringwald main_iterator->usage_pos += iterator.descriptor_pos; 31412ccb71bSMatthias Ringwald } 31512ccb71bSMatthias Ringwald 3165dd091fdSMatthias Ringwald static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 3175dd091fdSMatthias Ringwald int valid_field = 0; 3185dd091fdSMatthias Ringwald uint16_t report_id_before; 3195dd091fdSMatthias Ringwald switch ((TagType)item->item_type){ 3205dd091fdSMatthias Ringwald case Main: 3215dd091fdSMatthias Ringwald valid_field = btstack_hid_usage_iterator_main_item_tag_matches_report_type((MainItemTag) item->item_tag, 3225dd091fdSMatthias Ringwald iterator->report_type); 3235dd091fdSMatthias Ringwald break; 3245dd091fdSMatthias Ringwald case Global: 3255dd091fdSMatthias Ringwald report_id_before = iterator->global_report_id; 3265dd091fdSMatthias Ringwald btstack_hid_usage_iterator_handle_global_item(iterator, item); 3275dd091fdSMatthias Ringwald // track record id for report handling, reset report position 3285dd091fdSMatthias Ringwald if (report_id_before != iterator->global_report_id){ 329619253e6SMatthias Ringwald iterator->report_pos_in_bit = 0u; 3305dd091fdSMatthias Ringwald } 3315dd091fdSMatthias Ringwald break; 3325dd091fdSMatthias Ringwald case Local: 3335dd091fdSMatthias Ringwald case Reserved: 3345dd091fdSMatthias Ringwald break; 3355dd091fdSMatthias Ringwald default: 3365dd091fdSMatthias Ringwald btstack_assert(false); 3375dd091fdSMatthias Ringwald break; 3385dd091fdSMatthias Ringwald } 3395dd091fdSMatthias Ringwald if (!valid_field) return; 34012ccb71bSMatthias Ringwald 3415dd091fdSMatthias Ringwald // handle constant fields used for padding 3425dd091fdSMatthias Ringwald if (item->item_value & 1){ 3435dd091fdSMatthias Ringwald int item_bits = iterator->global_report_size * iterator->global_report_count; 3445dd091fdSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 3455dd091fdSMatthias Ringwald log_info("- Skip %u constant bits", item_bits); 3465dd091fdSMatthias Ringwald #endif 3475dd091fdSMatthias Ringwald iterator->report_pos_in_bit += item_bits; 3485dd091fdSMatthias Ringwald return; 3495dd091fdSMatthias Ringwald } 3505dd091fdSMatthias Ringwald // Empty Item 3515dd091fdSMatthias Ringwald if (iterator->global_report_count == 0u) return; 3525dd091fdSMatthias Ringwald // let's start 3535dd091fdSMatthias Ringwald iterator->required_usages = iterator->global_report_count; 3540c7e4a25SMatthias Ringwald } 3550c7e4a25SMatthias Ringwald 3565dd091fdSMatthias Ringwald static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) { 3575dd091fdSMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){ 3585dd091fdSMatthias Ringwald iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator); 3595dd091fdSMatthias Ringwald 3605dd091fdSMatthias Ringwald btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item); 3615dd091fdSMatthias Ringwald 3625dd091fdSMatthias Ringwald if (iterator->required_usages){ 3635dd091fdSMatthias Ringwald btstack_usage_iterator_hid_find_next_usage(iterator); 3645dd091fdSMatthias Ringwald if (iterator->available_usages) { 3655dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 3665dd091fdSMatthias Ringwald return; 3670c7e4a25SMatthias Ringwald } else { 3685dd091fdSMatthias Ringwald log_debug("no usages found"); 3695dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 3705dd091fdSMatthias Ringwald return; 3715dd091fdSMatthias Ringwald } 3725dd091fdSMatthias Ringwald } else { 3735dd091fdSMatthias Ringwald if ((TagType) (&iterator->descriptor_item)->item_type == Main) { 3745dd091fdSMatthias Ringwald // reset usage 3755dd091fdSMatthias Ringwald iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos; 3765dd091fdSMatthias Ringwald iterator->usage_page = iterator->global_usage_page; 3770c7e4a25SMatthias Ringwald } 3780c7e4a25SMatthias Ringwald } 3795dd091fdSMatthias Ringwald } 3805dd091fdSMatthias Ringwald // end of descriptor 3815dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 3820c7e4a25SMatthias Ringwald } 3830c7e4a25SMatthias Ringwald 3845dd091fdSMatthias Ringwald void btstack_hid_usage_iterator_init(btstack_hid_usage_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){ 3855dd091fdSMatthias Ringwald memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t)); 3865dd091fdSMatthias Ringwald 3875dd091fdSMatthias Ringwald iterator->descriptor = hid_descriptor; 3885dd091fdSMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 3895dd091fdSMatthias Ringwald iterator->report_type = hid_report_type; 3905dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 3915dd091fdSMatthias Ringwald iterator->global_report_id = HID_REPORT_ID_UNDEFINED; 3925dd091fdSMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len); 3930c7e4a25SMatthias Ringwald } 3940c7e4a25SMatthias Ringwald 3955dd091fdSMatthias Ringwald bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){ 3965dd091fdSMatthias Ringwald while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){ 3975dd091fdSMatthias Ringwald btstack_hid_usage_iterator_find_next_usage(iterator); 3985dd091fdSMatthias Ringwald } 3995dd091fdSMatthias Ringwald return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 4000c7e4a25SMatthias Ringwald } 4010c7e4a25SMatthias Ringwald 4025dd091fdSMatthias Ringwald void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){ 4035dd091fdSMatthias Ringwald // cache current values 4045dd091fdSMatthias Ringwald memset(item, 0, sizeof(btstack_hid_usage_item_t)); 4055dd091fdSMatthias Ringwald item->size = iterator->global_report_size; 4065dd091fdSMatthias Ringwald item->report_id = iterator->global_report_id; 4075dd091fdSMatthias Ringwald item->usage_page = iterator->usage_minimum >> 16; 4085dd091fdSMatthias Ringwald item->bit_pos = iterator->report_pos_in_bit; 4095dd091fdSMatthias Ringwald 4105dd091fdSMatthias Ringwald bool is_variable = (iterator->descriptor_item.item_value & 2) != 0; 4115dd091fdSMatthias Ringwald if (is_variable){ 4125dd091fdSMatthias Ringwald item->usage = iterator->usage_minimum & 0xffffu; 4135dd091fdSMatthias Ringwald } 4145dd091fdSMatthias Ringwald iterator->required_usages--; 4155dd091fdSMatthias Ringwald iterator->report_pos_in_bit += iterator->global_report_size; 4165dd091fdSMatthias Ringwald 4175dd091fdSMatthias Ringwald // cache descriptor item and 4185dd091fdSMatthias Ringwald item->descriptor_item = iterator->descriptor_item; 4195dd091fdSMatthias Ringwald item->global_logical_minimum = iterator->global_logical_minimum; 4205dd091fdSMatthias Ringwald 4215dd091fdSMatthias Ringwald // next usage 4225dd091fdSMatthias Ringwald if (is_variable){ 4235dd091fdSMatthias Ringwald iterator->usage_minimum++; 4245dd091fdSMatthias Ringwald iterator->available_usages--; 4255dd091fdSMatthias Ringwald if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){ 4265dd091fdSMatthias Ringwald // usage min - max range smaller than report count, ignore remaining bit in report 4275dd091fdSMatthias Ringwald log_debug("Ignoring %u items without Usage", parser->required_usages); 4285dd091fdSMatthias Ringwald iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages; 4295dd091fdSMatthias Ringwald iterator->required_usages = 0; 4305dd091fdSMatthias Ringwald } 4315dd091fdSMatthias Ringwald } else { 4325dd091fdSMatthias Ringwald if (iterator->required_usages == 0u){ 4335dd091fdSMatthias Ringwald iterator->available_usages = 0; 4345dd091fdSMatthias Ringwald } 4355dd091fdSMatthias Ringwald } 4365dd091fdSMatthias Ringwald 4375dd091fdSMatthias Ringwald if (iterator->available_usages) { 4385dd091fdSMatthias Ringwald return; 4395dd091fdSMatthias Ringwald } 4405dd091fdSMatthias Ringwald if (iterator->required_usages == 0u){ 4415dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 4425dd091fdSMatthias Ringwald } else { 4435dd091fdSMatthias Ringwald btstack_usage_iterator_hid_find_next_usage(iterator); 4445dd091fdSMatthias Ringwald if (iterator->available_usages == 0u) { 4455dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 4465dd091fdSMatthias Ringwald } 4475dd091fdSMatthias Ringwald } 4485dd091fdSMatthias Ringwald } 4495dd091fdSMatthias Ringwald 4505dd091fdSMatthias Ringwald 4515dd091fdSMatthias Ringwald // HID Report Parser 4525dd091fdSMatthias Ringwald 4535dd091fdSMatthias 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){ 4545dd091fdSMatthias Ringwald btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type); 4555dd091fdSMatthias Ringwald parser->report = hid_report; 4565dd091fdSMatthias Ringwald parser->report_len = hid_report_len; 4575dd091fdSMatthias Ringwald parser->have_report_usage_ready = false; 4585dd091fdSMatthias Ringwald } 4595dd091fdSMatthias Ringwald 4605dd091fdSMatthias Ringwald /** 4615dd091fdSMatthias Ringwald * @brief Checks if more fields are available 4625dd091fdSMatthias Ringwald * @param parser 4635dd091fdSMatthias Ringwald */ 4645dd091fdSMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 4655dd091fdSMatthias Ringwald while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){ 466a05e6df3SMatthias Ringwald btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor_usage_item); 4675dd091fdSMatthias Ringwald // ignore usages for other report ids 468a05e6df3SMatthias Ringwald if (parser->descriptor_usage_item.report_id != HID_REPORT_ID_UNDEFINED){ 469a05e6df3SMatthias Ringwald if (parser->descriptor_usage_item.report_id != parser->report[0]){ 4705dd091fdSMatthias Ringwald continue; 4715dd091fdSMatthias Ringwald } 4725dd091fdSMatthias Ringwald } 4735dd091fdSMatthias Ringwald parser->have_report_usage_ready = true; 4745dd091fdSMatthias Ringwald } 4755dd091fdSMatthias Ringwald return parser->have_report_usage_ready; 4765dd091fdSMatthias Ringwald } 4775dd091fdSMatthias Ringwald 4785dd091fdSMatthias Ringwald /** 4795dd091fdSMatthias Ringwald * @brief Get next field 4805dd091fdSMatthias Ringwald * @param parser 4815dd091fdSMatthias Ringwald * @param usage_page 4825dd091fdSMatthias Ringwald * @param usage 4835dd091fdSMatthias Ringwald * @param value provided in HID report 4845dd091fdSMatthias Ringwald */ 4855dd091fdSMatthias Ringwald 4865dd091fdSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 4875dd091fdSMatthias Ringwald 4885dd091fdSMatthias Ringwald // fetch data from descriptor usage item 489a05e6df3SMatthias Ringwald uint16_t bit_pos = parser->descriptor_usage_item.bit_pos; 490a05e6df3SMatthias Ringwald uint16_t size = parser->descriptor_usage_item.size; 491a05e6df3SMatthias Ringwald *usage_page = parser->descriptor_usage_item.usage_page; 492a05e6df3SMatthias Ringwald *usage = parser->descriptor_usage_item.usage; 4935dd091fdSMatthias Ringwald 494619253e6SMatthias Ringwald // skip optional Report ID 495619253e6SMatthias Ringwald if (parser->descriptor_usage_item.report_id != HID_REPORT_ID_UNDEFINED){ 496619253e6SMatthias Ringwald bit_pos += 8; 497619253e6SMatthias Ringwald } 4985dd091fdSMatthias Ringwald 4995dd091fdSMatthias Ringwald // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 500a05e6df3SMatthias Ringwald bool is_variable = (parser->descriptor_usage_item.descriptor_item.item_value & 2) != 0; 501a05e6df3SMatthias Ringwald bool is_signed = parser->descriptor_usage_item.global_logical_minimum < 0; 5025dd091fdSMatthias Ringwald int pos_start = btstack_min( bit_pos >> 3, parser->report_len); 5035dd091fdSMatthias Ringwald int pos_end = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len); 5045dd091fdSMatthias Ringwald int bytes_to_read = pos_end - pos_start + 1; 5055dd091fdSMatthias Ringwald 5065dd091fdSMatthias Ringwald int i; 5075dd091fdSMatthias Ringwald uint32_t multi_byte_value = 0; 5085dd091fdSMatthias Ringwald for (i=0;i < bytes_to_read;i++){ 5095dd091fdSMatthias Ringwald multi_byte_value |= parser->report[pos_start+i] << (i*8); 5105dd091fdSMatthias Ringwald } 5115dd091fdSMatthias Ringwald uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u); 5125dd091fdSMatthias 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); 5135dd091fdSMatthias Ringwald if (is_variable){ 5145dd091fdSMatthias Ringwald if (is_signed && (unsigned_value & (1u<<(size-1u)))){ 5155dd091fdSMatthias Ringwald *value = unsigned_value - (1u<<size); 5165dd091fdSMatthias Ringwald } else { 5175dd091fdSMatthias Ringwald *value = unsigned_value; 5185dd091fdSMatthias Ringwald } 5195dd091fdSMatthias Ringwald } else { 5205dd091fdSMatthias Ringwald *usage = unsigned_value; 5215dd091fdSMatthias Ringwald *value = 1; 5225dd091fdSMatthias Ringwald } 5235dd091fdSMatthias Ringwald 5245dd091fdSMatthias Ringwald parser->have_report_usage_ready = false; 5255dd091fdSMatthias Ringwald } 5265dd091fdSMatthias Ringwald 5275dd091fdSMatthias Ringwald 5285dd091fdSMatthias Ringwald // Utility functions 5290ba748f2SMatthias Ringwald 5302cca3b08SMatthias Ringwald int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor, 5312cca3b08SMatthias Ringwald uint16_t hid_descriptor_len) { 532fada7179SMilanka Ringwald int total_report_size = 0; 533fada7179SMilanka Ringwald int report_size = 0; 534fada7179SMilanka Ringwald int report_count = 0; 535279225e6SMatthias Ringwald int current_report_id = HID_REPORT_ID_UNDEFINED; 536fada7179SMilanka Ringwald 537960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 538960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 539960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 540960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 541fada7179SMilanka Ringwald int valid_report_type = 0; 542960622b0SMatthias Ringwald switch (item->item_type) { 543fada7179SMilanka Ringwald case Global: 544960622b0SMatthias Ringwald switch ((GlobalItemTag) item->item_tag) { 545fada7179SMilanka Ringwald case ReportID: 546960622b0SMatthias Ringwald current_report_id = item->item_value; 547fada7179SMilanka Ringwald break; 548fada7179SMilanka Ringwald case ReportCount: 549960622b0SMatthias Ringwald report_count = item->item_value; 550fada7179SMilanka Ringwald break; 551fada7179SMilanka Ringwald case ReportSize: 552960622b0SMatthias Ringwald report_size = item->item_value; 553fada7179SMilanka Ringwald break; 554fada7179SMilanka Ringwald default: 555fada7179SMilanka Ringwald break; 556fada7179SMilanka Ringwald } 557fada7179SMilanka Ringwald break; 558fada7179SMilanka Ringwald case Main: 559fada7179SMilanka Ringwald if (current_report_id != report_id) break; 560960622b0SMatthias Ringwald switch ((MainItemTag) item->item_tag) { 561fada7179SMilanka Ringwald case Input: 562662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_INPUT) break; 563fada7179SMilanka Ringwald valid_report_type = 1; 564fada7179SMilanka Ringwald break; 565fada7179SMilanka Ringwald case Output: 566662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_OUTPUT) break; 567fada7179SMilanka Ringwald valid_report_type = 1; 568fada7179SMilanka Ringwald break; 569fada7179SMilanka Ringwald case Feature: 570662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_FEATURE) break; 571fada7179SMilanka Ringwald valid_report_type = 1; 572fada7179SMilanka Ringwald break; 573fada7179SMilanka Ringwald default: 574fada7179SMilanka Ringwald break; 575fada7179SMilanka Ringwald } 576fada7179SMilanka Ringwald if (!valid_report_type) break; 577fada7179SMilanka Ringwald total_report_size += report_count * report_size; 578fada7179SMilanka Ringwald break; 579fada7179SMilanka Ringwald default: 580fada7179SMilanka Ringwald break; 581fada7179SMilanka Ringwald } 58215cf8612Sx0rloser if (total_report_size > 0 && current_report_id != report_id) break; 583fada7179SMilanka Ringwald } 584960622b0SMatthias Ringwald 585960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)){ 586fada7179SMilanka Ringwald return (total_report_size + 7) / 8; 587960622b0SMatthias Ringwald } else { 588960622b0SMatthias Ringwald return 0; 589960622b0SMatthias Ringwald } 590fada7179SMilanka Ringwald } 591dbcaefc7SMilanka Ringwald 592279225e6SMatthias Ringwald hid_report_id_status_t btstack_hid_report_id_valid(uint16_t report_id, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 593279225e6SMatthias Ringwald uint16_t current_report_id = HID_REPORT_ID_UNDEFINED; 594960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 595960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 596279225e6SMatthias Ringwald bool report_id_found = false; 597960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 598960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 599960622b0SMatthias Ringwald switch (item->item_type){ 600dbcaefc7SMilanka Ringwald case Global: 601960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 602dbcaefc7SMilanka Ringwald case ReportID: 603960622b0SMatthias Ringwald current_report_id = item->item_value; 604279225e6SMatthias Ringwald if (current_report_id == report_id) { 605279225e6SMatthias Ringwald report_id_found = true; 606279225e6SMatthias Ringwald } 607dbcaefc7SMilanka Ringwald default: 608dbcaefc7SMilanka Ringwald break; 609dbcaefc7SMilanka Ringwald } 610dbcaefc7SMilanka Ringwald break; 611dbcaefc7SMilanka Ringwald default: 612dbcaefc7SMilanka Ringwald break; 613dbcaefc7SMilanka Ringwald } 614dbcaefc7SMilanka Ringwald } 615960622b0SMatthias Ringwald 616960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)) { 617279225e6SMatthias Ringwald if (report_id_found){ 618279225e6SMatthias Ringwald return HID_REPORT_ID_VALID; 619dbcaefc7SMilanka Ringwald } 620279225e6SMatthias Ringwald if ((report_id == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) { 621279225e6SMatthias Ringwald return HID_REPORT_ID_VALID; 622279225e6SMatthias Ringwald } 623279225e6SMatthias Ringwald return HID_REPORT_ID_UNDECLARED; 624960622b0SMatthias Ringwald } else { 625960622b0SMatthias Ringwald return HID_REPORT_ID_INVALID; 626960622b0SMatthias Ringwald } 627960622b0SMatthias Ringwald } 628dbcaefc7SMilanka Ringwald 6292cca3b08SMatthias Ringwald bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) { 630960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 631960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 632960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 633960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 634960622b0SMatthias Ringwald switch (item->item_type){ 635dbcaefc7SMilanka Ringwald case Global: 636960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 637dbcaefc7SMilanka Ringwald case ReportID: 638c17118d0SMatthias Ringwald return true; 639dbcaefc7SMilanka Ringwald default: 640dbcaefc7SMilanka Ringwald break; 641dbcaefc7SMilanka Ringwald } 642dbcaefc7SMilanka Ringwald break; 643dbcaefc7SMilanka Ringwald default: 644dbcaefc7SMilanka Ringwald break; 645dbcaefc7SMilanka Ringwald } 646dbcaefc7SMilanka Ringwald } 647c17118d0SMatthias Ringwald return false; 648dbcaefc7SMilanka Ringwald } 649