* Retrieves the device's Usage Page and Usage from the report descriptor. * The algorithm returns the current Usage Page/Usage pair whenever a new * Collection is found and a Usage Local Item is currently in scope. * Usage Local Items are consumed by each Main Item (See. 6.2.2.8). * The algorithm should give similar results as Apple's: * https://developer.apple.com/documentation/iokit/kiohi
| 289 | * -1 on a malformed report. |
| 290 | */ |
| 291 | static int get_next_hid_usage(__u8 *report_descriptor, __u32 size, unsigned int *pos, unsigned short *usage_page, unsigned short *usage) |
| 292 | { |
| 293 | int data_len, key_size; |
| 294 | int initial = *pos == 0; /* Used to handle case where no top-level application collection is defined */ |
| 295 | int usage_pair_ready = 0; |
| 296 | |
| 297 | /* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */ |
| 298 | int usage_found = 0; |
| 299 | |
| 300 | while (*pos < size) { |
| 301 | int key = report_descriptor[*pos]; |
| 302 | int key_cmd = key & 0xfc; |
| 303 | |
| 304 | /* Determine data_len and key_size */ |
| 305 | if (!get_hid_item_size(report_descriptor, *pos, size, &data_len, &key_size)) |
| 306 | return -1; /* malformed report */ |
| 307 | |
| 308 | switch (key_cmd) { |
| 309 | case 0x4: /* Usage Page 6.2.2.7 (Global) */ |
| 310 | *usage_page = get_hid_report_bytes(report_descriptor, size, data_len, *pos); |
| 311 | break; |
| 312 | |
| 313 | case 0x8: /* Usage 6.2.2.8 (Local) */ |
| 314 | *usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos); |
| 315 | usage_found = 1; |
| 316 | break; |
| 317 | |
| 318 | case 0xa0: /* Collection 6.2.2.4 (Main) */ |
| 319 | /* A Usage Item (Local) must be found for the pair to be valid */ |
| 320 | if (usage_found) |
| 321 | usage_pair_ready = 1; |
| 322 | |
| 323 | /* Usage is a Local Item, unset it */ |
| 324 | usage_found = 0; |
| 325 | break; |
| 326 | |
| 327 | case 0x80: /* Input 6.2.2.4 (Main) */ |
| 328 | case 0x90: /* Output 6.2.2.4 (Main) */ |
| 329 | case 0xb0: /* Feature 6.2.2.4 (Main) */ |
| 330 | case 0xc0: /* End Collection 6.2.2.4 (Main) */ |
| 331 | /* Usage is a Local Item, unset it */ |
| 332 | usage_found = 0; |
| 333 | break; |
| 334 | } |
| 335 | |
| 336 | /* Skip over this key and its associated data */ |
| 337 | *pos += data_len + key_size; |
| 338 | |
| 339 | /* Return usage pair */ |
| 340 | if (usage_pair_ready) |
| 341 | return 0; |
| 342 | } |
| 343 | |
| 344 | /* If no top-level application collection is found and usage page/usage pair is found, pair is valid |
| 345 | https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */ |
| 346 | if (initial && usage_found) |
| 347 | return 0; /* success */ |
| 348 |
no test coverage detected