xref: /btstack/src/btstack_hid_parser.c (revision a8d51f092f1b660d0f6921369ad2bc3f9368296c)
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 MATTHIAS
24  * RINGWALD 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     int is_variable   = parser->descriptor_item.item_value & 2;
378     int 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         // printf("item: 0x%02x (%p)\n", *hid_descriptor, hid_descriptor);
437         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
438         switch (item.item_type){
439             case Global:
440                 switch ((GlobalItemTag)item.item_tag){
441                     case ReportID:
442                         current_report_id = item.item_value;
443                         break;
444                     case ReportCount:
445                         if (current_report_id != report_id) break;
446                         report_count = item.item_value;
447                         break;
448                     case ReportSize:
449                         if (current_report_id != report_id) break;
450                         report_size = item.item_value;
451                         break;
452                     default:
453                         break;
454                 }
455                 break;
456             case Main:
457                 if (current_report_id != report_id) break;
458                 switch ((MainItemTag)item.item_tag){
459                     case Input:
460                         if (report_type != HID_REPORT_TYPE_INPUT) break;
461                         valid_report_type = 1;
462                         break;
463                     case Output:
464                         if (report_type != HID_REPORT_TYPE_OUTPUT) break;
465                         valid_report_type = 1;
466                         break;
467                     case Feature:
468                         if (report_type != HID_REPORT_TYPE_FEATURE) break;
469                         valid_report_type = 1;
470                         break;
471                     default:
472                         break;
473                 }
474                 if (!valid_report_type) break;
475                 total_report_size += report_count * report_size;
476                 report_size = 0;
477                 report_count = 0;
478                 break;
479             default:
480                 break;
481         }
482         hid_descriptor_len -= item.item_size;
483         hid_descriptor += item.item_size;
484     }
485     return (total_report_size + 7)/8;
486 }
487 
488 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
489     int current_report_id = 0;
490     while (hid_descriptor_len){
491         hid_descriptor_item_t item;
492         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
493         switch (item.item_type){
494             case Global:
495                 switch ((GlobalItemTag)item.item_tag){
496                     case ReportID:
497                         current_report_id = item.item_value;
498                         // printf("current ID %d, searched ID %d\n", current_report_id, report_id);
499                         if (current_report_id != report_id) break;
500                         return HID_REPORT_ID_VALID;
501                     default:
502                         break;
503                 }
504                 break;
505             default:
506                 break;
507         }
508         hid_descriptor_len -= item.item_size;
509         hid_descriptor += item.item_size;
510     }
511     if (current_report_id != 0) return HID_REPORT_ID_INVALID;
512     return HID_REPORT_ID_UNDECLARED;
513 }
514 
515 int btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){
516     while (hid_descriptor_len){
517         hid_descriptor_item_t item;
518         btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len);
519         switch (item.item_type){
520             case Global:
521                 switch ((GlobalItemTag)item.item_tag){
522                     case ReportID:
523                         return 1;
524                     default:
525                         break;
526                 }
527                 break;
528             default:
529                 break;
530         }
531         hid_descriptor_len -= item.item_size;
532         hid_descriptor += item.item_size;
533     }
534     return 0;
535 }
536