xref: /btstack/src/btstack_hid_parser.c (revision 0a4fc5fb4cf07734e5d08b1a4aff4ac61888d491)
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 <inttypes.h>
41 #include <string.h>
42 
43 #include "btstack_hid_parser.h"
44 #include "btstack_util.h"
45 #include "btstack_debug.h"
46 
47 // Not implemented:
48 // - Support for Push/Pop
49 // - Optional Pretty Print of HID Descripor
50 // - Support to query descriptort for contained usages, e.g. to detect keyboard or mouse
51 
52 // #define HID_PARSER_PRETTY_PRINT
53 
54 /*
55  *  btstack_hid_parser.c
56  */
57 
58 #ifdef HID_PARSER_PRETTY_PRINT
59 
60 static const char * type_names[] = {
61     "Main",
62     "Global",
63     "Local",
64     "Reserved"
65 };
66 static const char * main_tags[] = {
67     "",
68     "",
69     "",
70     "",
71     "",
72     "",
73     "",
74     "",
75     "Input ",
76     "Output",
77     "Collection",
78     "Feature",
79     "End Collection",
80     "Reserved",
81     "Reserved",
82     "Reserved"
83 };
84 static const char * global_tags[] = {
85     "Usage Page",
86     "Logical Minimum",
87     "Logical Maximum",
88     "Physical Minimum",
89     "Physical Maximum",
90     "Unit Exponent",
91     "Unit",
92     "Report Size",
93     "Report ID",
94     "Report Count",
95     "Push",
96     "Pop",
97     "Reserved",
98     "Reserved",
99     "Reserved",
100     "Reserved"
101 };
102 static const char * local_tags[] = {
103     "Usage",
104     "Usage Minimum",
105     "Usage Maximum",
106     "Designator Index",
107     "Designator Minimum",
108     "Designator Maximum",
109     "String Index",
110     "String Minimum",
111     "String Maximum",
112     "Delimiter",
113     "Reserved",
114     "Reserved",
115     "Reserved",
116     "Reserved",
117     "Reserved",
118     "Reserved"
119 };
120 #endif
121 
122 static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
123 #ifdef HID_PARSER_PRETTY_PRINT
124     const char ** item_tag_table;
125     switch ((TagType)item->item_type){
126         case Main:
127             item_tag_table = main_tags;
128             break;
129         case Global:
130             item_tag_table = global_tags;
131             break;
132         case Local:
133             item_tag_table = local_tags;
134             break;
135         default:
136             item_tag_table = NULL;
137             break;
138     }
139     const char * item_tag_name = "Invalid";
140     if (item_tag_table){
141         item_tag_name = item_tag_table[item->item_tag];
142     }
143     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
144 #else
145     UNUSED(parser);
146     UNUSED(item);
147 #endif
148 }
149 
150 // parse descriptor item and read up to 32-bit bit value
151 bool btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
152 
153     const int hid_item_sizes[] = { 0, 1, 2, 4 };
154 
155     // parse item header
156     if (hid_descriptor_len < 1u) return false;
157     uint16_t pos = 0;
158     uint8_t item_header = hid_descriptor[pos++];
159     item->data_size = hid_item_sizes[item_header & 0x03u];
160     item->item_type = (item_header & 0x0cu) >> 2u;
161     item->item_tag  = (item_header & 0xf0u) >> 4u;
162     // long item
163     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
164         if (hid_descriptor_len < 3u) return false;
165         item->data_size = hid_descriptor[pos++];
166         item->item_tag  = hid_descriptor[pos++];
167     }
168     item->item_size =  pos + item->data_size;
169     item->item_value = 0;
170 
171     // read item value
172     if (hid_descriptor_len < item->item_size) return false;
173     if (item->data_size > 4u) return false;
174     int i;
175     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
176     int32_t value = 0;
177     uint8_t latest_byte = 0;
178     for (i=0;i<item->data_size;i++){
179         latest_byte = hid_descriptor[pos++];
180         value = (latest_byte << (8*i)) | value;
181     }
182     if (sgnd && (item->data_size > 0u)){
183         if (latest_byte & 0x80u) {
184             value -= 1u << (item->data_size*8u);
185         }
186     }
187     item->item_value = value;
188     return true;
189 }
190 
191 static bool btstack_hid_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){
192     switch (tag){
193         case Input:
194             return report_type == HID_REPORT_TYPE_INPUT;
195         case Output:
196             return report_type == HID_REPORT_TYPE_OUTPUT;
197         case Feature:
198             return report_type == HID_REPORT_TYPE_FEATURE;
199         default:
200             return false;
201     }
202 }
203 
204 static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
205     switch((GlobalItemTag)item->item_tag){
206         case UsagePage:
207             parser->global_usage_page = item->item_value;
208             break;
209         case LogicalMinimum:
210             parser->global_logical_minimum = item->item_value;
211             break;
212         case LogicalMaximum:
213             parser->global_logical_maximum = item->item_value;
214             break;
215         case ReportSize:
216             parser->global_report_size = item->item_value;
217             break;
218         case ReportID:
219             parser->global_report_id = item->item_value;
220             break;
221         case ReportCount:
222             parser->global_report_count = item->item_value;
223             break;
224 
225         // TODO handle tags
226         case PhysicalMinimum:
227         case PhysicalMaximum:
228         case UnitExponent:
229         case Unit:
230         case Push:
231         case Pop:
232             break;
233 
234         default:
235             btstack_assert(false);
236             break;
237     }
238 }
239 
240 static void hid_find_next_usage(btstack_hid_parser_t * parser){
241     bool have_usage_min = false;
242     bool have_usage_max = false;
243     parser->usage_range = false;
244     while ((parser->available_usages == 0u) && (parser->usage_pos < parser->descriptor_pos)){
245         hid_descriptor_item_t usage_item;
246         // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len
247         bool ok = btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
248         if (ok == false){
249             break;
250         }
251         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
252             parser->usage_page = usage_item.item_value;
253         }
254         if (usage_item.item_type == Local){
255             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
256             switch (usage_item.item_tag){
257                 case Usage:
258                     parser->available_usages = 1;
259                     parser->usage_minimum = usage_value;
260                     break;
261                 case UsageMinimum:
262                     parser->usage_minimum = usage_value;
263                     have_usage_min = true;
264                     break;
265                 case UsageMaximum:
266                     parser->usage_maximum = usage_value;
267                     have_usage_max = true;
268                     break;
269                 default:
270                     break;
271             }
272             if (have_usage_min && have_usage_max){
273                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
274                 parser->usage_range = true;
275                 if (parser->available_usages < parser->required_usages){
276                     log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages);
277                 }
278             }
279         }
280         parser->usage_pos += usage_item.item_size;
281     }
282 }
283 
284 static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
285     hid_pretty_print_item(parser, item);
286     int valid_field = 0;
287     uint16_t report_id_before;
288     switch ((TagType)item->item_type){
289         case Main:
290             valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
291                                                                         parser->report_type);
292             break;
293         case Global:
294             report_id_before = parser->global_report_id;
295             btstack_hid_handle_global_item(parser, item);
296             // track record id for report handling
297             if ((GlobalItemTag)item->item_tag == ReportID){
298                 if (parser->active_record && (report_id_before != item->item_value)){
299                     parser->active_record = 0;
300                 }
301             }
302             break;
303         case Local:
304         case Reserved:
305             break;
306         default:
307             btstack_assert(false);
308             break;
309     }
310     if (!valid_field) return;
311 
312     // verify record id
313     if (parser->global_report_id && !parser->active_record){
314         if (parser->report[0] != parser->global_report_id){
315             return;
316         }
317         parser->report_pos_in_bit += 8u;
318     }
319     parser->active_record = 1;
320     // handle constant fields used for padding
321     if (item->item_value & 1){
322         int item_bits = parser->global_report_size * parser->global_report_count;
323 #ifdef HID_PARSER_PRETTY_PRINT
324         log_info("- Skip %u constant bits", item_bits);
325 #endif
326         parser->report_pos_in_bit += item_bits;
327         return;
328     }
329     // Empty Item
330     if (parser->global_report_count == 0u) return;
331     // let's start
332     parser->required_usages = parser->global_report_count;
333 }
334 
335 static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
336     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
337         if (parser->descriptor_pos >= parser->descriptor_len){
338             // end of descriptor
339             parser->state = BTSTACK_HID_PARSER_COMPLETE;
340             break;
341         }
342         bool ok = btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
343         if (ok == false){
344             // abort parsing
345             parser->state = BTSTACK_HID_PARSER_COMPLETE;
346             break;
347         }
348         hid_process_item(parser, &parser->descriptor_item);
349         if (parser->required_usages){
350             hid_find_next_usage(parser);
351             if (parser->available_usages) {
352                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
353             } else {
354                 log_debug("no usages found");
355                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
356             }
357         } else {
358             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
359                 // reset usage
360                 parser->usage_pos = parser->descriptor_pos;
361                 parser->usage_page = parser->global_usage_page;
362             }
363             parser->descriptor_pos += parser->descriptor_item.item_size;
364         }
365     }
366 }
367 
368 // PUBLIC API
369 
370 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){
371 
372     memset(parser, 0, sizeof(btstack_hid_parser_t));
373 
374     parser->descriptor     = hid_descriptor;
375     parser->descriptor_len = hid_descriptor_len;
376     parser->report_type    = hid_report_type;
377     parser->report         = hid_report;
378     parser->report_len     = hid_report_len;
379     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
380 
381     btstack_hid_parser_find_next_usage(parser);
382 }
383 
384 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
385     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
386 }
387 
388 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
389 
390     *usage_page = parser->usage_minimum >> 16;
391 
392     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
393     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
394     bool is_signed     = parser->global_logical_minimum < 0;
395     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
396     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
397     int bytes_to_read = pos_end - pos_start + 1;
398     int i;
399     uint32_t multi_byte_value = 0;
400     for (i=0;i < bytes_to_read;i++){
401         multi_byte_value |= parser->report[pos_start+i] << (i*8);
402     }
403     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
404     // 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);
405     if (is_variable){
406         *usage      = parser->usage_minimum & 0xffffu;
407         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
408             *value = unsigned_value - (1u<<parser->global_report_size);
409         } else {
410             *value = unsigned_value;
411         }
412     } else {
413         *usage  = unsigned_value;
414         *value  = 1;
415     }
416     parser->required_usages--;
417     parser->report_pos_in_bit += parser->global_report_size;
418 
419     // next usage
420     if (is_variable){
421         parser->usage_minimum++;
422         parser->available_usages--;
423         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
424             // usage min - max range smaller than report count, ignore remaining bit in report
425             log_debug("Ignoring %u items without Usage", parser->required_usages);
426             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
427             parser->required_usages = 0;
428         }
429     } else {
430         if (parser->required_usages == 0u){
431             parser->available_usages = 0;
432         }
433     }
434     if (parser->available_usages) {
435         return;
436     }
437     if (parser->required_usages == 0u){
438         parser->descriptor_pos += parser->descriptor_item.item_size;
439         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
440         btstack_hid_parser_find_next_usage(parser);
441     } else {
442         hid_find_next_usage(parser);
443         if (parser->available_usages == 0u) {
444             parser->state = BTSTACK_HID_PARSER_COMPLETE;
445         }
446     }
447 }
448 
449 void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
450     iterator->descriptor = hid_descriptor;
451     iterator->descriptor_pos = 0;
452     iterator->descriptor_len = hid_descriptor_len;
453     iterator->item_ready = false;
454     iterator->valid = true;
455 }
456 
457 bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
458     if ((iterator->item_ready == false) && (iterator->descriptor_len > 0)){
459         uint16_t  item_len = iterator->descriptor_len - iterator->descriptor_pos;
460         const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
461         bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len);
462         if (ok){
463             iterator->item_ready = true;
464         } else {
465             iterator->valid = false;
466         }
467     }
468     return iterator->item_ready;
469 }
470 
471 const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
472 
473     btstack_assert(iterator->descriptor_len >= iterator->descriptor_item.item_size);
474 
475     iterator->descriptor_len -= iterator->descriptor_item.item_size;
476     iterator->descriptor     += iterator->descriptor_item.item_size;
477     iterator->item_ready = false;
478     return &iterator->descriptor_item;
479 }
480 
481 bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
482     return iterator->valid;
483 }
484 
485 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) {
486     int total_report_size = 0;
487     int report_size = 0;
488     int report_count = 0;
489     int current_report_id = 0;
490 
491     btstack_hid_descriptor_iterator_t iterator;
492     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
493     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
494         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
495         int valid_report_type = 0;
496         switch (item->item_type) {
497             case Global:
498                 switch ((GlobalItemTag) item->item_tag) {
499                     case ReportID:
500                         current_report_id = item->item_value;
501                         break;
502                     case ReportCount:
503                         report_count = item->item_value;
504                         break;
505                     case ReportSize:
506                         report_size = item->item_value;
507                         break;
508                     default:
509                         break;
510                 }
511                 break;
512             case Main:
513                 if (current_report_id != report_id) break;
514                 switch ((MainItemTag) item->item_tag) {
515                     case Input:
516                         if (report_type != HID_REPORT_TYPE_INPUT) break;
517                         valid_report_type = 1;
518                         break;
519                     case Output:
520                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
521                         valid_report_type = 1;
522                         break;
523                     case Feature:
524                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
525                         valid_report_type = 1;
526                         break;
527                     default:
528                         break;
529                 }
530                 if (!valid_report_type) break;
531                 total_report_size += report_count * report_size;
532                 break;
533             default:
534                 break;
535         }
536         if (total_report_size > 0 && current_report_id != report_id) break;
537     }
538 
539     if (btstack_hid_descriptor_iterator_valid(&iterator)){
540         return (total_report_size + 7) / 8;
541     } else {
542         return 0;
543     }
544 }
545 
546 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
547     int current_report_id = -1;
548     btstack_hid_descriptor_iterator_t iterator;
549     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
550     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
551         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
552         switch (item->item_type){
553             case Global:
554                 switch ((GlobalItemTag)item->item_tag){
555                     case ReportID:
556                         current_report_id = item->item_value;
557                         if (current_report_id != report_id) break;
558                         return HID_REPORT_ID_VALID;
559                     default:
560                         break;
561                 }
562                 break;
563             default:
564                 break;
565         }
566     }
567 
568     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
569         if (current_report_id != -1) {
570             return HID_REPORT_ID_INVALID;
571         } else {
572             return HID_REPORT_ID_UNDECLARED;
573         }
574     } else {
575         return HID_REPORT_ID_INVALID;
576     }
577 }
578 
579 bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
580     btstack_hid_descriptor_iterator_t iterator;
581     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
582     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
583         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
584         switch (item->item_type){
585             case Global:
586                 switch ((GlobalItemTag)item->item_tag){
587                     case ReportID:
588                         return true;
589                     default:
590                         break;
591                 }
592                 break;
593             default:
594                 break;
595         }
596     }
597     return false;
598 }
599