xref: /btstack/src/btstack_hid_parser.c (revision 2cca3b08fab9e5cbf4664a619dcdc6709126157d)
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 
12112ccb71bSMatthias Ringwald static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
12212ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
12312ccb71bSMatthias Ringwald     const char ** item_tag_table;
1247bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
12512ccb71bSMatthias Ringwald         case Main:
12612ccb71bSMatthias Ringwald             item_tag_table = main_tags;
12712ccb71bSMatthias Ringwald             break;
12812ccb71bSMatthias Ringwald         case Global:
12912ccb71bSMatthias Ringwald             item_tag_table = global_tags;
13012ccb71bSMatthias Ringwald             break;
13112ccb71bSMatthias Ringwald         case Local:
13212ccb71bSMatthias Ringwald             item_tag_table = local_tags;
13312ccb71bSMatthias Ringwald             break;
13412ccb71bSMatthias Ringwald         default:
13512ccb71bSMatthias Ringwald             item_tag_table = NULL;
13612ccb71bSMatthias Ringwald             break;
13712ccb71bSMatthias Ringwald     }
13812ccb71bSMatthias Ringwald     const char * item_tag_name = "Invalid";
13912ccb71bSMatthias Ringwald     if (item_tag_table){
14012ccb71bSMatthias Ringwald         item_tag_name = item_tag_table[item->item_tag];
14112ccb71bSMatthias Ringwald     }
142ea1e21c2SMatthias Ringwald     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
14312ccb71bSMatthias Ringwald #else
14412ccb71bSMatthias Ringwald     UNUSED(parser);
14512ccb71bSMatthias Ringwald     UNUSED(item);
14612ccb71bSMatthias Ringwald #endif
14712ccb71bSMatthias Ringwald }
14812ccb71bSMatthias Ringwald 
14912ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value
15076fa2448SMatthias Ringwald bool btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
1518334d3d8SMatthias Ringwald 
1528334d3d8SMatthias Ringwald     const int hid_item_sizes[] = { 0, 1, 2, 4 };
1538334d3d8SMatthias Ringwald 
15412ccb71bSMatthias Ringwald     // parse item header
15576fa2448SMatthias Ringwald     if (hid_descriptor_len < 1u) return false;
15612ccb71bSMatthias Ringwald     uint16_t pos = 0;
15712ccb71bSMatthias Ringwald     uint8_t item_header = hid_descriptor[pos++];
1584ea43905SMatthias Ringwald     item->data_size = hid_item_sizes[item_header & 0x03u];
1594ea43905SMatthias Ringwald     item->item_type = (item_header & 0x0cu) >> 2u;
1604ea43905SMatthias Ringwald     item->item_tag  = (item_header & 0xf0u) >> 4u;
16112ccb71bSMatthias Ringwald     // long item
1624ea43905SMatthias Ringwald     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
16376fa2448SMatthias Ringwald         if (hid_descriptor_len < 3u) return false;
16412ccb71bSMatthias Ringwald         item->data_size = hid_descriptor[pos++];
16512ccb71bSMatthias Ringwald         item->item_tag  = hid_descriptor[pos++];
16612ccb71bSMatthias Ringwald     }
16712ccb71bSMatthias Ringwald     item->item_size =  pos + item->data_size;
16812ccb71bSMatthias Ringwald     item->item_value = 0;
16912ccb71bSMatthias Ringwald 
17012ccb71bSMatthias Ringwald     // read item value
17176fa2448SMatthias Ringwald     if (hid_descriptor_len < item->item_size) return false;
17276fa2448SMatthias Ringwald     if (item->data_size > 4u) return false;
17312ccb71bSMatthias Ringwald     int i;
1744ea43905SMatthias Ringwald     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
17512ccb71bSMatthias Ringwald     int32_t value = 0;
17612ccb71bSMatthias Ringwald     uint8_t latest_byte = 0;
17712ccb71bSMatthias Ringwald     for (i=0;i<item->data_size;i++){
17812ccb71bSMatthias Ringwald         latest_byte = hid_descriptor[pos++];
17912ccb71bSMatthias Ringwald         value = (latest_byte << (8*i)) | value;
18012ccb71bSMatthias Ringwald     }
1814ea43905SMatthias Ringwald     if (sgnd && (item->data_size > 0u)){
1824ea43905SMatthias Ringwald         if (latest_byte & 0x80u) {
1834ea43905SMatthias Ringwald             value -= 1u << (item->data_size*8u);
18412ccb71bSMatthias Ringwald         }
18512ccb71bSMatthias Ringwald     }
18612ccb71bSMatthias Ringwald     item->item_value = value;
18776fa2448SMatthias Ringwald     return true;
18812ccb71bSMatthias Ringwald }
18912ccb71bSMatthias Ringwald 
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 
20312ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
2047bbeb3adSMilanka Ringwald     switch((GlobalItemTag)item->item_tag){
20512ccb71bSMatthias Ringwald         case UsagePage:
20612ccb71bSMatthias Ringwald             parser->global_usage_page = item->item_value;
20712ccb71bSMatthias Ringwald             break;
20812ccb71bSMatthias Ringwald         case LogicalMinimum:
20912ccb71bSMatthias Ringwald             parser->global_logical_minimum = item->item_value;
21012ccb71bSMatthias Ringwald             break;
21112ccb71bSMatthias Ringwald         case LogicalMaximum:
21212ccb71bSMatthias Ringwald             parser->global_logical_maximum = item->item_value;
21312ccb71bSMatthias Ringwald             break;
21412ccb71bSMatthias Ringwald         case ReportSize:
21512ccb71bSMatthias Ringwald             parser->global_report_size = item->item_value;
21612ccb71bSMatthias Ringwald             break;
21712ccb71bSMatthias Ringwald         case ReportID:
21812ccb71bSMatthias Ringwald             parser->global_report_id = item->item_value;
21912ccb71bSMatthias Ringwald             break;
22012ccb71bSMatthias Ringwald         case ReportCount:
22112ccb71bSMatthias Ringwald             parser->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 
23912ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){
2401a05cde1SMatthias Ringwald     bool have_usage_min = false;
2411a05cde1SMatthias Ringwald     bool have_usage_max = false;
2421a05cde1SMatthias Ringwald     parser->usage_range = false;
24341afcabaSMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
24441afcabaSMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
24541afcabaSMatthias Ringwald     while ((parser->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)){
24812ccb71bSMatthias Ringwald             parser->usage_page = usage_item.item_value;
24912ccb71bSMatthias Ringwald         }
25012ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
2514ea43905SMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
25212ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
25312ccb71bSMatthias Ringwald                 case Usage:
25412ccb71bSMatthias Ringwald                     parser->available_usages = 1;
25512ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
25612ccb71bSMatthias Ringwald                     break;
25712ccb71bSMatthias Ringwald                 case UsageMinimum:
25812ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
2591a05cde1SMatthias Ringwald                     have_usage_min = true;
26012ccb71bSMatthias Ringwald                     break;
26112ccb71bSMatthias Ringwald                 case UsageMaximum:
26212ccb71bSMatthias Ringwald                     parser->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){
2694ea43905SMatthias Ringwald                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
2701a05cde1SMatthias Ringwald                 parser->usage_range = true;
271dfb01e77SMatthias Ringwald                 if (parser->available_usages < parser->required_usages){
272cb406331SDirk Helbig                     log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages);
273dfb01e77SMatthias Ringwald                 }
27412ccb71bSMatthias Ringwald             }
27512ccb71bSMatthias Ringwald         }
27612ccb71bSMatthias Ringwald     }
27741afcabaSMatthias Ringwald     parser->usage_pos += iterator.descriptor_pos;
27812ccb71bSMatthias Ringwald }
27912ccb71bSMatthias Ringwald 
28012ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
28112ccb71bSMatthias Ringwald     hid_pretty_print_item(parser, item);
28212ccb71bSMatthias Ringwald     int valid_field = 0;
283eb78fadaSMatthias Ringwald     uint16_t report_id_before;
2847bbeb3adSMilanka Ringwald     switch ((TagType)item->item_type){
28512ccb71bSMatthias Ringwald         case Main:
2860a4fc5fbSMatthias Ringwald             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
2870a4fc5fbSMatthias Ringwald                                                                         parser->report_type);
28812ccb71bSMatthias Ringwald             break;
28912ccb71bSMatthias Ringwald         case Global:
290eb78fadaSMatthias Ringwald             report_id_before = parser->global_report_id;
29112ccb71bSMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
292eb78fadaSMatthias Ringwald             // track record id for report handling
293eb78fadaSMatthias Ringwald             if ((GlobalItemTag)item->item_tag == ReportID){
294eb78fadaSMatthias Ringwald                 if (parser->active_record && (report_id_before != item->item_value)){
295eb78fadaSMatthias Ringwald                     parser->active_record = 0;
296eb78fadaSMatthias Ringwald                 }
297eb78fadaSMatthias Ringwald             }
29812ccb71bSMatthias Ringwald             break;
29912ccb71bSMatthias Ringwald         case Local:
3007bbeb3adSMilanka Ringwald         case Reserved:
3017bbeb3adSMilanka Ringwald             break;
3027bbeb3adSMilanka Ringwald         default:
3037bbeb3adSMilanka Ringwald             btstack_assert(false);
30412ccb71bSMatthias Ringwald             break;
30512ccb71bSMatthias Ringwald     }
30612ccb71bSMatthias Ringwald     if (!valid_field) return;
30712ccb71bSMatthias Ringwald 
30812ccb71bSMatthias Ringwald     // verify record id
30912ccb71bSMatthias Ringwald     if (parser->global_report_id && !parser->active_record){
31012ccb71bSMatthias Ringwald         if (parser->report[0] != parser->global_report_id){
31112ccb71bSMatthias Ringwald             return;
31212ccb71bSMatthias Ringwald         }
3134ea43905SMatthias Ringwald         parser->report_pos_in_bit += 8u;
31412ccb71bSMatthias Ringwald     }
31512ccb71bSMatthias Ringwald     parser->active_record = 1;
31612ccb71bSMatthias Ringwald     // handle constant fields used for padding
31712ccb71bSMatthias Ringwald     if (item->item_value & 1){
31812ccb71bSMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
31912ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
32012ccb71bSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
32112ccb71bSMatthias Ringwald #endif
32212ccb71bSMatthias Ringwald         parser->report_pos_in_bit += item_bits;
32312ccb71bSMatthias Ringwald         return;
32412ccb71bSMatthias Ringwald     }
32512ccb71bSMatthias Ringwald     // Empty Item
3264ea43905SMatthias Ringwald     if (parser->global_report_count == 0u) return;
32712ccb71bSMatthias Ringwald     // let's start
32812ccb71bSMatthias Ringwald     parser->required_usages = parser->global_report_count;
32912ccb71bSMatthias Ringwald }
33012ccb71bSMatthias Ringwald 
33112ccb71bSMatthias Ringwald // PUBLIC API
33212ccb71bSMatthias Ringwald 
3330ba748f2SMatthias Ringwald // HID Descriptor Iterator
334fada7179SMilanka Ringwald 
3350c7e4a25SMatthias Ringwald void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
3360c7e4a25SMatthias Ringwald     iterator->descriptor = hid_descriptor;
3370c7e4a25SMatthias Ringwald     iterator->descriptor_pos = 0;
3380c7e4a25SMatthias Ringwald     iterator->descriptor_len = hid_descriptor_len;
3390c7e4a25SMatthias Ringwald     iterator->item_ready = false;
3400c7e4a25SMatthias Ringwald     iterator->valid = true;
3410c7e4a25SMatthias Ringwald }
3420c7e4a25SMatthias Ringwald 
3430c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
3443cc55c4aSMatthias Ringwald     if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){
3450c7e4a25SMatthias Ringwald         uint16_t  item_len = iterator->descriptor_len - iterator->descriptor_pos;
3460c7e4a25SMatthias Ringwald         const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
3470c7e4a25SMatthias Ringwald         bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len);
3480c7e4a25SMatthias Ringwald         if (ok){
3490c7e4a25SMatthias Ringwald             iterator->item_ready = true;
3500c7e4a25SMatthias Ringwald         } else {
3510c7e4a25SMatthias Ringwald             iterator->valid = false;
3520c7e4a25SMatthias Ringwald         }
3530c7e4a25SMatthias Ringwald     }
3540c7e4a25SMatthias Ringwald     return iterator->item_ready;
3550c7e4a25SMatthias Ringwald }
3560c7e4a25SMatthias Ringwald 
3570c7e4a25SMatthias Ringwald const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
3583cc55c4aSMatthias Ringwald     iterator->descriptor_pos += iterator->descriptor_item.item_size;
3590c7e4a25SMatthias Ringwald     iterator->item_ready = false;
3600c7e4a25SMatthias Ringwald     return &iterator->descriptor_item;
3610c7e4a25SMatthias Ringwald }
3620c7e4a25SMatthias Ringwald 
3630c7e4a25SMatthias Ringwald bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
3640c7e4a25SMatthias Ringwald     return iterator->valid;
3650c7e4a25SMatthias Ringwald }
3660c7e4a25SMatthias Ringwald 
3670ba748f2SMatthias Ringwald 
368*2cca3b08SMatthias Ringwald int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor,
369*2cca3b08SMatthias Ringwald                                        uint16_t hid_descriptor_len) {
370fada7179SMilanka Ringwald     int total_report_size = 0;
371fada7179SMilanka Ringwald     int report_size = 0;
372fada7179SMilanka Ringwald     int report_count = 0;
373279225e6SMatthias Ringwald     int current_report_id = HID_REPORT_ID_UNDEFINED;
374fada7179SMilanka Ringwald 
375960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
376960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
377960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
378960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
379fada7179SMilanka Ringwald         int valid_report_type = 0;
380960622b0SMatthias Ringwald         switch (item->item_type) {
381fada7179SMilanka Ringwald             case Global:
382960622b0SMatthias Ringwald                 switch ((GlobalItemTag) item->item_tag) {
383fada7179SMilanka Ringwald                     case ReportID:
384960622b0SMatthias Ringwald                         current_report_id = item->item_value;
385fada7179SMilanka Ringwald                         break;
386fada7179SMilanka Ringwald                     case ReportCount:
387960622b0SMatthias Ringwald                         report_count = item->item_value;
388fada7179SMilanka Ringwald                         break;
389fada7179SMilanka Ringwald                     case ReportSize:
390960622b0SMatthias Ringwald                         report_size = item->item_value;
391fada7179SMilanka Ringwald                         break;
392fada7179SMilanka Ringwald                     default:
393fada7179SMilanka Ringwald                         break;
394fada7179SMilanka Ringwald                 }
395fada7179SMilanka Ringwald                 break;
396fada7179SMilanka Ringwald             case Main:
397fada7179SMilanka Ringwald                 if (current_report_id != report_id) break;
398960622b0SMatthias Ringwald                 switch ((MainItemTag) item->item_tag) {
399fada7179SMilanka Ringwald                     case Input:
400662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_INPUT) break;
401fada7179SMilanka Ringwald                         valid_report_type = 1;
402fada7179SMilanka Ringwald                         break;
403fada7179SMilanka Ringwald                     case Output:
404662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
405fada7179SMilanka Ringwald                         valid_report_type = 1;
406fada7179SMilanka Ringwald                         break;
407fada7179SMilanka Ringwald                     case Feature:
408662cddc2SMilanka Ringwald                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
409fada7179SMilanka Ringwald                         valid_report_type = 1;
410fada7179SMilanka Ringwald                         break;
411fada7179SMilanka Ringwald                     default:
412fada7179SMilanka Ringwald                         break;
413fada7179SMilanka Ringwald                 }
414fada7179SMilanka Ringwald                 if (!valid_report_type) break;
415fada7179SMilanka Ringwald                 total_report_size += report_count * report_size;
416fada7179SMilanka Ringwald                 break;
417fada7179SMilanka Ringwald             default:
418fada7179SMilanka Ringwald                 break;
419fada7179SMilanka Ringwald         }
42015cf8612Sx0rloser         if (total_report_size > 0 && current_report_id != report_id) break;
421fada7179SMilanka Ringwald     }
422960622b0SMatthias Ringwald 
423960622b0SMatthias Ringwald     if (btstack_hid_descriptor_iterator_valid(&iterator)){
424fada7179SMilanka Ringwald         return (total_report_size + 7) / 8;
425960622b0SMatthias Ringwald     } else {
426960622b0SMatthias Ringwald         return 0;
427960622b0SMatthias Ringwald     }
428fada7179SMilanka Ringwald }
429dbcaefc7SMilanka Ringwald 
430279225e6SMatthias 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){
431279225e6SMatthias Ringwald     uint16_t current_report_id = HID_REPORT_ID_UNDEFINED;
432960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
433960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
434279225e6SMatthias Ringwald     bool report_id_found = false;
435960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
436960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
437960622b0SMatthias Ringwald         switch (item->item_type){
438dbcaefc7SMilanka Ringwald             case Global:
439960622b0SMatthias Ringwald                 switch ((GlobalItemTag)item->item_tag){
440dbcaefc7SMilanka Ringwald                     case ReportID:
441960622b0SMatthias Ringwald                         current_report_id = item->item_value;
442279225e6SMatthias Ringwald                         if (current_report_id == report_id) {
443279225e6SMatthias Ringwald                             report_id_found = true;
444279225e6SMatthias Ringwald                         }
445dbcaefc7SMilanka Ringwald                     default:
446dbcaefc7SMilanka Ringwald                         break;
447dbcaefc7SMilanka Ringwald                 }
448dbcaefc7SMilanka Ringwald                 break;
449dbcaefc7SMilanka Ringwald             default:
450dbcaefc7SMilanka Ringwald                 break;
451dbcaefc7SMilanka Ringwald         }
452dbcaefc7SMilanka Ringwald     }
453960622b0SMatthias Ringwald 
454960622b0SMatthias Ringwald     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
455279225e6SMatthias Ringwald         if (report_id_found){
456279225e6SMatthias Ringwald             return HID_REPORT_ID_VALID;
457dbcaefc7SMilanka Ringwald         }
458279225e6SMatthias Ringwald         if ((report_id  == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) {
459279225e6SMatthias Ringwald             return HID_REPORT_ID_VALID;
460279225e6SMatthias Ringwald         }
461279225e6SMatthias Ringwald         return HID_REPORT_ID_UNDECLARED;
462960622b0SMatthias Ringwald     } else {
463960622b0SMatthias Ringwald         return HID_REPORT_ID_INVALID;
464960622b0SMatthias Ringwald     }
465960622b0SMatthias Ringwald }
466dbcaefc7SMilanka Ringwald 
467*2cca3b08SMatthias Ringwald bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) {
468960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_t iterator;
469960622b0SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
470960622b0SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
471960622b0SMatthias Ringwald         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
472960622b0SMatthias Ringwald         switch (item->item_type){
473dbcaefc7SMilanka Ringwald             case Global:
474960622b0SMatthias Ringwald                 switch ((GlobalItemTag)item->item_tag){
475dbcaefc7SMilanka Ringwald                     case ReportID:
476c17118d0SMatthias Ringwald                         return true;
477dbcaefc7SMilanka Ringwald                     default:
478dbcaefc7SMilanka Ringwald                         break;
479dbcaefc7SMilanka Ringwald                 }
480dbcaefc7SMilanka Ringwald                 break;
481dbcaefc7SMilanka Ringwald             default:
482dbcaefc7SMilanka Ringwald                 break;
483dbcaefc7SMilanka Ringwald         }
484dbcaefc7SMilanka Ringwald     }
485c17118d0SMatthias Ringwald     return false;
486dbcaefc7SMilanka Ringwald }
487cdb96ae5SMatthias Ringwald 
4880ba748f2SMatthias Ringwald // HID Descriptor Usage Iterator
4890ba748f2SMatthias Ringwald 
4905285211dSMatthias Ringwald static void btstack_parser_usage_iterator_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
491cdb96ae5SMatthias Ringwald     hid_pretty_print_item(parser, item);
492cdb96ae5SMatthias Ringwald     int valid_field = 0;
493cdb96ae5SMatthias Ringwald     uint16_t report_id_before;
494cdb96ae5SMatthias Ringwald     switch ((TagType)item->item_type){
495cdb96ae5SMatthias Ringwald         case Main:
496cdb96ae5SMatthias Ringwald             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
497cdb96ae5SMatthias Ringwald                                                                         parser->report_type);
498cdb96ae5SMatthias Ringwald             break;
499cdb96ae5SMatthias Ringwald         case Global:
500cdb96ae5SMatthias Ringwald             report_id_before = parser->global_report_id;
501cdb96ae5SMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
502cdb96ae5SMatthias Ringwald             // track record id for report handling, reset report position
503cdb96ae5SMatthias Ringwald             if (report_id_before != parser->global_report_id){
504cdb96ae5SMatthias Ringwald                 parser->report_pos_in_bit = 8u;
505cdb96ae5SMatthias Ringwald             }
506cdb96ae5SMatthias Ringwald             break;
507cdb96ae5SMatthias Ringwald         case Local:
508cdb96ae5SMatthias Ringwald         case Reserved:
509cdb96ae5SMatthias Ringwald             break;
510cdb96ae5SMatthias Ringwald         default:
511cdb96ae5SMatthias Ringwald             btstack_assert(false);
512cdb96ae5SMatthias Ringwald             break;
513cdb96ae5SMatthias Ringwald     }
514cdb96ae5SMatthias Ringwald     if (!valid_field) return;
515cdb96ae5SMatthias Ringwald 
516cdb96ae5SMatthias Ringwald     // handle constant fields used for padding
517cdb96ae5SMatthias Ringwald     if (item->item_value & 1){
518cdb96ae5SMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
519cdb96ae5SMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
520cdb96ae5SMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
521cdb96ae5SMatthias Ringwald #endif
522cdb96ae5SMatthias Ringwald         parser->report_pos_in_bit += item_bits;
523cdb96ae5SMatthias Ringwald         return;
524cdb96ae5SMatthias Ringwald     }
525cdb96ae5SMatthias Ringwald     // Empty Item
526cdb96ae5SMatthias Ringwald     if (parser->global_report_count == 0u) return;
527cdb96ae5SMatthias Ringwald     // let's start
528cdb96ae5SMatthias Ringwald     parser->required_usages = parser->global_report_count;
529cdb96ae5SMatthias Ringwald }
530cdb96ae5SMatthias Ringwald 
531cdb96ae5SMatthias Ringwald static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_parser_t * parser) {
532cdb96ae5SMatthias Ringwald     while (btstack_hid_descriptor_iterator_has_more(&parser->descriptor_iterator)){
533cdb96ae5SMatthias Ringwald         parser->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&parser->descriptor_iterator);
534cdb96ae5SMatthias Ringwald 
5355285211dSMatthias Ringwald         btstack_parser_usage_iterator_process_item(parser, &parser->descriptor_item);
536cdb96ae5SMatthias Ringwald 
537cdb96ae5SMatthias Ringwald         if (parser->required_usages){
538cdb96ae5SMatthias Ringwald             hid_find_next_usage(parser);
539cdb96ae5SMatthias Ringwald             if (parser->available_usages) {
540cdb96ae5SMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
541cdb96ae5SMatthias Ringwald                 return;
542cdb96ae5SMatthias Ringwald             } else {
543cdb96ae5SMatthias Ringwald                 log_debug("no usages found");
544cdb96ae5SMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
545cdb96ae5SMatthias Ringwald                 return;
546cdb96ae5SMatthias Ringwald             }
547cdb96ae5SMatthias Ringwald         } else {
548cdb96ae5SMatthias Ringwald             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
549cdb96ae5SMatthias Ringwald                 // reset usage
550cdb96ae5SMatthias Ringwald                 parser->usage_pos = parser->descriptor_iterator.descriptor_pos;
551cdb96ae5SMatthias Ringwald                 parser->usage_page = parser->global_usage_page;
552cdb96ae5SMatthias Ringwald             }
553cdb96ae5SMatthias Ringwald         }
554cdb96ae5SMatthias Ringwald     }
555cdb96ae5SMatthias Ringwald     // end of descriptor
556cdb96ae5SMatthias Ringwald     parser->state = BTSTACK_HID_PARSER_COMPLETE;
557cdb96ae5SMatthias Ringwald }
558cdb96ae5SMatthias Ringwald 
559cdb96ae5SMatthias Ringwald void btstack_hid_usage_iterator_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){
560cdb96ae5SMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
561cdb96ae5SMatthias Ringwald 
562cdb96ae5SMatthias Ringwald     parser->descriptor     = hid_descriptor;
563cdb96ae5SMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
564cdb96ae5SMatthias Ringwald     parser->report_type    = hid_report_type;
565cdb96ae5SMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
566279225e6SMatthias Ringwald     parser->global_report_id = HID_REPORT_ID_UNDEFINED;
567cdb96ae5SMatthias Ringwald     btstack_hid_descriptor_iterator_init(&parser->descriptor_iterator, hid_descriptor, hid_descriptor_len);
568cdb96ae5SMatthias Ringwald 
569cdb96ae5SMatthias Ringwald     btstack_hid_usage_iterator_find_next_usage(parser);
570cdb96ae5SMatthias Ringwald }
571cdb96ae5SMatthias Ringwald 
572cdb96ae5SMatthias Ringwald bool btstack_hid_usage_iterator_has_more(btstack_hid_parser_t * parser){
573cdb96ae5SMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
574cdb96ae5SMatthias Ringwald }
575cdb96ae5SMatthias Ringwald 
576cdb96ae5SMatthias Ringwald void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_hid_usage_item_t * item){
577cdb96ae5SMatthias Ringwald     // cache current values
578cdb96ae5SMatthias Ringwald     memset(item, 0, sizeof(btstack_hid_usage_item_t));
579cdb96ae5SMatthias Ringwald     item->size = parser->global_report_size;
580cdb96ae5SMatthias Ringwald     item->report_id = parser->global_report_id;
581cdb96ae5SMatthias Ringwald     item->usage_page = parser->usage_minimum >> 16;
582cdb96ae5SMatthias Ringwald     item->bit_pos = parser->report_pos_in_bit;
583cdb96ae5SMatthias Ringwald 
584cdb96ae5SMatthias Ringwald     bool is_variable  = (parser->descriptor_item.item_value & 2) != 0;
585cdb96ae5SMatthias Ringwald     if (is_variable){
586cdb96ae5SMatthias Ringwald         item->usage = parser->usage_minimum & 0xffffu;
587cdb96ae5SMatthias Ringwald     }
588cdb96ae5SMatthias Ringwald     parser->required_usages--;
589cdb96ae5SMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
590cdb96ae5SMatthias Ringwald 
5910ba748f2SMatthias Ringwald     // cache descriptor item and
5920ba748f2SMatthias Ringwald     item->descriptor_item = parser->descriptor_item;
5930ba748f2SMatthias Ringwald     item->global_logical_minimum = parser->global_logical_minimum;
5940ba748f2SMatthias Ringwald 
595cdb96ae5SMatthias Ringwald     // next usage
596cdb96ae5SMatthias Ringwald     if (is_variable){
597cdb96ae5SMatthias Ringwald         parser->usage_minimum++;
598cdb96ae5SMatthias Ringwald         parser->available_usages--;
599cdb96ae5SMatthias Ringwald         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
600cdb96ae5SMatthias Ringwald             // usage min - max range smaller than report count, ignore remaining bit in report
601cdb96ae5SMatthias Ringwald             log_debug("Ignoring %u items without Usage", parser->required_usages);
602cdb96ae5SMatthias Ringwald             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
603cdb96ae5SMatthias Ringwald             parser->required_usages = 0;
604cdb96ae5SMatthias Ringwald         }
605cdb96ae5SMatthias Ringwald     } else {
606cdb96ae5SMatthias Ringwald         if (parser->required_usages == 0u){
607cdb96ae5SMatthias Ringwald             parser->available_usages = 0;
608cdb96ae5SMatthias Ringwald         }
609cdb96ae5SMatthias Ringwald     }
610cdb96ae5SMatthias Ringwald     if (parser->available_usages) {
611cdb96ae5SMatthias Ringwald         return;
612cdb96ae5SMatthias Ringwald     }
613cdb96ae5SMatthias Ringwald     if (parser->required_usages == 0u){
614cdb96ae5SMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
615cdb96ae5SMatthias Ringwald         btstack_hid_usage_iterator_find_next_usage(parser);
616cdb96ae5SMatthias Ringwald     } else {
617cdb96ae5SMatthias Ringwald         hid_find_next_usage(parser);
618cdb96ae5SMatthias Ringwald         if (parser->available_usages == 0u) {
619cdb96ae5SMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
620cdb96ae5SMatthias Ringwald         }
621cdb96ae5SMatthias Ringwald     }
622cdb96ae5SMatthias Ringwald }
623cdb96ae5SMatthias Ringwald 
6240ba748f2SMatthias Ringwald 
6250ba748f2SMatthias Ringwald // HID Report Parser
6260ba748f2SMatthias Ringwald 
6270ba748f2SMatthias 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){
6280ba748f2SMatthias Ringwald     btstack_hid_usage_iterator_init(parser, hid_descriptor, hid_descriptor_len, hid_report_type);
6290ba748f2SMatthias Ringwald     parser->report = hid_report;
6300ba748f2SMatthias Ringwald     parser->report_len = hid_report_len;
6310ba748f2SMatthias Ringwald     parser->have_report_usage_ready = false;
6320ba748f2SMatthias Ringwald }
6330ba748f2SMatthias Ringwald 
6340ba748f2SMatthias Ringwald /**
6350ba748f2SMatthias Ringwald  * @brief Checks if more fields are available
6360ba748f2SMatthias Ringwald  * @param parser
6370ba748f2SMatthias Ringwald  */
6380ba748f2SMatthias Ringwald bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
6390ba748f2SMatthias Ringwald     while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(parser)){
6400ba748f2SMatthias Ringwald         btstack_hid_usage_iterator_get_item(parser, &parser->descriptor__usage_item);
6410ba748f2SMatthias Ringwald         // ignore usages for other report ids
642279225e6SMatthias Ringwald         if (parser->descriptor__usage_item.report_id != HID_REPORT_ID_UNDEFINED){
6430ba748f2SMatthias Ringwald             if (parser->descriptor__usage_item.report_id != parser->report[0]){
6440ba748f2SMatthias Ringwald                 continue;
6450ba748f2SMatthias Ringwald             }
6460ba748f2SMatthias Ringwald         }
6470ba748f2SMatthias Ringwald         parser->have_report_usage_ready = true;
6480ba748f2SMatthias Ringwald     }
6490ba748f2SMatthias Ringwald     return parser->have_report_usage_ready;
6500ba748f2SMatthias Ringwald }
6510ba748f2SMatthias Ringwald 
6520ba748f2SMatthias Ringwald /**
6530ba748f2SMatthias Ringwald  * @brief Get next field
6540ba748f2SMatthias Ringwald  * @param parser
6550ba748f2SMatthias Ringwald  * @param usage_page
6560ba748f2SMatthias Ringwald  * @param usage
6570ba748f2SMatthias Ringwald  * @param value provided in HID report
6580ba748f2SMatthias Ringwald  */
6590ba748f2SMatthias Ringwald 
6600ba748f2SMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
6610ba748f2SMatthias Ringwald 
6620ba748f2SMatthias Ringwald     // fetch data from descriptor usage item
6630ba748f2SMatthias Ringwald     uint16_t bit_pos = parser->descriptor__usage_item.bit_pos;
6640ba748f2SMatthias Ringwald     uint16_t size    = parser->descriptor__usage_item.size;
6650ba748f2SMatthias Ringwald     *usage_page      = parser->descriptor__usage_item.usage_page;
6660ba748f2SMatthias Ringwald     *usage           = parser->descriptor__usage_item.usage;
6670ba748f2SMatthias Ringwald 
6680ba748f2SMatthias Ringwald 
6690ba748f2SMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
6700ba748f2SMatthias Ringwald     bool is_variable   = (parser->descriptor__usage_item.descriptor_item.item_value & 2) != 0;
6710ba748f2SMatthias Ringwald     bool is_signed     = parser->descriptor__usage_item.global_logical_minimum < 0;
6720ba748f2SMatthias Ringwald     int pos_start     = btstack_min(  bit_pos >> 3, parser->report_len);
6730ba748f2SMatthias Ringwald     int pos_end       = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len);
6740ba748f2SMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
6750ba748f2SMatthias Ringwald 
6760ba748f2SMatthias Ringwald     int i;
6770ba748f2SMatthias Ringwald     uint32_t multi_byte_value = 0;
6780ba748f2SMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
6790ba748f2SMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
6800ba748f2SMatthias Ringwald     }
6810ba748f2SMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u);
6820ba748f2SMatthias 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);
6830ba748f2SMatthias Ringwald     if (is_variable){
6840ba748f2SMatthias Ringwald         if (is_signed && (unsigned_value & (1u<<(size-1u)))){
6850ba748f2SMatthias Ringwald             *value = unsigned_value - (1u<<size);
6860ba748f2SMatthias Ringwald         } else {
6870ba748f2SMatthias Ringwald             *value = unsigned_value;
6880ba748f2SMatthias Ringwald         }
6890ba748f2SMatthias Ringwald     } else {
6900ba748f2SMatthias Ringwald         *usage  = unsigned_value;
6910ba748f2SMatthias Ringwald         *value  = 1;
6920ba748f2SMatthias Ringwald     }
6930ba748f2SMatthias Ringwald 
6940ba748f2SMatthias Ringwald     parser->have_report_usage_ready = false;
6950ba748f2SMatthias Ringwald }
696