summaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-generic.c
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2017-11-20 05:48:44 -0500
committerJiri Kosina <jkosina@suse.cz>2017-11-21 05:14:48 -0500
commite04a0442d33b8cf183bba38646447b891bb02123 (patch)
tree4d61ef8247b214bfc4f91fc79bd6e72c4dee77c4 /drivers/hid/hid-generic.c
parentf745d162f469a4b1e805779a8b0d9157100c813c (diff)
HID: core: remove the absolute need of hid_have_special_driver[]
Most HID devices behave properly when they are used with hid-generic. Since kernel v4.12, we do not poll for input reports at plug in, so hid-generic should behave properly with all HID devices. There has been a long standing list of HID devices that have a special driver. It used to be just a few, but with time, this list went too big, and we can not ask users to know which HID special driver will pick up their device. We can teach hid-generic to be nice with others. If a device is not explicitly marked with HID_QUIRK_HAVE_SPECIAL_DRIVER, we can allow hid-generic to pick up the device as long as no other loaded HID driver will match the device. When the special driver appears, hid-generic can step back and let the special driver handling the device. In case this special driver is removed, this good old pal of hid-generic will rebind to the device. This basically makes the list hid_have_special_driver[] useless. It still allows to not see a hid-generic driver bound and removed during boot, so we can keep it around. This will also help other people to have a special HID driver without the need of recompiling hid-core. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-generic.c')
-rw-r--r--drivers/hid/hid-generic.c68
1 files changed, 67 insertions, 1 deletions
diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c
index e288a4a06fe8..3c0a1bf433d7 100644
--- a/drivers/hid/hid-generic.c
+++ b/drivers/hid/hid-generic.c
@@ -24,8 +24,71 @@
24 24
25#include <linux/hid.h> 25#include <linux/hid.h>
26 26
27static struct hid_driver hid_generic;
28
29static int __unmap_hid_generic(struct device *dev, void *data)
30{
31 struct hid_driver *hdrv = data;
32 struct hid_device *hdev = to_hid_device(dev);
33
34 /* only unbind matching devices already bound to hid-generic */
35 if (hdev->driver != &hid_generic ||
36 hid_match_device(hdev, hdrv) == NULL)
37 return 0;
38
39 if (dev->parent) /* Needed for USB */
40 device_lock(dev->parent);
41 device_release_driver(dev);
42 if (dev->parent)
43 device_unlock(dev->parent);
44
45 return 0;
46}
47
48static void hid_generic_add_driver(struct hid_driver *hdrv)
49{
50 bus_for_each_dev(&hid_bus_type, NULL, hdrv, __unmap_hid_generic);
51}
52
53static void hid_generic_removed_driver(struct hid_driver *hdrv)
54{
55 int ret;
56
57 ret = driver_attach(&hid_generic.driver);
58}
59
60static int __check_hid_generic(struct device_driver *drv, void *data)
61{
62 struct hid_driver *hdrv = to_hid_driver(drv);
63 struct hid_device *hdev = data;
64
65 if (hdrv == &hid_generic)
66 return 0;
67
68 return hid_match_device(hdev, hdrv) != NULL;
69}
70
71static bool hid_generic_match(struct hid_device *hdev,
72 bool ignore_special_driver)
73{
74 if (ignore_special_driver)
75 return true;
76
77 if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)
78 return false;
79
80 /*
81 * If any other driver wants the device, leave the device to this other
82 * driver.
83 */
84 if (bus_for_each_drv(&hid_bus_type, NULL, hdev, __check_hid_generic))
85 return false;
86
87 return true;
88}
89
27static const struct hid_device_id hid_table[] = { 90static const struct hid_device_id hid_table[] = {
28 { HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) }, 91 { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, HID_ANY_ID, HID_ANY_ID) },
29 { } 92 { }
30}; 93};
31MODULE_DEVICE_TABLE(hid, hid_table); 94MODULE_DEVICE_TABLE(hid, hid_table);
@@ -33,6 +96,9 @@ MODULE_DEVICE_TABLE(hid, hid_table);
33static struct hid_driver hid_generic = { 96static struct hid_driver hid_generic = {
34 .name = "hid-generic", 97 .name = "hid-generic",
35 .id_table = hid_table, 98 .id_table = hid_table,
99 .match = hid_generic_match,
100 .bus_add_driver = hid_generic_add_driver,
101 .bus_removed_driver = hid_generic_removed_driver,
36}; 102};
37module_hid_driver(hid_generic); 103module_hid_driver(hid_generic);
38 104