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