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