xref: /btstack/src/btstack_hid_parser.c (revision 12ccb71b494e77efac73450222b5c4f45658c543)
1*12ccb71bSMatthias Ringwald /*
2*12ccb71bSMatthias Ringwald  * Copyright (C) 2017 BlueKitchen GmbH
3*12ccb71bSMatthias Ringwald  *
4*12ccb71bSMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5*12ccb71bSMatthias Ringwald  * modification, are permitted provided that the following conditions
6*12ccb71bSMatthias Ringwald  * are met:
7*12ccb71bSMatthias Ringwald  *
8*12ccb71bSMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9*12ccb71bSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10*12ccb71bSMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11*12ccb71bSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12*12ccb71bSMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13*12ccb71bSMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14*12ccb71bSMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15*12ccb71bSMatthias Ringwald  *    from this software without specific prior written permission.
16*12ccb71bSMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17*12ccb71bSMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18*12ccb71bSMatthias Ringwald  *    monetary gain.
19*12ccb71bSMatthias Ringwald  *
20*12ccb71bSMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21*12ccb71bSMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*12ccb71bSMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23*12ccb71bSMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24*12ccb71bSMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25*12ccb71bSMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26*12ccb71bSMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27*12ccb71bSMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28*12ccb71bSMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29*12ccb71bSMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30*12ccb71bSMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*12ccb71bSMatthias Ringwald  * SUCH DAMAGE.
32*12ccb71bSMatthias Ringwald  *
33*12ccb71bSMatthias Ringwald  * Please inquire about commercial licensing options at
34*12ccb71bSMatthias Ringwald  * [email protected]
35*12ccb71bSMatthias Ringwald  *
36*12ccb71bSMatthias Ringwald  */
37*12ccb71bSMatthias Ringwald 
38*12ccb71bSMatthias Ringwald #define __BTSTACK_FILE__ "btstack_hid_parser.c"
39*12ccb71bSMatthias Ringwald 
40*12ccb71bSMatthias Ringwald #include <string.h>
41*12ccb71bSMatthias Ringwald 
42*12ccb71bSMatthias Ringwald #include "btstack_hid_parser.h"
43*12ccb71bSMatthias Ringwald #include "btstack_util.h"
44*12ccb71bSMatthias Ringwald #include "btstack_debug.h"
45*12ccb71bSMatthias Ringwald 
46*12ccb71bSMatthias Ringwald // Not implemented:
47*12ccb71bSMatthias Ringwald // - Support for Push/Pop
48*12ccb71bSMatthias Ringwald // - Optional Pretty Print of HID Descripor
49*12ccb71bSMatthias Ringwald // - Support to query descriptort for contained usages, e.g. to detect keyboard or mouse
50*12ccb71bSMatthias Ringwald 
51*12ccb71bSMatthias Ringwald // #define HID_PARSER_PRETTY_PRINT
52*12ccb71bSMatthias Ringwald 
53*12ccb71bSMatthias Ringwald /*
54*12ccb71bSMatthias Ringwald  *  btstack_hid_parser.c
55*12ccb71bSMatthias Ringwald  */
56*12ccb71bSMatthias Ringwald 
57*12ccb71bSMatthias Ringwald 
58*12ccb71bSMatthias Ringwald typedef enum {
59*12ccb71bSMatthias Ringwald     Main=0,
60*12ccb71bSMatthias Ringwald     Global,
61*12ccb71bSMatthias Ringwald     Local,
62*12ccb71bSMatthias Ringwald     Reserved
63*12ccb71bSMatthias Ringwald } TagType;
64*12ccb71bSMatthias Ringwald 
65*12ccb71bSMatthias Ringwald typedef enum {
66*12ccb71bSMatthias Ringwald     Input=8,
67*12ccb71bSMatthias Ringwald     Output,
68*12ccb71bSMatthias Ringwald     Coll,
69*12ccb71bSMatthias Ringwald     Feature,
70*12ccb71bSMatthias Ringwald     EndColl
71*12ccb71bSMatthias Ringwald } MainItemTag;
72*12ccb71bSMatthias Ringwald 
73*12ccb71bSMatthias Ringwald typedef enum {
74*12ccb71bSMatthias Ringwald     UsagePage,
75*12ccb71bSMatthias Ringwald     LogicalMinimum,
76*12ccb71bSMatthias Ringwald     LogicalMaximum,
77*12ccb71bSMatthias Ringwald     PhysicalMinimum,
78*12ccb71bSMatthias Ringwald     PhysicalMaximum,
79*12ccb71bSMatthias Ringwald     UnitExponent,
80*12ccb71bSMatthias Ringwald     Unit,
81*12ccb71bSMatthias Ringwald     ReportSize,
82*12ccb71bSMatthias Ringwald     ReportID,
83*12ccb71bSMatthias Ringwald     ReportCount,
84*12ccb71bSMatthias Ringwald     Push,
85*12ccb71bSMatthias Ringwald     Pop
86*12ccb71bSMatthias Ringwald } GlobalItemTag;
87*12ccb71bSMatthias Ringwald 
88*12ccb71bSMatthias Ringwald typedef enum {
89*12ccb71bSMatthias Ringwald     Usage,
90*12ccb71bSMatthias Ringwald     UsageMinimum,
91*12ccb71bSMatthias Ringwald     UsageMaximum,
92*12ccb71bSMatthias Ringwald     DesignatorIndex,
93*12ccb71bSMatthias Ringwald     DesignatorMinimum,
94*12ccb71bSMatthias Ringwald     DesignatorMaximum,
95*12ccb71bSMatthias Ringwald     StringIndex,
96*12ccb71bSMatthias Ringwald     StringMinimum,
97*12ccb71bSMatthias Ringwald     StringMaximum,
98*12ccb71bSMatthias Ringwald     Delimiter
99*12ccb71bSMatthias Ringwald } LocalItemTag;
100*12ccb71bSMatthias Ringwald 
101*12ccb71bSMatthias Ringwald const int hid_item_sizes[] = { 0, 1, 2, 4 };
102*12ccb71bSMatthias Ringwald 
103*12ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
104*12ccb71bSMatthias Ringwald 
105*12ccb71bSMatthias Ringwald static const char * type_names[] = {
106*12ccb71bSMatthias Ringwald     "Main",
107*12ccb71bSMatthias Ringwald     "Global",
108*12ccb71bSMatthias Ringwald     "Local",
109*12ccb71bSMatthias Ringwald     "Reserved"
110*12ccb71bSMatthias Ringwald };
111*12ccb71bSMatthias Ringwald static const char * main_tags[] = {
112*12ccb71bSMatthias Ringwald     "",
113*12ccb71bSMatthias Ringwald     "",
114*12ccb71bSMatthias Ringwald     "",
115*12ccb71bSMatthias Ringwald     "",
116*12ccb71bSMatthias Ringwald     "",
117*12ccb71bSMatthias Ringwald     "",
118*12ccb71bSMatthias Ringwald     "",
119*12ccb71bSMatthias Ringwald     "",
120*12ccb71bSMatthias Ringwald     "Input ",
121*12ccb71bSMatthias Ringwald     "Output",
122*12ccb71bSMatthias Ringwald     "Collection",
123*12ccb71bSMatthias Ringwald     "Feature",
124*12ccb71bSMatthias Ringwald     "End Collection",
125*12ccb71bSMatthias Ringwald     "Reserved",
126*12ccb71bSMatthias Ringwald     "Reserved",
127*12ccb71bSMatthias Ringwald     "Reserved"
128*12ccb71bSMatthias Ringwald };
129*12ccb71bSMatthias Ringwald static const char * global_tags[] = {
130*12ccb71bSMatthias Ringwald     "Usage Page",
131*12ccb71bSMatthias Ringwald     "Logical Minimum",
132*12ccb71bSMatthias Ringwald     "Logical Maximum",
133*12ccb71bSMatthias Ringwald     "Physical Minimum",
134*12ccb71bSMatthias Ringwald     "Physical Maximum",
135*12ccb71bSMatthias Ringwald     "Unit Exponent",
136*12ccb71bSMatthias Ringwald     "Unit",
137*12ccb71bSMatthias Ringwald     "Report Size",
138*12ccb71bSMatthias Ringwald     "Report ID",
139*12ccb71bSMatthias Ringwald     "Report Count",
140*12ccb71bSMatthias Ringwald     "Push",
141*12ccb71bSMatthias Ringwald     "Pop",
142*12ccb71bSMatthias Ringwald     "Reserved",
143*12ccb71bSMatthias Ringwald     "Reserved",
144*12ccb71bSMatthias Ringwald     "Reserved",
145*12ccb71bSMatthias Ringwald     "Reserved"
146*12ccb71bSMatthias Ringwald };
147*12ccb71bSMatthias Ringwald static const char * local_tags[] = {
148*12ccb71bSMatthias Ringwald     "Usage",
149*12ccb71bSMatthias Ringwald     "Usage Minimum",
150*12ccb71bSMatthias Ringwald     "Usage Maximum",
151*12ccb71bSMatthias Ringwald     "Designator Index",
152*12ccb71bSMatthias Ringwald     "Designator Minimum",
153*12ccb71bSMatthias Ringwald     "Designator Maximum",
154*12ccb71bSMatthias Ringwald     "String Index",
155*12ccb71bSMatthias Ringwald     "String Minimum",
156*12ccb71bSMatthias Ringwald     "String Maximum",
157*12ccb71bSMatthias Ringwald     "Delimiter",
158*12ccb71bSMatthias Ringwald     "Reserved",
159*12ccb71bSMatthias Ringwald     "Reserved",
160*12ccb71bSMatthias Ringwald     "Reserved",
161*12ccb71bSMatthias Ringwald     "Reserved",
162*12ccb71bSMatthias Ringwald     "Reserved",
163*12ccb71bSMatthias Ringwald     "Reserved"
164*12ccb71bSMatthias Ringwald };
165*12ccb71bSMatthias Ringwald #endif
166*12ccb71bSMatthias Ringwald 
167*12ccb71bSMatthias Ringwald static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
168*12ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
169*12ccb71bSMatthias Ringwald     const char ** item_tag_table;
170*12ccb71bSMatthias Ringwald     switch (item->item_type){
171*12ccb71bSMatthias Ringwald         case Main:
172*12ccb71bSMatthias Ringwald             item_tag_table = main_tags;
173*12ccb71bSMatthias Ringwald             break;
174*12ccb71bSMatthias Ringwald         case Global:
175*12ccb71bSMatthias Ringwald             item_tag_table = global_tags;
176*12ccb71bSMatthias Ringwald             break;
177*12ccb71bSMatthias Ringwald         case Local:
178*12ccb71bSMatthias Ringwald             item_tag_table = local_tags;
179*12ccb71bSMatthias Ringwald             break;
180*12ccb71bSMatthias Ringwald         default:
181*12ccb71bSMatthias Ringwald             item_tag_table = NULL;
182*12ccb71bSMatthias Ringwald             break;
183*12ccb71bSMatthias Ringwald     }
184*12ccb71bSMatthias Ringwald     const char * item_tag_name = "Invalid";
185*12ccb71bSMatthias Ringwald     if (item_tag_table){
186*12ccb71bSMatthias Ringwald         item_tag_name = item_tag_table[item->item_tag];
187*12ccb71bSMatthias Ringwald     }
188*12ccb71bSMatthias Ringwald     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_table[item->item_tag], type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
189*12ccb71bSMatthias Ringwald #else
190*12ccb71bSMatthias Ringwald     UNUSED(parser);
191*12ccb71bSMatthias Ringwald     UNUSED(item);
192*12ccb71bSMatthias Ringwald #endif
193*12ccb71bSMatthias Ringwald }
194*12ccb71bSMatthias Ringwald 
195*12ccb71bSMatthias Ringwald // parse descriptor item and read up to 32-bit bit value
196*12ccb71bSMatthias Ringwald static void btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
197*12ccb71bSMatthias Ringwald     // parse item header
198*12ccb71bSMatthias Ringwald     if (hid_descriptor_len < 1) return;
199*12ccb71bSMatthias Ringwald     uint16_t pos = 0;
200*12ccb71bSMatthias Ringwald     uint8_t item_header = hid_descriptor[pos++];
201*12ccb71bSMatthias Ringwald     item->data_size = hid_item_sizes[item_header & 0x03];
202*12ccb71bSMatthias Ringwald     item->item_type = (item_header & 0x0c) >> 2;
203*12ccb71bSMatthias Ringwald     item->item_tag  = (item_header & 0xf0) >> 4;
204*12ccb71bSMatthias Ringwald     // long item
205*12ccb71bSMatthias Ringwald     if (item->data_size == 2 && item->item_tag == 0x0f && item->item_type == 3){
206*12ccb71bSMatthias Ringwald         if (hid_descriptor_len < 3) return;
207*12ccb71bSMatthias Ringwald         item->data_size = hid_descriptor[pos++];
208*12ccb71bSMatthias Ringwald         item->item_tag  = hid_descriptor[pos++];
209*12ccb71bSMatthias Ringwald     }
210*12ccb71bSMatthias Ringwald     item->item_size =  pos + item->data_size;
211*12ccb71bSMatthias Ringwald     item->item_value = 0;
212*12ccb71bSMatthias Ringwald 
213*12ccb71bSMatthias Ringwald     // read item value
214*12ccb71bSMatthias Ringwald     if (hid_descriptor_len < item->item_size) return;
215*12ccb71bSMatthias Ringwald     if (item->data_size > 4) return;
216*12ccb71bSMatthias Ringwald     int i;
217*12ccb71bSMatthias Ringwald     int sgnd = item->item_type == Global && item->item_tag > 0 && item->item_tag < 5;
218*12ccb71bSMatthias Ringwald     int32_t value = 0;
219*12ccb71bSMatthias Ringwald     uint8_t latest_byte = 0;
220*12ccb71bSMatthias Ringwald     for (i=0;i<item->data_size;i++){
221*12ccb71bSMatthias Ringwald         latest_byte = hid_descriptor[pos++];
222*12ccb71bSMatthias Ringwald         value = (latest_byte << (8*i)) | value;
223*12ccb71bSMatthias Ringwald     }
224*12ccb71bSMatthias Ringwald     if (sgnd && item->data_size > 0){
225*12ccb71bSMatthias Ringwald         if (latest_byte & 0x80) {
226*12ccb71bSMatthias Ringwald             value -= 1 << (item->data_size*8);
227*12ccb71bSMatthias Ringwald         }
228*12ccb71bSMatthias Ringwald     }
229*12ccb71bSMatthias Ringwald     item->item_value = value;
230*12ccb71bSMatthias Ringwald }
231*12ccb71bSMatthias Ringwald 
232*12ccb71bSMatthias Ringwald static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
233*12ccb71bSMatthias Ringwald     switch(item->item_tag){
234*12ccb71bSMatthias Ringwald         case UsagePage:
235*12ccb71bSMatthias Ringwald             parser->global_usage_page = item->item_value;
236*12ccb71bSMatthias Ringwald             break;
237*12ccb71bSMatthias Ringwald         case LogicalMinimum:
238*12ccb71bSMatthias Ringwald             parser->global_logical_minimum = item->item_value;
239*12ccb71bSMatthias Ringwald             break;
240*12ccb71bSMatthias Ringwald         case LogicalMaximum:
241*12ccb71bSMatthias Ringwald             parser->global_logical_maximum = item->item_value;
242*12ccb71bSMatthias Ringwald             break;
243*12ccb71bSMatthias Ringwald         case ReportSize:
244*12ccb71bSMatthias Ringwald             parser->global_report_size = item->item_value;
245*12ccb71bSMatthias Ringwald             break;
246*12ccb71bSMatthias Ringwald         case ReportID:
247*12ccb71bSMatthias Ringwald             if (parser->active_record && parser->global_report_id != item->item_value){
248*12ccb71bSMatthias Ringwald                 // log_debug("New report, don't match anymore");
249*12ccb71bSMatthias Ringwald                 parser->active_record = 0;
250*12ccb71bSMatthias Ringwald             }
251*12ccb71bSMatthias Ringwald             parser->global_report_id = item->item_value;
252*12ccb71bSMatthias Ringwald             // log_info("- Report ID: %02x", parser->global_report_id);
253*12ccb71bSMatthias Ringwald             break;
254*12ccb71bSMatthias Ringwald         case ReportCount:
255*12ccb71bSMatthias Ringwald             parser->global_report_count = item->item_value;
256*12ccb71bSMatthias Ringwald             break;
257*12ccb71bSMatthias Ringwald         default:
258*12ccb71bSMatthias Ringwald             break;
259*12ccb71bSMatthias Ringwald     }
260*12ccb71bSMatthias Ringwald }
261*12ccb71bSMatthias Ringwald 
262*12ccb71bSMatthias Ringwald static void hid_find_next_usage(btstack_hid_parser_t * parser){
263*12ccb71bSMatthias Ringwald     while (parser->available_usages == 0 && parser->usage_pos < parser->descriptor_pos){
264*12ccb71bSMatthias Ringwald         hid_descriptor_item_t usage_item;
265*12ccb71bSMatthias Ringwald         // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len
266*12ccb71bSMatthias Ringwald         btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
267*12ccb71bSMatthias Ringwald         if (usage_item.item_type == Global && usage_item.item_tag == UsagePage){
268*12ccb71bSMatthias Ringwald             parser->usage_page = usage_item.item_value;
269*12ccb71bSMatthias Ringwald         }
270*12ccb71bSMatthias Ringwald         if (usage_item.item_type == Local){
271*12ccb71bSMatthias Ringwald             uint32_t usage_value = (usage_item.data_size > 2) ? usage_item.item_value : (parser->usage_page << 16) | usage_item.item_value;
272*12ccb71bSMatthias Ringwald             switch (usage_item.item_tag){
273*12ccb71bSMatthias Ringwald                 case Usage:
274*12ccb71bSMatthias Ringwald                     parser->available_usages = 1;
275*12ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
276*12ccb71bSMatthias Ringwald                     break;
277*12ccb71bSMatthias Ringwald                 case UsageMinimum:
278*12ccb71bSMatthias Ringwald                     parser->usage_minimum = usage_value;
279*12ccb71bSMatthias Ringwald                     parser->have_usage_min = 1;
280*12ccb71bSMatthias Ringwald                     break;
281*12ccb71bSMatthias Ringwald                 case UsageMaximum:
282*12ccb71bSMatthias Ringwald                     parser->usage_maximum = usage_value;
283*12ccb71bSMatthias Ringwald                     parser->have_usage_max = 1;
284*12ccb71bSMatthias Ringwald                     break;
285*12ccb71bSMatthias Ringwald                 default:
286*12ccb71bSMatthias Ringwald                     break;
287*12ccb71bSMatthias Ringwald             }
288*12ccb71bSMatthias Ringwald             if (parser->have_usage_min && parser->have_usage_max){
289*12ccb71bSMatthias Ringwald                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1;
290*12ccb71bSMatthias Ringwald                 parser->have_usage_min = 0;
291*12ccb71bSMatthias Ringwald                 parser->have_usage_max = 0;
292*12ccb71bSMatthias Ringwald             }
293*12ccb71bSMatthias Ringwald         }
294*12ccb71bSMatthias Ringwald         parser->usage_pos += usage_item.item_size;
295*12ccb71bSMatthias Ringwald     }
296*12ccb71bSMatthias Ringwald }
297*12ccb71bSMatthias Ringwald 
298*12ccb71bSMatthias Ringwald static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
299*12ccb71bSMatthias Ringwald     hid_pretty_print_item(parser, item);
300*12ccb71bSMatthias Ringwald     int valid_field = 0;
301*12ccb71bSMatthias Ringwald     switch (item->item_type){
302*12ccb71bSMatthias Ringwald         case Main:
303*12ccb71bSMatthias Ringwald             switch (item->item_tag){
304*12ccb71bSMatthias Ringwald                 case Input:
305*12ccb71bSMatthias Ringwald                     valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_INPUT;
306*12ccb71bSMatthias Ringwald                     break;
307*12ccb71bSMatthias Ringwald                 case Output:
308*12ccb71bSMatthias Ringwald                     valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_OUTPUT;
309*12ccb71bSMatthias Ringwald                     break;
310*12ccb71bSMatthias Ringwald                 case Feature:
311*12ccb71bSMatthias Ringwald                     valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_FEATURE;
312*12ccb71bSMatthias Ringwald                     break;
313*12ccb71bSMatthias Ringwald                 default:
314*12ccb71bSMatthias Ringwald                     break;
315*12ccb71bSMatthias Ringwald             }
316*12ccb71bSMatthias Ringwald             break;
317*12ccb71bSMatthias Ringwald         case Global:
318*12ccb71bSMatthias Ringwald             btstack_hid_handle_global_item(parser, item);
319*12ccb71bSMatthias Ringwald             break;
320*12ccb71bSMatthias Ringwald         case Local:
321*12ccb71bSMatthias Ringwald             break;
322*12ccb71bSMatthias Ringwald     }
323*12ccb71bSMatthias Ringwald     if (!valid_field) return;
324*12ccb71bSMatthias Ringwald 
325*12ccb71bSMatthias Ringwald     // verify record id
326*12ccb71bSMatthias Ringwald     if (parser->global_report_id && !parser->active_record){
327*12ccb71bSMatthias Ringwald         if (parser->report[0] != parser->global_report_id){
328*12ccb71bSMatthias Ringwald             return;
329*12ccb71bSMatthias Ringwald         }
330*12ccb71bSMatthias Ringwald         parser->report_pos_in_bit += 8;
331*12ccb71bSMatthias Ringwald     }
332*12ccb71bSMatthias Ringwald     parser->active_record = 1;
333*12ccb71bSMatthias Ringwald     // handle constant fields used for padding
334*12ccb71bSMatthias Ringwald     if (item->item_value & 1){
335*12ccb71bSMatthias Ringwald         int item_bits = parser->global_report_size * parser->global_report_count;
336*12ccb71bSMatthias Ringwald #ifdef HID_PARSER_PRETTY_PRINT
337*12ccb71bSMatthias Ringwald         log_info("- Skip %u constant bits", item_bits);
338*12ccb71bSMatthias Ringwald #endif
339*12ccb71bSMatthias Ringwald         parser->report_pos_in_bit += item_bits;
340*12ccb71bSMatthias Ringwald         return;
341*12ccb71bSMatthias Ringwald     }
342*12ccb71bSMatthias Ringwald     // Empty Item
343*12ccb71bSMatthias Ringwald     if (parser->global_report_count == 0) return;
344*12ccb71bSMatthias Ringwald     // let's start
345*12ccb71bSMatthias Ringwald     parser->required_usages = parser->global_report_count;
346*12ccb71bSMatthias Ringwald }
347*12ccb71bSMatthias Ringwald 
348*12ccb71bSMatthias Ringwald static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
349*12ccb71bSMatthias Ringwald     if (item->item_type == Main){
350*12ccb71bSMatthias Ringwald         // reset usage
351*12ccb71bSMatthias Ringwald         parser->usage_pos  = parser->descriptor_pos;
352*12ccb71bSMatthias Ringwald         parser->usage_page = parser->global_usage_page;
353*12ccb71bSMatthias Ringwald     }
354*12ccb71bSMatthias Ringwald     parser->descriptor_pos += item->item_size;
355*12ccb71bSMatthias Ringwald }
356*12ccb71bSMatthias Ringwald 
357*12ccb71bSMatthias Ringwald static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
358*12ccb71bSMatthias Ringwald     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
359*12ccb71bSMatthias Ringwald         if (parser->descriptor_pos >= parser->descriptor_len){
360*12ccb71bSMatthias Ringwald             // end of descriptor
361*12ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
362*12ccb71bSMatthias Ringwald             break;
363*12ccb71bSMatthias Ringwald         }
364*12ccb71bSMatthias Ringwald         btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
365*12ccb71bSMatthias Ringwald         hid_process_item(parser, &parser->descriptor_item);
366*12ccb71bSMatthias Ringwald         if (parser->required_usages){
367*12ccb71bSMatthias Ringwald             hid_find_next_usage(parser);
368*12ccb71bSMatthias Ringwald             if (parser->available_usages) {
369*12ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
370*12ccb71bSMatthias Ringwald             } else {
371*12ccb71bSMatthias Ringwald                 log_error("no usages found");
372*12ccb71bSMatthias Ringwald                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
373*12ccb71bSMatthias Ringwald             }
374*12ccb71bSMatthias Ringwald         } else {
375*12ccb71bSMatthias Ringwald             hid_post_process_item(parser, &parser->descriptor_item);
376*12ccb71bSMatthias Ringwald         }
377*12ccb71bSMatthias Ringwald     }
378*12ccb71bSMatthias Ringwald }
379*12ccb71bSMatthias Ringwald 
380*12ccb71bSMatthias Ringwald // PUBLIC API
381*12ccb71bSMatthias Ringwald 
382*12ccb71bSMatthias Ringwald void btstack_hid_parser_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, btstack_hid_report_type_t hid_report_type, const uint8_t * hid_report, uint16_t hid_report_len){
383*12ccb71bSMatthias Ringwald 
384*12ccb71bSMatthias Ringwald     memset(parser, 0, sizeof(btstack_hid_parser_t));
385*12ccb71bSMatthias Ringwald 
386*12ccb71bSMatthias Ringwald     parser->descriptor     = hid_descriptor;
387*12ccb71bSMatthias Ringwald     parser->descriptor_len = hid_descriptor_len;
388*12ccb71bSMatthias Ringwald     parser->report_type    = hid_report_type;
389*12ccb71bSMatthias Ringwald     parser->report         = hid_report;
390*12ccb71bSMatthias Ringwald     parser->report_len     = hid_report_len;
391*12ccb71bSMatthias Ringwald     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
392*12ccb71bSMatthias Ringwald 
393*12ccb71bSMatthias Ringwald     btstack_hid_parser_find_next_usage(parser);
394*12ccb71bSMatthias Ringwald }
395*12ccb71bSMatthias Ringwald 
396*12ccb71bSMatthias Ringwald int  btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
397*12ccb71bSMatthias Ringwald     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
398*12ccb71bSMatthias Ringwald }
399*12ccb71bSMatthias Ringwald 
400*12ccb71bSMatthias Ringwald void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
401*12ccb71bSMatthias Ringwald 
402*12ccb71bSMatthias Ringwald     *usage_page = parser->usage_minimum >> 16;
403*12ccb71bSMatthias Ringwald 
404*12ccb71bSMatthias Ringwald     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
405*12ccb71bSMatthias Ringwald     int is_variable   = parser->descriptor_item.item_value & 2;
406*12ccb71bSMatthias Ringwald     int is_signed     = parser->global_logical_minimum < 0;
407*12ccb71bSMatthias Ringwald     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
408*12ccb71bSMatthias Ringwald     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1) >> 3, parser->report_len);
409*12ccb71bSMatthias Ringwald     int bytes_to_read = pos_end - pos_start + 1;
410*12ccb71bSMatthias Ringwald     int i;
411*12ccb71bSMatthias Ringwald     uint32_t multi_byte_value = 0;
412*12ccb71bSMatthias Ringwald     for (i=0;i < bytes_to_read;i++){
413*12ccb71bSMatthias Ringwald         multi_byte_value |= parser->report[pos_start+i] << (i*8);
414*12ccb71bSMatthias Ringwald     }
415*12ccb71bSMatthias Ringwald     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07)) & ((1<<parser->global_report_size)-1);
416*12ccb71bSMatthias 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);
417*12ccb71bSMatthias Ringwald     if (is_variable){
418*12ccb71bSMatthias Ringwald         *usage      = parser->usage_minimum & 0xffff;
419*12ccb71bSMatthias Ringwald         if (is_signed && (unsigned_value & (1<<(parser->global_report_size-1)))){
420*12ccb71bSMatthias Ringwald             *value = unsigned_value - (1<<parser->global_report_size);
421*12ccb71bSMatthias Ringwald         } else {
422*12ccb71bSMatthias Ringwald             *value = unsigned_value;
423*12ccb71bSMatthias Ringwald         }
424*12ccb71bSMatthias Ringwald     } else {
425*12ccb71bSMatthias Ringwald         *usage  = unsigned_value;
426*12ccb71bSMatthias Ringwald         *value  = 1;
427*12ccb71bSMatthias Ringwald     }
428*12ccb71bSMatthias Ringwald     parser->required_usages--;
429*12ccb71bSMatthias Ringwald     parser->report_pos_in_bit += parser->global_report_size;
430*12ccb71bSMatthias Ringwald 
431*12ccb71bSMatthias Ringwald     // next usage
432*12ccb71bSMatthias Ringwald     if (is_variable){
433*12ccb71bSMatthias Ringwald         parser->usage_minimum++;
434*12ccb71bSMatthias Ringwald         parser->available_usages--;
435*12ccb71bSMatthias Ringwald     } else {
436*12ccb71bSMatthias Ringwald         if (parser->required_usages == 0){
437*12ccb71bSMatthias Ringwald             parser->available_usages = 0;
438*12ccb71bSMatthias Ringwald         }
439*12ccb71bSMatthias Ringwald     }
440*12ccb71bSMatthias Ringwald     if (parser->available_usages) {
441*12ccb71bSMatthias Ringwald         return;
442*12ccb71bSMatthias Ringwald     }
443*12ccb71bSMatthias Ringwald     if (parser->required_usages == 0){
444*12ccb71bSMatthias Ringwald         hid_post_process_item(parser, &parser->descriptor_item);
445*12ccb71bSMatthias Ringwald         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
446*12ccb71bSMatthias Ringwald         btstack_hid_parser_find_next_usage(parser);
447*12ccb71bSMatthias Ringwald     } else {
448*12ccb71bSMatthias Ringwald         hid_find_next_usage(parser);
449*12ccb71bSMatthias Ringwald         if (parser->available_usages == 0) {
450*12ccb71bSMatthias Ringwald             parser->state = BTSTACK_HID_PARSER_COMPLETE;
451*12ccb71bSMatthias Ringwald         }
452*12ccb71bSMatthias Ringwald     }
453*12ccb71bSMatthias Ringwald }
454