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>
diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c
index e288a4a..3c0a1bf 100644
--- a/drivers/hid/hid-generic.c
+++ b/drivers/hid/hid-generic.c
@@ -24,8 +24,71 @@
 
 #include <linux/hid.h>
 
+static struct hid_driver hid_generic;
+
+static int __unmap_hid_generic(struct device *dev, void *data)
+{
+	struct hid_driver *hdrv = data;
+	struct hid_device *hdev = to_hid_device(dev);
+
+	/* only unbind matching devices already bound to hid-generic */
+	if (hdev->driver != &hid_generic ||
+	    hid_match_device(hdev, hdrv) == NULL)
+		return 0;
+
+	if (dev->parent)	/* Needed for USB */
+		device_lock(dev->parent);
+	device_release_driver(dev);
+	if (dev->parent)
+		device_unlock(dev->parent);
+
+	return 0;
+}
+
+static void hid_generic_add_driver(struct hid_driver *hdrv)
+{
+	bus_for_each_dev(&hid_bus_type, NULL, hdrv, __unmap_hid_generic);
+}
+
+static void hid_generic_removed_driver(struct hid_driver *hdrv)
+{
+	int ret;
+
+	ret = driver_attach(&hid_generic.driver);
+}
+
+static int __check_hid_generic(struct device_driver *drv, void *data)
+{
+	struct hid_driver *hdrv = to_hid_driver(drv);
+	struct hid_device *hdev = data;
+
+	if (hdrv == &hid_generic)
+		return 0;
+
+	return hid_match_device(hdev, hdrv) != NULL;
+}
+
+static bool hid_generic_match(struct hid_device *hdev,
+			      bool ignore_special_driver)
+{
+	if (ignore_special_driver)
+		return true;
+
+	if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)
+		return false;
+
+	/*
+	 * If any other driver wants the device, leave the device to this other
+	 * driver.
+	 */
+	if (bus_for_each_drv(&hid_bus_type, NULL, hdev, __check_hid_generic))
+		return false;
+
+	return true;
+}
+
 static const struct hid_device_id hid_table[] = {
-	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) },
+	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, HID_ANY_ID, HID_ANY_ID) },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, hid_table);
@@ -33,6 +96,9 @@ MODULE_DEVICE_TABLE(hid, hid_table);
 static struct hid_driver hid_generic = {
 	.name = "hid-generic",
 	.id_table = hid_table,
+	.match = hid_generic_match,
+	.bus_add_driver = hid_generic_add_driver,
+	.bus_removed_driver = hid_generic_removed_driver,
 };
 module_hid_driver(hid_generic);