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