Some HID drivers initialize their own force-feedback support within
their .input_configured() callback. In such cases, we should skip
the generic PID force-feedback initialization to avoid conflicts and
redundant setup.
Add hid_has_ff_input() helper and use it to check for existing FF
capabilities before calling hdev->ff_init().
Since we now have a dynamic way to detect if force-feedback is needed,
the HID_CONNECT_FF flag is redundant for conflict resolution and can
be ignored in the core initialization logic. Generic PID support will
now be attempted by default for any claimed input device that doesn't
already have FF capabilities.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-core.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
HID drivers that rely on the HID core to register input devices must
ensure that all private data and capabilities (like force-feedback) are
fully initialized before registration.
When hid_hw_start() is called with HID_CONNECT_HIDINPUT, the input
device is registered immediately. This is racy if the driver attempts to
augment the input device in probe() after starting the hardware.
The correct way to handle this is to use the .input_configured()
callback.
Add documentation and a Coccinelle script to detect and prevent this
anti-pattern.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
Documentation/hid/hidintro.rst | 50 ++++++++++++++++++++++++++++++++++++
scripts/coccinelle/hid/ff_race.cocci | 34 ++++++++++++++++++++++++
2 files changed, 84 insertions(+)
@@ -522,3 +522,53 @@ This should really be your last resort. vendor: 0x093a product: 0x2510 ...++Input Device Registration and Lifecycle+========================================++HID drivers that rely on the HID core to register input devices (by using the+``HID_CONNECT_HIDINPUT`` flag, which is part of ``HID_CONNECT_DEFAULT``)+must be aware of the registration timing.++When ``hid_hw_start(hdev, flags)`` is called with ``HID_CONNECT_HIDINPUT``,+the HID core immediately parses the report descriptor, allocates ``input_dev``+structures, and calls ``input_register_device()`` for each of them.++This means the input device becomes **live and visible to userspace** before+``hid_hw_start()`` returns.++If a driver needs to perform additional configuration on the input device (such+as adding force-feedback support, setting extra bits in ``evbit``, or+assigning custom event handlers), doing so in the ``probe`` function after+``hid_hw_start()`` is **incorrect and racy**. Userspace may trigger+callbacks (like ``play_effect``) via ioctls immediately after registration,+leading to potential NULL pointer dereferences if the driver hasn't finished+initializing its private data.++The correct way to augment an input device before it is registered is to use the+``.input_configured`` callback in ``struct hid_driver``. This hook is+called by the HID core after the ``input_dev`` is fully formed but **before**+``input_register_device()`` is invoked.++Example:++..code-block:: c++ static int my_input_configured(struct hid_device *hdev, struct hid_input *hidinput)+ {+ struct input_dev *input = hidinput->input;++ /* Initialize private data and capabilities here */+ set_bit(EV_FF, input->evbit);+ return input_ff_create_memless(input, NULL, my_play_effect);+ }++ static struct hid_driver my_driver = {+ .name = "my_driver",+ .probe = my_probe,+ .input_configured = my_input_configured,+ };++Drivers that require even more control over the lifecycle should mask out+``HID_CONNECT_HIDINPUT`` and call ``input_register_device()`` manually+when they are ready.
@@ -0,0 +1,34 @@+/// Detect HID drivers that initialize force-feedback after hid_hw_start()+/// when HID_CONNECT_HIDINPUT is used. This is a lifecycle violation as+/// the input device is already registered.+//+// Confidence: High+// Copyright: (C) 2026 Gemini. GPLv2.++virtual report++@r@+identifier probe_fn;+expression hdev, flags;+position p1, p2;+@@++probe_fn(struct hid_device *hdev, ...) {+ <...+ hid_hw_start@p1(hdev, flags)+ ...+ \(input_ff_create\|input_ff_create_memless\)@p2(...)+ ...>+}++@script:python depends on report@+p1 << r.p1;+p2 << r.p2;+flags << r.flags;+@@++# Check if flags include HID_CONNECT_HIDINPUT (0x01) or HID_CONNECT_DEFAULT (0x0f)+# Note: HID_CONNECT_DEFAULT is 0x0f, HID_CONNECT_HIDINPUT is 0x01+if "HID_CONNECT_HIDINPUT" in flags or "HID_CONNECT_DEFAULT" in flags:+ msg = "WARNING: force-feedback initialized after hid_hw_start() with HID_CONNECT_HIDINPUT. Input device is already registered at this point. Use .input_configured() instead."+ coccilib.report.print_report(p2[0], msg)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-axff.c | 40 ++++++++++++----------------------------
1 file changed, 12 insertions(+), 28 deletions(-)
@@ -100,13 +94,13 @@ static int axff_init(struct hid_device *hid)if(!axff)return-ENOMEM;+axff->report=report;set_bit(FF_RUMBLE,dev->ffbit);error=input_ff_create_memless(dev,axff,axff_play);if(error)gotoerr_free_mem;-axff->report=report;hid_hw_request(hid,axff->report,HID_REQ_SET_REPORT);hid_info(hid,"Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n");
@@ -118,7 +112,8 @@ static int axff_init(struct hid_device *hid)returnerror;}#else-staticinlineintaxff_init(structhid_device*hid)+staticinlineintax_input_configured(structhid_device*hid,+structhid_input*hidinput){return0;}
@@ -136,23 +131,11 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id)returnerror;}-error=hid_hw_start(hdev,HID_CONNECT_DEFAULT&~HID_CONNECT_FF);+error=hid_hw_start(hdev,HID_CONNECT_DEFAULT);if(error){hid_err(hdev,"hw start failed\n");returnerror;}--error=axff_init(hdev);-if(error){-/*-*Donotfaildeviceinitializationcompletelyasdevice-*maystillbepartiallyoperable,justwarn.-*/-hid_warn(hdev,-"Failed to enable force feedback support, error: %d\n",-error);-}-/**Weneedtostartpollingdevicerightaway,otherwise*itwillgointoacoma.
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-betopff.c | 33 +++++++++++----------------------
1 file changed, 11 insertions(+), 22 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-bigbenff.c | 89 ++++++++++++++++++++++------------------------
1 file changed, 43 insertions(+), 46 deletions(-)
@@ -427,10 +398,9 @@ static int bigben_probe(struct hid_device *hid,sizeof(structled_classdev)+name_sz,GFP_KERNEL);-if(!led){-error=-ENOMEM;-gotoerror_hw_stop;-}+if(!led)+return-ENOMEM;+name=(void*)(&led[1]);snprintf(name,name_sz,"%s:red:bigben%d",
@@ -444,7 +414,7 @@ static int bigben_probe(struct hid_device *hid,bigben->leds[n]=led;error=devm_led_classdev_register(&hid->dev,led);if(error)-gotoerror_hw_stop;+returnerror;}/* initial state: LED1 is on, no rumble effect */
@@ -458,10 +428,36 @@ static int bigben_probe(struct hid_device *hid,hid_info(hid,"LED and force feedback support for BigBen gamepad\n");return0;+}-error_hw_stop:-hid_hw_stop(hid);-returnerror;+staticintbigben_probe(structhid_device*hid,conststructhid_device_id*id)+{+structbigben_device*bigben;+interror;++bigben=devm_kzalloc(&hid->dev,sizeof(*bigben),GFP_KERNEL);+if(!bigben)+return-ENOMEM;++hid_set_drvdata(hid,bigben);+bigben->hid=hid;+bigben->removed=false;+INIT_WORK(&bigben->worker,bigben_worker);+spin_lock_init(&bigben->lock);++error=hid_parse(hid);+if(error){+hid_err(hid,"parse failed\n");+returnerror;+}++error=hid_hw_start(hid,HID_CONNECT_DEFAULT);+if(error){+hid_err(hid,"hw start failed\n");+returnerror;+}++return0;}staticconst__u8*bigben_report_fixup(structhid_device*hid,__u8*rdesc,
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-dr.c | 66 ++++++++++++----------------------------------------
1 file changed, 15 insertions(+), 51 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-emsff.c | 50 ++++++++-----------------------------------------
1 file changed, 8 insertions(+), 42 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-gaff.c | 53 ++++++++++----------------------------------------
1 file changed, 10 insertions(+), 43 deletions(-)
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 <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-google-stadiaff.c | 71 +++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 33 deletions(-)
@@ -90,11 +107,13 @@ static int stadiaff_init(struct hid_device *hid)if(error)returnerror;-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");
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-google-stadiaff.c | 41 +++++----------------------------------
1 file changed, 5 insertions(+), 36 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start() with HID_CONNECT_DEFAULT. This is racy as
the input device is already registered and visible to userspace at that
point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-holtekff.c | 46 +++++++++-------------------------------------
1 file changed, 9 insertions(+), 37 deletions(-)
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 <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-core.c | 21 ++-------------------
drivers/hid/hid-input.c | 21 +++++++++++++++++++--
include/linux/hid.h | 2 +-
3 files changed, 22 insertions(+), 22 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-microsoft.c | 38 ++++++++------------------------------
1 file changed, 8 insertions(+), 30 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-pl.c | 150 ++++++++++++++++++++++-----------------------------
1 file changed, 65 insertions(+), 85 deletions(-)
@@ -83,89 +81,80 @@ static int plff_init(struct hid_device *hid)Theinputreportsalsocontainafieldwhichcontains8ff00.0001usagesand8booleanvalues.Theirmeaningiscurrentlyunknown.-+Aversionofthe0e8f:0003existsthathasallthevaluesinseparatefieldsandmissestheextrainputfield,thusresemblingZeroplus(hid-zpff)devices.*/-if(list_empty(report_list)){+if(!list_is_first(&hidinput->list,&hid->inputs))+return0;++report=list_first_entry_or_null(report_list,structhid_report,list);+if(!report){hid_err(hid,"no output reports found\n");return-ENODEV;}+if(report->maxfield<1){+hid_err(hid,"no fields in the report\n");+return-ENODEV;+}-list_for_each_entry(hidinput,&hid->inputs,list){--report_ptr=report_ptr->next;--if(report_ptr==report_list){-hid_err(hid,"required output report is missing\n");-return-ENODEV;-}--report=list_entry(report_ptr,structhid_report,list);-if(report->maxfield<1){-hid_err(hid,"no fields in the report\n");-return-ENODEV;-}--maxval=0x7f;-if(report->field[0]->report_count>=4){-report->field[0]->value[0]=0x00;-report->field[0]->value[1]=0x00;-strong=&report->field[0]->value[2];-weak=&report->field[0]->value[3];-hid_dbg(hid,"detected single-field device");-}elseif(report->field[0]->maxusage==1&&-report->field[0]->usage[0].hid==-(HID_UP_LED|0x43)&&-report->maxfield>=4&&-report->field[0]->report_count>=1&&-report->field[1]->report_count>=1&&-report->field[2]->report_count>=1&&-report->field[3]->report_count>=1){-report->field[0]->value[0]=0x00;-report->field[1]->value[0]=0x00;-strong=&report->field[2]->value[0];-weak=&report->field[3]->value[0];-if(hid->vendor==USB_VENDOR_ID_JESS2)-maxval=0xff;-hid_dbg(hid,"detected 4-field device");-}else{-hid_err(hid,"not enough fields or values\n");-return-ENODEV;-}--plff=kzalloc_obj(structplff_device);-if(!plff)-return-ENOMEM;--dev=hidinput->input;--set_bit(FF_RUMBLE,dev->ffbit);--error=input_ff_create_memless(dev,plff,hid_plff_play);-if(error){-kfree(plff);-returnerror;-}--plff->report=report;-plff->strong=strong;-plff->weak=weak;-plff->maxval=maxval;--*strong=0x00;-*weak=0x00;-hid_hw_request(hid,plff->report,HID_REQ_SET_REPORT);+maxval=0x7f;+if(report->field[0]->report_count>=4){+report->field[0]->value[0]=0x00;+report->field[0]->value[1]=0x00;+strong=&report->field[0]->value[2];+weak=&report->field[0]->value[3];+hid_dbg(hid,"detected single-field device");+}elseif(report->field[0]->maxusage==1&&+report->field[0]->usage[0].hid==+(HID_UP_LED|0x43)&&+report->maxfield>=4&&+report->field[0]->report_count>=1&&+report->field[1]->report_count>=1&&+report->field[2]->report_count>=1&&+report->field[3]->report_count>=1){+report->field[0]->value[0]=0x00;+report->field[1]->value[0]=0x00;+strong=&report->field[2]->value[0];+weak=&report->field[3]->value[0];+if(hid->vendor==USB_VENDOR_ID_JESS2)+maxval=0xff;+hid_dbg(hid,"detected 4-field device");+}else{+hid_err(hid,"not enough fields or values\n");+return-ENODEV;}-hid_info(hid,"Force feedback for PantherLord/GreenAsia devices by Anssi Hannula <anssi.hannula@gmail.com>\n");+plff=kzalloc_obj(structplff_device);+if(!plff)+return-ENOMEM;++dev=hidinput->input;++set_bit(FF_RUMBLE,dev->ffbit);++error=input_ff_create_memless(dev,plff,hid_plff_play);+if(error){+kfree(plff);+returnerror;+}++plff->report=report;+plff->strong=strong;+plff->weak=weak;+plff->maxval=maxval;++*strong=0x00;+*weak=0x00;+hid_hw_request(hid,plff->report,HID_REQ_SET_REPORT);return0;}#else-staticinlineintplff_init(structhid_device*hid)+staticinlineintpl_input_configured(structhid_device*hid,+structhid_input*hidinput){return0;}
@@ -181,27 +170,17 @@ static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)ret=hid_parse(hdev);if(ret){hid_err(hdev,"parse failed\n");-gotoerr;+returnret;}-ret=hid_hw_start(hdev,HID_CONNECT_DEFAULT&~HID_CONNECT_FF);+ret=hid_hw_start(hdev,HID_CONNECT_DEFAULT);if(ret){hid_err(hdev,"hw start failed\n");-gotoerr;+returnret;}-ret=plff_init(hdev);-if(ret)-gotostop;-return0;--stop:-hid_hw_stop(hdev);-err:-returnret;}-staticconststructhid_device_idpl_devices[]={{HID_USB_DEVICE(USB_VENDOR_ID_GAMERON,USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),.driver_data=1},/* Twin USB Joystick */
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-tmff.c | 47 ++++++++++++++---------------------------------
1 file changed, 14 insertions(+), 33 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-zpff.c | 43 ++++++++-----------------------------------
1 file changed, 8 insertions(+), 35 deletions(-)
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-mf.c | 77 ++++++++++++++++++----------------------------------
1 file changed, 27 insertions(+), 50 deletions(-)
@@ -54,61 +54,45 @@ static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect)return0;}-staticintmf_init(structhid_device*hid)+staticintmf_input_configured(structhid_device*hid,structhid_input*hidinput){structmf_device*mf;-structlist_head*report_list=&hid->report_enum[HID_OUTPUT_REPORT].report_list;--structlist_head*report_ptr;structhid_report*report;--structlist_head*input_ptr=&hid->inputs;-structhid_input*input;--structinput_dev*dev;-+structinput_dev*dev=hidinput->input;interror;-/* Setup each of the four inputs */-list_for_each(report_ptr,report_list){-report=list_entry(report_ptr,structhid_report,list);+if(!list_is_first(&hidinput->list,&hid->inputs))+return0;-if(report->maxfield<1||report->field[0]->report_count<2){-hid_err(hid,"Invalid report, this should never happen!\n");-return-ENODEV;-}--if(list_is_last(input_ptr,&hid->inputs)){-hid_err(hid,"Missing input, this should never happen!\n");-return-ENODEV;-}--input_ptr=input_ptr->next;-input=list_entry(input_ptr,structhid_input,list);+report=list_first_entry_or_null(report_list,structhid_report,list);+if(!report){+hid_err(hid,"no output reports found\n");+return-ENODEV;+}-mf=kzalloc_obj(structmf_device);-if(!mf)-return-ENOMEM;+if(report->maxfield<1||report->field[0]->report_count<2){+hid_err(hid,"Invalid report, this should never happen!\n");+return-ENODEV;+}-dev=input->input;-set_bit(FF_RUMBLE,dev->ffbit);+mf=kzalloc_obj(structmf_device);+if(!mf)+return-ENOMEM;-error=input_ff_create_memless(dev,mf,mf_play);-if(error){-kfree(mf);-returnerror;-}+mf->report=report;+set_bit(FF_RUMBLE,dev->ffbit);-mf->report=report;-mf->report->field[0]->value[0]=0x00;-mf->report->field[0]->value[1]=0x00;-hid_hw_request(hid,mf->report,HID_REQ_SET_REPORT);+error=input_ff_create_memless(dev,mf,mf_play);+if(error){+kfree(mf);+returnerror;}-hid_info(hid,"Force feedback for HJZ Mayflash game controller "-"adapters by Marcel Hasler <mahasler@gmail.com>\n");+mf->report->field[0]->value[0]=0x00;+mf->report->field[0]->value[1]=0x00;+hid_hw_request(hid,mf->report,HID_REQ_SET_REPORT);return0;}
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-sjoy.c | 83 +++++++++++++++++++++-----------------------------
1 file changed, 34 insertions(+), 49 deletions(-)
@@ -48,68 +48,56 @@ static int hid_sjoyff_play(struct input_dev *dev, void *data,return0;}-staticintsjoyff_init(structhid_device*hid)+staticintsjoy_input_configured(structhid_device*hid,structhid_input*hidinput){structsjoyff_device*sjoyff;structhid_report*report;-structhid_input*hidinput;structlist_head*report_list=&hid->report_enum[HID_OUTPUT_REPORT].report_list;-structlist_head*report_ptr=report_list;-structinput_dev*dev;+structinput_dev*dev=hidinput->input;interror;-if(list_empty(report_list)){+if(!list_is_first(&hidinput->list,&hid->inputs))+return0;++report=list_first_entry_or_null(report_list,structhid_report,list);+if(!report){hid_err(hid,"no output reports found\n");return-ENODEV;}+if(report->maxfield<1){+hid_err(hid,"no fields in the report\n");+return-ENODEV;+}-list_for_each_entry(hidinput,&hid->inputs,list){-report_ptr=report_ptr->next;--if(report_ptr==report_list){-hid_err(hid,"required output report is missing\n");-return-ENODEV;-}--report=list_entry(report_ptr,structhid_report,list);-if(report->maxfield<1){-hid_err(hid,"no fields in the report\n");-return-ENODEV;-}--if(report->field[0]->report_count<3){-hid_err(hid,"not enough values in the field\n");-return-ENODEV;-}--sjoyff=kzalloc_obj(structsjoyff_device);-if(!sjoyff)-return-ENOMEM;+if(report->field[0]->report_count<3){+hid_err(hid,"not enough values in the field\n");+return-ENODEV;+}-dev=hidinput->input;+sjoyff=kzalloc_obj(structsjoyff_device);+if(!sjoyff)+return-ENOMEM;-set_bit(FF_RUMBLE,dev->ffbit);+set_bit(FF_RUMBLE,dev->ffbit);-sjoyff->report=report;-sjoyff->report->field[0]->value[0]=0x01;-sjoyff->report->field[0]->value[1]=0x00;-sjoyff->report->field[0]->value[2]=0x00;-hid_hw_request(hid,sjoyff->report,HID_REQ_SET_REPORT);+sjoyff->report=report;+sjoyff->report->field[0]->value[0]=0x01;+sjoyff->report->field[0]->value[1]=0x00;+sjoyff->report->field[0]->value[2]=0x00;+hid_hw_request(hid,sjoyff->report,HID_REQ_SET_REPORT);-error=input_ff_create_memless(dev,sjoyff,hid_sjoyff_play);-if(error){-kfree(sjoyff);-returnerror;-}+error=input_ff_create_memless(dev,sjoyff,hid_sjoyff_play);+if(error){+kfree(sjoyff);+returnerror;}-hid_info(hid,"Force feedback for SmartJoy PLUS PS2/USB adapter\n");-return0;}#else-staticinlineintsjoyff_init(structhid_device*hid)+staticinlineintsjoy_input_configured(structhid_device*hid,+structhid_input*hidinput){return0;}
The driver currently initializes force-feedback in its probe() function
after calling hid_hw_start(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-megaworld.c | 51 ++++++++++-----------------------------------
1 file changed, 11 insertions(+), 40 deletions(-)
@@ -61,16 +56,7 @@ static int mwctrl_init(struct hid_device *hid)if(!mwctrl)return-ENOMEM;-set_bit(FF_RUMBLE,dev->ffbit);--error=input_ff_create_memless(dev,mwctrl,mwctrl_play);-if(error){-kfree(mwctrl);-returnerror;-}-mwctrl->report=report;-/* Field 0 is always 2, and field 1 is always 0. The original*windowsdriverhasa5bytescommand,wherethe5thbyteis*arepeatofthe3rdbyte,howeverthedevicehasonly4
The driver currently initializes force-feedback in its probe() function
after calling hid_connect(). This is racy as the input device is
already registered and visible to userspace at that point.
Move the FF initialization to the .input_configured() callback to ensure
the device is fully prepared before registration.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-logitech-hidpp.c | 36 +++++++++++++++++++-----------------
1 file changed, 19 insertions(+), 17 deletions(-)
Refactor hid_haptic_init() to take a direct pointer to input_dev and
integrate its invocation into hid_haptic_input_configured().
Update hid-multitouch to rely on the refactored callback to perform the
force-feedback initialization during the registration loop. This ensures
that force-feedback capabilities are set up before the input device is
registered and exposed to userspace, closing the registration race.
Assisted-by: Gemini:gemini-3.1-pro
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-haptic.c | 45 +++++++++++++++++---------------------------
drivers/hid/hid-haptic.h | 6 ++++--
drivers/hid/hid-multitouch.c | 10 +---------
3 files changed, 22 insertions(+), 39 deletions(-)
@@ -447,19 +453,6 @@ int hid_haptic_init(struct hid_device *hdev,for(r=0;r<haptic->auto_trigger_report->maxfield;r++)parse_auto_trigger_field(haptic,haptic->auto_trigger_report->field[r]);-list_for_each_entry(hidinput,&hdev->inputs,list){-if(hidinput->application==HID_DG_TOUCHPAD){-dev=hidinput->input;-break;-}-}--if(!dev){-dev_err(&hdev->dev,"Failed to find the input device\n");-ret=-ENODEV;-gotoduration_map;-}-haptic->input_dev=dev;haptic->manual_trigger_report_len=hid_report_len(haptic->manual_trigger_report);
@@ -535,10 +528,6 @@ int hid_haptic_init(struct hid_device *hdev,input_free:input_ff_destroy(dev);-/* Do not let double free happen, input_ff_destroy will call-*hid_haptic_destroy.-*/-*haptic_ptr=NULL;/* Restore dev flush and event */dev->flush=flush;dev->event=event;