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