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