xref: /btstack/src/btstack_hid_parser.c (revision fd8e652c142cbc2eb1aa512086594eb7f5f189cc)
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 void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
192     switch((GlobalItemTag)item->item_tag){
193         case UsagePage:
194             parser->global_usage_page = item->item_value;
195             break;
196         case LogicalMinimum:
197             parser->global_logical_minimum = item->item_value;
198             break;
199         case LogicalMaximum:
200             parser->global_logical_maximum = item->item_value;
201             break;
202         case ReportSize:
203             parser->global_report_size = item->item_value;
204             break;
205         case ReportID:
206             parser->global_report_id = item->item_value;
207             break;
208         case ReportCount:
209             parser->global_report_count = item->item_value;
210             break;
211 
212         // TODO handle tags
213         case PhysicalMinimum:
214         case PhysicalMaximum:
215         case UnitExponent:
216         case Unit:
217         case Push:
218         case Pop:
219             break;
220 
221         default:
222             btstack_assert(false);
223             break;
224     }
225 }
226 
227 static void hid_find_next_usage(btstack_hid_parser_t * parser){
228     bool have_usage_min = false;
229     bool have_usage_max = false;
230     parser->usage_range = false;
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         bool ok = btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
235         if (ok == false){
236             break;
237         }
238         if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
239             parser->usage_page = usage_item.item_value;
240         }
241         if (usage_item.item_type == Local){
242             uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
243             switch (usage_item.item_tag){
244                 case Usage:
245                     parser->available_usages = 1;
246                     parser->usage_minimum = usage_value;
247                     break;
248                 case UsageMinimum:
249                     parser->usage_minimum = usage_value;
250                     have_usage_min = true;
251                     break;
252                 case UsageMaximum:
253                     parser->usage_maximum = usage_value;
254                     have_usage_max = true;
255                     break;
256                 default:
257                     break;
258             }
259             if (have_usage_min && have_usage_max){
260                 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
261                 parser->usage_range = true;
262                 if (parser->available_usages < parser->required_usages){
263                     log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages);
264                 }
265             }
266         }
267         parser->usage_pos += usage_item.item_size;
268     }
269 }
270 
271 static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
272     hid_pretty_print_item(parser, item);
273     int valid_field = 0;
274     uint16_t report_id_before;
275     switch ((TagType)item->item_type){
276         case Main:
277             switch ((MainItemTag)item->item_tag){
278                 case Input:
279                     valid_field = parser->report_type == HID_REPORT_TYPE_INPUT;
280                     break;
281                 case Output:
282                     valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT;
283                     break;
284                 case Feature:
285                     valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE;
286                     break;
287                 default:
288                     break;
289             }
290             break;
291         case Global:
292             report_id_before = parser->global_report_id;
293             btstack_hid_handle_global_item(parser, item);
294             // track record id for report handling
295             if ((GlobalItemTag)item->item_tag == ReportID){
296                 if (parser->active_record && (report_id_before != item->item_value)){
297                     parser->active_record = 0;
298                 }
299             }
300             break;
301         case Local:
302         case Reserved:
303             break;
304         default:
305             btstack_assert(false);
306             break;
307     }
308     if (!valid_field) return;
309 
310     // verify record id
311     if (parser->global_report_id && !parser->active_record){
312         if (parser->report[0] != parser->global_report_id){
313             return;
314         }
315         parser->report_pos_in_bit += 8u;
316     }
317     parser->active_record = 1;
318     // handle constant fields used for padding
319     if (item->item_value & 1){
320         int item_bits = parser->global_report_size * parser->global_report_count;
321 #ifdef HID_PARSER_PRETTY_PRINT
322         log_info("- Skip %u constant bits", item_bits);
323 #endif
324         parser->report_pos_in_bit += item_bits;
325         return;
326     }
327     // Empty Item
328     if (parser->global_report_count == 0u) return;
329     // let's start
330     parser->required_usages = parser->global_report_count;
331 }
332 
333 static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
334     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
335         if (parser->descriptor_pos >= parser->descriptor_len){
336             // end of descriptor
337             parser->state = BTSTACK_HID_PARSER_COMPLETE;
338             break;
339         }
340         bool ok = btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
341         if (ok == false){
342             // abort parsing
343             parser->state = BTSTACK_HID_PARSER_COMPLETE;
344             break;
345         }
346         hid_process_item(parser, &parser->descriptor_item);
347         if (parser->required_usages){
348             hid_find_next_usage(parser);
349             if (parser->available_usages) {
350                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
351             } else {
352                 log_debug("no usages found");
353                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
354             }
355         } else {
356             if ((TagType) (&parser->descriptor_item)->item_type == Main) {
357                 // reset usage
358                 parser->usage_pos = parser->descriptor_pos;
359                 parser->usage_page = parser->global_usage_page;
360             }
361             parser->descriptor_pos += parser->descriptor_item.item_size;
362         }
363     }
364 }
365 
366 // PUBLIC API
367 
368 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){
369 
370     memset(parser, 0, sizeof(btstack_hid_parser_t));
371 
372     parser->descriptor     = hid_descriptor;
373     parser->descriptor_len = hid_descriptor_len;
374     parser->report_type    = hid_report_type;
375     parser->report         = hid_report;
376     parser->report_len     = hid_report_len;
377     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
378 
379     btstack_hid_parser_find_next_usage(parser);
380 }
381 
382 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
383     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
384 }
385 
386 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
387 
388     *usage_page = parser->usage_minimum >> 16;
389 
390     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
391     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
392     bool is_signed     = parser->global_logical_minimum < 0;
393     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
394     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
395     int bytes_to_read = pos_end - pos_start + 1;
396     int i;
397     uint32_t multi_byte_value = 0;
398     for (i=0;i < bytes_to_read;i++){
399         multi_byte_value |= parser->report[pos_start+i] << (i*8);
400     }
401     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
402     // 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);
403     if (is_variable){
404         *usage      = parser->usage_minimum & 0xffffu;
405         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
406             *value = unsigned_value - (1u<<parser->global_report_size);
407         } else {
408             *value = unsigned_value;
409         }
410     } else {
411         *usage  = unsigned_value;
412         *value  = 1;
413     }
414     parser->required_usages--;
415     parser->report_pos_in_bit += parser->global_report_size;
416 
417     // next usage
418     if (is_variable){
419         parser->usage_minimum++;
420         parser->available_usages--;
421         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
422             // usage min - max range smaller than report count, ignore remaining bit in report
423             log_debug("Ignoring %u items without Usage", parser->required_usages);
424             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
425             parser->required_usages = 0;
426         }
427     } else {
428         if (parser->required_usages == 0u){
429             parser->available_usages = 0;
430         }
431     }
432     if (parser->available_usages) {
433         return;
434     }
435     if (parser->required_usages == 0u){
436         parser->descriptor_pos += parser->descriptor_item.item_size;
437         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
438         btstack_hid_parser_find_next_usage(parser);
439     } else {
440         hid_find_next_usage(parser);
441         if (parser->available_usages == 0u) {
442             parser->state = BTSTACK_HID_PARSER_COMPLETE;
443         }
444     }
445 }
446 
447 void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
448     iterator->descriptor = hid_descriptor;
449     iterator->descriptor_pos = 0;
450     iterator->descriptor_len = hid_descriptor_len;
451     iterator->item_ready = false;
452     iterator->valid = true;
453 }
454 
455 bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
456     if ((iterator->item_ready == false) && (iterator->descriptor_len > 0)){
457         uint16_t  item_len = iterator->descriptor_len - iterator->descriptor_pos;
458         const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
459         bool ok = btstack_hid_parse_descriptor_item(&iterator->descriptor_item, item_data, item_len);
460         if (ok){
461             iterator->item_ready = true;
462         } else {
463             iterator->valid = false;
464         }
465     }
466     return iterator->item_ready;
467 }
468 
469 const hid_descriptor_item_t * const btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
470 
471     btstack_assert(iterator->descriptor_len >= iterator->descriptor_item.item_size);
472 
473     iterator->descriptor_len -= iterator->descriptor_item.item_size;
474     iterator->descriptor     += iterator->descriptor_item.item_size;
475     iterator->item_ready = false;
476     return &iterator->descriptor_item;
477 }
478 
479 bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
480     return iterator->valid;
481 }
482 
483 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) {
484     int total_report_size = 0;
485     int report_size = 0;
486     int report_count = 0;
487     int current_report_id = 0;
488 
489     btstack_hid_descriptor_iterator_t iterator;
490     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
491     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
492         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
493         int valid_report_type = 0;
494         switch (item->item_type) {
495             case Global:
496                 switch ((GlobalItemTag) item->item_tag) {
497                     case ReportID:
498                         current_report_id = item->item_value;
499                         break;
500                     case ReportCount:
501                         report_count = item->item_value;
502                         break;
503                     case ReportSize:
504                         report_size = item->item_value;
505                         break;
506                     default:
507                         break;
508                 }
509                 break;
510             case Main:
511                 if (current_report_id != report_id) break;
512                 switch ((MainItemTag) item->item_tag) {
513                     case Input:
514                         if (report_type != HID_REPORT_TYPE_INPUT) break;
515                         valid_report_type = 1;
516                         break;
517                     case Output:
518                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
519                         valid_report_type = 1;
520                         break;
521                     case Feature:
522                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
523                         valid_report_type = 1;
524                         break;
525                     default:
526                         break;
527                 }
528                 if (!valid_report_type) break;
529                 total_report_size += report_count * report_size;
530                 break;
531             default:
532                 break;
533         }
534         if (total_report_size > 0 && current_report_id != report_id) break;
535     }
536 
537     if (btstack_hid_descriptor_iterator_valid(&iterator)){
538         return (total_report_size + 7) / 8;
539     } else {
540         return 0;
541     }
542 }
543 
544 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
545     int current_report_id = -1;
546     btstack_hid_descriptor_iterator_t iterator;
547     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
548     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
549         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
550         switch (item->item_type){
551             case Global:
552                 switch ((GlobalItemTag)item->item_tag){
553                     case ReportID:
554                         current_report_id = item->item_value;
555                         if (current_report_id != report_id) break;
556                         return HID_REPORT_ID_VALID;
557                     default:
558                         break;
559                 }
560                 break;
561             default:
562                 break;
563         }
564     }
565 
566     if (btstack_hid_descriptor_iterator_valid(&iterator)) {
567         if (current_report_id != -1) {
568             return HID_REPORT_ID_INVALID;
569         } else {
570             return HID_REPORT_ID_UNDECLARED;
571         }
572     } else {
573         return HID_REPORT_ID_INVALID;
574     }
575 }
576 
577 bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
578     btstack_hid_descriptor_iterator_t iterator;
579     btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
580     while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
581         const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
582         switch (item->item_type){
583             case Global:
584                 switch ((GlobalItemTag)item->item_tag){
585                     case ReportID:
586                         return true;
587                     default:
588                         break;
589                 }
590                 break;
591             default:
592                 break;
593         }
594     }
595     return false;
596 }
597