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