xref: /btstack/src/btstack_hid_parser.c (revision eb78fada061f58e4189707eba6bc4158d30cb49e)
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 hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
334     if ((TagType)item->item_type == Main){
335         // reset usage
336         parser->usage_pos  = parser->descriptor_pos;
337         parser->usage_page = parser->global_usage_page;
338     }
339     parser->descriptor_pos += item->item_size;
340 }
341 
342 static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
343     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
344         if (parser->descriptor_pos >= parser->descriptor_len){
345             // end of descriptor
346             parser->state = BTSTACK_HID_PARSER_COMPLETE;
347             break;
348         }
349         bool ok = btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
350         if (ok == false){
351             // abort parsing
352             parser->state = BTSTACK_HID_PARSER_COMPLETE;
353             break;
354         }
355         hid_process_item(parser, &parser->descriptor_item);
356         if (parser->required_usages){
357             hid_find_next_usage(parser);
358             if (parser->available_usages) {
359                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
360             } else {
361                 log_debug("no usages found");
362                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
363             }
364         } else {
365             hid_post_process_item(parser, &parser->descriptor_item);
366         }
367     }
368 }
369 
370 // PUBLIC API
371 
372 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){
373 
374     memset(parser, 0, sizeof(btstack_hid_parser_t));
375 
376     parser->descriptor     = hid_descriptor;
377     parser->descriptor_len = hid_descriptor_len;
378     parser->report_type    = hid_report_type;
379     parser->report         = hid_report;
380     parser->report_len     = hid_report_len;
381     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
382 
383     btstack_hid_parser_find_next_usage(parser);
384 }
385 
386 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
387     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
388 }
389 
390 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
391 
392     *usage_page = parser->usage_minimum >> 16;
393 
394     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
395     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
396     bool is_signed     = parser->global_logical_minimum < 0;
397     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
398     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
399     int bytes_to_read = pos_end - pos_start + 1;
400     int i;
401     uint32_t multi_byte_value = 0;
402     for (i=0;i < bytes_to_read;i++){
403         multi_byte_value |= parser->report[pos_start+i] << (i*8);
404     }
405     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
406     // 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);
407     if (is_variable){
408         *usage      = parser->usage_minimum & 0xffffu;
409         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
410             *value = unsigned_value - (1u<<parser->global_report_size);
411         } else {
412             *value = unsigned_value;
413         }
414     } else {
415         *usage  = unsigned_value;
416         *value  = 1;
417     }
418     parser->required_usages--;
419     parser->report_pos_in_bit += parser->global_report_size;
420 
421     // next usage
422     if (is_variable){
423         parser->usage_minimum++;
424         parser->available_usages--;
425         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
426             // usage min - max range smaller than report count, ignore remaining bit in report
427             log_debug("Ignoring %u items without Usage", parser->required_usages);
428             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
429             parser->required_usages = 0;
430         }
431     } else {
432         if (parser->required_usages == 0u){
433             parser->available_usages = 0;
434         }
435     }
436     if (parser->available_usages) {
437         return;
438     }
439     if (parser->required_usages == 0u){
440         hid_post_process_item(parser, &parser->descriptor_item);
441         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
442         btstack_hid_parser_find_next_usage(parser);
443     } else {
444         hid_find_next_usage(parser);
445         if (parser->available_usages == 0u) {
446             parser->state = BTSTACK_HID_PARSER_COMPLETE;
447         }
448     }
449 }
450 
451 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){
452     int total_report_size = 0;
453     int report_size = 0;
454     int report_count = 0;
455     int current_report_id = 0;
456 
457     while (hid_descriptor_len){
458         int valid_report_type = 0;
459         hid_descriptor_item_t item;
460         bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
461         if (ok == false) {
462             return 0;
463         }
464         switch (item.item_type){
465             case Global:
466                 switch ((GlobalItemTag)item.item_tag){
467                     case ReportID:
468                         current_report_id = item.item_value;
469                         break;
470                     case ReportCount:
471                         report_count = item.item_value;
472                         break;
473                     case ReportSize:
474                         report_size = item.item_value;
475                         break;
476                     default:
477                         break;
478                 }
479                 break;
480             case Main:
481                 if (current_report_id != report_id) break;
482                 switch ((MainItemTag)item.item_tag){
483                     case Input:
484                         if (report_type != HID_REPORT_TYPE_INPUT) break;
485                         valid_report_type = 1;
486                         break;
487                     case Output:
488                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
489                         valid_report_type = 1;
490                         break;
491                     case Feature:
492                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
493                         valid_report_type = 1;
494                         break;
495                     default:
496                         break;
497                 }
498                 if (!valid_report_type) break;
499                 total_report_size += report_count * report_size;
500                 break;
501             default:
502                 break;
503         }
504 		if (total_report_size > 0 && current_report_id != report_id) break;
505         hid_descriptor_len -= item.item_size;
506         hid_descriptor += item.item_size;
507     }
508     return (total_report_size + 7)/8;
509 }
510 
511 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
512     int current_report_id = 0;
513     while (hid_descriptor_len){
514         hid_descriptor_item_t item;
515         bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
516         if (ok == false){
517             return HID_REPORT_ID_INVALID;
518         }
519         switch (item.item_type){
520             case Global:
521                 switch ((GlobalItemTag)item.item_tag){
522                     case ReportID:
523                         current_report_id = item.item_value;
524                         if (current_report_id != report_id) break;
525                         return HID_REPORT_ID_VALID;
526                     default:
527                         break;
528                 }
529                 break;
530             default:
531                 break;
532         }
533         hid_descriptor_len -= item.item_size;
534         hid_descriptor += item.item_size;
535     }
536     if (current_report_id != 0) return HID_REPORT_ID_INVALID;
537     return HID_REPORT_ID_UNDECLARED;
538 }
539 
540 bool btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
541     while (hid_descriptor_len){
542         hid_descriptor_item_t item;
543         bool ok = btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
544         if (ok == false){
545             break;
546         }
547         switch (item.item_type){
548             case Global:
549                 switch ((GlobalItemTag)item.item_tag){
550                     case ReportID:
551                         return true;
552                     default:
553                         break;
554                 }
555                 break;
556             default:
557                 break;
558         }
559         hid_descriptor_len -= item.item_size;
560         hid_descriptor += item.item_size;
561     }
562     return false;
563 }
564