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