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 const int hid_item_sizes[] = { 0, 1, 2, 4 }; 58 59 #ifdef HID_PARSER_PRETTY_PRINT 60 61 static const char * type_names[] = { 62 "Main", 63 "Global", 64 "Local", 65 "Reserved" 66 }; 67 static const char * main_tags[] = { 68 "", 69 "", 70 "", 71 "", 72 "", 73 "", 74 "", 75 "", 76 "Input ", 77 "Output", 78 "Collection", 79 "Feature", 80 "End Collection", 81 "Reserved", 82 "Reserved", 83 "Reserved" 84 }; 85 static const char * global_tags[] = { 86 "Usage Page", 87 "Logical Minimum", 88 "Logical Maximum", 89 "Physical Minimum", 90 "Physical Maximum", 91 "Unit Exponent", 92 "Unit", 93 "Report Size", 94 "Report ID", 95 "Report Count", 96 "Push", 97 "Pop", 98 "Reserved", 99 "Reserved", 100 "Reserved", 101 "Reserved" 102 }; 103 static const char * local_tags[] = { 104 "Usage", 105 "Usage Minimum", 106 "Usage Maximum", 107 "Designator Index", 108 "Designator Minimum", 109 "Designator Maximum", 110 "String Index", 111 "String Minimum", 112 "String Maximum", 113 "Delimiter", 114 "Reserved", 115 "Reserved", 116 "Reserved", 117 "Reserved", 118 "Reserved", 119 "Reserved" 120 }; 121 #endif 122 123 static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 124 #ifdef HID_PARSER_PRETTY_PRINT 125 const char ** item_tag_table; 126 switch (item->item_type){ 127 case Main: 128 item_tag_table = main_tags; 129 break; 130 case Global: 131 item_tag_table = global_tags; 132 break; 133 case Local: 134 item_tag_table = local_tags; 135 break; 136 default: 137 item_tag_table = NULL; 138 break; 139 } 140 const char * item_tag_name = "Invalid"; 141 if (item_tag_table){ 142 item_tag_name = item_tag_table[item->item_tag]; 143 } 144 log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value); 145 #else 146 UNUSED(parser); 147 UNUSED(item); 148 #endif 149 } 150 151 // parse descriptor item and read up to 32-bit bit value 152 void btstack_hid_parse_descriptor_item(hid_descriptor_item_t * item, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len){ 153 // parse item header 154 if (hid_descriptor_len < 1) return; 155 uint16_t pos = 0; 156 uint8_t item_header = hid_descriptor[pos++]; 157 item->data_size = hid_item_sizes[item_header & 0x03]; 158 item->item_type = (item_header & 0x0c) >> 2; 159 item->item_tag = (item_header & 0xf0) >> 4; 160 // long item 161 if ((item->data_size == 2) && (item->item_tag == 0x0f) && (item->item_type == 3)){ 162 if (hid_descriptor_len < 3) return; 163 item->data_size = hid_descriptor[pos++]; 164 item->item_tag = hid_descriptor[pos++]; 165 } 166 item->item_size = pos + item->data_size; 167 item->item_value = 0; 168 169 // read item value 170 if (hid_descriptor_len < item->item_size) return; 171 if (item->data_size > 4) return; 172 int i; 173 int sgnd = (item->item_type == Global) && (item->item_tag > 0) && (item->item_tag < 5); 174 int32_t value = 0; 175 uint8_t latest_byte = 0; 176 for (i=0;i<item->data_size;i++){ 177 latest_byte = hid_descriptor[pos++]; 178 value = (latest_byte << (8*i)) | value; 179 } 180 if (sgnd && (item->data_size > 0)){ 181 if (latest_byte & 0x80) { 182 value -= 1 << (item->data_size*8); 183 } 184 } 185 item->item_value = value; 186 } 187 188 static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 189 switch(item->item_tag){ 190 case UsagePage: 191 parser->global_usage_page = item->item_value; 192 break; 193 case LogicalMinimum: 194 parser->global_logical_minimum = item->item_value; 195 break; 196 case LogicalMaximum: 197 parser->global_logical_maximum = item->item_value; 198 break; 199 case ReportSize: 200 parser->global_report_size = item->item_value; 201 break; 202 case ReportID: 203 if (parser->active_record && (parser->global_report_id != item->item_value)){ 204 // log_debug("New report, don't match anymore"); 205 parser->active_record = 0; 206 } 207 parser->global_report_id = item->item_value; 208 // log_info("- Report ID: %02x", parser->global_report_id); 209 break; 210 case ReportCount: 211 parser->global_report_count = item->item_value; 212 break; 213 default: 214 break; 215 } 216 } 217 218 static void hid_find_next_usage(btstack_hid_parser_t * parser){ 219 while ((parser->available_usages == 0) && (parser->usage_pos < parser->descriptor_pos)){ 220 hid_descriptor_item_t usage_item; 221 // parser->usage_pos < parser->descriptor_pos < parser->descriptor_len 222 btstack_hid_parse_descriptor_item(&usage_item, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos); 223 if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){ 224 parser->usage_page = usage_item.item_value; 225 } 226 if (usage_item.item_type == Local){ 227 uint32_t usage_value = (usage_item.data_size > 2) ? usage_item.item_value : ((parser->usage_page << 16) | usage_item.item_value); 228 switch (usage_item.item_tag){ 229 case Usage: 230 parser->available_usages = 1; 231 parser->usage_minimum = usage_value; 232 break; 233 case UsageMinimum: 234 parser->usage_minimum = usage_value; 235 parser->have_usage_min = 1; 236 break; 237 case UsageMaximum: 238 parser->usage_maximum = usage_value; 239 parser->have_usage_max = 1; 240 break; 241 default: 242 break; 243 } 244 if (parser->have_usage_min && parser->have_usage_max){ 245 parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1; 246 parser->have_usage_min = 0; 247 parser->have_usage_max = 0; 248 } 249 } 250 parser->usage_pos += usage_item.item_size; 251 } 252 } 253 254 static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 255 hid_pretty_print_item(parser, item); 256 int valid_field = 0; 257 switch (item->item_type){ 258 case Main: 259 switch (item->item_tag){ 260 case Input: 261 valid_field = parser->report_type == HID_REPORT_TYPE_INPUT; 262 break; 263 case Output: 264 valid_field = parser->report_type == HID_REPORT_TYPE_OUTPUT; 265 break; 266 case Feature: 267 valid_field = parser->report_type == HID_REPORT_TYPE_FEATURE; 268 break; 269 default: 270 break; 271 } 272 break; 273 case Global: 274 btstack_hid_handle_global_item(parser, item); 275 break; 276 case Local: 277 break; 278 } 279 if (!valid_field) return; 280 281 // verify record id 282 if (parser->global_report_id && !parser->active_record){ 283 if (parser->report[0] != parser->global_report_id){ 284 return; 285 } 286 parser->report_pos_in_bit += 8; 287 } 288 parser->active_record = 1; 289 // handle constant fields used for padding 290 if (item->item_value & 1){ 291 int item_bits = parser->global_report_size * parser->global_report_count; 292 #ifdef HID_PARSER_PRETTY_PRINT 293 log_info("- Skip %u constant bits", item_bits); 294 #endif 295 parser->report_pos_in_bit += item_bits; 296 return; 297 } 298 // Empty Item 299 if (parser->global_report_count == 0) return; 300 // let's start 301 parser->required_usages = parser->global_report_count; 302 } 303 304 static void hid_post_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){ 305 if (item->item_type == Main){ 306 // reset usage 307 parser->usage_pos = parser->descriptor_pos; 308 parser->usage_page = parser->global_usage_page; 309 } 310 parser->descriptor_pos += item->item_size; 311 } 312 313 static void btstack_hid_parser_find_next_usage(btstack_hid_parser_t * parser){ 314 while (parser->state == BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM){ 315 if (parser->descriptor_pos >= parser->descriptor_len){ 316 // end of descriptor 317 parser->state = BTSTACK_HID_PARSER_COMPLETE; 318 break; 319 } 320 btstack_hid_parse_descriptor_item(&parser->descriptor_item, &parser->descriptor[parser->descriptor_pos], parser->descriptor_len - parser->descriptor_pos); 321 hid_process_item(parser, &parser->descriptor_item); 322 if (parser->required_usages){ 323 hid_find_next_usage(parser); 324 if (parser->available_usages) { 325 parser->state = BTSTACK_HID_PARSER_USAGES_AVAILABLE; 326 } else { 327 log_error("no usages found"); 328 parser->state = BTSTACK_HID_PARSER_COMPLETE; 329 } 330 } else { 331 hid_post_process_item(parser, &parser->descriptor_item); 332 } 333 } 334 } 335 336 // PUBLIC API 337 338 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){ 339 340 memset(parser, 0, sizeof(btstack_hid_parser_t)); 341 342 parser->descriptor = hid_descriptor; 343 parser->descriptor_len = hid_descriptor_len; 344 parser->report_type = hid_report_type; 345 parser->report = hid_report; 346 parser->report_len = hid_report_len; 347 parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM; 348 349 btstack_hid_parser_find_next_usage(parser); 350 } 351 352 int btstack_hid_parser_has_more(btstack_hid_parser_t * parser){ 353 return parser->state == BTSTACK_HID_PARSER_USAGES_AVAILABLE; 354 } 355 356 void btstack_hid_parser_get_field(btstack_hid_parser_t * parser, uint16_t * usage_page, uint16_t * usage, int32_t * value){ 357 358 *usage_page = parser->usage_minimum >> 16; 359 360 // read field (up to 32 bit unsigned, up to 31 bit signed - 32 bit signed behaviour is undefined) - check report len 361 int is_variable = parser->descriptor_item.item_value & 2; 362 int is_signed = parser->global_logical_minimum < 0; 363 int pos_start = btstack_min( parser->report_pos_in_bit >> 3, parser->report_len); 364 int pos_end = btstack_min( (parser->report_pos_in_bit + parser->global_report_size - 1) >> 3, parser->report_len); 365 int bytes_to_read = pos_end - pos_start + 1; 366 int i; 367 uint32_t multi_byte_value = 0; 368 for (i=0;i < bytes_to_read;i++){ 369 multi_byte_value |= parser->report[pos_start+i] << (i*8); 370 } 371 uint32_t unsigned_value = (multi_byte_value >> (parser->report_pos_in_bit & 0x07)) & ((1<<parser->global_report_size)-1); 372 // 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); 373 if (is_variable){ 374 *usage = parser->usage_minimum & 0xffff; 375 if (is_signed && (unsigned_value & (1<<(parser->global_report_size-1)))){ 376 *value = unsigned_value - (1<<parser->global_report_size); 377 } else { 378 *value = unsigned_value; 379 } 380 } else { 381 *usage = unsigned_value; 382 *value = 1; 383 } 384 parser->required_usages--; 385 parser->report_pos_in_bit += parser->global_report_size; 386 387 // next usage 388 if (is_variable){ 389 parser->usage_minimum++; 390 parser->available_usages--; 391 } else { 392 if (parser->required_usages == 0){ 393 parser->available_usages = 0; 394 } 395 } 396 if (parser->available_usages) { 397 return; 398 } 399 if (parser->required_usages == 0){ 400 hid_post_process_item(parser, &parser->descriptor_item); 401 parser->state = BTSTACK_HID_PARSER_SCAN_FOR_REPORT_ITEM; 402 btstack_hid_parser_find_next_usage(parser); 403 } else { 404 hid_find_next_usage(parser); 405 if (parser->available_usages == 0) { 406 parser->state = BTSTACK_HID_PARSER_COMPLETE; 407 } 408 } 409 } 410 411 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){ 412 int total_report_size = 0; 413 int report_size = 0; 414 int report_count = 0; 415 int current_report_id = 0; 416 417 while (hid_descriptor_len){ 418 int valid_report_type = 0; 419 hid_descriptor_item_t item; 420 // printf("item: 0x%02x (%p)\n", *hid_descriptor, hid_descriptor); 421 btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 422 switch (item.item_type){ 423 case Global: 424 switch ((GlobalItemTag)item.item_tag){ 425 case ReportID: 426 current_report_id = item.item_value; 427 break; 428 case ReportCount: 429 if (current_report_id != report_id) break; 430 report_count = item.item_value; 431 break; 432 case ReportSize: 433 if (current_report_id != report_id) break; 434 report_size = item.item_value; 435 break; 436 default: 437 break; 438 } 439 break; 440 case Main: 441 if (current_report_id != report_id) break; 442 switch ((MainItemTag)item.item_tag){ 443 case Input: 444 if (report_type != HID_REPORT_TYPE_INPUT) break; 445 valid_report_type = 1; 446 break; 447 case Output: 448 if (report_type != HID_REPORT_TYPE_OUTPUT) break; 449 valid_report_type = 1; 450 break; 451 case Feature: 452 if (report_type != HID_REPORT_TYPE_FEATURE) break; 453 valid_report_type = 1; 454 break; 455 default: 456 break; 457 } 458 if (!valid_report_type) break; 459 total_report_size += report_count * report_size; 460 report_size = 0; 461 report_count = 0; 462 break; 463 default: 464 break; 465 } 466 hid_descriptor_len -= item.item_size; 467 hid_descriptor += item.item_size; 468 } 469 return (total_report_size + 7)/8; 470 } 471 472 hid_report_id_status_t btstack_hid_id_valid(int report_id, uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 473 int current_report_id = 0; 474 while (hid_descriptor_len){ 475 hid_descriptor_item_t item; 476 btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 477 switch (item.item_type){ 478 case Global: 479 switch ((GlobalItemTag)item.item_tag){ 480 case ReportID: 481 current_report_id = item.item_value; 482 // printf("current ID %d, searched ID %d\n", current_report_id, report_id); 483 if (current_report_id != report_id) break; 484 return HID_REPORT_ID_VALID; 485 default: 486 break; 487 } 488 break; 489 default: 490 break; 491 } 492 hid_descriptor_len -= item.item_size; 493 hid_descriptor += item.item_size; 494 } 495 if (current_report_id != 0) return HID_REPORT_ID_INVALID; 496 return HID_REPORT_ID_UNDECLARED; 497 } 498 499 int btstack_hid_report_id_declared(uint16_t hid_descriptor_len, const uint8_t * hid_descriptor){ 500 while (hid_descriptor_len){ 501 hid_descriptor_item_t item; 502 btstack_hid_parse_descriptor_item(&item, hid_descriptor, hid_descriptor_len); 503 switch (item.item_type){ 504 case Global: 505 switch ((GlobalItemTag)item.item_tag){ 506 case ReportID: 507 return 1; 508 default: 509 break; 510 } 511 break; 512 default: 513 break; 514 } 515 hid_descriptor_len -= item.item_size; 516 hid_descriptor += item.item_size; 517 } 518 return 0; 519 } 520