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