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