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