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 bool is_variable = (parser->descriptor_item.item_value & 2) != 0; 378 bool 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 btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 437 switch (item.item_type){ 438 case Global: 439 switch ((GlobalItemTag)item.item_tag){ 440 case ReportID: 441 current_report_id = item.item_value; 442 break; 443 case ReportCount: 444 if (current_report_id != report_id) break; 445 report_count = item.item_value; 446 break; 447 case ReportSize: 448 if (current_report_id != report_id) break; 449 report_size = item.item_value; 450 break; 451 default: 452 break; 453 } 454 break; 455 case Main: 456 if (current_report_id != report_id) break; 457 switch ((MainItemTag)item.item_tag){ 458 case Input: 459 if (report_type != HID_REPORT_TYPE_INPUT) break; 460 valid_report_type = 1; 461 break; 462 case Output: 463 if (report_type != HID_REPORT_TYPE_OUTPUT) break; 464 valid_report_type = 1; 465 break; 466 case Feature: 467 if (report_type != HID_REPORT_TYPE_FEATURE) break; 468 valid_report_type = 1; 469 break; 470 default: 471 break; 472 } 473 if (!valid_report_type) break; 474 total_report_size += report_count * report_size; 475 report_size = 0; 476 report_count = 0; 477 break; 478 default: 479 break; 480 } 481 hid_descriptor_len -= item.item_size; 482 hid_descriptor += item.item_size; 483 } 484 return (total_report_size + 7)/8; 485 } 486 487 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 488 int current_report_id = 0; 489 while (hid_descriptor_len){ 490 hid_descriptor_item_t item; 491 btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 492 switch (item.item_type){ 493 case Global: 494 switch ((GlobalItemTag)item.item_tag){ 495 case ReportID: 496 current_report_id = item.item_value; 497 if (current_report_id != report_id) break; 498 return HID_REPORT_ID_VALID; 499 default: 500 break; 501 } 502 break; 503 default: 504 break; 505 } 506 hid_descriptor_len -= item.item_size; 507 hid_descriptor += item.item_size; 508 } 509 if (current_report_id != 0) return HID_REPORT_ID_INVALID; 510 return HID_REPORT_ID_UNDECLARED; 511 } 512 513 int btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 514 while (hid_descriptor_len){ 515 hid_descriptor_item_t item; 516 btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 517 switch (item.item_type){ 518 case Global: 519 switch ((GlobalItemTag)item.item_tag){ 520 case ReportID: 521 return 1; 522 default: 523 break; 524 } 525 break; 526 default: 527 break; 528 } 529 hid_descriptor_len -= item.item_size; 530 hid_descriptor += item.item_size; 531 } 532 return 0; 533 } 534