diff options
author | Jiri Kosina <jkosina@suse.cz> | 2023-06-27 22:38:37 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2023-06-27 22:38:37 +0200 |
commit | 1639f986e37ada6407f21825dd079ca3c17141b7 (patch) | |
tree | 58dec5de2bed7a2ef64660f0eb97c9068f4aba06 | |
parent | e80b500370e71b8cd7dd64be4080cee0a3e5068f (diff) | |
parent | 49904a0ebf23b15aad288a10f5354e7cd8193121 (diff) |
Merge branch 'for-6.5/core' into for-linus
- more bullet-proof handling of devres-managed resources in HID core
(Dmitry Torokhov)
- kunit test Kconfig dependency fix (Geert Uytterhoeven)
-rw-r--r-- | drivers/hid/Kconfig | 2 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 118 | ||||
-rw-r--r-- | include/linux/hid.h | 1 |
3 files changed, 73 insertions, 48 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4ce012f83253..b977450cac75 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1285,7 +1285,7 @@ config HID_MCP2221 config HID_KUNIT_TEST tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS - depends on KUNIT=y + depends on KUNIT depends on HID_BATTERY_STRENGTH depends on HID_UCLOGIC default KUNIT_ALL_TESTS diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 22623eb4f72f..8992e3c1e769 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2587,64 +2587,84 @@ bool hid_compare_device_paths(struct hid_device *hdev_a, } EXPORT_SYMBOL_GPL(hid_compare_device_paths); +static bool hid_check_device_match(struct hid_device *hdev, + struct hid_driver *hdrv, + const struct hid_device_id **id) +{ + *id = hid_match_device(hdev, hdrv); + if (!*id) + return false; + + if (hdrv->match) + return hdrv->match(hdev, hid_ignore_special_drivers); + + /* + * hid-generic implements .match(), so we must be dealing with a + * different HID driver here, and can simply check if + * hid_ignore_special_drivers is set or not. + */ + return !hid_ignore_special_drivers; +} + +static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) +{ + const struct hid_device_id *id; + int ret; + + if (!hid_check_device_match(hdev, hdrv, &id)) + return -ENODEV; + + hdev->devres_group_id = devres_open_group(&hdev->dev, NULL, GFP_KERNEL); + if (!hdev->devres_group_id) + return -ENOMEM; + + /* reset the quirks that has been previously set */ + hdev->quirks = hid_lookup_quirk(hdev); + hdev->driver = hdrv; + + if (hdrv->probe) { + ret = hdrv->probe(hdev, id); + } else { /* default probe */ + ret = hid_open_report(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + } + + /* + * Note that we are not closing the devres group opened above so + * even resources that were attached to the device after probe is + * run are released when hid_device_remove() is executed. This is + * needed as some drivers would allocate additional resources, + * for example when updating firmware. + */ + + if (ret) { + devres_release_group(&hdev->dev, hdev->devres_group_id); + hid_close_report(hdev); + hdev->driver = NULL; + } + + return ret; +} + static int hid_device_probe(struct device *dev) { - struct hid_driver *hdrv = to_hid_driver(dev->driver); struct hid_device *hdev = to_hid_device(dev); - const struct hid_device_id *id; + struct hid_driver *hdrv = to_hid_driver(dev->driver); int ret = 0; - if (down_interruptible(&hdev->driver_input_lock)) { - ret = -EINTR; - goto end; - } - hdev->io_started = false; + if (down_interruptible(&hdev->driver_input_lock)) + return -EINTR; + hdev->io_started = false; clear_bit(ffs(HID_STAT_REPROBED), &hdev->status); - if (!hdev->driver) { - id = hid_match_device(hdev, hdrv); - if (id == NULL) { - ret = -ENODEV; - goto unlock; - } - - if (hdrv->match) { - if (!hdrv->match(hdev, hid_ignore_special_drivers)) { - ret = -ENODEV; - goto unlock; - } - } else { - /* - * hid-generic implements .match(), so if - * hid_ignore_special_drivers is set, we can safely - * return. - */ - if (hid_ignore_special_drivers) { - ret = -ENODEV; - goto unlock; - } - } + if (!hdev->driver) + ret = __hid_device_probe(hdev, hdrv); - /* reset the quirks that has been previously set */ - hdev->quirks = hid_lookup_quirk(hdev); - hdev->driver = hdrv; - if (hdrv->probe) { - ret = hdrv->probe(hdev, id); - } else { /* default probe */ - ret = hid_open_report(hdev); - if (!ret) - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - } - if (ret) { - hid_close_report(hdev); - hdev->driver = NULL; - } - } -unlock: if (!hdev->io_started) up(&hdev->driver_input_lock); -end: + return ret; } @@ -2662,6 +2682,10 @@ static void hid_device_remove(struct device *dev) hdrv->remove(hdev); else /* default remove */ hid_hw_stop(hdev); + + /* Release all devres resources allocated by the driver */ + devres_release_group(&hdev->dev, hdev->devres_group_id); + hid_close_report(hdev); hdev->driver = NULL; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 4e4c4fe36911..39e21e3815ad 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -597,6 +597,7 @@ struct hid_device { /* device report descriptor */ struct semaphore driver_input_lock; /* protects the current driver */ struct device dev; /* device */ struct hid_driver *driver; + void *devres_group_id; /* ID of probe devres group */ const struct hid_ll_driver *ll_driver; struct mutex ll_open_lock; |