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 <inttypes.h>
41 #include <string.h>
42
43 #include "btstack_hid_parser.h"
44 #include "btstack_util.h"
45 #include "btstack_debug.h"
46
47 // Not implemented:
48 // - Support for Push/Pop
49 // - Optional Pretty Print of HID Descriptor
50
51 /*
52 * btstack_hid_parser.c
53 */
54
55 #ifdef HID_PARSER_PRETTY_PRINT
56
57 static const char * type_names[] = {
58 "Main",
59 "Global",
60 "Local",
61 "Reserved"
62 };
63 static const char * main_tags[] = {
64 "",
65 "",
66 "",
67 "",
68 "",
69 "",
70 "",
71 "",
72 "Input ",
73 "Output",
74 "Collection",
75 "Feature",
76 "End Collection",
77 "Reserved",
78 "Reserved",
79 "Reserved"
80 };
81 static const char * global_tags[] = {
82 "Usage Page",
83 "Logical Minimum",
84 "Logical Maximum",
85 "Physical Minimum",
86 "Physical Maximum",
87 "Unit Exponent",
88 "Unit",
89 "Report Size",
90 "Report ID",
91 "Report Count",
92 "Push",
93 "Pop",
94 "Reserved",
95 "Reserved",
96 "Reserved",
97 "Reserved"
98 };
99 static const char * local_tags[] = {
100 "Usage",
101 "Usage Minimum",
102 "Usage Maximum",
103 "Designator Index",
104 "Designator Minimum",
105 "Designator Maximum",
106 "String Index",
107 "String Minimum",
108 "String Maximum",
109 "Delimiter",
110 "Reserved",
111 "Reserved",
112 "Reserved",
113 "Reserved",
114 "Reserved",
115 "Reserved"
116 };
117 #endif
118
119 // HID Descriptor Iterator
120
121 // parse descriptor item and read up to 32-bit bit value
btstack_hid_descriptor_parse_item(hid_descriptor_item_t * item,const uint8_t * hid_descriptor,uint16_t hid_descriptor_len)122 static bool btstack_hid_descriptor_parse_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
123
124 const int hid_item_sizes[] = { 0, 1, 2, 4 };
125
126 // parse item header
127 if (hid_descriptor_len < 1u) return false;
128 uint16_t pos = 0;
129 uint8_t item_header = hid_descriptor[pos++];
130 item->data_size = hid_item_sizes[item_header & 0x03u];
131 item->item_type = (item_header & 0x0cu) >> 2u;
132 item->item_tag = (item_header & 0xf0u) >> 4u;
133 // long item
134 if ((item->data_size == 2u) && (item->item_tag == 0x0fu) && (item->item_type == 3u)){
135 if (hid_descriptor_len < 3u) return false;
136 item->data_size = hid_descriptor[pos++];
137 item->item_tag = hid_descriptor[pos++];
138 }
139 item->item_size = pos + item->data_size;
140 item->item_value = 0;
141
142 // read item value
143 if (hid_descriptor_len < item->item_size) return false;
144 if (item->data_size > 4u) return false;
145 int i;
146 int sgnd = (item->item_type == Global) && (item->item_tag > 0u) && (item->item_tag < 5u);
147 int32_t value = 0;
148 uint8_t latest_byte = 0;
149 for (i=0;i<item->data_size;i++){
150 latest_byte = hid_descriptor[pos++];
151 value = (latest_byte << (8*i)) | value;
152 }
153 if (sgnd && (item->data_size > 0u)){
154 if (latest_byte & 0x80u) {
155 value -= 1u << (item->data_size*8u);
156 }
157 }
158 item->item_value = value;
159 return true;
160 }
161
btstack_hid_descriptor_iterator_pretty_print_item(btstack_hid_descriptor_iterator_t * iterator,hid_descriptor_item_t * item)162 static void btstack_hid_descriptor_iterator_pretty_print_item(btstack_hid_descriptor_iterator_t * iterator, hid_descriptor_item_t * item){
163 #ifdef HID_PARSER_PRETTY_PRINT
164 const char ** item_tag_table;
165 switch ((TagType)item->item_type){
166 case Main:
167 item_tag_table = main_tags;
168 break;
169 case Global:
170 item_tag_table = global_tags;
171 break;
172 case Local:
173 item_tag_table = local_tags;
174 break;
175 default:
176 item_tag_table = NULL;
177 break;
178 }
179 const char * item_tag_name = "Invalid";
180 if (item_tag_table){
181 item_tag_name = item_tag_table[item->item_tag];
182 }
183 log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value);
184 printf("%-15s (%-6s) // %02x 0x%0008x\n", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value);
185 #else
186 UNUSED(iterator);
187 UNUSED(item);
188 #endif
189 }
190
btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator,const uint8_t * hid_descriptor,uint16_t hid_descriptor_len)191 void btstack_hid_descriptor_iterator_init(btstack_hid_descriptor_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
192 iterator->descriptor = hid_descriptor;
193 iterator->descriptor_pos = 0;
194 iterator->descriptor_len = hid_descriptor_len;
195 iterator->item_ready = false;
196 iterator->valid = true;
197 }
198
btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator)199 bool btstack_hid_descriptor_iterator_has_more(btstack_hid_descriptor_iterator_t * iterator){
200 if ((iterator->item_ready == false) && (iterator->descriptor_len > iterator->descriptor_pos)){
201 uint16_t item_len = iterator->descriptor_len - iterator->descriptor_pos;
202 const uint8_t *item_data = &iterator->descriptor[iterator->descriptor_pos];
203 bool ok = btstack_hid_descriptor_parse_item(&iterator->descriptor_item, item_data, item_len);
204 btstack_hid_descriptor_iterator_pretty_print_item(iterator, &iterator->descriptor_item);
205 if (ok){
206 iterator->item_ready = true;
207 } else {
208 iterator->valid = false;
209 }
210 }
211 return iterator->item_ready;
212 }
213
btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator)214 const hid_descriptor_item_t * btstack_hid_descriptor_iterator_get_item(btstack_hid_descriptor_iterator_t * iterator){
215 iterator->descriptor_pos += iterator->descriptor_item.item_size;
216 iterator->item_ready = false;
217 return &iterator->descriptor_item;
218 }
219
btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator)220 bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * iterator){
221 return iterator->valid;
222 }
223
224 // HID Descriptor Usage Iterator
225
btstack_hid_usage_iterator_main_item_tag_matches_report_type(MainItemTag tag,hid_report_type_t report_type)226 static bool btstack_hid_usage_iterator_main_item_tag_matches_report_type(MainItemTag tag, hid_report_type_t report_type){
227 switch (tag){
228 case Input:
229 return report_type == HID_REPORT_TYPE_INPUT;
230 case Output:
231 return report_type == HID_REPORT_TYPE_OUTPUT;
232 case Feature:
233 return report_type == HID_REPORT_TYPE_FEATURE;
234 default:
235 return false;
236 }
237 }
238
btstack_hid_usage_iterator_handle_global_item(btstack_hid_usage_iterator_t * iterator,hid_descriptor_item_t * item)239 static void btstack_hid_usage_iterator_handle_global_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
240 switch((GlobalItemTag)item->item_tag){
241 case UsagePage:
242 iterator->global_usage_page = item->item_value;
243 break;
244 case LogicalMinimum:
245 iterator->global_logical_minimum = item->item_value;
246 break;
247 case LogicalMaximum:
248 iterator->global_logical_maximum = item->item_value;
249 break;
250 case ReportSize:
251 iterator->global_report_size = item->item_value;
252 break;
253 case ReportID:
254 iterator->global_report_id = item->item_value;
255 break;
256 case ReportCount:
257 iterator->global_report_count = item->item_value;
258 break;
259
260 // TODO handle tags
261 case PhysicalMinimum:
262 case PhysicalMaximum:
263 case UnitExponent:
264 case Unit:
265 case Push:
266 case Pop:
267 break;
268
269 default:
270 btstack_assert(false);
271 break;
272 }
273 }
274
btstack_usage_iterator_hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator)275 static void btstack_usage_iterator_hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator){
276 bool have_usage_min = false;
277 bool have_usage_max = false;
278 main_iterator->usage_range = false;
279 btstack_hid_descriptor_iterator_t iterator;
280 btstack_hid_descriptor_iterator_init(&iterator, &main_iterator->descriptor[main_iterator->usage_pos], main_iterator->descriptor_len - main_iterator->usage_pos);
281 while ((main_iterator->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){
282 hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator);
283 if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
284 main_iterator->usage_page = usage_item.item_value;
285 }
286 if (usage_item.item_type == Local){
287 uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((main_iterator->usage_page << 16u) | usage_item.item_value);
288 switch (usage_item.item_tag){
289 case Usage:
290 main_iterator->available_usages = 1;
291 main_iterator->usage_minimum = usage_value;
292 break;
293 case UsageMinimum:
294 main_iterator->usage_minimum = usage_value;
295 have_usage_min = true;
296 break;
297 case UsageMaximum:
298 main_iterator->usage_maximum = usage_value;
299 have_usage_max = true;
300 break;
301 default:
302 break;
303 }
304 if (have_usage_min && have_usage_max){
305 main_iterator->available_usages = main_iterator->usage_maximum - main_iterator->usage_minimum + 1u;
306 main_iterator->usage_range = true;
307 if (main_iterator->available_usages < main_iterator->required_usages){
308 log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", main_iterator->usage_minimum & 0xffff, main_iterator->usage_maximum & 0xffff, main_iterator->required_usages);
309 }
310 }
311 }
312 }
313 main_iterator->usage_pos += iterator.descriptor_pos;
314 }
315
btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator,hid_descriptor_item_t * item)316 static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
317 int valid_field = 0;
318 uint16_t report_id_before;
319 switch ((TagType)item->item_type){
320 case Main:
321 valid_field = btstack_hid_usage_iterator_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
322 iterator->report_type);
323 break;
324 case Global:
325 report_id_before = iterator->global_report_id;
326 btstack_hid_usage_iterator_handle_global_item(iterator, item);
327 // track record id for report handling, reset report position
328 if (report_id_before != iterator->global_report_id){
329 iterator->report_pos_in_bit = 0u;
330 }
331 break;
332 case Local:
333 case Reserved:
334 break;
335 default:
336 btstack_assert(false);
337 break;
338 }
339 if (!valid_field) return;
340
341 // handle constant fields used for padding
342 if (item->item_value & 1){
343 int item_bits = iterator->global_report_size * iterator->global_report_count;
344 #ifdef HID_PARSER_PRETTY_PRINT
345 log_info("- Skip %u constant bits", item_bits);
346 #endif
347 iterator->report_pos_in_bit += item_bits;
348 return;
349 }
350 // Empty Item
351 if (iterator->global_report_count == 0u) return;
352 // let's start
353 iterator->required_usages = iterator->global_report_count;
354 }
355
btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator)356 static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) {
357 while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){
358 iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator);
359
360 btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item);
361
362 if (iterator->required_usages){
363 btstack_usage_iterator_hid_find_next_usage(iterator);
364 if (iterator->available_usages) {
365 iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
366 return;
367 } else {
368 log_debug("no usages found");
369 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
370 return;
371 }
372 } else {
373 if ((TagType) (&iterator->descriptor_item)->item_type == Main) {
374 // reset usage
375 iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos;
376 iterator->usage_page = iterator->global_usage_page;
377 }
378 }
379 }
380 // end of descriptor
381 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
382 }
383
btstack_hid_usage_iterator_init(btstack_hid_usage_iterator_t * iterator,const uint8_t * hid_descriptor,uint16_t hid_descriptor_len,hid_report_type_t hid_report_type)384 void btstack_hid_usage_iterator_init(btstack_hid_usage_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){
385 memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t));
386
387 iterator->descriptor = hid_descriptor;
388 iterator->descriptor_len = hid_descriptor_len;
389 iterator->report_type = hid_report_type;
390 iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
391 iterator->global_report_id = HID_REPORT_ID_UNDEFINED;
392 btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len);
393 }
394
btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator)395 bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){
396 while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){
397 btstack_hid_usage_iterator_find_next_usage(iterator);
398 }
399 return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
400 }
401
btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator,btstack_hid_usage_item_t * item)402 void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){
403 // cache current values
404 memset(item, 0, sizeof(btstack_hid_usage_item_t));
405 item->size = iterator->global_report_size;
406 item->report_id = iterator->global_report_id;
407 item->usage_page = iterator->usage_minimum >> 16;
408 item->bit_pos = iterator->report_pos_in_bit;
409
410 bool is_variable = (iterator->descriptor_item.item_value & 2) != 0;
411 if (is_variable){
412 item->usage = iterator->usage_minimum & 0xffffu;
413 }
414 iterator->required_usages--;
415 iterator->report_pos_in_bit += iterator->global_report_size;
416
417 // cache descriptor item and
418 item->descriptor_item = iterator->descriptor_item;
419 item->global_logical_minimum = iterator->global_logical_minimum;
420
421 // next usage
422 if (is_variable){
423 iterator->usage_minimum++;
424 iterator->available_usages--;
425 if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){
426 // usage min - max range smaller than report count, ignore remaining bit in report
427 log_debug("Ignoring %u items without Usage", iterator->required_usages);
428 iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages;
429 iterator->required_usages = 0;
430 }
431 } else {
432 if (iterator->required_usages == 0u){
433 iterator->available_usages = 0;
434 }
435 }
436
437 if (iterator->available_usages) {
438 return;
439 }
440 if (iterator->required_usages == 0u){
441 iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
442 } else {
443 btstack_usage_iterator_hid_find_next_usage(iterator);
444 if (iterator->available_usages == 0u) {
445 iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
446 }
447 }
448 }
449
450
451 // HID Report Parser
452
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)453 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){
454 btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type);
455 parser->report = hid_report;
456 parser->report_len = hid_report_len;
457 parser->have_report_usage_ready = false;
458 }
459
460 /**
461 * @brief Checks if more fields are available
462 * @param parser
463 */
btstack_hid_parser_has_more(btstack_hid_parser_t * parser)464 bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
465 while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){
466 btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor_usage_item);
467 // ignore usages for other report ids
468 if (parser->descriptor_usage_item.report_id != HID_REPORT_ID_UNDEFINED){
469 if (parser->descriptor_usage_item.report_id != parser->report[0]){
470 continue;
471 }
472 }
473 parser->have_report_usage_ready = true;
474 }
475 return parser->have_report_usage_ready;
476 }
477
478 /**
479 * @brief Get next field
480 * @param parser
481 * @param usage_page
482 * @param usage
483 * @param value provided in HID report
484 */
485
btstack_hid_parser_get_field(btstack_hid_parser_t * parser,uint16_t * usage_page,uint16_t * usage,int32_t * value)486 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){
487
488 // fetch data from descriptor usage item
489 uint16_t bit_pos = parser->descriptor_usage_item.bit_pos;
490 uint16_t size = parser->descriptor_usage_item.size;
491 *usage_page = parser->descriptor_usage_item.usage_page;
492 *usage = parser->descriptor_usage_item.usage;
493
494 // skip optional Report ID
495 if (parser->descriptor_usage_item.report_id != HID_REPORT_ID_UNDEFINED){
496 bit_pos += 8;
497 }
498
499 // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len
500 bool is_variable = (parser->descriptor_usage_item.descriptor_item.item_value & 2) != 0;
501 bool is_signed = parser->descriptor_usage_item.global_logical_minimum < 0;
502 int pos_start = btstack_min( bit_pos >> 3, parser->report_len);
503 int pos_end = btstack_min( (bit_pos + size - 1u) >> 3u, parser->report_len);
504 int bytes_to_read = pos_end - pos_start + 1;
505
506 int i;
507 uint32_t multi_byte_value = 0;
508 for (i=0;i < bytes_to_read;i++){
509 multi_byte_value |= parser->report[pos_start+i] << (i*8);
510 }
511 uint32_t unsigned_value = (multi_byte_value >> (bit_pos & 0x07u)) & ((1u<<size)-1u);
512 // 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);
513 if (is_variable){
514 if (is_signed && (unsigned_value & (1u<<(size-1u)))){
515 *value = unsigned_value - (1u<<size);
516 } else {
517 *value = unsigned_value;
518 }
519 } else {
520 *usage = unsigned_value;
521 *value = 1;
522 }
523
524 parser->have_report_usage_ready = false;
525 }
526
527
528 // Utility functions
529
btstack_hid_get_report_size_for_id(uint16_t report_id,hid_report_type_t report_type,const uint8_t * hid_descriptor,uint16_t hid_descriptor_len)530 int btstack_hid_get_report_size_for_id(uint16_t report_id, hid_report_type_t report_type, const uint8_t *hid_descriptor,
531 uint16_t hid_descriptor_len) {
532 int total_report_size = 0;
533 int report_size = 0;
534 int report_count = 0;
535 int current_report_id = HID_REPORT_ID_UNDEFINED;
536
537 btstack_hid_descriptor_iterator_t iterator;
538 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
539 while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
540 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
541 int valid_report_type = 0;
542 switch (item->item_type) {
543 case Global:
544 switch ((GlobalItemTag) item->item_tag) {
545 case ReportID:
546 current_report_id = item->item_value;
547 break;
548 case ReportCount:
549 report_count = item->item_value;
550 break;
551 case ReportSize:
552 report_size = item->item_value;
553 break;
554 default:
555 break;
556 }
557 break;
558 case Main:
559 if (current_report_id != report_id) break;
560 switch ((MainItemTag) item->item_tag) {
561 case Input:
562 if (report_type != HID_REPORT_TYPE_INPUT) break;
563 valid_report_type = 1;
564 break;
565 case Output:
566 if (report_type != HID_REPORT_TYPE_OUTPUT) break;
567 valid_report_type = 1;
568 break;
569 case Feature:
570 if (report_type != HID_REPORT_TYPE_FEATURE) break;
571 valid_report_type = 1;
572 break;
573 default:
574 break;
575 }
576 if (!valid_report_type) break;
577 total_report_size += report_count * report_size;
578 break;
579 default:
580 break;
581 }
582 if (total_report_size > 0 && current_report_id != report_id) break;
583 }
584
585 if (btstack_hid_descriptor_iterator_valid(&iterator)){
586 return (total_report_size + 7) / 8;
587 } else {
588 return 0;
589 }
590 }
591
btstack_hid_report_id_valid(uint16_t report_id,const uint8_t * hid_descriptor,uint16_t hid_descriptor_len)592 hid_report_id_status_t btstack_hid_report_id_valid(uint16_t report_id, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){
593 uint16_t current_report_id = HID_REPORT_ID_UNDEFINED;
594 btstack_hid_descriptor_iterator_t iterator;
595 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
596 bool report_id_found = false;
597 while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
598 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
599 switch (item->item_type){
600 case Global:
601 switch ((GlobalItemTag)item->item_tag){
602 case ReportID:
603 current_report_id = item->item_value;
604 if (current_report_id == report_id) {
605 report_id_found = true;
606 }
607 default:
608 break;
609 }
610 break;
611 default:
612 break;
613 }
614 }
615
616 if (btstack_hid_descriptor_iterator_valid(&iterator)) {
617 if (report_id_found){
618 return HID_REPORT_ID_VALID;
619 }
620 if ((report_id == HID_REPORT_ID_UNDEFINED) && (current_report_id == HID_REPORT_ID_UNDEFINED)) {
621 return HID_REPORT_ID_VALID;
622 }
623 return HID_REPORT_ID_UNDECLARED;
624 } else {
625 return HID_REPORT_ID_INVALID;
626 }
627 }
628
btstack_hid_report_id_declared(const uint8_t * hid_descriptor,uint16_t hid_descriptor_len)629 bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_descriptor_len) {
630 btstack_hid_descriptor_iterator_t iterator;
631 btstack_hid_descriptor_iterator_init(&iterator, hid_descriptor, hid_descriptor_len);
632 while (btstack_hid_descriptor_iterator_has_more(&iterator)) {
633 const hid_descriptor_item_t *const item = btstack_hid_descriptor_iterator_get_item(&iterator);
634 switch (item->item_type){
635 case Global:
636 switch ((GlobalItemTag)item->item_tag){
637 case ReportID:
638 return true;
639 default:
640 break;
641 }
642 break;
643 default:
644 break;
645 }
646 }
647 return false;
648 }
649