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_table[item->item_tag], 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 == BTSTACK_HID_REPORT_TYPE_INPUT; 262 break; 263 case Output: 264 valid_field = parser->report_type == BTSTACK_HID_REPORT_TYPE_OUTPUT; 265 break; 266 case Feature: 267 valid_field = parser->report_type == BTSTACK_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, btstack_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, btstack_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 // printf("tag %d, report_type %d\n", item.item_tag, report_type); 443 switch ((MainItemTag)item.item_tag){ 444 case Input: 445 if (report_type != BTSTACK_HID_REPORT_TYPE_INPUT) break; 446 valid_report_type = 1; 447 break; 448 case Output: 449 if (report_type != BTSTACK_HID_REPORT_TYPE_OUTPUT) break; 450 valid_report_type = 1; 451 break; 452 case Feature: 453 if (report_type != BTSTACK_HID_REPORT_TYPE_FEATURE) break; 454 valid_report_type = 1; 455 break; 456 default: 457 break; 458 } 459 if (!valid_report_type) break; 460 total_report_size += report_count * report_size; 461 report_size = 0; 462 report_count = 0; 463 break; 464 default: 465 break; 466 } 467 hid_descriptor_len -= item.item_size; 468 hid_descriptor += item.item_size; 469 } 470 return (total_report_size + 7)/8; 471 }