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 // #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 121*c4241e61SMatthias Ringwald static void hid_pretty_print_item(btstack_hid_usage_iterator_t * iterator, 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 } 142*c4241e61SMatthias Ringwald log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 14312ccb71bSMatthias Ringwald #else 144*c4241e61SMatthias Ringwald UNUSED(iterator); 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 1900a4fc5fbSMatthias Ringwald static bool btstack_hid_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){ 1910a4fc5fbSMatthias Ringwald switch (tag){ 1920a4fc5fbSMatthias Ringwald case Input: 1930a4fc5fbSMatthias Ringwald return report_type == HID_REPORT_TYPE_INPUT; 1940a4fc5fbSMatthias Ringwald case Output: 1950a4fc5fbSMatthias Ringwald return report_type == HID_REPORT_TYPE_OUTPUT; 1960a4fc5fbSMatthias Ringwald case Feature: 1970a4fc5fbSMatthias Ringwald return report_type == HID_REPORT_TYPE_FEATURE; 1980a4fc5fbSMatthias Ringwald default: 1990a4fc5fbSMatthias Ringwald return false; 2000a4fc5fbSMatthias Ringwald } 2010a4fc5fbSMatthias Ringwald } 2020a4fc5fbSMatthias Ringwald 203*c4241e61SMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 2047bbeb3adSMilanka Ringwald switch((GlobalItemTag)item->item_tag){ 20512ccb71bSMatthias Ringwald case UsagePage: 206*c4241e61SMatthias Ringwald iterator->global_usage_page = item->item_value; 20712ccb71bSMatthias Ringwald break; 20812ccb71bSMatthias Ringwald case LogicalMinimum: 209*c4241e61SMatthias Ringwald iterator->global_logical_minimum = item->item_value; 21012ccb71bSMatthias Ringwald break; 21112ccb71bSMatthias Ringwald case LogicalMaximum: 212*c4241e61SMatthias Ringwald iterator->global_logical_maximum = item->item_value; 21312ccb71bSMatthias Ringwald break; 21412ccb71bSMatthias Ringwald case ReportSize: 215*c4241e61SMatthias Ringwald iterator->global_report_size = item->item_value; 21612ccb71bSMatthias Ringwald break; 21712ccb71bSMatthias Ringwald case ReportID: 218*c4241e61SMatthias Ringwald iterator->global_report_id = item->item_value; 21912ccb71bSMatthias Ringwald break; 22012ccb71bSMatthias Ringwald case ReportCount: 221*c4241e61SMatthias Ringwald iterator->global_report_count = item->item_value; 22212ccb71bSMatthias Ringwald break; 2237bbeb3adSMilanka Ringwald 2247bbeb3adSMilanka Ringwald // TODO handle tags 2257bbeb3adSMilanka Ringwald case PhysicalMinimum: 2267bbeb3adSMilanka Ringwald case PhysicalMaximum: 2277bbeb3adSMilanka Ringwald case UnitExponent: 2287bbeb3adSMilanka Ringwald case Unit: 2297bbeb3adSMilanka Ringwald case Push: 2307bbeb3adSMilanka Ringwald case Pop: 2317bbeb3adSMilanka Ringwald break; 2327bbeb3adSMilanka Ringwald 23312ccb71bSMatthias Ringwald default: 2347bbeb3adSMilanka Ringwald btstack_assert(false); 23512ccb71bSMatthias Ringwald break; 23612ccb71bSMatthias Ringwald } 23712ccb71bSMatthias Ringwald } 23812ccb71bSMatthias Ringwald 239*c4241e61SMatthias Ringwald static void hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator){ 2401a05cde1SMatthias Ringwald bool have_usage_min = false; 2411a05cde1SMatthias Ringwald bool have_usage_max = false; 242*c4241e61SMatthias Ringwald main_iterator->usage_range = false; 24341afcabaSMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 244*c4241e61SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, &main_iterator->descriptor[main_iterator->usage_pos], main_iterator->descriptor_len - main_iterator->usage_pos); 245*c4241e61SMatthias Ringwald while ((main_iterator->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){ 24641afcabaSMatthias Ringwald hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator); 2470e588213SMatthias Ringwald if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){ 248*c4241e61SMatthias Ringwald main_iterator->usage_page = usage_item.item_value; 24912ccb71bSMatthias Ringwald } 25012ccb71bSMatthias Ringwald if (usage_item.item_type == Local){ 251*c4241e61SMatthias Ringwald uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((main_iterator->usage_page << 16u) | usage_item.item_value); 25212ccb71bSMatthias Ringwald switch (usage_item.item_tag){ 25312ccb71bSMatthias Ringwald case Usage: 254*c4241e61SMatthias Ringwald main_iterator->available_usages = 1; 255*c4241e61SMatthias Ringwald main_iterator->usage_minimum = usage_value; 25612ccb71bSMatthias Ringwald break; 25712ccb71bSMatthias Ringwald case UsageMinimum: 258*c4241e61SMatthias Ringwald main_iterator->usage_minimum = usage_value; 2591a05cde1SMatthias Ringwald have_usage_min = true; 26012ccb71bSMatthias Ringwald break; 26112ccb71bSMatthias Ringwald case UsageMaximum: 262*c4241e61SMatthias Ringwald main_iterator->usage_maximum = usage_value; 2631a05cde1SMatthias Ringwald have_usage_max = true; 26412ccb71bSMatthias Ringwald break; 26512ccb71bSMatthias Ringwald default: 26612ccb71bSMatthias Ringwald break; 26712ccb71bSMatthias Ringwald } 2681a05cde1SMatthias Ringwald if (have_usage_min && have_usage_max){ 269*c4241e61SMatthias Ringwald main_iterator->available_usages = main_iterator->usage_maximum - main_iterator->usage_minimum + 1u; 270*c4241e61SMatthias Ringwald main_iterator->usage_range = true; 271*c4241e61SMatthias Ringwald if (main_iterator->available_usages < main_iterator->required_usages){ 272*c4241e61SMatthias 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); 273dfb01e77SMatthias Ringwald } 27412ccb71bSMatthias Ringwald } 27512ccb71bSMatthias Ringwald } 27612ccb71bSMatthias Ringwald } 277*c4241e61SMatthias Ringwald main_iterator->usage_pos += iterator.descriptor_pos; 27812ccb71bSMatthias Ringwald } 27912ccb71bSMatthias Ringwald 28012ccb71bSMatthias Ringwald 28112ccb71bSMatthias Ringwald // PUBLIC API 28212ccb71bSMatthias Ringwald 2830ba748f2SMatthias Ringwald // HID Descriptor Iterator 284fada7179SMilanka Ringwald 2850c7e4a25SMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 2860c7e4a25SMatthias Ringwald iterator->descriptor = hid_descriptor; 2870c7e4a25SMatthias Ringwald iterator->descriptor_pos = 0; 2880c7e4a25SMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 2890c7e4a25SMatthias Ringwald iterator->item_ready = false; 2900c7e4a25SMatthias Ringwald iterator->valid = true; 2910c7e4a25SMatthias Ringwald } 2920c7e4a25SMatthias Ringwald 2930c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){ 2943cc55c4aSMatthias Ringwald if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){ 2950c7e4a25SMatthias Ringwald uint16_t item_len = iterator->descriptor_len - iterator->descriptor_pos; 2960c7e4a25SMatthias Ringwald const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos]; 2970c7e4a25SMatthias Ringwald bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len); 2980c7e4a25SMatthias Ringwald if (ok){ 2990c7e4a25SMatthias Ringwald iterator->item_ready = true; 3000c7e4a25SMatthias Ringwald } else { 3010c7e4a25SMatthias Ringwald iterator->valid = false; 3020c7e4a25SMatthias Ringwald } 3030c7e4a25SMatthias Ringwald } 3040c7e4a25SMatthias Ringwald return iterator->item_ready; 3050c7e4a25SMatthias Ringwald } 3060c7e4a25SMatthias Ringwald 3070c7e4a25SMatthias Ringwald const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){ 3083cc55c4aSMatthias Ringwald iterator->descriptor_pos += iterator->descriptor_item.item_size; 3090c7e4a25SMatthias Ringwald iterator->item_ready = false; 3100c7e4a25SMatthias Ringwald return &iterator->descriptor_item; 3110c7e4a25SMatthias Ringwald } 3120c7e4a25SMatthias Ringwald 3130c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){ 3140c7e4a25SMatthias Ringwald return iterator->valid; 3150c7e4a25SMatthias Ringwald } 3160c7e4a25SMatthias Ringwald 3170ba748f2SMatthias Ringwald 3182cca3b08SMatthias Ringwald int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor, 3192cca3b08SMatthias Ringwald uint16_t hid_descriptor_len) { 320fada7179SMilanka Ringwald int total_report_size = 0; 321fada7179SMilanka Ringwald int report_size = 0; 322fada7179SMilanka Ringwald int report_count = 0; 323279225e6SMatthias Ringwald int current_report_id = HID_REPORT_ID_UNDEFINED; 324fada7179SMilanka Ringwald 325960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 326960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 327960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 328960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 329fada7179SMilanka Ringwald int valid_report_type = 0; 330960622b0SMatthias Ringwald switch (item->item_type) { 331fada7179SMilanka Ringwald case Global: 332960622b0SMatthias Ringwald switch ((GlobalItemTag) item->item_tag) { 333fada7179SMilanka Ringwald case ReportID: 334960622b0SMatthias Ringwald current_report_id = item->item_value; 335fada7179SMilanka Ringwald break; 336fada7179SMilanka Ringwald case ReportCount: 337960622b0SMatthias Ringwald report_count = item->item_value; 338fada7179SMilanka Ringwald break; 339fada7179SMilanka Ringwald case ReportSize: 340960622b0SMatthias Ringwald report_size = item->item_value; 341fada7179SMilanka Ringwald break; 342fada7179SMilanka Ringwald default: 343fada7179SMilanka Ringwald break; 344fada7179SMilanka Ringwald } 345fada7179SMilanka Ringwald break; 346fada7179SMilanka Ringwald case Main: 347fada7179SMilanka Ringwald if (current_report_id != report_id) break; 348960622b0SMatthias Ringwald switch ((MainItemTag) item->item_tag) { 349fada7179SMilanka Ringwald case Input: 350662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_INPUT) break; 351fada7179SMilanka Ringwald valid_report_type = 1; 352fada7179SMilanka Ringwald break; 353fada7179SMilanka Ringwald case Output: 354662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_OUTPUT) break; 355fada7179SMilanka Ringwald valid_report_type = 1; 356fada7179SMilanka Ringwald break; 357fada7179SMilanka Ringwald case Feature: 358662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_FEATURE) break; 359fada7179SMilanka Ringwald valid_report_type = 1; 360fada7179SMilanka Ringwald break; 361fada7179SMilanka Ringwald default: 362fada7179SMilanka Ringwald break; 363fada7179SMilanka Ringwald } 364fada7179SMilanka Ringwald if (!valid_report_type) break; 365fada7179SMilanka Ringwald total_report_size += report_count * report_size; 366fada7179SMilanka Ringwald break; 367fada7179SMilanka Ringwald default: 368fada7179SMilanka Ringwald break; 369fada7179SMilanka Ringwald } 37015cf8612Sx0rloser if (total_report_size > 0 && current_report_id != report_id) break; 371fada7179SMilanka Ringwald } 372960622b0SMatthias Ringwald 373960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)){ 374fada7179SMilanka Ringwald return (total_report_size + 7) / 8; 375960622b0SMatthias Ringwald } else { 376960622b0SMatthias Ringwald return 0; 377960622b0SMatthias Ringwald } 378fada7179SMilanka Ringwald } 379dbcaefc7SMilanka Ringwald 380279225e6SMatthias 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){ 381279225e6SMatthias Ringwald uint16_t current_report_id = HID_REPORT_ID_UNDEFINED; 382960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 383960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 384279225e6SMatthias Ringwald bool report_id_found = false; 385960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 386960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 387960622b0SMatthias Ringwald switch (item->item_type){ 388dbcaefc7SMilanka Ringwald case Global: 389960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 390dbcaefc7SMilanka Ringwald case ReportID: 391960622b0SMatthias Ringwald current_report_id = item->item_value; 392279225e6SMatthias Ringwald if (current_report_id == report_id) { 393279225e6SMatthias Ringwald report_id_found = true; 394279225e6SMatthias Ringwald } 395dbcaefc7SMilanka Ringwald default: 396dbcaefc7SMilanka Ringwald break; 397dbcaefc7SMilanka Ringwald } 398dbcaefc7SMilanka Ringwald break; 399dbcaefc7SMilanka Ringwald default: 400dbcaefc7SMilanka Ringwald break; 401dbcaefc7SMilanka Ringwald } 402dbcaefc7SMilanka Ringwald } 403960622b0SMatthias Ringwald 404960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)) { 405279225e6SMatthias Ringwald if (report_id_found){ 406279225e6SMatthias Ringwald return HID_REPORT_ID_VALID; 407dbcaefc7SMilanka Ringwald } 408279225e6SMatthias Ringwald if ((report_id == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) { 409279225e6SMatthias Ringwald return HID_REPORT_ID_VALID; 410279225e6SMatthias Ringwald } 411279225e6SMatthias Ringwald return HID_REPORT_ID_UNDECLARED; 412960622b0SMatthias Ringwald } else { 413960622b0SMatthias Ringwald return HID_REPORT_ID_INVALID; 414960622b0SMatthias Ringwald } 415960622b0SMatthias Ringwald } 416dbcaefc7SMilanka Ringwald 4172cca3b08SMatthias Ringwald bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) { 418960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 419960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 420960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 421960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 422960622b0SMatthias Ringwald switch (item->item_type){ 423dbcaefc7SMilanka Ringwald case Global: 424960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 425dbcaefc7SMilanka Ringwald case ReportID: 426c17118d0SMatthias Ringwald return true; 427dbcaefc7SMilanka Ringwald default: 428dbcaefc7SMilanka Ringwald break; 429dbcaefc7SMilanka Ringwald } 430dbcaefc7SMilanka Ringwald break; 431dbcaefc7SMilanka Ringwald default: 432dbcaefc7SMilanka Ringwald break; 433dbcaefc7SMilanka Ringwald } 434dbcaefc7SMilanka Ringwald } 435c17118d0SMatthias Ringwald return false; 436dbcaefc7SMilanka Ringwald } 437cdb96ae5SMatthias Ringwald 4380ba748f2SMatthias Ringwald // HID Descriptor Usage Iterator 4390ba748f2SMatthias Ringwald 440*c4241e61SMatthias Ringwald static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 441*c4241e61SMatthias Ringwald hid_pretty_print_item(iterator, item); 442cdb96ae5SMatthias Ringwald int valid_field = 0; 443cdb96ae5SMatthias Ringwald uint16_t report_id_before; 444cdb96ae5SMatthias Ringwald switch ((TagType)item->item_type){ 445cdb96ae5SMatthias Ringwald case Main: 446cdb96ae5SMatthias Ringwald valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag, 447*c4241e61SMatthias Ringwald iterator->report_type); 448cdb96ae5SMatthias Ringwald break; 449cdb96ae5SMatthias Ringwald case Global: 450*c4241e61SMatthias Ringwald report_id_before = iterator->global_report_id; 451*c4241e61SMatthias Ringwald btstack_hid_handle_global_item(iterator, item); 452cdb96ae5SMatthias Ringwald // track record id for report handling, reset report position 453*c4241e61SMatthias Ringwald if (report_id_before != iterator->global_report_id){ 454*c4241e61SMatthias Ringwald iterator->report_pos_in_bit = 8u; 455cdb96ae5SMatthias Ringwald } 456cdb96ae5SMatthias Ringwald break; 457cdb96ae5SMatthias Ringwald case Local: 458cdb96ae5SMatthias Ringwald case Reserved: 459cdb96ae5SMatthias Ringwald break; 460cdb96ae5SMatthias Ringwald default: 461cdb96ae5SMatthias Ringwald btstack_assert(false); 462cdb96ae5SMatthias Ringwald break; 463cdb96ae5SMatthias Ringwald } 464cdb96ae5SMatthias Ringwald if (!valid_field) return; 465cdb96ae5SMatthias Ringwald 466cdb96ae5SMatthias Ringwald // handle constant fields used for padding 467cdb96ae5SMatthias Ringwald if (item->item_value & 1){ 468*c4241e61SMatthias Ringwald int item_bits = iterator->global_report_size * iterator->global_report_count; 469cdb96ae5SMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 470cdb96ae5SMatthias Ringwald log_info("- Skip %u constant bits", item_bits); 471cdb96ae5SMatthias Ringwald #endif 472*c4241e61SMatthias Ringwald iterator->report_pos_in_bit += item_bits; 473cdb96ae5SMatthias Ringwald return; 474cdb96ae5SMatthias Ringwald } 475cdb96ae5SMatthias Ringwald // Empty Item 476*c4241e61SMatthias Ringwald if (iterator->global_report_count == 0u) return; 477cdb96ae5SMatthias Ringwald // let's start 478*c4241e61SMatthias Ringwald iterator->required_usages = iterator->global_report_count; 479cdb96ae5SMatthias Ringwald } 480cdb96ae5SMatthias Ringwald 481*c4241e61SMatthias Ringwald static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) { 482*c4241e61SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){ 483*c4241e61SMatthias Ringwald iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator); 484cdb96ae5SMatthias Ringwald 485*c4241e61SMatthias Ringwald btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item); 486cdb96ae5SMatthias Ringwald 487*c4241e61SMatthias Ringwald if (iterator->required_usages){ 488*c4241e61SMatthias Ringwald hid_find_next_usage(iterator); 489*c4241e61SMatthias Ringwald if (iterator->available_usages) { 490*c4241e61SMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 491cdb96ae5SMatthias Ringwald return; 492cdb96ae5SMatthias Ringwald } else { 493cdb96ae5SMatthias Ringwald log_debug("no usages found"); 494*c4241e61SMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 495cdb96ae5SMatthias Ringwald return; 496cdb96ae5SMatthias Ringwald } 497cdb96ae5SMatthias Ringwald } else { 498*c4241e61SMatthias Ringwald if ((TagType) (&iterator->descriptor_item)->item_type == Main) { 499cdb96ae5SMatthias Ringwald // reset usage 500*c4241e61SMatthias Ringwald iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos; 501*c4241e61SMatthias Ringwald iterator->usage_page = iterator->global_usage_page; 502cdb96ae5SMatthias Ringwald } 503cdb96ae5SMatthias Ringwald } 504cdb96ae5SMatthias Ringwald } 505cdb96ae5SMatthias Ringwald // end of descriptor 506*c4241e61SMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 507cdb96ae5SMatthias Ringwald } 508cdb96ae5SMatthias Ringwald 509*c4241e61SMatthias 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){ 510*c4241e61SMatthias Ringwald memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t)); 511cdb96ae5SMatthias Ringwald 512*c4241e61SMatthias Ringwald iterator->descriptor = hid_descriptor; 513*c4241e61SMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 514*c4241e61SMatthias Ringwald iterator->report_type = hid_report_type; 515*c4241e61SMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 516*c4241e61SMatthias Ringwald iterator->global_report_id = HID_REPORT_ID_UNDEFINED; 517*c4241e61SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len); 518cdb96ae5SMatthias Ringwald } 519cdb96ae5SMatthias Ringwald 520*c4241e61SMatthias Ringwald bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){ 521*c4241e61SMatthias Ringwald while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){ 522*c4241e61SMatthias Ringwald btstack_hid_usage_iterator_find_next_usage(iterator); 52387256a6aSMatthias Ringwald } 524*c4241e61SMatthias Ringwald return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 525cdb96ae5SMatthias Ringwald } 526cdb96ae5SMatthias Ringwald 527*c4241e61SMatthias Ringwald void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){ 528cdb96ae5SMatthias Ringwald // cache current values 529cdb96ae5SMatthias Ringwald memset(item, 0, sizeof(btstack_hid_usage_item_t)); 530*c4241e61SMatthias Ringwald item->size = iterator->global_report_size; 531*c4241e61SMatthias Ringwald item->report_id = iterator->global_report_id; 532*c4241e61SMatthias Ringwald item->usage_page = iterator->usage_minimum >> 16; 533*c4241e61SMatthias Ringwald item->bit_pos = iterator->report_pos_in_bit; 534cdb96ae5SMatthias Ringwald 535*c4241e61SMatthias Ringwald bool is_variable = (iterator->descriptor_item.item_value & 2) != 0; 536cdb96ae5SMatthias Ringwald if (is_variable){ 537*c4241e61SMatthias Ringwald item->usage = iterator->usage_minimum & 0xffffu; 538cdb96ae5SMatthias Ringwald } 539*c4241e61SMatthias Ringwald iterator->required_usages--; 540*c4241e61SMatthias Ringwald iterator->report_pos_in_bit += iterator->global_report_size; 541cdb96ae5SMatthias Ringwald 5420ba748f2SMatthias Ringwald // cache descriptor item and 543*c4241e61SMatthias Ringwald item->descriptor_item = iterator->descriptor_item; 544*c4241e61SMatthias Ringwald item->global_logical_minimum = iterator->global_logical_minimum; 5450ba748f2SMatthias Ringwald 546cdb96ae5SMatthias Ringwald // next usage 547cdb96ae5SMatthias Ringwald if (is_variable){ 548*c4241e61SMatthias Ringwald iterator->usage_minimum++; 549*c4241e61SMatthias Ringwald iterator->available_usages--; 550*c4241e61SMatthias Ringwald if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){ 551cdb96ae5SMatthias Ringwald // usage min - max range smaller than report count, ignore remaining bit in report 552cdb96ae5SMatthias Ringwald log_debug("Ignoring %u items without Usage", parser->required_usages); 553*c4241e61SMatthias Ringwald iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages; 554*c4241e61SMatthias Ringwald iterator->required_usages = 0; 555cdb96ae5SMatthias Ringwald } 556cdb96ae5SMatthias Ringwald } else { 557*c4241e61SMatthias Ringwald if (iterator->required_usages == 0u){ 558*c4241e61SMatthias Ringwald iterator->available_usages = 0; 559cdb96ae5SMatthias Ringwald } 560cdb96ae5SMatthias Ringwald } 56187256a6aSMatthias Ringwald 562*c4241e61SMatthias Ringwald if (iterator->available_usages) { 563cdb96ae5SMatthias Ringwald return; 564cdb96ae5SMatthias Ringwald } 565*c4241e61SMatthias Ringwald if (iterator->required_usages == 0u){ 566*c4241e61SMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 567cdb96ae5SMatthias Ringwald } else { 568*c4241e61SMatthias Ringwald hid_find_next_usage(iterator); 569*c4241e61SMatthias Ringwald if (iterator->available_usages == 0u) { 570*c4241e61SMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 571cdb96ae5SMatthias Ringwald } 572cdb96ae5SMatthias Ringwald } 573cdb96ae5SMatthias Ringwald } 574cdb96ae5SMatthias Ringwald 5750ba748f2SMatthias Ringwald 5760ba748f2SMatthias Ringwald // HID Report Parser 5770ba748f2SMatthias Ringwald 5780ba748f2SMatthias 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){ 579*c4241e61SMatthias Ringwald btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type); 5800ba748f2SMatthias Ringwald parser->report = hid_report; 5810ba748f2SMatthias Ringwald parser->report_len = hid_report_len; 5820ba748f2SMatthias Ringwald parser->have_report_usage_ready = false; 5830ba748f2SMatthias Ringwald } 5840ba748f2SMatthias Ringwald 5850ba748f2SMatthias Ringwald /** 5860ba748f2SMatthias Ringwald * @brief Checks if more fields are available 5870ba748f2SMatthias Ringwald * @param parser 5880ba748f2SMatthias Ringwald */ 5890ba748f2SMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 590*c4241e61SMatthias Ringwald while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){ 591*c4241e61SMatthias Ringwald btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor__usage_item); 5920ba748f2SMatthias Ringwald // ignore usages for other report ids 593279225e6SMatthias Ringwald if (parser->descriptor__usage_item.report_id != HID_REPORT_ID_UNDEFINED){ 5940ba748f2SMatthias Ringwald if (parser->descriptor__usage_item.report_id != parser->report[0]){ 5950ba748f2SMatthias Ringwald continue; 5960ba748f2SMatthias Ringwald } 5970ba748f2SMatthias Ringwald } 5980ba748f2SMatthias Ringwald parser->have_report_usage_ready = true; 5990ba748f2SMatthias Ringwald } 6000ba748f2SMatthias Ringwald return parser->have_report_usage_ready; 6010ba748f2SMatthias Ringwald } 6020ba748f2SMatthias Ringwald 6030ba748f2SMatthias Ringwald /** 6040ba748f2SMatthias Ringwald * @brief Get next field 6050ba748f2SMatthias Ringwald * @param parser 6060ba748f2SMatthias Ringwald * @param usage_page 6070ba748f2SMatthias Ringwald * @param usage 6080ba748f2SMatthias Ringwald * @param value provided in HID report 6090ba748f2SMatthias Ringwald */ 6100ba748f2SMatthias Ringwald 6110ba748f2SMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 6120ba748f2SMatthias Ringwald 6130ba748f2SMatthias Ringwald // fetch data from descriptor usage item 6140ba748f2SMatthias Ringwald uint16_t bit_pos = parser->descriptor__usage_item.bit_pos; 6150ba748f2SMatthias Ringwald uint16_t size = parser->descriptor__usage_item.size; 6160ba748f2SMatthias Ringwald *usage_page = parser->descriptor__usage_item.usage_page; 6170ba748f2SMatthias Ringwald *usage = parser->descriptor__usage_item.usage; 6180ba748f2SMatthias Ringwald 6190ba748f2SMatthias Ringwald 6200ba748f2SMatthias Ringwald // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 6210ba748f2SMatthias Ringwald bool is_variable = (parser->descriptor__usage_item.descriptor_item.item_value & 2) != 0; 6220ba748f2SMatthias Ringwald bool is_signed = parser->descriptor__usage_item.global_logical_minimum < 0; 6230ba748f2SMatthias Ringwald int pos_start = btstack_min( bit_pos >> 3, parser->report_len); 6240ba748f2SMatthias Ringwald int pos_end = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len); 6250ba748f2SMatthias Ringwald int bytes_to_read = pos_end - pos_start + 1; 6260ba748f2SMatthias Ringwald 6270ba748f2SMatthias Ringwald int i; 6280ba748f2SMatthias Ringwald uint32_t multi_byte_value = 0; 6290ba748f2SMatthias Ringwald for (i=0;i < bytes_to_read;i++){ 6300ba748f2SMatthias Ringwald multi_byte_value |= parser->report[pos_start+i] << (i*8); 6310ba748f2SMatthias Ringwald } 6320ba748f2SMatthias Ringwald uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u); 6330ba748f2SMatthias 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); 6340ba748f2SMatthias Ringwald if (is_variable){ 6350ba748f2SMatthias Ringwald if (is_signed && (unsigned_value & (1u<<(size-1u)))){ 6360ba748f2SMatthias Ringwald *value = unsigned_value - (1u<<size); 6370ba748f2SMatthias Ringwald } else { 6380ba748f2SMatthias Ringwald *value = unsigned_value; 6390ba748f2SMatthias Ringwald } 6400ba748f2SMatthias Ringwald } else { 6410ba748f2SMatthias Ringwald *usage = unsigned_value; 6420ba748f2SMatthias Ringwald *value = 1; 6430ba748f2SMatthias Ringwald } 6440ba748f2SMatthias Ringwald 6450ba748f2SMatthias Ringwald parser->have_report_usage_ready = false; 6460ba748f2SMatthias Ringwald } 647