xref: /btstack/src/btstack_hid_parser.c (revision 1a05cde17523aeefa494ec53fab09148bf6f7a2e)
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 #define ENABLE_LOG_DEBUG
41 
42 #include <string.h>
43 
44 #include "btstack_hid_parser.h"
45 #include "btstack_util.h"
46 #include "btstack_debug.h"
47 
48 // Not implemented:
49 // - Support for Push/Pop
50 // - Optional Pretty Print of HID Descripor
51 // - Support to query descriptort for contained usages, e.g. to detect keyboard or mouse
52 
53 // #define HID_PARSER_PRETTY_PRINT
54 
55 /*
56  *  btstack_hid_parser.c
57  */
58 
59 #ifdef HID_PARSER_PRETTY_PRINT
60 
61 static const char * type_names[] = {
62     "Main",
63     "Global",
64     "Local",
65     "Reserved"
66 };
67 static const char * main_tags[] = {
68     "",
69     "",
70     "",
71     "",
72     "",
73     "",
74     "",
75     "",
76     "Input ",
77     "Output",
78     "Collection",
79     "Feature",
80     "End Collection",
81     "Reserved",
82     "Reserved",
83     "Reserved"
84 };
85 static const char * global_tags[] = {
86     "Usage Page",
87     "Logical Minimum",
88     "Logical Maximum",
89     "Physical Minimum",
90     "Physical Maximum",
91     "Unit Exponent",
92     "Unit",
93     "Report Size",
94     "Report ID",
95     "Report Count",
96     "Push",
97     "Pop",
98     "Reserved",
99     "Reserved",
100     "Reserved",
101     "Reserved"
102 };
103 static const char * local_tags[] = {
104     "Usage",
105     "Usage Minimum",
106     "Usage Maximum",
107     "Designator Index",
108     "Designator Minimum",
109     "Designator Maximum",
110     "String Index",
111     "String Minimum",
112     "String Maximum",
113     "Delimiter",
114     "Reserved",
115     "Reserved",
116     "Reserved",
117     "Reserved",
118     "Reserved",
119     "Reserved"
120 };
121 #endif
122 
123 static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
124 #ifdef HID_PARSER_PRETTY_PRINT
125     const char ** item_tag_table;
126     switch ((TagType)item->item_type){
127         case Main:
128             item_tag_table = main_tags;
129             break;
130         case Global:
131             item_tag_table = global_tags;
132             break;
133         case Local:
134             item_tag_table = local_tags;
135             break;
136         default:
137             item_tag_table = NULL;
138             break;
139     }
140     const char * item_tag_name = "Invalid";
141     if (item_tag_table){
142         item_tag_name = item_tag_table[item->item_tag];
143     }
144     log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
145 #else
146     UNUSED(parser);
147     UNUSED(item);
148 #endif
149 }
150 
151 // parse descriptor item and read up to 32-bit bit value
152 void btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
153 
154     const int hid_item_sizes[] = { 0, 1, 2, 4 };
155 
156     // parse item header
157     if (hid_descriptor_len < 1u) return;
158     uint16_t pos = 0;
159     uint8_t item_header = hid_descriptor[pos++];
160     item->data_size = hid_item_sizes[item_header & 0x03u];
161     item->item_type = (item_header & 0x0cu) >> 2u;
162     item->item_tag  = (item_header & 0xf0u) >> 4u;
163     // long item
164     if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
165         if (hid_descriptor_len < 3u) return;
166         item->data_size = hid_descriptor[pos++];
167         item->item_tag  = hid_descriptor[pos++];
168     }
169     item->item_size =  pos + item->data_size;
170     item->item_value = 0;
171 
172     // read item value
173     if (hid_descriptor_len < item->item_size) return;
174     if (item->data_size > 4u) return;
175     int i;
176     int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
177     int32_t value = 0;
178     uint8_t latest_byte = 0;
179     for (i=0;i<item->data_size;i++){
180         latest_byte = hid_descriptor[pos++];
181         value = (latest_byte << (8*i)) | value;
182     }
183     if (sgnd && (item->data_size > 0u)){
184         if (latest_byte & 0x80u) {
185             value -= 1u << (item->data_size*8u);
186         }
187     }
188     item->item_value = value;
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         btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
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 [%04x..%04x] < Report Count %u", parser->usage_minimum, parser->usage_maximum, 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     switch ((TagType)item->item_type){
275         case Main:
276             switch ((MainItemTag)item->item_tag){
277                 case Input:
278                     valid_field = parser->report_type == HID_REPORT_TYPE_INPUT;
279                     break;
280                 case Output:
281                     valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT;
282                     break;
283                 case Feature:
284                     valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE;
285                     break;
286                 default:
287                     break;
288             }
289             break;
290         case Global:
291             btstack_hid_handle_global_item(parser, item);
292             break;
293         case Local:
294         case Reserved:
295             break;
296         default:
297             btstack_assert(false);
298             break;
299     }
300     if (!valid_field) return;
301 
302     // verify record id
303     if (parser->global_report_id && !parser->active_record){
304         if (parser->report[0] != parser->global_report_id){
305             return;
306         }
307         parser->report_pos_in_bit += 8u;
308     }
309     parser->active_record = 1;
310     // handle constant fields used for padding
311     if (item->item_value & 1){
312         int item_bits = parser->global_report_size * parser->global_report_count;
313 #ifdef HID_PARSER_PRETTY_PRINT
314         log_info("- Skip %u constant bits", item_bits);
315 #endif
316         parser->report_pos_in_bit += item_bits;
317         return;
318     }
319     // Empty Item
320     if (parser->global_report_count == 0u) return;
321     // let's start
322     parser->required_usages = parser->global_report_count;
323 }
324 
325 static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
326     if ((TagType)item->item_type == Main){
327         // reset usage
328         parser->usage_pos  = parser->descriptor_pos;
329         parser->usage_page = parser->global_usage_page;
330     }
331     parser->descriptor_pos += item->item_size;
332 }
333 
334 static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){
335     while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){
336         if (parser->descriptor_pos >= parser->descriptor_len){
337             // end of descriptor
338             parser->state = BTSTACK_HID_PARSER_COMPLETE;
339             break;
340         }
341         btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos);
342         hid_process_item(parser, &parser->descriptor_item);
343         if (parser->required_usages){
344             hid_find_next_usage(parser);
345             if (parser->available_usages) {
346                 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE;
347             } else {
348                 log_debug("no usages found");
349                 parser->state = BTSTACK_HID_PARSER_COMPLETE;
350             }
351         } else {
352             hid_post_process_item(parser, &parser->descriptor_item);
353         }
354     }
355 }
356 
357 // PUBLIC API
358 
359 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){
360 
361     memset(parser, 0, sizeof(btstack_hid_parser_t));
362 
363     parser->descriptor     = hid_descriptor;
364     parser->descriptor_len = hid_descriptor_len;
365     parser->report_type    = hid_report_type;
366     parser->report         = hid_report;
367     parser->report_len     = hid_report_len;
368     parser->state          = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
369 
370     btstack_hid_parser_find_next_usage(parser);
371 }
372 
373 int  btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
374     return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE;
375 }
376 
377 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
378 
379     *usage_page = parser->usage_minimum >> 16;
380 
381     // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
382     bool is_variable   = (parser->descriptor_item.item_value & 2) != 0;
383     bool is_signed     = parser->global_logical_minimum < 0;
384     int pos_start     = btstack_min(  parser->report_pos_in_bit >> 3, parser->report_len);
385     int pos_end       = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1u) >> 3u, parser->report_len);
386     int bytes_to_read = pos_end - pos_start + 1;
387     int i;
388     uint32_t multi_byte_value = 0;
389     for (i=0;i < bytes_to_read;i++){
390         multi_byte_value |= parser->report[pos_start+i] << (i*8);
391     }
392     uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07u)) & ((1u<<parser->global_report_size)-1u);
393     // 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);
394     if (is_variable){
395         *usage      = parser->usage_minimum & 0xffffu;
396         if (is_signed && (unsigned_value & (1u<<(parser->global_report_size-1u)))){
397             *value = unsigned_value - (1u<<parser->global_report_size);
398         } else {
399             *value = unsigned_value;
400         }
401     } else {
402         *usage  = unsigned_value;
403         *value  = 1;
404     }
405     parser->required_usages--;
406     parser->report_pos_in_bit += parser->global_report_size;
407 
408     // next usage
409     if (is_variable){
410         parser->usage_minimum++;
411         parser->available_usages--;
412         if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
413             // usage min - max range smaller than report count, ignore remaining bit in report
414             log_debug("Ignoring %u items without Usage", parser->required_usages);
415             parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
416             parser->required_usages = 0;
417         }
418     } else {
419         if (parser->required_usages == 0u){
420             parser->available_usages = 0;
421         }
422     }
423     if (parser->available_usages) {
424         return;
425     }
426     if (parser->required_usages == 0u){
427         hid_post_process_item(parser, &parser->descriptor_item);
428         parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM;
429         btstack_hid_parser_find_next_usage(parser);
430     } else {
431         hid_find_next_usage(parser);
432         if (parser->available_usages == 0u) {
433             parser->state = BTSTACK_HID_PARSER_COMPLETE;
434         }
435     }
436 }
437 
438 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){
439     int total_report_size = 0;
440     int report_size = 0;
441     int report_count = 0;
442     int current_report_id = 0;
443 
444     while (hid_descriptor_len){
445         int valid_report_type = 0;
446         hid_descriptor_item_t item;
447         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
448         switch (item.item_type){
449             case Global:
450                 switch ((GlobalItemTag)item.item_tag){
451                     case ReportID:
452                         current_report_id = item.item_value;
453                         break;
454                     case ReportCount:
455                         report_count = item.item_value;
456                         break;
457                     case ReportSize:
458                         report_size = item.item_value;
459                         break;
460                     default:
461                         break;
462                 }
463                 break;
464             case Main:
465                 if (current_report_id != report_id) break;
466                 switch ((MainItemTag)item.item_tag){
467                     case Input:
468                         if (report_type != HID_REPORT_TYPE_INPUT) break;
469                         valid_report_type = 1;
470                         break;
471                     case Output:
472                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
473                         valid_report_type = 1;
474                         break;
475                     case Feature:
476                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
477                         valid_report_type = 1;
478                         break;
479                     default:
480                         break;
481                 }
482                 if (!valid_report_type) break;
483                 total_report_size += report_count * report_size;
484                 break;
485             default:
486                 break;
487         }
488 		if (total_report_size > 0 && current_report_id != report_id) break;
489         hid_descriptor_len -= item.item_size;
490         hid_descriptor += item.item_size;
491     }
492     return (total_report_size + 7)/8;
493 }
494 
495 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
496     int current_report_id = 0;
497     while (hid_descriptor_len){
498         hid_descriptor_item_t item;
499         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
500         switch (item.item_type){
501             case Global:
502                 switch ((GlobalItemTag)item.item_tag){
503                     case ReportID:
504                         current_report_id = item.item_value;
505                         if (current_report_id != report_id) break;
506                         return HID_REPORT_ID_VALID;
507                     default:
508                         break;
509                 }
510                 break;
511             default:
512                 break;
513         }
514         hid_descriptor_len -= item.item_size;
515         hid_descriptor += item.item_size;
516     }
517     if (current_report_id != 0) return HID_REPORT_ID_INVALID;
518     return HID_REPORT_ID_UNDECLARED;
519 }
520 
521 int btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
522     while (hid_descriptor_len){
523         hid_descriptor_item_t item;
524         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
525         switch (item.item_type){
526             case Global:
527                 switch ((GlobalItemTag)item.item_tag){
528                     case ReportID:
529                         return 1;
530                     default:
531                         break;
532                 }
533                 break;
534             default:
535                 break;
536         }
537         hid_descriptor_len -= item.item_size;
538         hid_descriptor += item.item_size;
539     }
540     return 0;
541 }
542