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 119*5dd091fdSMatthias Ringwald // HID Descriptor Iterator 12012ccb71bSMatthias Ringwald 12112ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value 122*5dd091fdSMatthias 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 162*5dd091fdSMatthias Ringwald static void btstack_hid_descriptor_iterator_pretty_print_item(btstack_hid_descriptor_iterator_t * iterator, hid_descriptor_item_t * item){ 163*5dd091fdSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 164*5dd091fdSMatthias Ringwald const char ** item_tag_table; 165*5dd091fdSMatthias Ringwald switch ((TagType)item->item_type){ 166*5dd091fdSMatthias Ringwald case Main: 167*5dd091fdSMatthias Ringwald item_tag_table = main_tags; 168*5dd091fdSMatthias Ringwald break; 169*5dd091fdSMatthias Ringwald case Global: 170*5dd091fdSMatthias Ringwald item_tag_table = global_tags; 171*5dd091fdSMatthias Ringwald break; 172*5dd091fdSMatthias Ringwald case Local: 173*5dd091fdSMatthias Ringwald item_tag_table = local_tags; 174*5dd091fdSMatthias Ringwald break; 175*5dd091fdSMatthias Ringwald default: 176*5dd091fdSMatthias Ringwald item_tag_table = NULL; 177*5dd091fdSMatthias Ringwald break; 178*5dd091fdSMatthias Ringwald } 179*5dd091fdSMatthias Ringwald const char * item_tag_name = "Invalid"; 180*5dd091fdSMatthias Ringwald if (item_tag_table){ 181*5dd091fdSMatthias Ringwald item_tag_name = item_tag_table[item->item_tag]; 182*5dd091fdSMatthias Ringwald } 183*5dd091fdSMatthias Ringwald log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 184*5dd091fdSMatthias Ringwald printf("%-15s (%-6s) // %02x 0x%0008x\n", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value); 185*5dd091fdSMatthias Ringwald #else 186*5dd091fdSMatthias Ringwald UNUSED(iterator); 187*5dd091fdSMatthias Ringwald UNUSED(item); 188*5dd091fdSMatthias Ringwald #endif 189*5dd091fdSMatthias Ringwald } 190*5dd091fdSMatthias Ringwald 191*5dd091fdSMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 192*5dd091fdSMatthias Ringwald iterator->descriptor = hid_descriptor; 193*5dd091fdSMatthias Ringwald iterator->descriptor_pos = 0; 194*5dd091fdSMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 195*5dd091fdSMatthias Ringwald iterator->item_ready = false; 196*5dd091fdSMatthias Ringwald iterator->valid = true; 197*5dd091fdSMatthias Ringwald } 198*5dd091fdSMatthias Ringwald 199*5dd091fdSMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){ 200*5dd091fdSMatthias Ringwald if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){ 201*5dd091fdSMatthias Ringwald uint16_t item_len = iterator->descriptor_len - iterator->descriptor_pos; 202*5dd091fdSMatthias Ringwald const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos]; 203*5dd091fdSMatthias Ringwald bool ok = btstack_hid_descriptor_parse_item(&iterator->descriptor_item, item_data, item_len); 204*5dd091fdSMatthias Ringwald btstack_hid_descriptor_iterator_pretty_print_item(iterator, &iterator->descriptor_item); 205*5dd091fdSMatthias Ringwald if (ok){ 206*5dd091fdSMatthias Ringwald iterator->item_ready = true; 207*5dd091fdSMatthias Ringwald } else { 208*5dd091fdSMatthias Ringwald iterator->valid = false; 209*5dd091fdSMatthias Ringwald } 210*5dd091fdSMatthias Ringwald } 211*5dd091fdSMatthias Ringwald return iterator->item_ready; 212*5dd091fdSMatthias Ringwald } 213*5dd091fdSMatthias Ringwald 214*5dd091fdSMatthias Ringwald const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){ 215*5dd091fdSMatthias Ringwald iterator->descriptor_pos += iterator->descriptor_item.item_size; 216*5dd091fdSMatthias Ringwald iterator->item_ready = false; 217*5dd091fdSMatthias Ringwald return &iterator->descriptor_item; 218*5dd091fdSMatthias Ringwald } 219*5dd091fdSMatthias Ringwald 220*5dd091fdSMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){ 221*5dd091fdSMatthias Ringwald return iterator->valid; 222*5dd091fdSMatthias Ringwald } 223*5dd091fdSMatthias Ringwald 224*5dd091fdSMatthias Ringwald // HID Descriptor Usage Iterator 225*5dd091fdSMatthias Ringwald 226*5dd091fdSMatthias 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 239*5dd091fdSMatthias 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 275*5dd091fdSMatthias 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 316*5dd091fdSMatthias Ringwald static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){ 317*5dd091fdSMatthias Ringwald int valid_field = 0; 318*5dd091fdSMatthias Ringwald uint16_t report_id_before; 319*5dd091fdSMatthias Ringwald switch ((TagType)item->item_type){ 320*5dd091fdSMatthias Ringwald case Main: 321*5dd091fdSMatthias Ringwald valid_field = btstack_hid_usage_iterator_main_item_tag_matches_report_type((MainItemTag) item->item_tag, 322*5dd091fdSMatthias Ringwald iterator->report_type); 323*5dd091fdSMatthias Ringwald break; 324*5dd091fdSMatthias Ringwald case Global: 325*5dd091fdSMatthias Ringwald report_id_before = iterator->global_report_id; 326*5dd091fdSMatthias Ringwald btstack_hid_usage_iterator_handle_global_item(iterator, item); 327*5dd091fdSMatthias Ringwald // track record id for report handling, reset report position 328*5dd091fdSMatthias Ringwald if (report_id_before != iterator->global_report_id){ 329*5dd091fdSMatthias Ringwald iterator->report_pos_in_bit = 8u; 330*5dd091fdSMatthias Ringwald } 331*5dd091fdSMatthias Ringwald break; 332*5dd091fdSMatthias Ringwald case Local: 333*5dd091fdSMatthias Ringwald case Reserved: 334*5dd091fdSMatthias Ringwald break; 335*5dd091fdSMatthias Ringwald default: 336*5dd091fdSMatthias Ringwald btstack_assert(false); 337*5dd091fdSMatthias Ringwald break; 338*5dd091fdSMatthias Ringwald } 339*5dd091fdSMatthias Ringwald if (!valid_field) return; 34012ccb71bSMatthias Ringwald 341*5dd091fdSMatthias Ringwald // handle constant fields used for padding 342*5dd091fdSMatthias Ringwald if (item->item_value & 1){ 343*5dd091fdSMatthias Ringwald int item_bits = iterator->global_report_size * iterator->global_report_count; 344*5dd091fdSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT 345*5dd091fdSMatthias Ringwald log_info("- Skip %u constant bits", item_bits); 346*5dd091fdSMatthias Ringwald #endif 347*5dd091fdSMatthias Ringwald iterator->report_pos_in_bit += item_bits; 348*5dd091fdSMatthias Ringwald return; 349*5dd091fdSMatthias Ringwald } 350*5dd091fdSMatthias Ringwald // Empty Item 351*5dd091fdSMatthias Ringwald if (iterator->global_report_count == 0u) return; 352*5dd091fdSMatthias Ringwald // let's start 353*5dd091fdSMatthias Ringwald iterator->required_usages = iterator->global_report_count; 3540c7e4a25SMatthias Ringwald } 3550c7e4a25SMatthias Ringwald 356*5dd091fdSMatthias Ringwald static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) { 357*5dd091fdSMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){ 358*5dd091fdSMatthias Ringwald iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator); 359*5dd091fdSMatthias Ringwald 360*5dd091fdSMatthias Ringwald btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item); 361*5dd091fdSMatthias Ringwald 362*5dd091fdSMatthias Ringwald if (iterator->required_usages){ 363*5dd091fdSMatthias Ringwald btstack_usage_iterator_hid_find_next_usage(iterator); 364*5dd091fdSMatthias Ringwald if (iterator->available_usages) { 365*5dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 366*5dd091fdSMatthias Ringwald return; 3670c7e4a25SMatthias Ringwald } else { 368*5dd091fdSMatthias Ringwald log_debug("no usages found"); 369*5dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 370*5dd091fdSMatthias Ringwald return; 371*5dd091fdSMatthias Ringwald } 372*5dd091fdSMatthias Ringwald } else { 373*5dd091fdSMatthias Ringwald if ((TagType) (&iterator->descriptor_item)->item_type == Main) { 374*5dd091fdSMatthias Ringwald // reset usage 375*5dd091fdSMatthias Ringwald iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos; 376*5dd091fdSMatthias Ringwald iterator->usage_page = iterator->global_usage_page; 3770c7e4a25SMatthias Ringwald } 3780c7e4a25SMatthias Ringwald } 379*5dd091fdSMatthias Ringwald } 380*5dd091fdSMatthias Ringwald // end of descriptor 381*5dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 3820c7e4a25SMatthias Ringwald } 3830c7e4a25SMatthias Ringwald 384*5dd091fdSMatthias 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){ 385*5dd091fdSMatthias Ringwald memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t)); 386*5dd091fdSMatthias Ringwald 387*5dd091fdSMatthias Ringwald iterator->descriptor = hid_descriptor; 388*5dd091fdSMatthias Ringwald iterator->descriptor_len = hid_descriptor_len; 389*5dd091fdSMatthias Ringwald iterator->report_type = hid_report_type; 390*5dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 391*5dd091fdSMatthias Ringwald iterator->global_report_id = HID_REPORT_ID_UNDEFINED; 392*5dd091fdSMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len); 3930c7e4a25SMatthias Ringwald } 3940c7e4a25SMatthias Ringwald 395*5dd091fdSMatthias Ringwald bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){ 396*5dd091fdSMatthias Ringwald while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){ 397*5dd091fdSMatthias Ringwald btstack_hid_usage_iterator_find_next_usage(iterator); 398*5dd091fdSMatthias Ringwald } 399*5dd091fdSMatthias Ringwald return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE; 4000c7e4a25SMatthias Ringwald } 4010c7e4a25SMatthias Ringwald 402*5dd091fdSMatthias Ringwald void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){ 403*5dd091fdSMatthias Ringwald // cache current values 404*5dd091fdSMatthias Ringwald memset(item, 0, sizeof(btstack_hid_usage_item_t)); 405*5dd091fdSMatthias Ringwald item->size = iterator->global_report_size; 406*5dd091fdSMatthias Ringwald item->report_id = iterator->global_report_id; 407*5dd091fdSMatthias Ringwald item->usage_page = iterator->usage_minimum >> 16; 408*5dd091fdSMatthias Ringwald item->bit_pos = iterator->report_pos_in_bit; 409*5dd091fdSMatthias Ringwald 410*5dd091fdSMatthias Ringwald bool is_variable = (iterator->descriptor_item.item_value & 2) != 0; 411*5dd091fdSMatthias Ringwald if (is_variable){ 412*5dd091fdSMatthias Ringwald item->usage = iterator->usage_minimum & 0xffffu; 413*5dd091fdSMatthias Ringwald } 414*5dd091fdSMatthias Ringwald iterator->required_usages--; 415*5dd091fdSMatthias Ringwald iterator->report_pos_in_bit += iterator->global_report_size; 416*5dd091fdSMatthias Ringwald 417*5dd091fdSMatthias Ringwald // cache descriptor item and 418*5dd091fdSMatthias Ringwald item->descriptor_item = iterator->descriptor_item; 419*5dd091fdSMatthias Ringwald item->global_logical_minimum = iterator->global_logical_minimum; 420*5dd091fdSMatthias Ringwald 421*5dd091fdSMatthias Ringwald // next usage 422*5dd091fdSMatthias Ringwald if (is_variable){ 423*5dd091fdSMatthias Ringwald iterator->usage_minimum++; 424*5dd091fdSMatthias Ringwald iterator->available_usages--; 425*5dd091fdSMatthias Ringwald if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){ 426*5dd091fdSMatthias Ringwald // usage min - max range smaller than report count, ignore remaining bit in report 427*5dd091fdSMatthias Ringwald log_debug("Ignoring %u items without Usage", parser->required_usages); 428*5dd091fdSMatthias Ringwald iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages; 429*5dd091fdSMatthias Ringwald iterator->required_usages = 0; 430*5dd091fdSMatthias Ringwald } 431*5dd091fdSMatthias Ringwald } else { 432*5dd091fdSMatthias Ringwald if (iterator->required_usages == 0u){ 433*5dd091fdSMatthias Ringwald iterator->available_usages = 0; 434*5dd091fdSMatthias Ringwald } 435*5dd091fdSMatthias Ringwald } 436*5dd091fdSMatthias Ringwald 437*5dd091fdSMatthias Ringwald if (iterator->available_usages) { 438*5dd091fdSMatthias Ringwald return; 439*5dd091fdSMatthias Ringwald } 440*5dd091fdSMatthias Ringwald if (iterator->required_usages == 0u){ 441*5dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM; 442*5dd091fdSMatthias Ringwald } else { 443*5dd091fdSMatthias Ringwald btstack_usage_iterator_hid_find_next_usage(iterator); 444*5dd091fdSMatthias Ringwald if (iterator->available_usages == 0u) { 445*5dd091fdSMatthias Ringwald iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE; 446*5dd091fdSMatthias Ringwald } 447*5dd091fdSMatthias Ringwald } 448*5dd091fdSMatthias Ringwald } 449*5dd091fdSMatthias Ringwald 450*5dd091fdSMatthias Ringwald 451*5dd091fdSMatthias Ringwald // HID Report Parser 452*5dd091fdSMatthias Ringwald 453*5dd091fdSMatthias 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){ 454*5dd091fdSMatthias Ringwald btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type); 455*5dd091fdSMatthias Ringwald parser->report = hid_report; 456*5dd091fdSMatthias Ringwald parser->report_len = hid_report_len; 457*5dd091fdSMatthias Ringwald parser->have_report_usage_ready = false; 458*5dd091fdSMatthias Ringwald } 459*5dd091fdSMatthias Ringwald 460*5dd091fdSMatthias Ringwald /** 461*5dd091fdSMatthias Ringwald * @brief Checks if more fields are available 462*5dd091fdSMatthias Ringwald * @param parser 463*5dd091fdSMatthias Ringwald */ 464*5dd091fdSMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 465*5dd091fdSMatthias Ringwald while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){ 466*5dd091fdSMatthias Ringwald btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor__usage_item); 467*5dd091fdSMatthias Ringwald // ignore usages for other report ids 468*5dd091fdSMatthias Ringwald if (parser->descriptor__usage_item.report_id != HID_REPORT_ID_UNDEFINED){ 469*5dd091fdSMatthias Ringwald if (parser->descriptor__usage_item.report_id != parser->report[0]){ 470*5dd091fdSMatthias Ringwald continue; 471*5dd091fdSMatthias Ringwald } 472*5dd091fdSMatthias Ringwald } 473*5dd091fdSMatthias Ringwald parser->have_report_usage_ready = true; 474*5dd091fdSMatthias Ringwald } 475*5dd091fdSMatthias Ringwald return parser->have_report_usage_ready; 476*5dd091fdSMatthias Ringwald } 477*5dd091fdSMatthias Ringwald 478*5dd091fdSMatthias Ringwald /** 479*5dd091fdSMatthias Ringwald * @brief Get next field 480*5dd091fdSMatthias Ringwald * @param parser 481*5dd091fdSMatthias Ringwald * @param usage_page 482*5dd091fdSMatthias Ringwald * @param usage 483*5dd091fdSMatthias Ringwald * @param value provided in HID report 484*5dd091fdSMatthias Ringwald */ 485*5dd091fdSMatthias Ringwald 486*5dd091fdSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 487*5dd091fdSMatthias Ringwald 488*5dd091fdSMatthias Ringwald // fetch data from descriptor usage item 489*5dd091fdSMatthias Ringwald uint16_t bit_pos = parser->descriptor__usage_item.bit_pos; 490*5dd091fdSMatthias Ringwald uint16_t size = parser->descriptor__usage_item.size; 491*5dd091fdSMatthias Ringwald *usage_page = parser->descriptor__usage_item.usage_page; 492*5dd091fdSMatthias Ringwald *usage = parser->descriptor__usage_item.usage; 493*5dd091fdSMatthias Ringwald 494*5dd091fdSMatthias Ringwald 495*5dd091fdSMatthias Ringwald // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 496*5dd091fdSMatthias Ringwald bool is_variable = (parser->descriptor__usage_item.descriptor_item.item_value & 2) != 0; 497*5dd091fdSMatthias Ringwald bool is_signed = parser->descriptor__usage_item.global_logical_minimum < 0; 498*5dd091fdSMatthias Ringwald int pos_start = btstack_min( bit_pos >> 3, parser->report_len); 499*5dd091fdSMatthias Ringwald int pos_end = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len); 500*5dd091fdSMatthias Ringwald int bytes_to_read = pos_end - pos_start + 1; 501*5dd091fdSMatthias Ringwald 502*5dd091fdSMatthias Ringwald int i; 503*5dd091fdSMatthias Ringwald uint32_t multi_byte_value = 0; 504*5dd091fdSMatthias Ringwald for (i=0;i < bytes_to_read;i++){ 505*5dd091fdSMatthias Ringwald multi_byte_value |= parser->report[pos_start+i] << (i*8); 506*5dd091fdSMatthias Ringwald } 507*5dd091fdSMatthias Ringwald uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u); 508*5dd091fdSMatthias 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); 509*5dd091fdSMatthias Ringwald if (is_variable){ 510*5dd091fdSMatthias Ringwald if (is_signed && (unsigned_value & (1u<<(size-1u)))){ 511*5dd091fdSMatthias Ringwald *value = unsigned_value - (1u<<size); 512*5dd091fdSMatthias Ringwald } else { 513*5dd091fdSMatthias Ringwald *value = unsigned_value; 514*5dd091fdSMatthias Ringwald } 515*5dd091fdSMatthias Ringwald } else { 516*5dd091fdSMatthias Ringwald *usage = unsigned_value; 517*5dd091fdSMatthias Ringwald *value = 1; 518*5dd091fdSMatthias Ringwald } 519*5dd091fdSMatthias Ringwald 520*5dd091fdSMatthias Ringwald parser->have_report_usage_ready = false; 521*5dd091fdSMatthias Ringwald } 522*5dd091fdSMatthias Ringwald 523*5dd091fdSMatthias Ringwald 524*5dd091fdSMatthias Ringwald // Utility functions 5250ba748f2SMatthias Ringwald 5262cca3b08SMatthias Ringwald int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor, 5272cca3b08SMatthias Ringwald uint16_t hid_descriptor_len) { 528fada7179SMilanka Ringwald int total_report_size = 0; 529fada7179SMilanka Ringwald int report_size = 0; 530fada7179SMilanka Ringwald int report_count = 0; 531279225e6SMatthias Ringwald int current_report_id = HID_REPORT_ID_UNDEFINED; 532fada7179SMilanka Ringwald 533960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 534960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 535960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 536960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 537fada7179SMilanka Ringwald int valid_report_type = 0; 538960622b0SMatthias Ringwald switch (item->item_type) { 539fada7179SMilanka Ringwald case Global: 540960622b0SMatthias Ringwald switch ((GlobalItemTag) item->item_tag) { 541fada7179SMilanka Ringwald case ReportID: 542960622b0SMatthias Ringwald current_report_id = item->item_value; 543fada7179SMilanka Ringwald break; 544fada7179SMilanka Ringwald case ReportCount: 545960622b0SMatthias Ringwald report_count = item->item_value; 546fada7179SMilanka Ringwald break; 547fada7179SMilanka Ringwald case ReportSize: 548960622b0SMatthias Ringwald report_size = item->item_value; 549fada7179SMilanka Ringwald break; 550fada7179SMilanka Ringwald default: 551fada7179SMilanka Ringwald break; 552fada7179SMilanka Ringwald } 553fada7179SMilanka Ringwald break; 554fada7179SMilanka Ringwald case Main: 555fada7179SMilanka Ringwald if (current_report_id != report_id) break; 556960622b0SMatthias Ringwald switch ((MainItemTag) item->item_tag) { 557fada7179SMilanka Ringwald case Input: 558662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_INPUT) break; 559fada7179SMilanka Ringwald valid_report_type = 1; 560fada7179SMilanka Ringwald break; 561fada7179SMilanka Ringwald case Output: 562662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_OUTPUT) break; 563fada7179SMilanka Ringwald valid_report_type = 1; 564fada7179SMilanka Ringwald break; 565fada7179SMilanka Ringwald case Feature: 566662cddc2SMilanka Ringwald if (report_type != HID_REPORT_TYPE_FEATURE) break; 567fada7179SMilanka Ringwald valid_report_type = 1; 568fada7179SMilanka Ringwald break; 569fada7179SMilanka Ringwald default: 570fada7179SMilanka Ringwald break; 571fada7179SMilanka Ringwald } 572fada7179SMilanka Ringwald if (!valid_report_type) break; 573fada7179SMilanka Ringwald total_report_size += report_count * report_size; 574fada7179SMilanka Ringwald break; 575fada7179SMilanka Ringwald default: 576fada7179SMilanka Ringwald break; 577fada7179SMilanka Ringwald } 57815cf8612Sx0rloser if (total_report_size > 0 && current_report_id != report_id) break; 579fada7179SMilanka Ringwald } 580960622b0SMatthias Ringwald 581960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)){ 582fada7179SMilanka Ringwald return (total_report_size + 7) / 8; 583960622b0SMatthias Ringwald } else { 584960622b0SMatthias Ringwald return 0; 585960622b0SMatthias Ringwald } 586fada7179SMilanka Ringwald } 587dbcaefc7SMilanka Ringwald 588279225e6SMatthias 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){ 589279225e6SMatthias Ringwald uint16_t current_report_id = HID_REPORT_ID_UNDEFINED; 590960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 591960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 592279225e6SMatthias Ringwald bool report_id_found = false; 593960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 594960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 595960622b0SMatthias Ringwald switch (item->item_type){ 596dbcaefc7SMilanka Ringwald case Global: 597960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 598dbcaefc7SMilanka Ringwald case ReportID: 599960622b0SMatthias Ringwald current_report_id = item->item_value; 600279225e6SMatthias Ringwald if (current_report_id == report_id) { 601279225e6SMatthias Ringwald report_id_found = true; 602279225e6SMatthias Ringwald } 603dbcaefc7SMilanka Ringwald default: 604dbcaefc7SMilanka Ringwald break; 605dbcaefc7SMilanka Ringwald } 606dbcaefc7SMilanka Ringwald break; 607dbcaefc7SMilanka Ringwald default: 608dbcaefc7SMilanka Ringwald break; 609dbcaefc7SMilanka Ringwald } 610dbcaefc7SMilanka Ringwald } 611960622b0SMatthias Ringwald 612960622b0SMatthias Ringwald if (btstack_hid_descriptor_iterator_valid(&iterator)) { 613279225e6SMatthias Ringwald if (report_id_found){ 614279225e6SMatthias Ringwald return HID_REPORT_ID_VALID; 615dbcaefc7SMilanka Ringwald } 616279225e6SMatthias Ringwald if ((report_id == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) { 617279225e6SMatthias Ringwald return HID_REPORT_ID_VALID; 618279225e6SMatthias Ringwald } 619279225e6SMatthias Ringwald return HID_REPORT_ID_UNDECLARED; 620960622b0SMatthias Ringwald } else { 621960622b0SMatthias Ringwald return HID_REPORT_ID_INVALID; 622960622b0SMatthias Ringwald } 623960622b0SMatthias Ringwald } 624dbcaefc7SMilanka Ringwald 6252cca3b08SMatthias Ringwald bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) { 626960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_t iterator; 627960622b0SMatthias Ringwald btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len); 628960622b0SMatthias Ringwald while (btstack_hid_descriptor_iterator_has_more(&iterator)) { 629960622b0SMatthias Ringwald const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator); 630960622b0SMatthias Ringwald switch (item->item_type){ 631dbcaefc7SMilanka Ringwald case Global: 632960622b0SMatthias Ringwald switch ((GlobalItemTag)item->item_tag){ 633dbcaefc7SMilanka Ringwald case ReportID: 634c17118d0SMatthias Ringwald return true; 635dbcaefc7SMilanka Ringwald default: 636dbcaefc7SMilanka Ringwald break; 637dbcaefc7SMilanka Ringwald } 638dbcaefc7SMilanka Ringwald break; 639dbcaefc7SMilanka Ringwald default: 640dbcaefc7SMilanka Ringwald break; 641dbcaefc7SMilanka Ringwald } 642dbcaefc7SMilanka Ringwald } 643c17118d0SMatthias Ringwald return false; 644dbcaefc7SMilanka Ringwald } 645