* Re: [PATCH] HID: i2c-hid: Refactor _DSM helper and add i2c-hid-acpi-prp0001 driver
From: sashiko-bot @ 2026-06-05 1:41 UTC (permalink / raw)
Cc: linux-input, dmitry.torokhov
In-Reply-To: <tencent_9F8936ABE7C35AED6FF779A83DFF2729D005@qq.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing `acpi_device_fix_up_power()` before device probe may leave the touchpad unpowered.
- [Low] Compilation error due to omitted parameter name in C function definition.
--
commit a6cad016330e4f17120f69020be7c1d0cb8f3e5d
Author: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
HID: i2c-hid: Refactor _DSM helper and add i2c-hid-acpi-prp0001 driver
This patch adds a new i2c-hid-acpi-prp0001 driver to support devices with
_HID "PRP0001" that lack the "hid-descr-addr" property but provide the HID
descriptor address via an ACPI _DSM. It also refactors the _DSM helper out
of i2c-hid-acpi.c into a shared header.
> diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
> new file mode 100644
> index 0000000000000..35cbbab347acf
> --- /dev/null
> +++ b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
[ ... ]
> +static int i2c_hid_acpi_prp0001_power_up(struct i2chid_ops *)
> +{
[Severity: Low]
Does omitting the parameter name here cause a compilation error?
Standard C requires parameter names in function definitions, and omitting
them generally results in a build failure.
> + /* give the device time to power up */
> + msleep(250);
> + return 0;
> +}
[ ... ]
> + adev = ACPI_COMPANION(dev);
> + if (!adev)
> + return -ENODEV;
> +
> + ret = i2c_hid_acpi_get_descriptor(adev);
> + if (ret < 0)
> + return ret;
> + hid_descriptor_address = ret;
> +
[Severity: High]
Should acpi_device_fix_up_power() be called on the ACPI companion device
before calling i2c_hid_core_probe()?
ACPI I2C HID devices frequently lack the _PSC (Power State Current) object.
For such devices, standard ACPI PM attach assumes the device is already in
D0 and skips executing the _PS0 method, leaving the device unpowered.
The core i2c-hid-acpi driver explicitly works around this by calling
acpi_device_fix_up_power().
Without it here, will affected touchpads remain unpowered and result in I2C
timeouts during probe?
> + return i2c_hid_core_probe(client, &i2c_hid_acpi_prp0001_ops,
> + hid_descriptor_address, 0);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/tencent_9F8936ABE7C35AED6FF779A83DFF2729D005@qq.com?part=1
^ permalink raw reply
* [PATCH] HID: i2c-hid: Refactor _DSM helper and add i2c-hid-acpi-prp0001 driver
From: 谢致邦 (XIE Zhibang) @ 2026-06-05 1:29 UTC (permalink / raw)
To: linux-input, hansg, dmitry.torokhov, bentiss, yeking
Cc: dianders, error27, jikos, kenkinming2002, linux-kernel, raoxu,
superm1, treapking, u.kleine-koenig,
谢致邦 (XIE Zhibang)
In-Reply-To: <tencent_F6FC553D1BB737FC00062AD0FEF43C580F0A@qq.com>
Move the _DSM call that gets the HID descriptor address from
i2c-hid-acpi.c into i2c-hid-acpi.h as a static inline so both the ACPI
and the new PRP0001 driver can use it. While refactoring, move the
blacklist check and the _DSM call to the top of probe() to avoid a
pointless alloc when the device is blacklisted or does not implement the
_DSM.
Some devices, for example the Lenovo KaiTian N60d and Inspur CP300L3,
are declared with _HID "PRP0001" and _DSD compatible "hid-over-i2c" but
lack "hid-descr-addr" from the _DSD and provide the HID descriptor
address only through an ACPI _DSM. The OF driver fails to probe them
because it requires hid-descr-addr. Add a new driver that handles these
devices by calling the shared _DSM helper.
Fixes: b33752c30023 ("HID: i2c-hid: Reorganize so ACPI and OF are separate modules")
Signed-off-by: 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
---
drivers/hid/i2c-hid/Kconfig | 18 +++++
drivers/hid/i2c-hid/Makefile | 1 +
drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c | 92 ++++++++++++++++++++++
drivers/hid/i2c-hid/i2c-hid-acpi.c | 48 +++--------
drivers/hid/i2c-hid/i2c-hid-acpi.h | 32 ++++++++
5 files changed, 155 insertions(+), 36 deletions(-)
create mode 100644 drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
create mode 100644 drivers/hid/i2c-hid/i2c-hid-acpi.h
diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig
index e8d51f410cc1..7db8b2abff78 100644
--- a/drivers/hid/i2c-hid/Kconfig
+++ b/drivers/hid/i2c-hid/Kconfig
@@ -22,6 +22,24 @@ config I2C_HID_ACPI
will be called i2c-hid-acpi. It will also build/depend on the
module i2c-hid.
+config I2C_HID_ACPI_PRP0001
+ tristate "Driver for PRP0001 devices missing hid-descr-addr"
+ depends on ACPI
+ depends on DRM || !DRM
+ select I2C_HID_CORE
+ help
+ Say Y here if you want support for I2C HID touchpads that
+ are declared with _HID "PRP0001" and _DSD compatible
+ "hid-over-i2c" but lack the "hid-descr-addr" property from
+ the _DSD. The HID descriptor address is instead provided
+ through an ACPI _DSM. Known affected devices include the
+ Lenovo KaiTian N60d and Inspur CP300L3.
+
+ If unsure, say N.
+
+ This support is also available as a module. If so, the
+ module will be called i2c-hid-acpi-prp0001.
+
config I2C_HID_OF
tristate "HID over I2C transport layer Open Firmware driver"
# No "depends on OF" because this can also be used for manually
diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile
index 55bd5e0f35af..da420c1a8ec6 100644
--- a/drivers/hid/i2c-hid/Makefile
+++ b/drivers/hid/i2c-hid/Makefile
@@ -9,6 +9,7 @@ i2c-hid-objs = i2c-hid-core.o
i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o
+obj-$(CONFIG_I2C_HID_ACPI_PRP0001) += i2c-hid-acpi-prp0001.o
obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o
obj-$(CONFIG_I2C_HID_OF_ELAN) += i2c-hid-of-elan.o
obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
new file mode 100644
index 000000000000..35cbbab347ac
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi-prp0001.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HID over I2C driver for PRP0001 devices missing hid-descr-addr
+ *
+ * Some devices, for example the Lenovo KaiTian N60d and Inspur CP300L3, use
+ * _HID "PRP0001" with _DSD compatible "hid-over-i2c" but lack "hid-descr-addr"
+ * from the _DSD. The HID descriptor address is provided only through an ACPI
+ * _DSM. The TPD0 node in the DSDT shows _DSM Function 1 returning 0x20.
+ *
+ * Copyright (C) 2026 谢致邦 (XIE Zhibang) <Yeking@Red54.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "i2c-hid.h"
+#include "i2c-hid-acpi.h"
+
+static int i2c_hid_acpi_prp0001_power_up(struct i2chid_ops *)
+{
+ /* give the device time to power up */
+ msleep(250);
+ return 0;
+}
+
+static struct i2chid_ops i2c_hid_acpi_prp0001_ops = {
+ .power_up = i2c_hid_acpi_prp0001_power_up,
+ /*
+ * No .restore_sequence needed: the _DSM on these devices returns a
+ * constant (0x20) with no side effects, unlike some PNP0C50 _DSM
+ * implementations that switch the hardware between PS/2 and I2C modes.
+ */
+};
+
+static int i2c_hid_acpi_prp0001_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct acpi_device *adev;
+ u16 hid_descriptor_address;
+ int ret;
+
+ /* If hid-descr-addr is present, let i2c-hid-of handle it */
+ if (device_property_present(dev, "hid-descr-addr"))
+ return -ENODEV;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return -ENODEV;
+
+ ret = i2c_hid_acpi_get_descriptor(adev);
+ if (ret < 0)
+ return ret;
+ hid_descriptor_address = ret;
+
+ return i2c_hid_core_probe(client, &i2c_hid_acpi_prp0001_ops,
+ hid_descriptor_address, 0);
+}
+
+static const struct of_device_id i2c_hid_acpi_prp0001_of_match[] = {
+ { .compatible = "hid-over-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_hid_acpi_prp0001_of_match);
+
+static const struct i2c_device_id i2c_hid_acpi_prp0001_id[] = {
+ { .name = "hid-over-i2c" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, i2c_hid_acpi_prp0001_id);
+
+static struct i2c_driver i2c_hid_acpi_prp0001_driver = {
+ .driver = {
+ .name = "i2c_hid_acpi_prp0001",
+ .pm = &i2c_hid_core_pm,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = of_match_ptr(i2c_hid_acpi_prp0001_of_match),
+ },
+
+ .probe = i2c_hid_acpi_prp0001_probe,
+ .remove = i2c_hid_core_remove,
+ .shutdown = i2c_hid_core_shutdown,
+ .id_table = i2c_hid_acpi_prp0001_id,
+};
+
+module_i2c_driver(i2c_hid_acpi_prp0001_driver);
+
+MODULE_DESCRIPTION("HID over I2C driver for PRP0001 devices missing hid-descr-addr");
+MODULE_AUTHOR("谢致邦 (XIE Zhibang) <Yeking@Red54.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c
index abd700a101f4..f0bcfb2663ef 100644
--- a/drivers/hid/i2c-hid/i2c-hid-acpi.c
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c
@@ -25,9 +25,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm.h>
-#include <linux/uuid.h>
#include "i2c-hid.h"
+#include "i2c-hid-acpi.h"
struct i2c_hid_acpi {
struct i2chid_ops ops;
@@ -48,39 +48,11 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
{ }
};
-/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
-static guid_t i2c_hid_guid =
- GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
- 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
-
-static int i2c_hid_acpi_get_descriptor(struct i2c_hid_acpi *ihid_acpi)
-{
- struct acpi_device *adev = ihid_acpi->adev;
- acpi_handle handle = acpi_device_handle(adev);
- union acpi_object *obj;
- u16 hid_descriptor_address;
-
- if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
- return -ENODEV;
-
- obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
- ACPI_TYPE_INTEGER);
- if (!obj) {
- acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n");
- return -ENODEV;
- }
-
- hid_descriptor_address = obj->integer.value;
- ACPI_FREE(obj);
-
- return hid_descriptor_address;
-}
-
static void i2c_hid_acpi_restore_sequence(struct i2chid_ops *ops)
{
struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);
- i2c_hid_acpi_get_descriptor(ihid_acpi);
+ i2c_hid_acpi_get_descriptor(ihid_acpi->adev);
}
static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
@@ -93,23 +65,27 @@ static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
static int i2c_hid_acpi_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
+ struct acpi_device *adev = ACPI_COMPANION(dev);
struct i2c_hid_acpi *ihid_acpi;
u16 hid_descriptor_address;
int ret;
+ if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
+ return -ENODEV;
+
+ ret = i2c_hid_acpi_get_descriptor(adev);
+ if (ret < 0)
+ return ret;
+ hid_descriptor_address = ret;
+
ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
if (!ihid_acpi)
return -ENOMEM;
- ihid_acpi->adev = ACPI_COMPANION(dev);
+ ihid_acpi->adev = adev;
ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
ihid_acpi->ops.restore_sequence = i2c_hid_acpi_restore_sequence;
- ret = i2c_hid_acpi_get_descriptor(ihid_acpi);
- if (ret < 0)
- return ret;
- hid_descriptor_address = ret;
-
acpi_device_fix_up_power(ihid_acpi->adev);
return i2c_hid_core_probe(client, &ihid_acpi->ops,
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.h b/drivers/hid/i2c-hid/i2c-hid-acpi.h
new file mode 100644
index 000000000000..8cebbeebcc23
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _I2C_HID_ACPI_H
+#define _I2C_HID_ACPI_H
+
+#include <linux/acpi.h>
+#include <linux/uuid.h>
+
+static inline int i2c_hid_acpi_get_descriptor(struct acpi_device *adev)
+{
+ /* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
+ static const guid_t i2c_hid_guid =
+ GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
+ 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
+
+ acpi_handle handle = acpi_device_handle(adev);
+ union acpi_object *obj;
+ u16 addr;
+
+ obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid,
+ 1, 1, NULL, ACPI_TYPE_INTEGER);
+ if (!obj) {
+ acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n");
+ return -ENODEV;
+ }
+
+ addr = obj->integer.value;
+ ACPI_FREE(obj);
+ return addr;
+}
+
+#endif
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v2 0/4] dt-bindings: remove redundant .txt redirect stubs
From: patchwork-bot+netdevbpf @ 2026-06-05 1:30 UTC (permalink / raw)
To: Akash Sukhavasi
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, robh,
krzk+dt, conor+dt, mchehab, olteanv, horms, corbet, skhan,
dmitry.torokhov, thierry.reding, jonathanh, lee, netdev,
devicetree, linux-kernel, linux-media, linux-doc, linux-input,
linux-tegra
In-Reply-To: <20260603-b4-remove-redirect-stubs-v2-0-c8c19876ab64@gmail.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Wed, 03 Jun 2026 15:42:17 -0500 you wrote:
> Several .txt files under Documentation/devicetree/bindings/ contain
> only a redirect notice pointing to a .yaml schema with the same base
> filename in the same directory. These stubs were useful during the
> .txt to .yaml transition but are now redundant, since the .yaml is
> discoverable by name. Meanwhile, other documentation still references
> some of these stubs, forcing readers through an unnecessary extra hop
> to reach the actual schema.
>
> [...]
Here is the summary with links:
- [v2,1/4] dt-bindings: net: remove obsolete mdio.txt
https://git.kernel.org/netdev/net-next/c/ed2b921b542b
- [v2,2/4] dt-bindings: media: remove obsolete rc.txt
(no matching commit)
- [v2,3/4] dt-bindings: net: dsa: remove obsolete dsa.txt
https://git.kernel.org/netdev/net-next/c/0fbbc19482bd
- [v2,4/4] dt-bindings: input: remove obsolete matrix-keymap.txt
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH] HID: intel-thc-hid: intel-quickspi: reset touch IC on system resume
From: d3z @ 2026-06-04 20:26 UTC (permalink / raw)
To: even.xu, xinpeng.sun, jikos, bentiss
Cc: Danny D ., linux-input, linux-kernel, abhishektamboli9,
sakari.ailus
In-Reply-To: <IA1PR11MB60989CC8623B35747E0EFF7EF4132@IA1PR11MB6098.namprd11.prod.outlook.com>
From: Danny D. <d3z.the.dev@gmail.com>
Hi Even,
Good catch on the missing state restore. I rebuilt with your version and
re-ran the test.
This is exactly your proposal: reset_tic() after thc_dma_configure(),
then qsdev->state = QUICKSPI_ENABLED, with no thc_spi_*_config and no
thc_ltr_*. I built it as a module and swapped it into a clean
linux-surface 6.19.8 on the Surface Pro 10.
It still doesn't survive s2idle - touch is fine at probe, but after one
suspend/resume it's dead again. This time resume() fails outright instead
of coming back quietly broken:
intel_quickspi 0000:00:10.0: Wait DEVICE_DESCRIPTOR timeout, ret:0
intel_quickspi 0000:00:10.0: PM: dpm_run_callback(): pci_pm_resume returns -110
intel_quickspi 0000:00:10.0: PM: failed to resume async: error -110
So reset_tic() can't complete here. It clears the reset handshake, then
times out in quickspi_get_device_descriptor() and the whole resume bails
with -ETIMEDOUT. The device isn't a wake source on this box - "Can't find
wake GPIO resource" - so device_may_wakeup() is false and we're on the
reset path, as expected.
And the timeout is the THC, not the touch IC: across s2idle its SPI
input/output address and read/write config are gone, so reset_tic()'s
descriptor exchange has nothing valid to run against. Reprogram those
first - the thc_spi_input_output_address_config + read_config +
write_config block, same as quickspi_restore(), same as v2 on the no-wake
path - and reset_tic() completes and touch comes back. That's exactly why
v2 reconfigures the THC instead of only resetting the IC.
One more platform note: the SP10 never reaches S3. Firmware only offers
s2idle - mem_sleep is just "[s2idle]", with no "deep" entry to select -
yet the THC still loses its context across plain s2idle. So a reset gated
on S3 would never fire here, which is why it has to run on the ordinary
resume path.
Thanks,
Danny
^ permalink raw reply
* [REGRESSION] Sino Wealth 258a:002a keyboard enters stuck shift state on USB disconnect
From: Orlando Ulises Aguilar Rojas @ 2026-06-04 15:43 UTC (permalink / raw)
To: linux-input; +Cc: linux-usb, benjamin.tissoires
Hi everyone,
I am reporting a regression that I first observed in linux-zen
7.0.8-zen1. The zen-kernel maintainer (heftig) confirmed that the
responsible commits also landed in upstream stable 7.0.10, so this is
not zen-specific:
- HID: pass the buffer size to hid_report_raw_event
- HID: core: introduce hid_safe_input_report()
--- Summary ---
A Sino Wealth 258a:002a generic mechanical keyboard (Machenike K500)
enters a permanent "stuck shift" / modifier ghosting state when any
other USB device disconnects from the host. This worked flawlessly on
7.0.7.
--- Root cause ---
Before these commits, a report where csize < rsize returned -EINVAL
and was discarded in hid_report_raw_event().
After the commits, the same condition is silently zero-padded and
processed. Additionally, drivers/hid/usbhid/hid-core.c was updated to
call hid_safe_input_report() passing urb->transfer_buffer_length as
bufsize instead of urb->actual_length. Since the USB transfer
buffer (typically 64 bytes) is always larger than rsize for this
device, the new bsize < rsize early-exit guard never fires. The code
reaches the zero-fill path unconditionally.
This produces no kernel warnings — dbg_hid is not visible in normal
dmesg — which is why the fault leaves no trace in the logs.
--- Affected device ---
Bus 001 Device XXX: ID 258a:002a Sino Wealth (Machenike K500)
USB Host: Intel xHCI 0000:00:14.0, port 1-2.2
This keyboard uses a non-compliant NKRO implementation. It registers a
spurious mouse interface alongside its real keyboard interfaces:
SINO WEALTH Gaming Keyboard -> input0 (interface 0)
SINO WEALTH Gaming Keyboard -> input1 (interface 1)
SINO WEALTH Gaming Keyboard Mouse -> input1 (interface 1) <- spurious
That spurious mouse interface has always emitted short/malformed
reports. Under 7.0.7 those reports were safely discarded. Under 7.0.8+
they are zero-padded, processed, and produce a modifier key event with
Shift held.
--- Exact trigger ---
The fault occurs at runtime, not at boot. Boot dmesg is structurally
identical on both kernels.
Trigger captured via udevadm monitor: when a separate Realtek wireless
mouse receiver fires unbind -> remove on the USB bus, the Sino Wealth
spurious mouse interface reacts to the hub topology change, emits a
short report, and the zero-fill behavior causes the keyboard to output
"!@#!@#" (Shift held). Physically unplugging and replugging the
keyboard does not clear this state; a system reboot is required to
restore normal functionality.
--- What does NOT mitigate the issue ---
- Forcing power/control = on on the device USB path
- usbhid.quirks=0x258a:002a:0x00000004 boot parameter
--- Suggested fix ---
Reconsider the zero-fill path introduced in hid_report_raw_event():
the pre-7.0.8 behavior (discard short reports with -EINVAL) was safer
for devices with non-compliant NKRO implementations that have always
emitted structurally invalid reports.
If the zero-fill behavior is intentional, adding 258a:002a to the HID
quirks table with a discard-on-short-report quirk would be an
acceptable mitigation.
--- System ---
OS: CachyOS x86_64
Working kernel: Linux 7.0.7-zen2-1-zen
Broken kernel: Linux 7.0.8-zen1 through 7.0.11-zen (upstream: 7.0.10+)
CPU: Intel Core i5-13600KF
dmesg (working/broken) and udev runtime event logs are available on
request, or can be viewed in the downstream zen-kernel issue:
https://github.com/zen-kernel/zen-kernel/issues/414
Thanks for your time.
^ permalink raw reply
* Re: [PATCH v14 2/2] HID: cp2112: Configure I2C bus speed from firmware
From: Danny Kaehn @ 2026-06-04 14:23 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: sashiko-reviews, dmitry.torokhov, linux-input
In-Reply-To: <ah6MeSb-NlzniiCA@beelink>
Hi Benjamin,
Thanks for the review; will fix the issue pointed out here.
On Tue, Jun 02, 2026 at 09:59:23AM +0200, Benjamin Tissoires wrote:
> On May 20 2026, sashiko-bot@kernel.org wrote:
> > Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> >
> > New issues:
> > - [Medium] The driver silently overwrites custom hardware I2C clock speeds with a 100kHz default for devices without firmware nodes.
> >
> > Pre-existing issues:
> > - [High] Firmware nodes are attached to device structures without holding a reference, leading to a dangling pointer and use-after-free.
> > - [High] The delayed work `gpio_poll_worker` is not canceled in the probe error path, leading to a use-after-free if the probe fails.
> > --
> >
> > commit 07ff467e4d8df706a7a27476591cd82570fb8287
> > Author: Danny Kaehn <danny.kaehn@plexus.com>
> >
> > HID: cp2112: Configure I2C bus speed from firmware
> >
> > Now that the I2C adapter on the CP2112 can have an associated firmware
> > node, set the bus speed based on firmware configuration.
> >
> > > diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
> > > index e960fc988058f..04379db93571b 100644
> > > --- a/drivers/hid/hid-cp2112.c
> > > +++ b/drivers/hid/hid-cp2112.c
> > [ ... ]
> > > @@ -1308,6 +1309,9 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
...
> > > + config.clock_speed = cpu_to_be32(timings.bus_freq_hz);
> >
> > [Severity: Medium]
> > Since use_defaults is set to true, does this overwrite custom hardware I2C
> > clock speeds with a 100kHz default for devices without firmware nodes?
> >
> > Before this patch, it looks like cp2112_probe() read the current SMBus
> > configuration from the device, only modified retry_time, and wrote it back.
> > This preserved the device's original configured clock speed (which might be
> > set to 400kHz in the EEPROM).
> >
> > By calling i2c_parse_fw_timings() with an uninitialized local timings
> > struct and use_defaults = true, timings.bus_freq_hz will be
> > unconditionally set to 100kHz if the device lacks a firmware property.
> >
> > Is it intended to silently downgrade the speed and override user-configured
> > EEPROM settings?
>
> I think we should fix this:
> i2c_parse_fw_timings(&dev->adap.dev, &timings, false);
> if (timings.bus_freq_hz)
> config.clock_speed = cpu_to_be32(timings.bus_freq_hz);
>
Yeah, agreed, a good find. The CP2112's default is 100kHz, but
technically possible the bootloader or something has changed it from the
default. Will fix.
> >
> > [Severity: High]
> > This is a pre-existing issue, but is there a missing cancellation of the
> > delayed work in the probe error path?
>
> This one would be nice to fix in a follow up patch.
>
(Not planning to include with this patchset for sake of churn, but could
perhaps be fixed separately in the future... I do have a handfull of
other CP2112 patches to send on once these merge).
> > >
> > > ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config),
> >
> > --
> > Sashiko AI review · https://sashiko.dev/#/patchset/20260520-cp2112-dt-v14-0-b1b4b6734b6f@plexus.com?part=2
> >
>
> Cheers,
> Benjamin
Thanks,
Danny Kaehn
^ permalink raw reply
* Re: [PATCH v14 1/2] HID: cp2112: Add fwnode support
From: Benjamin Tissoires @ 2026-06-04 14:41 UTC (permalink / raw)
To: Danny Kaehn; +Cc: sashiko-reviews, dmitry.torokhov, linux-input
In-Reply-To: <20260604143104.GB1355442@LNDCL34533.neenah.na.plexus.com>
On Jun 04 2026, Danny Kaehn wrote:
>
>
> On Tue, Jun 02, 2026 at 09:55:18AM +0200, Benjamin Tissoires wrote:
> > On May 20 2026, sashiko-bot@kernel.org wrote:
> > > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > > - [High] Premature release of firmware node references results in use-after-free vulnerabilities when assigning the I2C adapter and GPIO chip firmware nodes.
> > > --
> > >
> > > commit 3dfaf368e855835bf23009205c4b93a2a070aae1
> > > Author: Danny Kaehn <danny.kaehn@plexus.com>
> > >
> > > HID: cp2112: Add fwnode support
> > >
> > > This commit adds firmware node support to describe the CP2112's I2C and
> > > GPIO interfaces. It differentiates between ACPI and DeviceTree bindings,
> > > mapping the child nodes to the respective adapter and gpio_chip devices.
> > >
> > > > diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
> > > > index 803b883ae8750..e960fc988058f 100644
> > > > --- a/drivers/hid/hid-cp2112.c
> > > > +++ b/drivers/hid/hid-cp2112.c
> > > [ ... ]
> > > > @@ -1226,6 +1241,28 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
> > > > return ret;
> > > > }
> > > >
> > > > + cp2112_fwnode = dev_fwnode(&hdev->dev);
> > > > + if (is_acpi_device_node(cp2112_fwnode)) {
> > > > + fwnode_for_each_child_node(cp2112_fwnode, child) {
> > > > + ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr);
> > > > + if (ret)
> > > > + continue;
> > > > +
> > > > + switch (addr) {
> > > > + case CP2112_I2C_ADR:
> > > > + device_set_node(&dev->adap.dev, child);
> > > > + break;
> > > > + case CP2112_GPIO_ADR:
> > > > + dev->gc.fwnode = child;
> > > > + break;
> > > > + }
> > >
> > > [Severity: High]
> > > Does this code leave dangling pointers in the device structures?
> > >
> > > The fwnode_for_each_child_node() macro automatically drops the reference to
> > > the child node when advancing to the next node or exiting the loop.
> > >
> > > Because device_set_node() and direct pointer assignments do not increment
> > > the reference count, the nodes assigned here could be prematurely freed
> > > resulting in a use-after-free.
> >
> > Sashiko is overly cautious here. ACPI handles are not refcounted, so we
> > are fine.
> >
> > That being said, we could unify the OF and ACPI behaviour by calling the
> > noops fwnode_handle_get(child) for ACPI as well and clean them up in
> > cp2112_remove(). This would also shut up the next warning from Sashiko
> > below.
> >
> > >
> > > > + }
> > > > + } else if (is_of_node(cp2112_fwnode)) {
> > > > + child = fwnode_get_named_child_node(cp2112_fwnode, "i2c");
> > > > + device_set_node(&dev->adap.dev, child);
> > > > + fwnode_handle_put(child);
> > >
> > > [Severity: High]
> > > Are we releasing the firmware node prematurely here?
> > >
> > > fwnode_get_named_child_node() returns a node with an incremented reference
> > > count. However, since device_set_node() does not take its own reference on
> > > the child node, dropping the reference immediately after could leave
> > > dev->adap.dev with a dangling pointer if the node is later removed.
> >
> > OF children are tied to the lifespan of the OF device, so we are fine,
> > but technically incorrect, as mentioned by Sashiko.
> >
> > If you regroup the 2 behaviours and clean them in cp2112_remove(), this
> > would be cleaner IMO.
> >
>
> Ack, this is something I've gone back-and-forth on in the revisions of
> this patch. Will update the driver to hold the fwnode reference for the
> I2C subnode.
>
> However, for the GPIO chip, DT uses the parent node (which shouldn't
> need an increment to the refcount), while ACPI uses a child node. Given
Oh, if they are different, then maybe following that advice is not such
a good idea.
> this discussion, it sounds like I ought to still increment the refcount
> for the GPIO child node to comply to the interface, even though ACPI
> handles aren't refcounted.
Yeah, sashiko says that this is harmless and would provide symetrical
behavior (but artificial)
> This will require another bit of DT/ACPI
> diverging behavior in _remove(), which is fine by me, just announcing
> that intent in case it doesn't match your expectation / might appear as
> a code smell in the follow-up patch.
I thought you would just deref the stored node, so it doesn't matter
whether the original node is a parent or a child. Especially because it
would be a noop for ACPI.
But if there are no way of having a single fwnode_handle_put(node) (per
ref, of course), then maybe we can stick to the current code.
We should probably fix the other speed downgrade from 2/2.
Cheers,
Benjamin
>
>
>
>
> > Cheers,
> > Benjamin
> >
> > > --
> > > Sashiko AI review � https://sashiko.dev/#/patchset/20260520-cp2112-dt-v14-0-b1b4b6734b6f@plexus.com?part=1
>
> Thanks,
>
> Danny Kaehn
>
>
^ permalink raw reply
* Re: [PATCH v14 1/2] HID: cp2112: Add fwnode support
From: Danny Kaehn @ 2026-06-04 14:31 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: sashiko-reviews, dmitry.torokhov, linux-input
In-Reply-To: <ah6LJw5bgVyrtnyI@beelink>
On Tue, Jun 02, 2026 at 09:55:18AM +0200, Benjamin Tissoires wrote:
> On May 20 2026, sashiko-bot@kernel.org wrote:
> > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > - [High] Premature release of firmware node references results in use-after-free vulnerabilities when assigning the I2C adapter and GPIO chip firmware nodes.
> > --
> >
> > commit 3dfaf368e855835bf23009205c4b93a2a070aae1
> > Author: Danny Kaehn <danny.kaehn@plexus.com>
> >
> > HID: cp2112: Add fwnode support
> >
> > This commit adds firmware node support to describe the CP2112's I2C and
> > GPIO interfaces. It differentiates between ACPI and DeviceTree bindings,
> > mapping the child nodes to the respective adapter and gpio_chip devices.
> >
> > > diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
> > > index 803b883ae8750..e960fc988058f 100644
> > > --- a/drivers/hid/hid-cp2112.c
> > > +++ b/drivers/hid/hid-cp2112.c
> > [ ... ]
> > > @@ -1226,6 +1241,28 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
> > > return ret;
> > > }
> > >
> > > + cp2112_fwnode = dev_fwnode(&hdev->dev);
> > > + if (is_acpi_device_node(cp2112_fwnode)) {
> > > + fwnode_for_each_child_node(cp2112_fwnode, child) {
> > > + ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr);
> > > + if (ret)
> > > + continue;
> > > +
> > > + switch (addr) {
> > > + case CP2112_I2C_ADR:
> > > + device_set_node(&dev->adap.dev, child);
> > > + break;
> > > + case CP2112_GPIO_ADR:
> > > + dev->gc.fwnode = child;
> > > + break;
> > > + }
> >
> > [Severity: High]
> > Does this code leave dangling pointers in the device structures?
> >
> > The fwnode_for_each_child_node() macro automatically drops the reference to
> > the child node when advancing to the next node or exiting the loop.
> >
> > Because device_set_node() and direct pointer assignments do not increment
> > the reference count, the nodes assigned here could be prematurely freed
> > resulting in a use-after-free.
>
> Sashiko is overly cautious here. ACPI handles are not refcounted, so we
> are fine.
>
> That being said, we could unify the OF and ACPI behaviour by calling the
> noops fwnode_handle_get(child) for ACPI as well and clean them up in
> cp2112_remove(). This would also shut up the next warning from Sashiko
> below.
>
> >
> > > + }
> > > + } else if (is_of_node(cp2112_fwnode)) {
> > > + child = fwnode_get_named_child_node(cp2112_fwnode, "i2c");
> > > + device_set_node(&dev->adap.dev, child);
> > > + fwnode_handle_put(child);
> >
> > [Severity: High]
> > Are we releasing the firmware node prematurely here?
> >
> > fwnode_get_named_child_node() returns a node with an incremented reference
> > count. However, since device_set_node() does not take its own reference on
> > the child node, dropping the reference immediately after could leave
> > dev->adap.dev with a dangling pointer if the node is later removed.
>
> OF children are tied to the lifespan of the OF device, so we are fine,
> but technically incorrect, as mentioned by Sashiko.
>
> If you regroup the 2 behaviours and clean them in cp2112_remove(), this
> would be cleaner IMO.
>
Ack, this is something I've gone back-and-forth on in the revisions of
this patch. Will update the driver to hold the fwnode reference for the
I2C subnode.
However, for the GPIO chip, DT uses the parent node (which shouldn't
need an increment to the refcount), while ACPI uses a child node. Given
this discussion, it sounds like I ought to still increment the refcount
for the GPIO child node to comply to the interface, even though ACPI
handles aren't refcounted. This will require another bit of DT/ACPI
diverging behavior in _remove(), which is fine by me, just announcing
that intent in case it doesn't match your expectation / might appear as
a code smell in the follow-up patch.
> Cheers,
> Benjamin
>
> > --
> > Sashiko AI review · https://sashiko.dev/#/patchset/20260520-cp2112-dt-v14-0-b1b4b6734b6f@plexus.com?part=1
Thanks,
Danny Kaehn
^ permalink raw reply
* Re: [PATCH] Input: psmouse-smbus - schedule deferred rescan when adapter present but device not ready
From: sashiko-bot @ 2026-06-04 13:27 UTC (permalink / raw)
To: Raphaël Larocque; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260604131211.9442-1-rlarocque@disroot.org>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Infinite unbind/rebind loop breaks the PS/2 fallback if the SMBus companion device never responds.
--
commit 18a210ed3c950add4486fdb9a71222e2b1590d6a
Author: Raphaël Larocque <rlarocque@disroot.org>
Input: psmouse-smbus - schedule deferred rescan when adapter present but device not ready
This commit addresses an issue where the SMBus adapter is registered early
but the companion device is not yet responsive. It schedules a deferred
rescan using a delayed work queue to retry the probe without blocking boot.
> diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
> index 7fb4cbb2aca2f..704d8c2a028ba 100644
> --- a/drivers/input/mouse/psmouse-smbus.c
> +++ b/drivers/input/mouse/psmouse-smbus.c
[ ... ]
> +static void psmouse_smbus_rescan_work_fn(struct work_struct *work)
> +{
> + struct psmouse_smbus_dev *smbdev =
> + container_of(to_delayed_work(work),
> + struct psmouse_smbus_dev, rescan_work);
>
> guard(mutex)(&psmouse_smbus_mutex);
>
> + if (!smbdev->dead && !smbdev->client) {
> + psmouse_dbg(smbdev->psmouse,
> + "SMBus adapter present but device not yet ready, retrying\n");
> + serio_rescan(smbdev->psmouse->ps2dev.serio);
[Severity: High]
Does this serio_rescan() call introduce an infinite unbind/rebind loop if the
SMBus companion device never responds?
When serio_rescan() triggers, it tears down the PS/2 driver and initiates a
reprobe. During the reprobe, psmouse_smbus_init() runs again.
> + }
> +}
[ ... ]
> @@ -290,6 +372,18 @@ int psmouse_smbus_init(struct psmouse *psmouse,
> }
>
> kfree(smbdev);
> + } else if (smbdev->adapter_seen) {
> + /*
> + * A HOST_NOTIFY-capable adapter was already registered when we
> + * scanned (so BUS_NOTIFY_ADD_DEVICE has already fired for it
> + * and won't fire again), but the companion device at 0x2c was
> + * not yet responsive — typical of a cold boot on machines like
> + * the ThinkPad T440p. Schedule a deferred rescan so we retry
> + * without blocking the boot path, mirroring what
> + * psmouse_smbus_check_adapter() does for late-arriving adapters.
> + */
> + queue_delayed_work(psmouse_smbus_wq, &smbdev->rescan_work,
> + msecs_to_jiffies(PSMOUSE_SMBUS_RESCAN_DELAY_MS));
> }
>
> return error < 0 ? error : -EAGAIN;
[Severity: High]
If the I2C device is permanently unresponsive (e.g. the hardware is physically
wired only for PS/2 despite firmware capabilities), won't the reprobe hit this
path again?
Since a new smbdev is allocated on every probe and there is no persistent
state tracking the retry count, this appears to unconditionally queue the
rescan again.
This would repeatedly destroy and recreate the PS/2 fallback every 1 second,
rendering the touchpad unusable. Can we limit the number of retry attempts or
track the state to avoid this loop?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604131211.9442-1-rlarocque@disroot.org?part=1
^ permalink raw reply
* [PATCH] Input: psmouse-smbus - schedule deferred rescan when adapter present but device not ready
From: Raphaël Larocque @ 2026-06-04 13:12 UTC (permalink / raw)
To: dmitry.torokhov
Cc: linux-input, linux-kernel, kees, marco.crivellari,
Raphaël Larocque
On some machines (e.g.: ThinkPad T440p 20AWS0H800) the Intel LPSS I2C
adapter that hosts the Synaptics RMI4 touchpad is already registered
by the time psmouse probes during early boot. psmouse_smbus_init()
finds that adapter via i2c_for_each_dev(), but i2c_new_scanned_device()
fails because the device at 0x2c is not yet responsive; -EAGAIN is
returned with breadcrumbs left in psmouse_smbus_list.
Because the adapter is already registered, BUS_NOTIFY_ADD_DEVICE has
already fired for it and will not fire again. The breadcrumb sits
idle until some other HOST_NOTIFY-capable adapter happens to appear
later and incidentally triggers psmouse_smbus_check_adapter(). When
this happens is non-deterministic: on one boot the rescan fires at
~11 s, on another at ~200 s, on a third never within the observation
window. The touchpad falls back to PS/2 mode in the interim.
Fix this by scheduling a one-shot deferred serio_rescan() from
psmouse_smbus_init() whenever a HOST_NOTIFY adapter was found but the
companion device was not yet ready. The work fires asynchronously
(1 s delay) without blocking the boot path, exactly as
psmouse_smbus_check_adapter() already does for late-arriving adapters.
If the device is still not ready on that attempt, a fresh breadcrumb
is left and the work is re-queued, retrying until the device responds.
rescan_work is always INIT_DELAYED_WORK'd so that
cancel_delayed_work_sync() is safe to call unconditionally in all
cleanup paths. All paths that free a psmouse_smbus_dev are
restructured to release psmouse_smbus_mutex before calling
cancel_delayed_work_sync(), preventing a deadlock with the work
function which also acquires that mutex.
Fixes: 2a02f6c8af14 ("Input: psmouse - fix SMBus companion")
Tested-by: Raphaël Larocque <rlarocque@disroot.org>
Signed-off-by: Raphaël Larocque <rlarocque@disroot.org>
---
drivers/input/mouse/psmouse-smbus.c | 168 ++++++++++++++++++++++------
1 file changed, 131 insertions(+), 37 deletions(-)
diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
index 7fb4cbb2aca2..704d8c2a028b 100644
--- a/drivers/input/mouse/psmouse-smbus.c
+++ b/drivers/input/mouse/psmouse-smbus.c
@@ -21,6 +21,8 @@ struct psmouse_smbus_dev {
struct list_head node;
bool dead;
bool need_deactivate;
+ bool adapter_seen; /* HOST_NOTIFY adapter found, device not yet ready */
+ struct delayed_work rescan_work;
};
static LIST_HEAD(psmouse_smbus_list);
@@ -28,6 +30,13 @@ static DEFINE_MUTEX(psmouse_smbus_mutex);
static struct workqueue_struct *psmouse_smbus_wq;
+/*
+ * Delay before re-scanning after a HOST_NOTIFY adapter was present but the
+ * companion device did not respond. The rescan is asynchronous so this does
+ * not add latency to the boot path.
+ */
+#define PSMOUSE_SMBUS_RESCAN_DELAY_MS 1000
+
static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
{
struct psmouse_smbus_dev *smbdev;
@@ -57,32 +66,68 @@ static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
}
}
-static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
+/*
+ * psmouse_smbus_rescan_work_fn - deferred rescan when adapter was present
+ * but companion device was not yet ready during psmouse_smbus_init().
+ *
+ * On some machines (e.g. ThinkPad T440p) the LPSS I2C adapter is already
+ * registered by the time psmouse probes, so BUS_NOTIFY_ADD_DEVICE has
+ * already fired and will not fire again. If the RMI4 device at 0x2c was
+ * not yet responsive at probe time, nothing would ever trigger a rescan.
+ * This work item closes that gap by calling serio_rescan() asynchronously,
+ * exactly as psmouse_smbus_check_adapter() does for late-arriving adapters.
+ */
+static void psmouse_smbus_rescan_work_fn(struct work_struct *work)
{
- struct psmouse_smbus_dev *smbdev, *tmp;
+ struct psmouse_smbus_dev *smbdev =
+ container_of(to_delayed_work(work),
+ struct psmouse_smbus_dev, rescan_work);
guard(mutex)(&psmouse_smbus_mutex);
- list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
- if (smbdev->client != client)
- continue;
+ if (!smbdev->dead && !smbdev->client) {
+ psmouse_dbg(smbdev->psmouse,
+ "SMBus adapter present but device not yet ready, retrying\n");
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
+ }
+}
- kfree(client->dev.platform_data);
- client->dev.platform_data = NULL;
+static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
+{
+ struct psmouse_smbus_dev *smbdev, *tmp;
+ LIST_HEAD(orphans);
- if (!smbdev->dead) {
- psmouse_dbg(smbdev->psmouse,
- "Marking SMBus companion %s as gone\n",
- dev_name(&smbdev->client->dev));
- smbdev->dead = true;
- device_link_remove(&smbdev->client->dev,
- &smbdev->psmouse->ps2dev.serio->dev);
- serio_rescan(smbdev->psmouse->ps2dev.serio);
- } else {
- list_del(&smbdev->node);
- kfree(smbdev);
+ scoped_guard(mutex, &psmouse_smbus_mutex) {
+ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+ if (smbdev->client != client)
+ continue;
+
+ kfree(client->dev.platform_data);
+ client->dev.platform_data = NULL;
+
+ if (!smbdev->dead) {
+ psmouse_dbg(smbdev->psmouse,
+ "Marking SMBus companion %s as gone\n",
+ dev_name(&smbdev->client->dev));
+ smbdev->dead = true;
+ device_link_remove(&smbdev->client->dev,
+ &smbdev->psmouse->ps2dev.serio->dev);
+ serio_rescan(smbdev->psmouse->ps2dev.serio);
+ } else {
+ list_del(&smbdev->node);
+ list_add_tail(&smbdev->node, &orphans);
+ }
}
}
+
+ /*
+ * Cancel any pending rescan work and free outside the mutex to avoid
+ * a potential deadlock with the work function.
+ */
+ list_for_each_entry_safe(smbdev, tmp, &orphans, node) {
+ cancel_delayed_work_sync(&smbdev->rescan_work);
+ kfree(smbdev);
+ }
}
static int psmouse_smbus_notifier_call(struct notifier_block *nb,
@@ -166,23 +211,29 @@ static void psmouse_smbus_schedule_remove(struct i2c_client *client)
static void psmouse_smbus_disconnect(struct psmouse *psmouse)
{
struct psmouse_smbus_dev *smbdev = psmouse->private;
+ struct psmouse_smbus_dev *free_smbdev = NULL;
- guard(mutex)(&psmouse_smbus_mutex);
+ scoped_guard(mutex, &psmouse_smbus_mutex) {
+ if (smbdev->dead) {
+ list_del(&smbdev->node);
+ free_smbdev = smbdev;
+ } else {
+ smbdev->dead = true;
+ device_link_remove(&smbdev->client->dev,
+ &psmouse->ps2dev.serio->dev);
+ psmouse_dbg(smbdev->psmouse,
+ "posting removal request for SMBus companion %s\n",
+ dev_name(&smbdev->client->dev));
+ psmouse_smbus_schedule_remove(smbdev->client);
+ }
- if (smbdev->dead) {
- list_del(&smbdev->node);
- kfree(smbdev);
- } else {
- smbdev->dead = true;
- device_link_remove(&smbdev->client->dev,
- &psmouse->ps2dev.serio->dev);
- psmouse_dbg(smbdev->psmouse,
- "posting removal request for SMBus companion %s\n",
- dev_name(&smbdev->client->dev));
- psmouse_smbus_schedule_remove(smbdev->client);
+ psmouse->private = NULL;
}
- psmouse->private = NULL;
+ if (free_smbdev) {
+ cancel_delayed_work_sync(&free_smbdev->rescan_work);
+ kfree(free_smbdev);
+ }
}
static int psmouse_smbus_create_companion(struct device *dev, void *data)
@@ -199,6 +250,14 @@ static int psmouse_smbus_create_companion(struct device *dev, void *data)
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
return 0;
+ /*
+ * Remember that a suitable adapter was seen; if the device scan
+ * below fails (device not yet ready), we know to schedule a deferred
+ * rescan rather than relying solely on BUS_NOTIFY_ADD_DEVICE (which
+ * won't fire again for an already-registered adapter).
+ */
+ smbdev->adapter_seen = true;
+
client = i2c_new_scanned_device(adapter, &smbdev->board,
addr_list, NULL);
if (IS_ERR(client))
@@ -212,15 +271,32 @@ static int psmouse_smbus_create_companion(struct device *dev, void *data)
void psmouse_smbus_cleanup(struct psmouse *psmouse)
{
struct psmouse_smbus_dev *smbdev, *tmp;
+ LIST_HEAD(orphans);
- guard(mutex)(&psmouse_smbus_mutex);
-
- list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
- if (psmouse == smbdev->psmouse) {
- list_del(&smbdev->node);
- kfree(smbdev);
+ scoped_guard(mutex, &psmouse_smbus_mutex) {
+ list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+ if (psmouse == smbdev->psmouse) {
+ /*
+ * Prevent the work function from calling
+ * serio_rescan() after we release the mutex.
+ */
+ smbdev->dead = true;
+ list_del(&smbdev->node);
+ list_add_tail(&smbdev->node, &orphans);
+ }
}
}
+
+ /*
+ * Cancel any pending rescan work outside the mutex. The work
+ * function also takes psmouse_smbus_mutex, so calling
+ * cancel_delayed_work_sync() under it would deadlock if the work
+ * happened to be running concurrently.
+ */
+ list_for_each_entry_safe(smbdev, tmp, &orphans, node) {
+ cancel_delayed_work_sync(&smbdev->rescan_work);
+ kfree(smbdev);
+ }
}
int psmouse_smbus_init(struct psmouse *psmouse,
@@ -249,6 +325,12 @@ int psmouse_smbus_init(struct psmouse *psmouse,
}
}
+ /*
+ * Always initialise the delayed work so cancel_delayed_work_sync()
+ * is safe to call unconditionally in all cleanup paths.
+ */
+ INIT_DELAYED_WORK(&smbdev->rescan_work, psmouse_smbus_rescan_work_fn);
+
if (need_deactivate)
psmouse_deactivate(psmouse);
@@ -290,6 +372,18 @@ int psmouse_smbus_init(struct psmouse *psmouse,
}
kfree(smbdev);
+ } else if (smbdev->adapter_seen) {
+ /*
+ * A HOST_NOTIFY-capable adapter was already registered when we
+ * scanned (so BUS_NOTIFY_ADD_DEVICE has already fired for it
+ * and won't fire again), but the companion device at 0x2c was
+ * not yet responsive — typical of a cold boot on machines like
+ * the ThinkPad T440p. Schedule a deferred rescan so we retry
+ * without blocking the boot path, mirroring what
+ * psmouse_smbus_check_adapter() does for late-arriving adapters.
+ */
+ queue_delayed_work(psmouse_smbus_wq, &smbdev->rescan_work,
+ msecs_to_jiffies(PSMOUSE_SMBUS_RESCAN_DELAY_MS));
}
return error < 0 ? error : -EAGAIN;
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v14 1/2] HID: cp2112: Add fwnode support
From: Bartosz Golaszewski @ 2026-06-04 12:45 UTC (permalink / raw)
To: Danny Kaehn
Cc: Jiri Kosina, linux-input, Dmitry Torokhov, Ethan Twardy,
linux-i2c, linux-kernel, Leo Huang, Arun D Patil, Willie Thai,
Ting-Kai Chen, Bartosz Golaszewski, Benjamin Tissoires,
Andy Shevchenko, Andi Shyti, Conor Dooley
In-Reply-To: <20260520-cp2112-dt-v14-1-b1b4b6734b6f@plexus.com>
On Wed, 20 May 2026 18:13:06 +0200, Danny Kaehn <danny.kaehn@plexus.com> said:
> Support describing the CP2112's I2C and GPIO interfaces in firmware.
>
> Bindings between the firmware nodes and the functions of the device
> are distinct between ACPI and DeviceTree.
>
> For ACPI, the i2c_adapter will use the child with _ADR equal to Zero
> and the gpio_chip will use the child with _ADR equal to One.
>
> For DeviceTree, the i2c_adapter will use the child with name "i2c",
> but the gpio_chip will share a firmware node with the CP2112.
>
> Signed-off-by: Danny Kaehn <danny.kaehn@plexus.com>
> ---
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* [dtor-input:next] BUILD SUCCESS c6f1363abbff69e5bd1cd1fcf060e1510e8d6d31
From: kernel test robot @ 2026-06-04 11:09 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
branch HEAD: c6f1363abbff69e5bd1cd1fcf060e1510e8d6d31 Input: userio - allow setting other id values
elapsed time: 2118m
configs tested: 223
configs skipped: 4
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-16.1.0
arc allmodconfig clang-17
arc allmodconfig gcc-15.2.0
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc allyesconfig gcc-15.2.0
arc defconfig gcc-16.1.0
arc randconfig-001-20260604 clang-17
arc randconfig-002-20260604 clang-17
arm allnoconfig clang-23
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-17
arm allyesconfig gcc-15.2.0
arm defconfig gcc-16.1.0
arm randconfig-001-20260604 clang-17
arm randconfig-002-20260604 clang-17
arm randconfig-003-20260604 clang-17
arm randconfig-004-20260604 clang-17
arm64 allmodconfig clang-19
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001-20260604 clang-17
arm64 randconfig-002-20260604 clang-17
arm64 randconfig-003-20260604 clang-17
arm64 randconfig-004-20260604 clang-17
csky allmodconfig gcc-15.2.0
csky allmodconfig gcc-16.1.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-16.1.0
csky randconfig-001-20260604 clang-17
csky randconfig-002-20260604 clang-17
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-16.1.0
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260604 gcc-11.5.0
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260604 gcc-11.5.0
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 buildonly-randconfig-001-20260604 clang-20
i386 buildonly-randconfig-002-20260604 clang-20
i386 buildonly-randconfig-003-20260604 clang-20
i386 buildonly-randconfig-004-20260604 clang-20
i386 buildonly-randconfig-005-20260604 clang-20
i386 buildonly-randconfig-006-20260604 clang-20
i386 defconfig gcc-16.1.0
i386 randconfig-001-20260604 clang-20
i386 randconfig-002-20260604 clang-20
i386 randconfig-003-20260604 clang-20
i386 randconfig-004-20260604 clang-20
i386 randconfig-005-20260604 clang-20
i386 randconfig-006-20260604 clang-20
i386 randconfig-007-20260604 clang-20
i386 randconfig-011-20260604 gcc-14
i386 randconfig-012-20260604 gcc-14
i386 randconfig-013-20260604 gcc-14
i386 randconfig-014-20260604 gcc-14
i386 randconfig-015-20260604 gcc-14
i386 randconfig-016-20260604 gcc-14
i386 randconfig-017-20260604 gcc-14
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-23
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260604 gcc-11.5.0
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260604 gcc-11.5.0
m68k allmodconfig gcc-15.2.0
m68k allmodconfig gcc-16.1.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-17
m68k allyesconfig gcc-15.2.0
m68k defconfig clang-23
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze allyesconfig gcc-16.1.0
microblaze defconfig clang-23
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips allyesconfig gcc-16.1.0
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-17
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-23
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260604 gcc-11.5.0
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260604 gcc-11.5.0
openrisc allmodconfig gcc-16.1.0
openrisc allnoconfig clang-17
openrisc allnoconfig gcc-15.2.0
openrisc defconfig gcc-16.1.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-17
parisc allnoconfig gcc-15.2.0
parisc allyesconfig clang-19
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-16.1.0
parisc randconfig-001 gcc-8.5.0
parisc randconfig-001-20260604 gcc-8.5.0
parisc randconfig-002 gcc-8.5.0
parisc randconfig-002-20260604 gcc-8.5.0
parisc64 defconfig clang-23
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-17
powerpc allnoconfig gcc-15.2.0
powerpc randconfig-001 gcc-8.5.0
powerpc randconfig-001-20260604 gcc-8.5.0
powerpc randconfig-002 gcc-8.5.0
powerpc randconfig-002-20260604 gcc-8.5.0
powerpc64 randconfig-001 gcc-8.5.0
powerpc64 randconfig-001-20260604 gcc-8.5.0
powerpc64 randconfig-002 gcc-8.5.0
powerpc64 randconfig-002-20260604 gcc-8.5.0
riscv allmodconfig clang-23
riscv allnoconfig clang-17
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv allyesconfig clang-17
riscv defconfig gcc-16.1.0
riscv randconfig-001 clang-17
riscv randconfig-001-20260604 clang-17
riscv randconfig-002 clang-17
riscv randconfig-002-20260604 clang-17
s390 allmodconfig clang-18
s390 allmodconfig clang-19
s390 allnoconfig clang-17
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-16.1.0
s390 randconfig-001 clang-17
s390 randconfig-001-20260604 clang-17
s390 randconfig-002 clang-17
s390 randconfig-002-20260604 clang-17
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-17
sh allnoconfig gcc-15.2.0
sh allyesconfig clang-19
sh allyesconfig gcc-15.2.0
sh defconfig gcc-14
sh randconfig-001 clang-17
sh randconfig-001-20260604 clang-17
sh randconfig-002 clang-17
sh randconfig-002-20260604 clang-17
sh urquell_defconfig gcc-16.1.0
sparc allnoconfig clang-17
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-16.1.0
sparc randconfig-001 gcc-11.5.0
sparc randconfig-001-20260604 gcc-11.5.0
sparc randconfig-002 gcc-11.5.0
sparc randconfig-002-20260604 gcc-11.5.0
sparc64 allmodconfig clang-20
sparc64 defconfig gcc-14
sparc64 randconfig-001 gcc-11.5.0
sparc64 randconfig-001-20260604 gcc-11.5.0
sparc64 randconfig-002 gcc-11.5.0
sparc64 randconfig-002-20260604 gcc-11.5.0
um allmodconfig clang-19
um allnoconfig clang-17
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001 gcc-11.5.0
um randconfig-001-20260604 gcc-11.5.0
um randconfig-002 gcc-11.5.0
um randconfig-002-20260604 gcc-11.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-17
x86_64 allnoconfig clang-20
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260604 gcc-14
x86_64 buildonly-randconfig-002-20260604 gcc-14
x86_64 buildonly-randconfig-003-20260604 gcc-14
x86_64 buildonly-randconfig-004-20260604 gcc-14
x86_64 buildonly-randconfig-005-20260604 gcc-14
x86_64 buildonly-randconfig-006-20260604 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001-20260604 clang-20
x86_64 randconfig-002-20260604 clang-20
x86_64 randconfig-003-20260604 clang-20
x86_64 randconfig-004-20260604 clang-20
x86_64 randconfig-005-20260604 clang-20
x86_64 randconfig-006-20260604 clang-20
x86_64 randconfig-011-20260604 clang-22
x86_64 randconfig-012-20260604 clang-22
x86_64 randconfig-013-20260604 clang-22
x86_64 randconfig-014-20260604 clang-22
x86_64 randconfig-015-20260604 clang-22
x86_64 randconfig-016-20260604 clang-22
x86_64 randconfig-071-20260604 clang-22
x86_64 randconfig-072-20260604 clang-22
x86_64 randconfig-073-20260604 clang-22
x86_64 randconfig-074-20260604 clang-22
x86_64 randconfig-075-20260604 clang-22
x86_64 randconfig-076-20260604 clang-22
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-kselftests clang-22
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-17
xtensa allnoconfig gcc-15.2.0
xtensa randconfig-001 gcc-11.5.0
xtensa randconfig-001-20260604 gcc-11.5.0
xtensa randconfig-002 gcc-11.5.0
xtensa randconfig-002-20260604 gcc-11.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [linux-5.15.y 2/3] HID: pass the buffer size to hid_report_raw_event
From: sashiko-bot @ 2026-06-04 9:39 UTC (permalink / raw)
To: Lee Jones; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260604092659.3953067-2-lee@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The patch fails to resolve the short report rejection regression for most HID devices because `hid_input_report()` unconditionally sets `bufsize = size`.
- [High] Incorrect `bufsize` argument in `wacom_wac_queue_flush` defeats the mitigation for short reports.
- [Low] Format string mismatch for `size_t` variable `bsize` causes compiler warnings and potential build failures on 32-bit platforms.
--
commit 410a0d731bb14f8c3685faa86ca65503898cc6f0
Author: Benjamin Tissoires <bentiss@kernel.org>
HID: pass the buffer size to hid_report_raw_event
This commit passes the buffer size along with the data size to
hid_report_raw_event() to make a better decision on whether to reject the
buffer or safely memset it to 0 and pass it to the rest of the stack.
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 4fb573ee31b2c..a8d4673c7b8e1 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1775,8 +1775,8 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
> }
> EXPORT_SYMBOL_GPL(__hid_request);
>
> -int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
> - int interrupt)
> +int hid_report_raw_event(struct hid_device *hid, int type, u8 *data,
> + size_t bufsize, u32 size, int interrupt)
> {
> struct hid_report_enum *report_enum = hid->report_enum + type;
> struct hid_report *report;
> @@ -1784,16 +1784,24 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
> int max_buffer_size = HID_MAX_BUFFER_SIZE;
> unsigned int a;
> u32 rsize, csize = size;
> + size_t bsize = bufsize;
> u8 *cdata = data;
> int ret = 0;
>
> report = hid_get_report(report_enum, data);
> if (!report)
> - goto out;
> + return 0;
> +
> + if (unlikely(bsize < csize)) {
> + hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
> + report->id, csize, bsize);
[Severity: Low]
Does this cause a compiler warning on 32-bit platforms? bsize is a size_t,
but is being printed with the %ld format specifier, which might trigger a
-Wformat warning since size_t is usually an unsigned int on those
architectures. Should this use %zu instead?
> + return -EINVAL;
> + }
>
> if (report_enum->numbered) {
> cdata++;
> csize--;
> + bsize--;
> }
>
> rsize = hid_compute_report_size(report);
> @@ -1806,9 +1814,15 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
> else if (rsize > max_buffer_size)
> rsize = max_buffer_size;
>
> + if (bsize < rsize) {
> + hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
> + report->id, rsize, bsize);
[Severity: Low]
This isn't a bug, but there's another instance here where bsize is printed
with %ld instead of %zu.
> + return -EINVAL;
> + }
> +
> if (csize < rsize) {
> dbg_hid("report %d is too short, (%d < %d)\n", report->id,
> - csize, rsize);
> + csize, rsize);
> memset(cdata + csize, 0, rsize - csize);
> }
[ ... ]
> @@ -1851,6 +1865,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int i
> struct hid_report_enum *report_enum;
> struct hid_driver *hdrv;
> struct hid_report *report;
> + size_t bufsize = size;
> int ret = 0;
>
> if (!hid)
> @@ -1889,7 +1904,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int i
> goto unlock;
> }
>
> - ret = hid_report_raw_event(hid, type, data, size, interrupt);
> + ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);
[Severity: High]
Does this defeat the short report mitigation for most devices?
Since hid_input_report() is the primary entry point for HID transport
drivers and lacks a bufsize argument, it sets bufsize equal to size (the
payload length). When a short report is received, this guarantees that
bsize < rsize evaluates to true in hid_report_raw_event().
Will this incorrectly drop valid short reports for most HID devices by
triggering the -EINVAL rejection path?
>
> unlock:
> up(&hid->driver_input_lock);
[ ... ]
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index c8a8daedff3d0..1b130ac45acad 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -79,7 +79,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
> int err;
>
> size = kfifo_out(fifo, buf, sizeof(buf));
> - err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false);
> + err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false);
[Severity: High]
Should the bufsize argument here be sizeof(buf) instead of size?
Because bufsize is being passed as the short data length rather than the
true allocated buffer size, hid_report_raw_event() will evaluate
bsize < rsize as true and incorrectly reject valid short wacom reports
with -EINVAL.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604092659.3953067-2-lee@kernel.org?part=1
^ permalink raw reply
* [linux-5.15.y 3/3] HID: core: Fix size_t specifier in hid_report_raw_event()
From: Lee Jones @ 2026-06-04 9:26 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Viresh Kumar, Johan Hovold,
Alex Elder, Greg Kroah-Hartman, linux-input, linux-kernel,
greybus-dev, linux-staging
Cc: stable, Nathan Chancellor, Miguel Ojeda, Linus Torvalds,
Sasha Levin
In-Reply-To: <20260604092659.3953067-1-lee@kernel.org>
From: Nathan Chancellor <nathan@kernel.org>
[ Upstream commit 4d3a2a466b8d68d852a1f3bbf11204b718428dc4 ]
When building for 32-bit platforms, for which 'size_t' is
'unsigned int', there are warnings around using the incorrect format
specifier to print bsize in hid_report_raw_event():
drivers/hid/hid-core.c:2054:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
2053 | hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
| ~~~
| %zu
2054 | report->id, csize, bsize);
| ^~~~~
drivers/hid/hid-core.c:2076:29: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat]
2075 | hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
| ~~~
| %zu
2076 | report->id, rsize, bsize);
| ^~~~~
Use the proper 'size_t' format specifier, '%zu', to clear up the
warnings.
Cc: stable@vger.kernel.org
Fixes: 2c85c61d1332 ("HID: pass the buffer size to hid_report_raw_event")
Reported-by: Miguel Ojeda <ojeda@kernel.org>
Closes: https://lore.kernel.org/20260516020430.110135-1-ojeda@kernel.org/
Signed-off-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 3ab135238832446399614e7a4bb796d620717806)
Signed-off-by: Lee Jones <lee@kernel.org>
(cherry picked from commit 0f77a993b5426cca1b046c9ab4b2f8355a4d45dc)
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/hid/hid-core.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index a8d4673c7b8e..e106b59b55da 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1793,7 +1793,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data,
return 0;
if (unlikely(bsize < csize)) {
- hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
+ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %zu)\n",
report->id, csize, bsize);
return -EINVAL;
}
@@ -1815,7 +1815,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data,
rsize = max_buffer_size;
if (bsize < rsize) {
- hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %zu)\n",
report->id, rsize, bsize);
return -EINVAL;
}
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [linux-5.15.y 2/3] HID: pass the buffer size to hid_report_raw_event
From: Lee Jones @ 2026-06-04 9:26 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Viresh Kumar, Johan Hovold,
Alex Elder, Greg Kroah-Hartman, linux-input, linux-kernel,
greybus-dev, linux-staging
Cc: stable, Benjamin Tissoires, Jiri Kosina, Sasha Levin
In-Reply-To: <20260604092659.3953067-1-lee@kernel.org>
From: Benjamin Tissoires <bentiss@kernel.org>
[ Upstream commit 2c85c61d1332e1e16f020d76951baf167dcb6f7a ]
commit 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing
bogus memset()") enforced the provided data to be at least the size of
the declared buffer in the report descriptor to prevent a buffer
overflow. However, we can try to be smarter by providing both the buffer
size and the data size, meaning that hid_report_raw_event() can make
better decision whether we should plaining reject the buffer (buffer
overflow attempt) or if we can safely memset it to 0 and pass it to the
rest of the stack.
Fixes: 0a3fe972a7cb ("HID: core: Mitigate potential OOB by removing bogus memset()")
Cc: stable@vger.kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Acked-by: Johan Hovold <johan@kernel.org>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
Stable-dep-of: 206342541fc8 ("HID: core: introduce hid_safe_input_report()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 509c2605065004fc4cd86ee50a9350d402785307)
[Lee: Backported to linux-6.12.y and beyond]
Signed-off-by: Lee Jones <lee@kernel.org>
---
drivers/hid/hid-core.c | 29 ++++++++++++++++++++++-------
drivers/hid/hid-gfrm.c | 4 ++--
drivers/hid/hid-logitech-hidpp.c | 2 +-
drivers/hid/hid-multitouch.c | 2 +-
drivers/hid/hid-primax.c | 2 +-
drivers/hid/hid-vivaldi.c | 2 +-
drivers/hid/wacom_sys.c | 6 +++---
drivers/staging/greybus/hid.c | 2 +-
include/linux/hid.h | 4 ++--
9 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 4fb573ee31b2..a8d4673c7b8e 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1775,8 +1775,8 @@ int __hid_request(struct hid_device *hid, struct hid_report *report,
}
EXPORT_SYMBOL_GPL(__hid_request);
-int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
- int interrupt)
+int hid_report_raw_event(struct hid_device *hid, int type, u8 *data,
+ size_t bufsize, u32 size, int interrupt)
{
struct hid_report_enum *report_enum = hid->report_enum + type;
struct hid_report *report;
@@ -1784,16 +1784,24 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
int max_buffer_size = HID_MAX_BUFFER_SIZE;
unsigned int a;
u32 rsize, csize = size;
+ size_t bsize = bufsize;
u8 *cdata = data;
int ret = 0;
report = hid_get_report(report_enum, data);
if (!report)
- goto out;
+ return 0;
+
+ if (unlikely(bsize < csize)) {
+ hid_warn_ratelimited(hid, "Event data for report %d is incorrect (%d vs %ld)\n",
+ report->id, csize, bsize);
+ return -EINVAL;
+ }
if (report_enum->numbered) {
cdata++;
csize--;
+ bsize--;
}
rsize = hid_compute_report_size(report);
@@ -1806,9 +1814,15 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
else if (rsize > max_buffer_size)
rsize = max_buffer_size;
+ if (bsize < rsize) {
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %ld)\n",
+ report->id, rsize, bsize);
+ return -EINVAL;
+ }
+
if (csize < rsize) {
dbg_hid("report %d is too short, (%d < %d)\n", report->id,
- csize, rsize);
+ csize, rsize);
memset(cdata + csize, 0, rsize - csize);
}
@@ -1817,7 +1831,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
if (hid->claimed & HID_CLAIMED_HIDRAW) {
ret = hidraw_report_event(hid, data, size);
if (ret)
- goto out;
+ return ret;
}
if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
@@ -1830,7 +1844,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report);
-out:
+
return ret;
}
EXPORT_SYMBOL_GPL(hid_report_raw_event);
@@ -1851,6 +1865,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int i
struct hid_report_enum *report_enum;
struct hid_driver *hdrv;
struct hid_report *report;
+ size_t bufsize = size;
int ret = 0;
if (!hid)
@@ -1889,7 +1904,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int i
goto unlock;
}
- ret = hid_report_raw_event(hid, type, data, size, interrupt);
+ ret = hid_report_raw_event(hid, type, data, bufsize, size, interrupt);
unlock:
up(&hid->driver_input_lock);
diff --git a/drivers/hid/hid-gfrm.c b/drivers/hid/hid-gfrm.c
index 699186ff2349..d2a56bf92b41 100644
--- a/drivers/hid/hid-gfrm.c
+++ b/drivers/hid/hid-gfrm.c
@@ -66,7 +66,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
switch (data[1]) {
case GFRM100_SEARCH_KEY_DOWN:
ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_dn,
- sizeof(search_key_dn), 1);
+ sizeof(search_key_dn), sizeof(search_key_dn), 1);
break;
case GFRM100_SEARCH_KEY_AUDIO_DATA:
@@ -74,7 +74,7 @@ static int gfrm_raw_event(struct hid_device *hdev, struct hid_report *report,
case GFRM100_SEARCH_KEY_UP:
ret = hid_report_raw_event(hdev, HID_INPUT_REPORT, search_key_up,
- sizeof(search_key_up), 1);
+ sizeof(search_key_up), sizeof(search_key_up), 1);
break;
default:
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 7ed851bf5bc8..812ed660c555 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -3440,7 +3440,7 @@ static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
memcpy(&consumer_report[1], &data[3], 4);
/* We are called from atomic context */
hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
- consumer_report, 5, 1);
+ consumer_report, sizeof(consumer_report), 5, 1);
return 1;
}
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 7a092a2a1bf0..df87d7ae94c4 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -479,7 +479,7 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
}
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
- size, 0);
+ size, size, 0);
if (ret)
dev_warn(&hdev->dev, "failed to report feature\n");
}
diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
index 1e6413d07cae..16e2a811eda9 100644
--- a/drivers/hid/hid-primax.c
+++ b/drivers/hid/hid-primax.c
@@ -44,7 +44,7 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
data[0] |= (1 << (data[idx] - 0xE0));
data[idx] = 0;
}
- hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, 0);
+ hid_report_raw_event(hid, HID_INPUT_REPORT, data, size, size, 0);
return 1;
default: /* unknown report */
diff --git a/drivers/hid/hid-vivaldi.c b/drivers/hid/hid-vivaldi.c
index d57ec1767037..fdfea1355ee7 100644
--- a/drivers/hid/hid-vivaldi.c
+++ b/drivers/hid/hid-vivaldi.c
@@ -126,7 +126,7 @@ static void vivaldi_feature_mapping(struct hid_device *hdev,
}
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
- report_len, 0);
+ report_len, report_len, 0);
if (ret) {
dev_warn(&hdev->dev, "failed to report feature %d\n",
field->report->id);
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index c8a8daedff3d..1b130ac45aca 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -79,7 +79,7 @@ static void wacom_wac_queue_flush(struct hid_device *hdev,
int err;
size = kfifo_out(fifo, buf, sizeof(buf));
- err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false);
+ err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false);
if (err) {
hid_warn(hdev, "%s: unable to flush event due to error %d\n",
__func__, err);
@@ -324,7 +324,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
data, n, WAC_CMD_RETRIES);
if (ret == n && features->type == HID_GENERIC) {
ret = hid_report_raw_event(hdev,
- HID_FEATURE_REPORT, data, n, 0);
+ HID_FEATURE_REPORT, data, n, n, 0);
} else if (ret == 2 && features->type != HID_GENERIC) {
features->touch_max = data[1];
} else {
@@ -385,7 +385,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
data, n, WAC_CMD_RETRIES);
if (ret == n) {
ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
- data, n, 0);
+ data, n, n, 0);
} else {
hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
__func__);
diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
index adb91286803a..49b42c0ab078 100644
--- a/drivers/staging/greybus/hid.c
+++ b/drivers/staging/greybus/hid.c
@@ -201,7 +201,7 @@ static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report)
* we just need to setup the input fields, so using
* hid_report_raw_event is safe.
*/
- hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1);
+ hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, ghid->bufsize, size, 1);
}
static void gb_hid_init_reports(struct gb_hid *ghid)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 3968fa039c26..c92c5e2ae24f 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1206,8 +1206,8 @@ static inline u32 hid_report_len(struct hid_report *report)
return DIV_ROUND_UP(report->size, 8) + (report->id > 0);
}
-int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
- int interrupt);
+int hid_report_raw_event(struct hid_device *hid, int type, u8 *data,
+ size_t bufsize, u32 size, int interrupt);
/* HID quirks API */
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* [linux-5.15.y 1/3] HID: core: Add printk_ratelimited variants to hid_warn() etc
From: Lee Jones @ 2026-06-04 9:26 UTC (permalink / raw)
To: lee, Jiri Kosina, Benjamin Tissoires, Viresh Kumar, Johan Hovold,
Alex Elder, Greg Kroah-Hartman, linux-input, linux-kernel,
greybus-dev, linux-staging
Cc: stable, Vicki Pfau, Jiri Kosina
From: Vicki Pfau <vi@endrift.com>
hid_warn_ratelimited() is needed. Add the others as part of the block.
Signed-off-by: Vicki Pfau <vi@endrift.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
(cherry picked from commit 1d64624243af8329b4b219d8c39e28ea448f9929)
Signed-off-by: Lee Jones <lee@kernel.org>
(cherry picked from commit 3dc96d0b81eae69bf71e129e3f331c982c5c70fd)
Signed-off-by: Lee Jones <lee@kernel.org>
---
include/linux/hid.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 671403f208c9..3968fa039c26 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1248,4 +1248,15 @@ do { \
#define hid_dbg_once(hid, fmt, ...) \
dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_err_ratelimited(hid, fmt, ...) \
+ dev_err_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_notice_ratelimited(hid, fmt, ...) \
+ dev_notice_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_warn_ratelimited(hid, fmt, ...) \
+ dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_info_ratelimited(hid, fmt, ...) \
+ dev_info_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+#define hid_dbg_ratelimited(hid, fmt, ...) \
+ dev_dbg_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
+
#endif
--
2.54.0.1032.g2f8565e1d1-goog
^ permalink raw reply related
* Re: [PATCH v2 4/4] Input: snvs_pwrkey - report press event in interrupt handler
From: sashiko-bot @ 2026-06-04 9:07 UTC (permalink / raw)
To: Joy Zou; +Cc: imx, Frank.Li, linux-input, dmitry.torokhov
In-Reply-To: <20260604-pwrkey-cleanup-v2-4-59fa0ddf0d7c@nxp.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Data race between hardirq and timer softirq leads to desynchronized key state
--
commit 9a729213fc1cfc21c866a352fad1a978bfa80359
Author: Joy Zou <joy.zou@nxp.com>
Input: snvs_pwrkey - report press event in interrupt handler
This commit modifies the snvs_pwrkey driver to directly report key press
events from the interrupt handler during system suspend. This bypasses the
debounce timer to prevent event loss during the no_irq resume phase.
> diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
> index 4a1d048984826..f212a6b26185d 100644
> --- a/drivers/input/keyboard/snvs_pwrkey.c
> +++ b/drivers/input/keyboard/snvs_pwrkey.c
[ ... ]
> @@ -92,6 +93,15 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
> input_sync(input);
> pm_relax(input->dev.parent);
> } else {
> + /*
> + * Report key press events directly in interrupt handler to prevent event
> + * loss during system suspend.
> + */
> + if (pdata->suspended) {
> + pdata->keystate = 1;
> + input_report_key(input, pdata->keycode, 1);
> + input_sync(input);
> + }
[Severity: High]
Does this introduce a data race with the timer callback
imx_imx_snvs_check_for_events()?
The new hardirq logic writes to pdata->keystate locklessly here, but
imx_imx_snvs_check_for_events() also reads and writes it locklessly in
softirq context.
Could this sequence occur:
1. The timer callback reads state = 0 (key released) and evaluates
(state ^ pdata->keystate) as true.
2. The timer sets pdata->keystate = 0.
3. Before the timer calls input_event(), a key press triggers the hardirq
on the same CPU (preemption) or another CPU.
4. The hardirq sees pdata->suspended == true, sets pdata->keystate = 1,
and reports a key press (1).
5. The timer resumes and reports the release (0) to the input core.
This would leave pdata->keystate as 1, while the input subsystem's last
reported state is 0. On the next physical key press, the timer would read
state = 1, evaluate 1 ^ 1 as 0, and ignore the event. This might cause the
power key to become logically stuck until physically released again.
> mod_timer(&pdata->check_timer,
> jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604-pwrkey-cleanup-v2-0-59fa0ddf0d7c@nxp.com?part=4
^ permalink raw reply
* Re: [PATCH v2 1/4] Input: snvs_pwrkey - make use of dev_err_probe()
From: sashiko-bot @ 2026-06-04 7:04 UTC (permalink / raw)
To: Joy Zou; +Cc: linux-input, imx, Frank.Li, dmitry.torokhov
In-Reply-To: <20260604-pwrkey-cleanup-v2-1-59fa0ddf0d7c@nxp.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Incorrect pairing of pm_wakeup_event(dev, 0) and pm_relax(dev) fails to keep the system awake.
- [Medium] Missing cleanup for dev_pm_set_wake_irq() causes resource leak and -EEXIST on rebind.
--
commit c91200344c06e4226bdcadd16b77ac3688786f5d
Author: Joy Zou <joy.zou@nxp.com>
Input: snvs_pwrkey - make use of dev_err_probe()
This patch updates the imx_snvs_pwrkey_probe function to use dev_err_probe()
on its error paths. This provides more concise error logging and helps
identify issues more easily during probe.
> diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
> index 954055aaf6e29..748196fcce75b 100644
> --- a/drivers/input/keyboard/snvs_pwrkey.c
> +++ b/drivers/input/keyboard/snvs_pwrkey.c
[ ... ]
> @@ -209,16 +203,12 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
> error = devm_request_irq(&pdev->dev, pdata->irq,
> imx_snvs_pwrkey_interrupt,
> 0, pdev->name, pdev);
> - if (error) {
> - dev_err(&pdev->dev, "interrupt not available.\n");
> - return error;
> - }
> + if (error)
> + return dev_err_probe(&pdev->dev, error, "interrupt not available.\n");
>
> error = input_register_device(input);
> - if (error < 0) {
> - dev_err(&pdev->dev, "failed to register input device\n");
> - return error;
> - }
> + if (error < 0)
> + return dev_err_probe(&pdev->dev, error, "failed to register input device\n");
>
> device_init_wakeup(&pdev->dev, pdata->wakeup);
> error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
[Severity: Medium]
This is a pre-existing issue, but since dev_pm_set_wake_irq() is called here
without a corresponding cleanup mechanism, does this cause a resource leak?
When the driver is unbound via sysfs, the allocated wake_irq structure is
leaked and the device remains marked with a wakeirq because there is no
.remove callback or devm action to call dev_pm_clear_wake_irq().
Will a subsequent rebind cause dev_pm_set_wake_irq() to fail with -EEXIST
because dev->power.wakeirq is still populated?
This is also a pre-existing issue, but I noticed something else regarding the
wake event handling.
drivers/input/keyboard/snvs_pwrkey.c:imx_snvs_pwrkey_interrupt() {
...
pm_wakeup_event(input->dev.parent, 0);
...
}
And later in the driver:
drivers/input/keyboard/snvs_pwrkey.c:imx_imx_snvs_check_for_events() {
...
pm_relax(pdata->input->dev.parent);
...
}
[Severity: High]
Does this incorrect pairing of pm_wakeup_event(dev, 0) and pm_relax(dev) fail
to keep the system awake?
Passing a 0 ms timeout to pm_wakeup_event() activates and immediately
deactivates the wakeup source. Because the wakeup source is already inactive,
the later call to pm_relax() acts as a no-op.
Does this mean the system is free to enter auto-sleep immediately after the
interrupt and before the timer executes, potentially delaying or dropping the
power key event delivery? Would it be better to use pm_stay_awake() here
instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604-pwrkey-cleanup-v2-0-59fa0ddf0d7c@nxp.com?part=1
^ permalink raw reply
* [PATCH v2 4/4] Input: snvs_pwrkey - report press event in interrupt handler
From: Joy Zou @ 2026-06-04 6:56 UTC (permalink / raw)
To: Dmitry Torokhov, Frank Li, Peng Fan, Jacky Bai, Ye Li
Cc: imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260604-pwrkey-cleanup-v2-0-59fa0ddf0d7c@nxp.com>
The driver implements debounce protection using a timer-based mechanism:
when a key interrupt occurs, a timer is scheduled to verify the key state
after DEBOUNCE_TIME before reporting the event. This works well during
normal operation.
However, key press events can be lost during system resume on platforms
like i.MX8MQ-EVK because:
1. During the no_irq resume phase, PCIe driver restoration can take up to
200ms with IRQs disabled.
2. The power key interrupt remains pending during the no_irq phase.
3. If the key is released before IRQs are re-enabled, the timer eventually
runs but sees the key as released and skips reporting the event.
Report key press events directly in interrupt handler to prevent event
loss during system suspend. This is safe because:
1. Only one event is reported per suspend cycle.
2. Normal operation retains the existing timer-based debounce mechanism.
Signed-off-by: Joy Zou <joy.zou@nxp.com>
---
Changes for v2:
1. Add a boolean variable suspended and PM callback functions to replace
the use of the is_suspended field per AI review comments.
2. Move event report handle to else branch in suspended state, since the
pdata->minor_rev == 0 branch has no debounce detection per AI review
comments.
3. Modify the commit message.
---
drivers/input/keyboard/snvs_pwrkey.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
index 4a1d04898482669894e9978014b62e4e9774b4e4..f212a6b26185d13e1af62728e7b2add5010adc5a 100644
--- a/drivers/input/keyboard/snvs_pwrkey.c
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -39,6 +39,7 @@ struct pwrkey_drv_data {
int keycode;
int keystate; /* 1:pressed */
int wakeup;
+ bool suspended; /* Track suspend state */
struct timer_list check_timer;
struct input_dev *input;
u8 minor_rev;
@@ -92,6 +93,15 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
input_sync(input);
pm_relax(input->dev.parent);
} else {
+ /*
+ * Report key press events directly in interrupt handler to prevent event
+ * loss during system suspend.
+ */
+ if (pdata->suspended) {
+ pdata->keystate = 1;
+ input_report_key(input, pdata->keycode, 1);
+ input_sync(input);
+ }
mod_timer(&pdata->check_timer,
jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
}
@@ -219,6 +229,30 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+ pdata->suspended = true;
+
+ return 0;
+}
+
+static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+ pdata->suspended = false;
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops,
+ imx_snvs_pwrkey_suspend,
+ imx_snvs_pwrkey_resume);
+
static const struct of_device_id imx_snvs_pwrkey_ids[] = {
{ .compatible = "fsl,sec-v4.0-pwrkey" },
{ /* sentinel */ }
@@ -229,6 +263,7 @@ static struct platform_driver imx_snvs_pwrkey_driver = {
.driver = {
.name = "snvs_pwrkey",
.of_match_table = imx_snvs_pwrkey_ids,
+ .pm = &imx_snvs_pwrkey_pm_ops,
},
.probe = imx_snvs_pwrkey_probe,
};
--
2.50.1
^ permalink raw reply related
* [PATCH v2 3/4] Input: snvs_pwrkey - use local device pointer to simple code
From: Joy Zou @ 2026-06-04 6:56 UTC (permalink / raw)
To: Dmitry Torokhov, Frank Li, Peng Fan, Jacky Bai, Ye Li
Cc: imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260604-pwrkey-cleanup-v2-0-59fa0ddf0d7c@nxp.com>
Use local struct device pointer to avoid reference the platform_device
pointer every time.
No functional change.
Signed-off-by: Joy Zou <joy.zou@nxp.com>
---
Changes for v2:
1. Use dev instead of &pdev->dev for devm_input_allocate_device(),
which was missed in patch v1 per AI review comments.
2. Modify commit message.
---
drivers/input/keyboard/snvs_pwrkey.c | 41 ++++++++++++++++++------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
index a291812e6d22ea963568cced5b889bad00658526..4a1d04898482669894e9978014b62e4e9774b4e4 100644
--- a/drivers/input/keyboard/snvs_pwrkey.c
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -112,6 +112,7 @@ static void imx_snvs_pwrkey_act(void *pdata)
static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct pwrkey_drv_data *pdata;
struct input_dev *input;
struct device_node *np;
@@ -122,26 +123,26 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
u32 vid;
/* Get SNVS register Page */
- np = pdev->dev.of_node;
+ np = dev->of_node;
if (!np)
- return dev_err_probe(&pdev->dev, -ENODEV, "Device tree node not found\n");
+ return dev_err_probe(dev, -ENODEV, "Device tree node not found\n");
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
if (IS_ERR(pdata->snvs))
- return dev_err_probe(&pdev->dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
+ return dev_err_probe(dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
pdata->keycode = KEY_POWER;
- dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+ dev_warn(dev, "KEY_POWER without setting in dts\n");
}
- clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ return dev_err_probe(dev, PTR_ERR(clk),
"Failed to get snvs clock (%pe)\n", clk);
pdata->wakeup = of_property_read_bool(np, "wakeup-source");
@@ -162,7 +163,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
bpt = (val / 5) - 1;
break;
default:
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"power-off-time-sec %d out of range\n", val);
}
@@ -180,9 +181,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
timer_setup(&pdata->check_timer, imx_imx_snvs_check_for_events, 0);
- input = devm_input_allocate_device(&pdev->dev);
+ input = devm_input_allocate_device(dev);
if (!input) {
- dev_err(&pdev->dev, "failed to allocate the input device\n");
+ dev_err(dev, "failed to allocate the input device\n");
return -ENOMEM;
}
@@ -193,27 +194,27 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
input_set_capability(input, EV_KEY, pdata->keycode);
/* input customer action to cancel release timer */
- error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
+ error = devm_add_action(dev, imx_snvs_pwrkey_act, pdata);
if (error)
- return dev_err_probe(&pdev->dev, error, "failed to register remove action\n");
+ return dev_err_probe(dev, error, "failed to register remove action\n");
pdata->input = input;
platform_set_drvdata(pdev, pdata);
- error = devm_request_irq(&pdev->dev, pdata->irq,
- imx_snvs_pwrkey_interrupt,
- 0, pdev->name, pdev);
+ error = devm_request_irq(dev, pdata->irq,
+ imx_snvs_pwrkey_interrupt,
+ 0, pdev->name, pdev);
if (error)
- return dev_err_probe(&pdev->dev, error, "interrupt not available.\n");
+ return dev_err_probe(dev, error, "interrupt not available.\n");
error = input_register_device(input);
if (error < 0)
- return dev_err_probe(&pdev->dev, error, "failed to register input device\n");
+ return dev_err_probe(dev, error, "failed to register input device\n");
- device_init_wakeup(&pdev->dev, pdata->wakeup);
- error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
+ device_init_wakeup(dev, pdata->wakeup);
+ error = dev_pm_set_wake_irq(dev, pdata->irq);
if (error)
- dev_err(&pdev->dev, "irq wake enable failed.\n");
+ dev_err(dev, "irq wake enable failed.\n");
return 0;
}
--
2.50.1
^ permalink raw reply related
* [PATCH v2 2/4] Input: snvs_pwrkey - replace hardcoding -EINVAL with original error code
From: Joy Zou @ 2026-06-04 6:56 UTC (permalink / raw)
To: Dmitry Torokhov, Frank Li, Peng Fan, Jacky Bai, Ye Li
Cc: imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260604-pwrkey-cleanup-v2-0-59fa0ddf0d7c@nxp.com>
Hardcoding -EINVAL discards the actual error code, which breaks probe
deferral (-EPROBE_DEFER) and loses critical diagnostic information
needed for proper kernel error handling.
Signed-off-by: Joy Zou <joy.zou@nxp.com>
---
drivers/input/keyboard/snvs_pwrkey.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
index 748196fcce75ba7192b1ee5fc516bdf4a9ae074d..a291812e6d22ea963568cced5b889bad00658526 100644
--- a/drivers/input/keyboard/snvs_pwrkey.c
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -148,7 +148,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
pdata->irq = platform_get_irq(pdev, 0);
if (pdata->irq < 0)
- return -EINVAL;
+ return pdata->irq;
error = of_property_read_u32(np, "power-off-time-sec", &val);
if (!error) {
--
2.50.1
^ permalink raw reply related
* [PATCH v2 1/4] Input: snvs_pwrkey - make use of dev_err_probe()
From: Joy Zou @ 2026-06-04 6:56 UTC (permalink / raw)
To: Dmitry Torokhov, Frank Li, Peng Fan, Jacky Bai, Ye Li
Cc: imx, linux-input, linux-kernel, Joy Zou
In-Reply-To: <20260604-pwrkey-cleanup-v2-0-59fa0ddf0d7c@nxp.com>
Add dev_err_probe() at return path of probe() to support users to
identify issues easier.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Joy Zou <joy.zou@nxp.com>
---
Changes in v2:
1. Drop dev_err_probe() change for platform_get_irq() per AI review comments.
2. Add Reviewed-by tag.
---
drivers/input/keyboard/snvs_pwrkey.c | 38 +++++++++++++-----------------------
1 file changed, 14 insertions(+), 24 deletions(-)
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
index 954055aaf6e29527f22f8129fd47ca17722e2bc9..748196fcce75ba7192b1ee5fc516bdf4a9ae074d 100644
--- a/drivers/input/keyboard/snvs_pwrkey.c
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -124,17 +124,15 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
/* Get SNVS register Page */
np = pdev->dev.of_node;
if (!np)
- return -ENODEV;
+ return dev_err_probe(&pdev->dev, -ENODEV, "Device tree node not found\n");
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
- if (IS_ERR(pdata->snvs)) {
- dev_err(&pdev->dev, "Can't get snvs syscon\n");
- return PTR_ERR(pdata->snvs);
- }
+ if (IS_ERR(pdata->snvs))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pdata->snvs), "Can't get snvs syscon\n");
if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
pdata->keycode = KEY_POWER;
@@ -142,10 +140,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
}
clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "Failed to get snvs clock (%pe)\n", clk);
- return PTR_ERR(clk);
- }
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "Failed to get snvs clock (%pe)\n", clk);
pdata->wakeup = of_property_read_bool(np, "wakeup-source");
@@ -165,9 +162,8 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
bpt = (val / 5) - 1;
break;
default:
- dev_err(&pdev->dev,
- "power-off-time-sec %d out of range\n", val);
- return -EINVAL;
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "power-off-time-sec %d out of range\n", val);
}
regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_BPT_MASK,
@@ -198,10 +194,8 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
/* input customer action to cancel release timer */
error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
- if (error) {
- dev_err(&pdev->dev, "failed to register remove action\n");
- return error;
- }
+ if (error)
+ return dev_err_probe(&pdev->dev, error, "failed to register remove action\n");
pdata->input = input;
platform_set_drvdata(pdev, pdata);
@@ -209,16 +203,12 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
error = devm_request_irq(&pdev->dev, pdata->irq,
imx_snvs_pwrkey_interrupt,
0, pdev->name, pdev);
- if (error) {
- dev_err(&pdev->dev, "interrupt not available.\n");
- return error;
- }
+ if (error)
+ return dev_err_probe(&pdev->dev, error, "interrupt not available.\n");
error = input_register_device(input);
- if (error < 0) {
- dev_err(&pdev->dev, "failed to register input device\n");
- return error;
- }
+ if (error < 0)
+ return dev_err_probe(&pdev->dev, error, "failed to register input device\n");
device_init_wakeup(&pdev->dev, pdata->wakeup);
error = dev_pm_set_wake_irq(&pdev->dev, pdata->irq);
--
2.50.1
^ permalink raw reply related
* [PATCH v2 0/4] snvs_pwrkey - code improvements and add report event
From: Joy Zou @ 2026-06-04 6:56 UTC (permalink / raw)
To: Dmitry Torokhov, Frank Li, Peng Fan, Jacky Bai, Ye Li
Cc: imx, linux-input, linux-kernel, Joy Zou
This patch series improves the snvs_pwrkey driver with better code quality
and add report press event.
The main improvements include:
1. Clean up the code by using local device pointers and dev_err_probe() for
better readability and easier debugging.
2. Fix potential event loss during system suspend by reporting key press events
directly in the interrupt handler.
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Frank Li <Frank.Li@nxp.com>
To: Peng Fan <peng.fan@nxp.com>
To: Jacky Bai <ping.bai@nxp.com>
To: Ye Li <ye.li@nxp.com>
Cc: imx@lists.linux.dev
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Joy Zou <joy.zou@nxp.com>
---
Changes in v2:
- Drop dev_err_probe() change for platform_get_irq() per AI review comments.
- Add new patch #2 replace hardcoding -EINVAL with original error code per
AI review comments.
- Use dev instead of &pdev->dev for devm_input_allocate_device(), which was
missed in patch v1 per AI review comments.
- Add a boolean variable suspended and PM callback functions to replace
the use of the is_suspended field per AI review comments.
- Move event report handle to else branch in suspended state, since the
pdata->minor_rev == 0 branch has no debounce detection per AI review
comments.
- Modify patch #3 and #4 commit message.
- Add Reviewed-by tag for patch #1.
- Link to v1: https://lore.kernel.org/r/20260326-pwrkey-cleanup-v1-0-d85d7c0bf275@nxp.com
---
Joy Zou (4):
Input: snvs_pwrkey - make use of dev_err_probe()
Input: snvs_pwrkey - replace hardcoding -EINVAL with original error code
Input: snvs_pwrkey - use local device pointer to simple code
Input: snvs_pwrkey - report press event in interrupt handler
drivers/input/keyboard/snvs_pwrkey.c | 102 ++++++++++++++++++++++-------------
1 file changed, 64 insertions(+), 38 deletions(-)
---
base-commit: f7af91adc230aa99e23330ecf85bc9badd9780ad
change-id: 20260326-pwrkey-cleanup-99d3de61ed6d
Best regards,
--
Joy Zou <joy.zou@nxp.com>
^ permalink raw reply
* Re: [PATCH v2] HID: wacom: stop hardware after post-start probe failures
From: sashiko-bot @ 2026-06-04 5:14 UTC (permalink / raw)
To: Myeonghun Pak; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260604045710.25512-1-mhun512@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Failing to cancel scheduled delayed works and timers on the probe error path leads to a Use-After-Free when the `wacom` structure is freed.
- [High] Nullifying input pointers in `wacom_register_inputs` before stopping the hardware leaves a race window where incoming interrupts can cause a NULL pointer dereference.
- [High] Hardware interrupts can arrive before `wacom->remote` is allocated, causing a NULL pointer dereference in the IRQ handlers.
--
commit 517a367ac93aa0d6c6470c3a21cdb477449345e3
Author: Myeonghun Pak <mhun512@gmail.com>
Subject: HID: wacom: stop hardware after post-start probe failures
This patch routes post-hid_hw_start() failures in wacom_parse_and_register()
through hid_hw_stop() before releasing driver resources. This ensures that the
HID hardware is properly stopped before the wacom resources are freed.
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 2220168bf1164..252771dcfd8f8 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -2462,16 +2462,16 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
>
> error = wacom_register_inputs(wacom);
> if (error)
> - goto fail;
> + goto fail_hw_stop;
[Severity: High]
This is a pre-existing issue, but does this leave a small race window where
an interrupt could cause a NULL pointer dereference?
If wacom_register_inputs() fails, its error path sets the input pointers to
NULL:
fail:
wacom_wac->pad_input = NULL;
wacom_wac->touch_input = NULL;
wacom_wac->pen_input = NULL;
return error;
The code then jumps to fail_hw_stop. Since hid_hw_start() has already been
called, the hardware is active. Could an interrupt arrive before hid_hw_stop()
completes, triggering an IRQ handler like wacom_intuos_irq() which
dereferences wacom->pen_input without checking for NULL?
>
> if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) {
> error = wacom_initialize_leds(wacom);
> if (error)
> - goto fail;
> + goto fail_hw_stop;
>
> error = wacom_initialize_remotes(wacom);
> if (error)
> - goto fail;
> + goto fail_hw_stop;
[Severity: High]
This is a pre-existing issue, but I have a question about how this handles
interrupts arriving before initialization is complete.
Since hid_hw_start() is called prior to wacom_initialize_remotes():
error = hid_hw_start(hdev, connect_mask);
...
error = wacom_initialize_remotes(wacom);
Could an early hardware event or a malicious USB report arrive and trigger
wacom_remote_irq() or wacom_remote_status_irq()? Those handlers appear to
dereference wacom->remote to acquire a spinlock:
spin_lock_irqsave(&remote->remote_lock, flags);
If the interrupt arrives before wacom_initialize_remotes() has allocated
wacom->remote, would this lead to a NULL pointer dereference?
[ ... ]
> @@ -2501,7 +2501,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
>
> return 0;
>
> -fail_quirks:
> +fail_hw_stop:
> hid_hw_stop(hdev);
> fail:
> wacom_release_resources(wacom);
[Severity: High]
This isn't a bug introduced by this patch, but it seems there might be a
pre-existing issue with pending works on the error path.
If wacom_query_tablet_data() is called earlier:
wacom_query_tablet_data(struct wacom *wacom)
{
schedule_delayed_work(&wacom->init_work, msecs_to_jiffies(1000));
}
And then a subsequent step like hid_hw_open() fails, we jump to fail_hw_stop
which stops the hardware and releases resources:
fail_hw_stop:
hid_hw_stop(hdev);
fail:
wacom_release_resources(wacom);
return error;
Since the error is returned to wacom_probe() which can cause the devres core
to free the wacom structure, could the uncancelled wacom->init_work (or other
works/timers scheduled by interrupts) execute later and dereference the freed
wacom memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260604045710.25512-1-mhun512@gmail.com?part=1
^ permalink raw reply
* [PATCH v2] HID: wacom: stop hardware after post-start probe failures
From: Myeonghun Pak @ 2026-06-04 4:56 UTC (permalink / raw)
To: Ping Cheng, Jason Gerecke
Cc: Jiri Kosina, Benjamin Tissoires, linux-input, linux-kernel,
Myeonghun Pak, stable, Ijae Kim, Dmitry Torokhov
In-Reply-To: <20260524175552.1973-1-mhun512@gmail.com>
wacom_parse_and_register() starts HID hardware before registering inputs
and initializing pad LEDs/remotes. Those later steps can fail, but their
error paths currently release Wacom resources without stopping the HID
hardware.
Route post-hid_hw_start() failures through hid_hw_stop() before
releasing driver resources.
This issue was identified during our ongoing static-analysis research while
reviewing kernel code.
Fixes: c1d6708bf0d3 ("HID: wacom: Do not register input devices until after hid_hw_start")
Cc: stable@vger.kernel.org
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
Reviewed-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
Changes in v2:
- Drop fail_quirks and use fail_hw_stop for every post-hid_hw_start()
failure path, as suggested by Dmitry.
drivers/hid/wacom_sys.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 0d1c6d90f..ee53186e0 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -2456,16 +2456,16 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
error = wacom_register_inputs(wacom);
if (error)
- goto fail;
+ goto fail_hw_stop;
if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) {
error = wacom_initialize_leds(wacom);
if (error)
- goto fail;
+ goto fail_hw_stop;
error = wacom_initialize_remotes(wacom);
if (error)
- goto fail;
+ goto fail_hw_stop;
}
if (!wireless) {
@@ -2479,14 +2479,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
cancel_delayed_work_sync(&wacom->init_work);
_wacom_query_tablet_data(wacom);
error = -ENODEV;
- goto fail_quirks;
+ goto fail_hw_stop;
}
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) {
error = hid_hw_open(hdev);
if (error) {
hid_err(hdev, "hw open failed\n");
- goto fail_quirks;
+ goto fail_hw_stop;
}
}
@@ -2495,7 +2495,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
return 0;
-fail_quirks:
+fail_hw_stop:
hid_hw_stop(hdev);
fail:
wacom_release_resources(wacom);
return error;
--
2.47.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox