[PATCH 12/21] HID: move generic FF initialization into hidinput_connect()

Dmitry Torokhov <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,org.kernel.vger.linux-doc,org.kernel.vger.linux-input
Message-ID <[email protected]>
Generic force-feedback initialization (pidff) currently happens in
hid_connect() after hidinput_connect() has already registered the input
devices. This is racy as the device is live and visible to userspace
before FF support is fully set up.

Move the call to hdev->ff_init() into hidinput_connect(), ensuring it
runs before input_register_device() is called. This closes the race
window for standard PID-capable devices.

The initialization now also checks (connect_mask & HID_CONNECT_FF) and
!hid_has_ff_input() to avoid conflicts with custom FF implementations
and respect driver opt-outs.

Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <[email protected]>
---
 drivers/hid/hid-core.c  | 21 ++-------------------
 drivers/hid/hid-input.c | 21 +++++++++++++++++++--
 include/linux/hid.h     |  2 +-
 3 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 2767a171eae9..d8cba852d2b5 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2287,18 +2287,6 @@ static const BIN_ATTR_RO(report_descriptor, HID_MAX_DESCRIPTOR_SIZE);
 
 static const DEVICE_ATTR_RO(country);
 
-static bool hid_has_ff_input(struct hid_device *hdev)
-{
-	struct hid_input *hidinput;
-
-	list_for_each_entry(hidinput, &hdev->inputs, list) {
-		if (test_bit(EV_FF, hidinput->input->evbit))
-			return true;
-	}
-
-	return false;
-}
-
 int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
 {
 	static const char *types[] = { "Device", "Pointer", "Mouse", "Device",
@@ -2324,8 +2312,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
 	if (hid_hiddev(hdev))
 		connect_mask |= HID_CONNECT_HIDDEV_FORCE;
 
-	if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
-				connect_mask & HID_CONNECT_HIDINPUT_FORCE))
+	if ((connect_mask & HID_CONNECT_HIDINPUT) &&
+	    !hidinput_connect(hdev, connect_mask))
 		hdev->claimed |= HID_CLAIMED_INPUT;
 
 	if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
@@ -2347,11 +2335,6 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
 
 	hid_process_ordering(hdev);
 
-	if ((hdev->claimed & HID_CLAIMED_INPUT) &&
-			(connect_mask & HID_CONNECT_FF) && hdev->ff_init &&
-			!hid_has_ff_input(hdev))
-		hdev->ff_init(hdev);
-
 	len = 0;
 	if (hdev->claimed & HID_CLAIMED_INPUT)
 		len += sprintf(buf + len, "input");
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 3487600cadb4..70ec1b7e7d38 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -2317,7 +2317,19 @@ static inline void hidinput_configure_usages(struct hid_input *hidinput,
  * Read all reports and initialize the absolute field values.
  */
 
-int hidinput_connect(struct hid_device *hid, unsigned int force)
+static bool hid_has_ff_input(struct hid_device *hdev)
+{
+	struct hid_input *hidinput;
+
+	list_for_each_entry(hidinput, &hdev->inputs, list) {
+		if (test_bit(EV_FF, hidinput->input->evbit))
+			return true;
+	}
+
+	return false;
+}
+
+int hidinput_connect(struct hid_device *hid, unsigned int connect_mask)
 {
 	struct hid_driver *drv = hid->driver;
 	struct hid_report *report;
@@ -2330,7 +2342,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
 
 	hid->status &= ~HID_STAT_DUP_DETECTED;
 
-	if (!force) {
+	if (!(connect_mask & HID_CONNECT_HIDINPUT_FORCE)) {
 		for (i = 0; i < hid->maxcollection; i++) {
 			struct hid_collection *col = &hid->collection[i];
 			if (col->type == HID_COLLECTION_APPLICATION ||
@@ -2396,6 +2408,11 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
 			continue;
 		}
 
+		if (list_is_first(&hidinput->list, &hid->inputs) &&
+		    (connect_mask & HID_CONNECT_FF) && hid->ff_init &&
+		    !hid_has_ff_input(hid))
+			hid->ff_init(hid);
+
 		if (input_register_device(hidinput->input))
 			goto out_unwind;
 		hidinput->registered = true;
diff --git a/include/linux/hid.h b/include/linux/hid.h
index b240baa95ab5..451c3e05d167 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1021,7 +1021,7 @@ extern void hid_unregister_driver(struct hid_driver *);
 
 extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
 extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
-extern int hidinput_connect(struct hid_device *hid, unsigned int force);
+extern int hidinput_connect(struct hid_device *hid, unsigned int connect_mask);
 extern void hidinput_disconnect(struct hid_device *);
 void hidinput_reset_resume(struct hid_device *hid);
 

-- 
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.