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