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