[PATCH 09/21] HID: stadia: use open/close to manage workqueue lifecycle

Dmitry Torokhov <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,org.kernel.vger.linux-doc,org.kernel.vger.linux-input
Message-ID <[email protected]>
Override input device open() and close() callbacks to enable and disable
the force-feedback workqueue item synchronously.

When the input device is opened by userspace, call hid_hw_open() and
enable_work(). When it is closed, disable_work_sync() ensures that any
pending or running work item is cancelled/flushed and no further work
items can be scheduled.

In close(), zero out magnitudes and issue a final report to turn off the
rumble motors on the physical controller before shutting down transport
I/O.

Pack strong and weak magnitudes into a single u32 integer using
WRITE_ONCE() and READ_ONCE() for atomic, lockless updates.

This allows eliminating the manual 'removed' boolean flag and spinlock
completely.

Assisted-by: Antigravity:gemini-3.6-flash
Signed-off-by: Dmitry Torokhov <[email protected]>
---
 drivers/hid/hid-google-stadiaff.c | 71 +++++++++++++++++++++------------------
 1 file changed, 38 insertions(+), 33 deletions(-)

diff --git a/drivers/hid/hid-google-stadiaff.c b/drivers/hid/hid-google-stadiaff.c
index 6b38d2421d3d..d6a73d210599 100644
--- a/drivers/hid/hid-google-stadiaff.c
+++ b/drivers/hid/hid-google-stadiaff.c
@@ -17,10 +17,7 @@
 struct stadiaff_device {
 	struct hid_device *hid;
 	struct hid_report *report;
-	spinlock_t lock;
-	bool removed;
-	uint16_t strong_magnitude;
-	uint16_t weak_magnitude;
+	u32 magnitudes;
 	struct work_struct work;
 };
 
@@ -29,12 +26,10 @@ static void stadiaff_work(struct work_struct *work)
 	struct stadiaff_device *stadiaff =
 		container_of(work, struct stadiaff_device, work);
 	struct hid_field *rumble_field = stadiaff->report->field[0];
-	unsigned long flags;
+	u32 mags = READ_ONCE(stadiaff->magnitudes);
 
-	spin_lock_irqsave(&stadiaff->lock, flags);
-	rumble_field->value[0] = stadiaff->strong_magnitude;
-	rumble_field->value[1] = stadiaff->weak_magnitude;
-	spin_unlock_irqrestore(&stadiaff->lock, flags);
+	rumble_field->value[0] = mags & 0xffff;
+	rumble_field->value[1] = (mags >> 16) & 0xffff;
 
 	hid_hw_request(stadiaff->hid, stadiaff->report, HID_REQ_SET_REPORT);
 }
@@ -44,19 +39,41 @@ static int stadiaff_play(struct input_dev *dev, void *data,
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
-	unsigned long flags;
+	u32 mags = (u32)effect->u.rumble.strong_magnitude |
+		  ((u32)effect->u.rumble.weak_magnitude << 16);
 
-	spin_lock_irqsave(&stadiaff->lock, flags);
-	if (!stadiaff->removed) {
-		stadiaff->strong_magnitude = effect->u.rumble.strong_magnitude;
-		stadiaff->weak_magnitude = effect->u.rumble.weak_magnitude;
-		schedule_work(&stadiaff->work);
-	}
-	spin_unlock_irqrestore(&stadiaff->lock, flags);
+	WRITE_ONCE(stadiaff->magnitudes, mags);
+	schedule_work(&stadiaff->work);
 
 	return 0;
 }
 
+static int stadia_input_open(struct input_dev *dev)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
+	int error;
+
+	error = hid_hw_open(hid);
+	if (error)
+		return error;
+
+	enable_work(&stadiaff->work);
+	return 0;
+}
+
+static void stadia_input_close(struct input_dev *dev)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
+
+	WRITE_ONCE(stadiaff->magnitudes, 0);
+	stadiaff_work(&stadiaff->work);
+	disable_work_sync(&stadiaff->work);
+
+	hid_hw_close(hid);
+}
+
 static int stadiaff_init(struct hid_device *hid)
 {
 	struct stadiaff_device *stadiaff;
@@ -90,11 +107,13 @@ static int stadiaff_init(struct hid_device *hid)
 	if (error)
 		return error;
 
-	stadiaff->removed = false;
 	stadiaff->hid = hid;
 	stadiaff->report = report;
 	INIT_WORK(&stadiaff->work, stadiaff_work);
-	spin_lock_init(&stadiaff->lock);
+	disable_work_sync(&stadiaff->work);
+
+	dev->open = stadia_input_open;
+	dev->close = stadia_input_close;
 
 	hid_info(hid, "Force Feedback for Google Stadia controller\n");
 
@@ -127,19 +146,6 @@ static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	return 0;
 }
 
-static void stadia_remove(struct hid_device *hid)
-{
-	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
-	unsigned long flags;
-
-	spin_lock_irqsave(&stadiaff->lock, flags);
-	stadiaff->removed = true;
-	spin_unlock_irqrestore(&stadiaff->lock, flags);
-
-	cancel_work_sync(&stadiaff->work);
-	hid_hw_stop(hid);
-}
-
 static const struct hid_device_id stadia_devices[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
@@ -151,7 +157,6 @@ static struct hid_driver stadia_driver = {
 	.name = "stadia",
 	.id_table = stadia_devices,
 	.probe = stadia_probe,
-	.remove = stadia_remove,
 };
 module_hid_driver(stadia_driver);
 

-- 
2.55.0.629.g250fe7f194-goog
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.