Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: Scan the device for group info before adding it

In order to allow the report descriptor to influence the hid device
properties, one needs to parse the descriptor early, without reference
to any driver. Scan the descriptor for group information during device
add, before the device has been broadcast to userland. The device
modalias will contain group information which can be used to
differentiate between modules. For starters, just handle the generic
group.

Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Acked-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Henrik Rydberg and committed by
Jiri Kosina
734c6609 4d53b801

+67
+62
drivers/hid/hid-core.c
··· 658 658 return NULL; 659 659 } 660 660 661 + static void hid_scan_usage(struct hid_device *hid, u32 usage) 662 + { 663 + } 664 + 665 + /* 666 + * Scan a report descriptor before the device is added to the bus. 667 + * Sets device groups and other properties that determine what driver 668 + * to load. 669 + */ 670 + static int hid_scan_report(struct hid_device *hid) 671 + { 672 + unsigned int page = 0, delim = 0; 673 + __u8 *start = hid->dev_rdesc; 674 + __u8 *end = start + hid->dev_rsize; 675 + unsigned int u, u_min = 0, u_max = 0; 676 + struct hid_item item; 677 + 678 + hid->group = HID_GROUP_GENERIC; 679 + while ((start = fetch_item(start, end, &item)) != NULL) { 680 + if (item.format != HID_ITEM_FORMAT_SHORT) 681 + return -EINVAL; 682 + if (item.type == HID_ITEM_TYPE_GLOBAL) { 683 + if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE) 684 + page = item_udata(&item) << 16; 685 + } else if (item.type == HID_ITEM_TYPE_LOCAL) { 686 + if (delim > 1) 687 + break; 688 + u = item_udata(&item); 689 + if (item.size <= 2) 690 + u += page; 691 + switch (item.tag) { 692 + case HID_LOCAL_ITEM_TAG_DELIMITER: 693 + delim += !!u; 694 + break; 695 + case HID_LOCAL_ITEM_TAG_USAGE: 696 + hid_scan_usage(hid, u); 697 + break; 698 + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: 699 + u_min = u; 700 + break; 701 + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: 702 + u_max = u; 703 + for (u = u_min; u <= u_max; u++) 704 + hid_scan_usage(hid, u); 705 + break; 706 + } 707 + } 708 + } 709 + 710 + return 0; 711 + } 712 + 661 713 /** 662 714 * hid_parse_report - parse device report 663 715 * ··· 2222 2170 return ret; 2223 2171 if (!hdev->dev_rdesc) 2224 2172 return -ENODEV; 2173 + 2174 + /* 2175 + * Scan generic devices for group information 2176 + */ 2177 + if (hid_ignore_special_drivers || 2178 + !hid_match_id(hdev, hid_have_special_driver)) { 2179 + ret = hid_scan_report(hdev); 2180 + if (ret) 2181 + hid_warn(hdev, "bad device descriptor (%d)\n", ret); 2182 + } 2225 2183 2226 2184 /* XXX hack, any other cleaner solution after the driver core 2227 2185 * is converted to allow more than 20 bytes as the device name? */
+5
include/linux/hid.h
··· 325 325 #define HID_QUIRK_NO_INPUT_SYNC 0x80000000 326 326 327 327 /* 328 + * HID device groups 329 + */ 330 + #define HID_GROUP_GENERIC 0x0001 331 + 332 + /* 328 333 * This is the global environment of the parser. This information is 329 334 * persistent for main-items. The global environment can be saved and 330 335 * restored with PUSH/POP statements.