[PATCH 04/12] HID: rmi: fix device lifecycle, suspend, and reset work handling

From
Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date
2026-11-14 06:00:08
Also in
lkml
Reply to
Dmitry Torokhov, 2026-11-14
Incoming events can schedule reset work before transport initialization
completes or re-queue it during suspend and removal, power management
callbacks can dereference uninitialized device structures on failure,
and failed resume steps can leave transport interrupts disabled.

Gate reset work scheduling and power management callbacks on device
state, track suspended state to prevent work execution, ensure resume
re-enables transport interrupts, and permanently disable reset work on
removal.

Fixes: 0b2c7a897378 ("HID: rmi: Make hid-rmi a transport driver for synaptics-rmi4")
Fixes: 8725aa4fa7de ("HID: rmi: Check that the RMI_STARTED bit is set before unregistering the RMI transport device")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/hid/hid-rmi.c | 35 ++++++++++++++++++++++-------------
 1 file changed, 22 insertions(+), 13 deletions(-)
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index d9181fd66c5c..8783edb7fd6a 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -31,6 +31,7 @@
 #define RMI_READ_REQUEST_PENDING	0
 #define RMI_READ_DATA_PENDING		1
 #define RMI_STARTED			2
+#define RMI_SUSPENDED			3
 
 /* device flags */
 #define RMI_DEVICE			BIT(0)
@@ -428,6 +429,8 @@ static int rmi_event(struct hid_device *hdev, struct hid_field *field,
 	struct rmi_data *data = hid_get_drvdata(hdev);
 
 	if ((data->device_flags & RMI_DEVICE) &&
+	    test_bit(RMI_STARTED, &data->flags) &&
+	    !test_bit(RMI_SUSPENDED, &data->flags) &&
 	    (field->application == HID_GD_POINTER ||
 	    field->application == HID_GD_MOUSE)) {
 		if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) {
@@ -469,12 +472,16 @@ static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
 	struct rmi_device *rmi_dev = data->xport.rmi_dev;
 	int ret;
 
-	if (!(data->device_flags & RMI_DEVICE))
+	if (!test_bit(RMI_STARTED, &data->flags))
 		return 0;
 
+	set_bit(RMI_SUSPENDED, &data->flags);
+	cancel_work_sync(&data->reset_work);
+
 	ret = rmi_driver_suspend(rmi_dev, false);
 	if (ret) {
 		hid_warn(hdev, "Failed to suspend device: %d\n", ret);
+		clear_bit(RMI_SUSPENDED, &data->flags);
 		return ret;
 	}
 
@@ -486,27 +493,31 @@ static int rmi_post_resume(struct hid_device *hdev)
 	struct rmi_data *data = hid_get_drvdata(hdev);
 	struct rmi_device *rmi_dev = data->xport.rmi_dev;
 	int ret;
+	int err;
 
-	if (!(data->device_flags & RMI_DEVICE))
+	if (!test_bit(RMI_STARTED, &data->flags))
 		return 0;
 
 	/* Make sure the HID device is ready to receive events */
 	ret = hid_hw_open(hdev);
 	if (ret)
-		return ret;
+		goto out;
 
 	ret = rmi_reset_attn_mode(hdev);
 	if (ret)
-		goto out;
+		hid_warn(hdev, "Failed to reset attention mode: %d\n", ret);
 
-	ret = rmi_driver_resume(rmi_dev, false);
-	if (ret) {
-		hid_warn(hdev, "Failed to resume device: %d\n", ret);
-		goto out;
+	err = rmi_driver_resume(rmi_dev, false);
+	if (err) {
+		hid_warn(hdev, "Failed to resume device: %d\n", err);
+		if (!ret)
+			ret = err;
 	}
 
-out:
 	hid_hw_close(hdev);
+
+out:
+	clear_bit(RMI_SUSPENDED, &data->flags);
 	return ret;
 }
 
@@ -787,10 +798,8 @@ static void rmi_remove(struct hid_device *hdev)
 {
 	struct rmi_data *hdata = hid_get_drvdata(hdev);
 
-	if ((hdata->device_flags & RMI_DEVICE)
-	    && test_bit(RMI_STARTED, &hdata->flags)) {
-		clear_bit(RMI_STARTED, &hdata->flags);
-		cancel_work_sync(&hdata->reset_work);
+	if (test_and_clear_bit(RMI_STARTED, &hdata->flags)) {
+		disable_work_sync(&hdata->reset_work);
 		rmi_unregister_transport_device(&hdata->xport);
 	}
 
-- 
2.56.0.679.gc527c65306-goog
Thread (14 messages) 14 messages, 2 authors, last reply 13h ago
Keyboard shortcuts
hback out one level
jnext message in thread / next series-diff position
kprevious message in thread / previous series-diff position
ldrill in
Escclose help / fold thread tree
?toggle this help