diff options
| author | Jiri Kosina <jkosina@suse.com> | 2026-08-19 09:27:26 +0200 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.com> | 2026-08-19 09:27:26 +0200 |
| commit | d89f04ac41e3b83a25b02c8d818ce09d7afc74c9 (patch) | |
| tree | 72f0fb2e3865895a82c38f5be88672915936388b | |
| parent | db95550340f45899edb1aecf8cf8310b877ab9b9 (diff) | |
| parent | 7939f787f450d9390477afba2540c0962c6cb643 (diff) | |
Merge branch 'for-7.3/core' into for-linus
- fix long-standing force-feedback initialization race across the subsystem
(Dmitry Torokhov)
- switch to system_dfl_wq (Marco Crivellari)
31 files changed, 544 insertions, 763 deletions
diff --git a/Documentation/hid/hidintro.rst b/Documentation/hid/hidintro.rst index 73523e315ebd..5d367dfca0b8 100644 --- a/Documentation/hid/hidintro.rst +++ b/Documentation/hid/hidintro.rst @@ -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. diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 1405167aa369..eda26a094d3f 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -124,19 +124,10 @@ static irqreturn_t amd_sfh_irq_handler(int irq, void *data) int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) { - int rc; - pcim_intx(privdata->pdev, true); - rc = devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq, - amd_sfh_irq_handler, 0, DRIVER_NAME, privdata); - if (rc) { - dev_err(&privdata->pdev->dev, "failed to request irq %d err=%d\n", - privdata->pdev->irq, rc); - return rc; - } - - return 0; + return devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq, + amd_sfh_irq_handler, 0, DRIVER_NAME, privdata); } static int amd_sfh_dis_sts_v2(struct amd_mp2_dev *privdata) diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c index 462010a75899..5cc27066f602 100644 --- a/drivers/hid/hid-appletb-kbd.c +++ b/drivers/hid/hid-appletb-kbd.c @@ -175,7 +175,7 @@ static void appletb_inactivity_work(struct work_struct *work) if (!kbd->has_dimmed) { backlight_device_set_brightness(kbd->backlight_dev, 1); kbd->has_dimmed = true; - mod_delayed_work(system_wq, &kbd->inactivity_work, + mod_delayed_work(system_dfl_wq, &kbd->inactivity_work, secs_to_jiffies(appletb_tb_idle_timeout)); } else if (!kbd->has_turned_off) { backlight_device_set_brightness(kbd->backlight_dev, 0); @@ -201,7 +201,7 @@ static void reset_inactivity_timer(struct appletb_kbd *kbd) kbd->has_turned_off = false; schedule_work(&kbd->restore_brightness_work); } - mod_delayed_work(system_wq, &kbd->inactivity_work, + mod_delayed_work(system_dfl_wq, &kbd->inactivity_work, secs_to_jiffies(appletb_tb_dim_timeout)); } } @@ -423,7 +423,7 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id INIT_DELAYED_WORK(&kbd->inactivity_work, appletb_inactivity_work); INIT_WORK(&kbd->restore_brightness_work, appletb_restore_brightness_work); - mod_delayed_work(system_wq, &kbd->inactivity_work, + mod_delayed_work(system_dfl_wq, &kbd->inactivity_work, secs_to_jiffies(appletb_tb_dim_timeout)); } diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 3c5c2bf02425..2f46447086df 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -59,30 +59,24 @@ static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect return 0; } -static int axff_init(struct hid_device *hid) +static int ax_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct axff_device *axff; struct hid_report *report; - struct hid_input *hidinput; - struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev; + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; int field_count = 0; int i, j; int error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_first_entry(&hid->inputs, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; - if (list_empty(report_list)) { + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { hid_err(hid, "no output reports found\n"); return -ENODEV; } - - report = list_first_entry(report_list, struct hid_report, list); for (i = 0; i < report->maxfield; i++) { for (j = 0; j < report->field[i]->report_count; j++) { report->field[i]->value[j] = 0x00; @@ -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) goto err_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 @@ err_free_mem: return error; } #else -static inline int axff_init(struct hid_device *hid) +static inline int ax_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } @@ -136,23 +131,11 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) return error; } - 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"); return error; } - - error = axff_init(hdev); - if (error) { - /* - * Do not fail device initialization completely as device - * may still be partially operable, just warn. - */ - hid_warn(hdev, - "Failed to enable force feedback support, error: %d\n", - error); - } - /* * We need to start polling device right away, otherwise * it will go into a coma. @@ -185,6 +168,7 @@ static struct hid_driver ax_driver = { .id_table = ax_devices, .probe = ax_probe, .remove = ax_remove, + .input_configured = ax_input_configured, }; module_hid_driver(ax_driver); diff --git a/drivers/hid/hid-betopff.c b/drivers/hid/hid-betopff.c index 8a7fe895926c..f802046a688a 100644 --- a/drivers/hid/hid-betopff.c +++ b/drivers/hid/hid-betopff.c @@ -52,31 +52,24 @@ static int hid_betopff_play(struct input_dev *dev, void *data, return 0; } -static int betopff_init(struct hid_device *hid) +static int betop_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct betopff_device *betopff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; int i, j; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; - hidinput = list_first_entry(&hid->inputs, struct hid_input, list); - dev = hidinput->input; - - if (list_empty(report_list)) { + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { hid_err(hid, "no output reports found\n"); return -ENODEV; } - - report = list_first_entry(report_list, struct hid_report, list); /* * Actually there are 4 fields for 4 Bytes as below: * ----------------------------------------- @@ -104,6 +97,7 @@ static int betopff_init(struct hid_device *hid) if (!betopff) return -ENOMEM; + betopff->report = report; set_bit(FF_RUMBLE, dev->ffbit); error = input_ff_create_memless(dev, betopff, hid_betopff_play); @@ -112,7 +106,6 @@ static int betopff_init(struct hid_device *hid) return error; } - betopff->report = report; hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT); hid_info(hid, "Force feedback for betop devices by huangbo <huangbobupt@163.com>\n"); @@ -130,20 +123,15 @@ static int betop_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err; + return ret; } - 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"); - goto err; + return ret; } - - betopff_init(hdev); - return 0; -err: - return ret; } static const struct hid_device_id betop_devices[] = { @@ -159,6 +147,7 @@ static struct hid_driver betop_driver = { .name = "betop", .id_table = betop_devices, .probe = betop_probe, + .input_configured = betop_input_configured, }; module_hid_driver(betop_driver); diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c index 9f05465358d9..3c87317ccc6b 100644 --- a/drivers/hid/hid-bigbenff.c +++ b/drivers/hid/hid-bigbenff.c @@ -366,58 +366,29 @@ static void bigben_remove(struct hid_device *hid) hid_hw_stop(hid); } -static int bigben_probe(struct hid_device *hid, - const struct hid_device_id *id) +static int bigben_input_configured(struct hid_device *hid, struct hid_input *hidinput) { - struct bigben_device *bigben; - struct hid_input *hidinput; + struct bigben_device *bigben = hid_get_drvdata(hid); + struct input_dev *input_dev = hidinput->input; struct led_classdev *led; char *name; size_t name_sz; int n, error; - bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL); - if (!bigben) - return -ENOMEM; - hid_set_drvdata(hid, bigben); - bigben->hid = hid; - bigben->removed = false; - - error = hid_parse(hid); - if (error) { - hid_err(hid, "parse failed\n"); - return error; - } - - error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (error) { - hid_err(hid, "hw start failed\n"); - return error; - } + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8); if (!bigben->report) { hid_err(hid, "no output report found\n"); - error = -ENODEV; - goto error_hw_stop; - } - - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - error = -ENODEV; - goto error_hw_stop; + return -ENODEV; } - hidinput = list_first_entry(&hid->inputs, struct hid_input, list); - set_bit(FF_RUMBLE, hidinput->input->ffbit); - - INIT_WORK(&bigben->worker, bigben_worker); - spin_lock_init(&bigben->lock); + set_bit(FF_RUMBLE, input_dev->ffbit); - error = input_ff_create_memless(hidinput->input, NULL, - hid_bigben_play_effect); + error = input_ff_create_memless(input_dev, NULL, hid_bigben_play_effect); if (error) - goto error_hw_stop; + return error; name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1; @@ -427,10 +398,9 @@ static int bigben_probe(struct hid_device *hid, sizeof(struct led_classdev) + name_sz, GFP_KERNEL ); - if (!led) { - error = -ENOMEM; - goto error_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) - goto error_hw_stop; + return error; } /* 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"); return 0; +} -error_hw_stop: - hid_hw_stop(hid); - return error; +static int bigben_probe(struct hid_device *hid, const struct hid_device_id *id) +{ + struct bigben_device *bigben; + int error; + + 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"); + return error; + } + + error = hid_hw_start(hid, HID_CONNECT_DEFAULT); + if (error) { + hid_err(hid, "hw start failed\n"); + return error; + } + + return 0; } static const __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, @@ -487,6 +483,7 @@ static struct hid_driver bigben_driver = { .probe = bigben_probe, .report_fixup = bigben_report_fixup, .remove = bigben_remove, + .input_configured = bigben_input_configured, }; module_hid_driver(bigben_driver); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 18e72a599951..a3ff0514f9cd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2319,8 +2319,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 && @@ -2342,10 +2342,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) - hdev->ff_init(hdev); - len = 0; if (hdev->claimed & HID_CLAIMED_INPUT) len += sprintf(buf + len, "input"); @@ -2454,9 +2450,16 @@ EXPORT_SYMBOL_GPL(hid_hw_start); * * This is usually called from remove function or from probe when something * failed and hid_hw_start was called already. + * + * If the caller enabled HID input via hid_device_io_start() and is unwinding + * without an explicit hid_device_io_stop(), quiesce input first so that + * in-flight reports cannot reach handlers (e.g. hidraw_report_event) whose + * backing objects hid_disconnect() is about to free. */ void hid_hw_stop(struct hid_device *hdev) { + if (hdev->io_started) + hid_device_io_stop(hdev); hid_disconnect(hdev); hdev->ll_driver->stop(hdev); } @@ -2916,6 +2919,45 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, } static DEVICE_ATTR_RO(modalias); +/* + * Expose this as bustype instead of bus as + * that's the name the input subsystem uses + */ +static ssize_t bustype_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct hid_device *hdev = to_hid_device(dev); + + return sysfs_emit(buf, "%04x\n", hdev->bus); +} +static DEVICE_ATTR_RO(bustype); + +#define HID_DEV_ID_ATTR(name) \ +static ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hid_device *hdev = to_hid_device(dev); \ + \ + return sysfs_emit(buf, "%04x\n", hdev->name); \ +} \ +static DEVICE_ATTR_RO(name) + +HID_DEV_ID_ATTR(vendor); +HID_DEV_ID_ATTR(product); +HID_DEV_ID_ATTR(version); + +static struct attribute *hid_dev_id_attrs[] = { + &dev_attr_bustype.attr, + &dev_attr_vendor.attr, + &dev_attr_product.attr, + &dev_attr_version.attr, + NULL +}; +static const struct attribute_group hid_dev_id_attr_group = { + .name = "id", + .attrs = hid_dev_id_attrs, +}; static struct attribute *hid_dev_attrs[] = { &dev_attr_modalias.attr, NULL, @@ -2928,7 +2970,11 @@ static const struct attribute_group hid_dev_group = { .attrs = hid_dev_attrs, .bin_attrs = hid_dev_bin_attrs, }; -__ATTRIBUTE_GROUPS(hid_dev); +static const struct attribute_group *hid_dev_groups[] = { + &hid_dev_group, + &hid_dev_id_attr_group, + NULL +}; static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env) { diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index 8a8f68a7feb0..a1e10ee8df4d 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -71,29 +71,26 @@ static int drff_play(struct input_dev *dev, void *data, return 0; } -static int drff_init(struct hid_device *hid) +static int dr_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct drff_device *drff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_first_entry(&hid->inputs, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; + + if (hid->product != 0x0006) + return 0; - if (list_empty(report_list)) { + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { hid_err(hid, "no output reports found\n"); return -ENODEV; } - - report = list_first_entry(report_list, struct hid_report, list); if (report->maxfield < 1) { hid_err(hid, "no fields in the report\n"); return -ENODEV; @@ -108,6 +105,7 @@ static int drff_init(struct hid_device *hid) if (!drff) return -ENOMEM; + drff->report = report; set_bit(FF_RUMBLE, dev->ffbit); error = input_ff_create_memless(dev, drff, drff_play); @@ -116,7 +114,6 @@ static int drff_init(struct hid_device *hid) return error; } - drff->report = report; drff->report->field[0]->value[0] = 0xf3; drff->report->field[0]->value[1] = 0x00; drff->report->field[0]->value[2] = 0x00; @@ -132,7 +129,8 @@ static int drff_init(struct hid_device *hid) return 0; } #else -static inline int drff_init(struct hid_device *hid) +static inline int dr_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } @@ -266,43 +264,9 @@ static int dr_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - dev_dbg(&hdev->dev, "DragonRise Inc. HID hardware probe..."); - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - switch (hdev->product) { - case 0x0006: - ret = drff_init(hdev); - if (ret) { - dev_err(&hdev->dev, "force feedback init failed\n"); - hid_hw_stop(hdev); - goto err; - } - break; - } - - return 0; -err: - return ret; -} - static const struct hid_device_id dr_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), }, - { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011), }, + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), }, + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011), }, { } }; MODULE_DEVICE_TABLE(hid, dr_devices); @@ -311,8 +275,8 @@ static struct hid_driver dr_driver = { .name = "dragonrise", .id_table = dr_devices, .report_fixup = dr_report_fixup, - .probe = dr_probe, .input_mapping = dr_input_mapping, + .input_configured = dr_input_configured, }; module_hid_driver(dr_driver); diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index 1b4ad18f6051..d8e559e0524f 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -43,29 +43,23 @@ static int emsff_play(struct input_dev *dev, void *data, return 0; } -static int emsff_init(struct hid_device *hid) +static int ems_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct emsff_device *emsff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_first_entry(&hid->inputs, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; - if (list_empty(report_list)) { + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { hid_err(hid, "no output reports found\n"); return -ENODEV; } - - report = list_first_entry(report_list, struct hid_report, list); if (report->maxfield < 1) { hid_err(hid, "no fields in the report\n"); return -ENODEV; @@ -80,6 +74,7 @@ static int emsff_init(struct hid_device *hid) if (!emsff) return -ENOMEM; + emsff->report = report; set_bit(FF_RUMBLE, dev->ffbit); error = input_ff_create_memless(dev, emsff, emsff_play); @@ -88,7 +83,6 @@ static int emsff_init(struct hid_device *hid) return error; } - emsff->report = report; emsff->report->field[0]->value[0] = 0x01; emsff->report->field[0]->value[1] = 0x00; emsff->report->field[0]->value[2] = 0x00; @@ -103,34 +97,6 @@ static int emsff_init(struct hid_device *hid) return 0; } -static int ems_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - ret = emsff_init(hdev); - if (ret) { - dev_err(&hdev->dev, "force feedback init failed\n"); - hid_hw_stop(hdev); - goto err; - } - - return 0; -err: - return ret; -} - static const struct hid_device_id ems_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { } @@ -140,7 +106,7 @@ MODULE_DEVICE_TABLE(hid, ems_devices); static struct hid_driver ems_driver = { .name = "hkems", .id_table = ems_devices, - .probe = ems_probe, + .input_configured = ems_input_configured, }; module_hid_driver(ems_driver); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index 8b99686b63df..ec793a179b47 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -60,32 +60,23 @@ static int hid_gaff_play(struct input_dev *dev, void *data, return 0; } -static int gaff_init(struct hid_device *hid) +static int gaff_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct gaff_device *gaff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct list_head *report_ptr = report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_entry(hid->inputs.next, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; - if (list_empty(report_list)) { + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { hid_err(hid, "no output reports found\n"); return -ENODEV; } - - report_ptr = report_ptr->next; - - report = list_entry(report_ptr, struct hid_report, list); if (report->maxfield < 1) { hid_err(hid, "no fields in the report\n"); return -ENODEV; @@ -100,6 +91,7 @@ static int gaff_init(struct hid_device *hid) if (!gaff) return -ENOMEM; + gaff->report = report; set_bit(FF_RUMBLE, dev->ffbit); error = input_ff_create_memless(dev, gaff, hid_gaff_play); @@ -108,7 +100,6 @@ static int gaff_init(struct hid_device *hid) return error; } - gaff->report = report; gaff->report->field[0]->value[0] = 0x51; gaff->report->field[0]->value[1] = 0x00; gaff->report->field[0]->value[2] = 0x00; @@ -125,37 +116,13 @@ static int gaff_init(struct hid_device *hid) return 0; } #else -static inline int gaff_init(struct hid_device *hdev) +static inline int gaff_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) { return 0; } #endif -static int ga_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - dev_dbg(&hdev->dev, "Greenasia HID hardware probe..."); - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - gaff_init(hdev); - - return 0; -err: - return ret; -} - static const struct hid_device_id ga_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012), }, { } @@ -165,7 +132,7 @@ MODULE_DEVICE_TABLE(hid, ga_devices); static struct hid_driver ga_driver = { .name = "greenasia", .id_table = ga_devices, - .probe = ga_probe, + .input_configured = gaff_input_configured, }; module_hid_driver(ga_driver); diff --git a/drivers/hid/hid-goodix-spi.c b/drivers/hid/hid-goodix-spi.c index 288cb827e9d6..03d549efbdce 100644 --- a/drivers/hid/hid-goodix-spi.c +++ b/drivers/hid/hid-goodix-spi.c @@ -722,11 +722,8 @@ static int goodix_spi_probe(struct spi_device *spi) error = devm_request_threaded_irq(&ts->spi->dev, ts->spi->irq, NULL, goodix_hid_irq, IRQF_ONESHOT, "goodix_spi_hid", ts); - if (error) { - dev_err(ts->dev, "could not register interrupt, irq = %d, %d", - ts->spi->irq, error); + if (error) goto err_destroy_hid; - } return 0; diff --git a/drivers/hid/hid-google-stadiaff.c b/drivers/hid/hid-google-stadiaff.c index 6b38d2421d3d..0214aae6b0fa 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,33 +39,50 @@ 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 stadiaff_init(struct hid_device *hid) +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 stadia_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct stadiaff_device *stadiaff; struct hid_report *report; - struct hid_input *hidinput; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_entry(hid->inputs.next, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; report = hid_validate_values(hid, HID_OUTPUT_REPORT, STADIA_FF_REPORT_ID, 0, 2); @@ -90,56 +102,19 @@ 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); - - hid_info(hid, "Force Feedback for Google Stadia controller\n"); + disable_work_sync(&stadiaff->work); - return 0; -} + dev->open = stadia_input_open; + dev->close = stadia_input_close; -static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - return ret; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - return ret; - } - - ret = stadiaff_init(hdev); - if (ret) { - hid_err(hdev, "force feedback init failed\n"); - hid_hw_stop(hdev); - return ret; - } + hid_info(hid, "Force Feedback for Google Stadia controller\n"); 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) }, @@ -150,8 +125,7 @@ MODULE_DEVICE_TABLE(hid, stadia_devices); static struct hid_driver stadia_driver = { .name = "stadia", .id_table = stadia_devices, - .probe = stadia_probe, - .remove = stadia_remove, + .input_configured = stadia_input_configured, }; module_hid_driver(stadia_driver); diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c index 66d90f0f78a3..8760eeb08b2c 100644 --- a/drivers/hid/hid-haptic.c +++ b/drivers/hid/hid-haptic.c @@ -82,16 +82,24 @@ int hid_haptic_input_configured(struct hid_device *hdev, struct hid_haptic_device *haptic, struct hid_input *hi) { + int error; - if (hi->application == HID_DG_TOUCHPAD) { - if (haptic->auto_trigger_report && - haptic->manual_trigger_report) { - __set_bit(INPUT_PROP_PRESSUREPAD, hi->input->propbit); - return 1; - } + if (hi->application != HID_DG_TOUCHPAD) + return -1; + + if (!haptic->auto_trigger_report || !haptic->manual_trigger_report) + return 0; + + __set_bit(INPUT_PROP_PRESSUREPAD, hi->input->propbit); + + error = hid_haptic_init(hdev, haptic, hi->input); + if (error) { + dev_warn(&hdev->dev, "Cannot allocate haptic for %s\n", + hdev->name); return 0; } - return -1; + + return 1; } EXPORT_SYMBOL_GPL(hid_haptic_input_configured); @@ -401,11 +409,9 @@ static void hid_haptic_destroy(struct ff_device *ff) } int hid_haptic_init(struct hid_device *hdev, - struct hid_haptic_device **haptic_ptr) + struct hid_haptic_device *haptic, + struct input_dev *dev) { - struct hid_haptic_device *haptic = *haptic_ptr; - struct input_dev *dev = NULL; - struct hid_input *hidinput; struct ff_device *ff; int ret = 0, r; struct ff_haptic_effect stop_effect = { @@ -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; - goto duration_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; diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h index c6539ac04c1d..6332991a7844 100644 --- a/drivers/hid/hid-haptic.h +++ b/drivers/hid/hid-haptic.h @@ -69,7 +69,8 @@ int hid_haptic_input_mapping(struct hid_device *hdev, int hid_haptic_input_configured(struct hid_device *hdev, struct hid_haptic_device *haptic, struct hid_input *hi); -int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr); +int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device *haptic, + struct input_dev *dev); void hid_haptic_handle_press_release(struct hid_haptic_device *haptic); void hid_haptic_pressure_reset(struct hid_haptic_device *haptic); void hid_haptic_pressure_increase(struct hid_haptic_device *haptic, @@ -107,7 +108,8 @@ static inline void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic) {} static inline -int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr) +int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device *haptic, + struct input_dev *dev) { return 0; } diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c index 32d08f7a660d..4834d42b2fa6 100644 --- a/drivers/hid/hid-holtekff.c +++ b/drivers/hid/hid-holtekff.c @@ -120,30 +120,24 @@ static int holtekff_play(struct input_dev *dev, void *data, return 0; } -static int holtekff_init(struct hid_device *hid) +static int holtek_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct holtekff_device *holtekff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_entry(hid->inputs.next, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; - if (list_empty(report_list)) { + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { hid_err(hid, "no output report found\n"); return -ENODEV; } - report = list_entry(report_list->next, struct hid_report, list); - if (report->maxfield < 1 || report->field[0]->report_count != 7) { hid_err(hid, "unexpected output report layout\n"); return -ENODEV; @@ -172,35 +166,13 @@ static int holtekff_init(struct hid_device *hid) return 0; } #else -static inline int holtekff_init(struct hid_device *hid) +static inline int holtek_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } #endif -static int holtek_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - holtekff_init(hdev); - - return 0; -err: - return ret; -} - static const struct hid_device_id holtek_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { } @@ -210,7 +182,7 @@ MODULE_DEVICE_TABLE(hid, holtek_devices); static struct hid_driver holtek_driver = { .name = "holtek", .id_table = holtek_devices, - .probe = holtek_probe, + .input_configured = holtek_input_configured, }; module_hid_driver(holtek_driver); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f78166f97229..edd6bbf8342b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -435,17 +435,25 @@ static int hidinput_scale_battery_capacity(struct hid_battery *bat, static int hidinput_query_battery_capacity(struct hid_battery *bat) { int ret; + /* + * The capacity field may not be the first field in the report: some + * devices (e.g. the Apple Magic Trackpad 2 over Bluetooth) precede it + * with status flags. Read it from its actual byte offset in the report + * (report_offset is in bits; the leading byte is the report id). + */ + int offset = 1 + bat->report_offset / 8; + int len = offset + 1; - u8 *buf __free(kfree) = kmalloc(4, GFP_KERNEL); + u8 *buf __free(kfree) = kmalloc(max(len, 4), GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, 4, + ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, max(len, 4), bat->report_type, HID_REQ_GET_REPORT); - if (ret < 2) + if (ret < len) return -ENODATA; - return hidinput_scale_battery_capacity(bat, buf[1]); + return hidinput_scale_battery_capacity(bat, buf[offset]); } static int hidinput_get_battery_property(struct power_supply *psy, @@ -596,6 +604,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, bat->max = max; bat->report_type = report_type; bat->report_id = field->report->id; + bat->report_offset = field->report_offset; bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; bat->status = HID_BATTERY_UNKNOWN; @@ -2320,7 +2329,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; @@ -2333,7 +2354,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 || @@ -2399,6 +2420,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/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index d2def19e4fcc..1504de32b1c8 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -4084,15 +4084,32 @@ static void hidpp_populate_input(struct hidpp_device *hidpp, hidpp20_reprog_controls_populate_input(hidpp, input); } -static int hidpp_input_configured(struct hid_device *hdev, - struct hid_input *hidinput) +static int hidpp_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct input_dev *input = hidinput->input; + int ret; if (!hidpp) return 0; + if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { + struct hidpp_ff_private_data data; + + if (!list_is_first(&hidinput->list, &hdev->inputs)) + return 0; + + ret = g920_get_config(hidpp, &data); + if (!ret) + ret = hidpp_ff_init(hidpp, &data); + + if (ret) { + hid_warn(hidpp->hid_dev, + "Unable to initialize force feedback support, errno %d\n", + ret); + } + } + hidpp_populate_input(hidpp, input); return 0; @@ -4803,21 +4820,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) schedule_work(&hidpp->work); flush_work(&hidpp->work); - if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { - struct hidpp_ff_private_data data; - - ret = g920_get_config(hidpp, &data); - if (!ret) - ret = hidpp_ff_init(hidpp, &data); - - if (ret) { - hid_warn(hidpp->hid_dev, - "Unable to initialize force feedback support, errno %d\n", - ret); - ret = 0; - } - } - /* * This relies on logi_dj_ll_close() being a no-op so that DJ connection * events will still be received. diff --git a/drivers/hid/hid-megaworld.c b/drivers/hid/hid-megaworld.c index 81acdbc3a00f..d5c868974275 100644 --- a/drivers/hid/hid-megaworld.c +++ b/drivers/hid/hid-megaworld.c @@ -35,21 +35,16 @@ static int mwctrl_play(struct input_dev *dev, void *data, return 0; } -static int mwctrl_init(struct hid_device *hid) +static int mwctrl_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct mwctrl_device *mwctrl; struct hid_report *report; - struct hid_input *hidinput; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; int i; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_entry(hid->inputs.next, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; for (i = 0; i < 4; i++) { report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1); @@ -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); - return error; - } - mwctrl->report = report; - /* Field 0 is always 2, and field 1 is always 0. The original * windows driver has a 5 bytes command, where the 5th byte is * a repeat of the 3rd byte, however the device has only 4 @@ -82,30 +68,15 @@ static int mwctrl_init(struct hid_device *hid) mwctrl->strong = &report->field[2]->value[0]; mwctrl->weak = &report->field[3]->value[0]; - return 0; -} - -static int mwctrl_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - return ret; - } + set_bit(FF_RUMBLE, dev->ffbit); - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - return ret; + error = input_ff_create_memless(dev, mwctrl, mwctrl_play); + if (error) { + kfree(mwctrl); + return error; } - ret = mwctrl_init(hdev); - if (ret) - hid_hw_stop(hdev); - - return ret; + return 0; } static const struct hid_device_id mwctrl_devices[] = { @@ -118,7 +89,7 @@ MODULE_DEVICE_TABLE(hid, mwctrl_devices); static struct hid_driver mwctrl_driver = { .name = "megaworld", .id_table = mwctrl_devices, - .probe = mwctrl_probe, + .input_configured = mwctrl_input_configured, }; module_hid_driver(mwctrl_driver); diff --git a/drivers/hid/hid-mf.c b/drivers/hid/hid-mf.c index 6ff54a1ec697..136e8b41d5f4 100644 --- a/drivers/hid/hid-mf.c +++ b/drivers/hid/hid-mf.c @@ -54,61 +54,45 @@ static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect) return 0; } -static int mf_init(struct hid_device *hid) +static int mf_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct mf_device *mf; - struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - - struct list_head *report_ptr; struct hid_report *report; - - struct list_head *input_ptr = &hid->inputs; - struct hid_input *input; - - struct input_dev *dev; - + struct input_dev *dev = hidinput->input; int error; - /* Setup each of the four inputs */ - list_for_each(report_ptr, report_list) { - report = list_entry(report_ptr, struct hid_report, list); + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; - 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, struct hid_input, list); + report = list_first_entry_or_null(report_list, struct hid_report, list); + if (!report) { + hid_err(hid, "no output reports found\n"); + return -ENODEV; + } - mf = kzalloc_obj(struct mf_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(struct mf_device); + if (!mf) + return -ENOMEM; - error = input_ff_create_memless(dev, mf, mf_play); - if (error) { - kfree(mf); - return error; - } + 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); + return error; } - 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); return 0; } @@ -128,22 +112,14 @@ static int mf_probe(struct hid_device *hid, const struct hid_device_id *id) return error; } - error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + error = hid_hw_start(hid, HID_CONNECT_DEFAULT); if (error) { hid_err(hid, "HID hw start failed\n"); return error; } - error = mf_init(hid); - if (error) { - hid_err(hid, "Force feedback init failed.\n"); - hid_hw_stop(hid); - return error; - } - return 0; } - static const struct hid_device_id mf_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), .driver_data = HID_QUIRK_MULTI_INPUT }, @@ -163,6 +139,7 @@ static struct hid_driver mf_driver = { .name = "hid_mf", .id_table = mf_devices, .probe = mf_probe, + .input_configured = mf_input_configured, }; module_hid_driver(mf_driver); diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 18ac21c0bcb2..a7d3493a6141 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -323,22 +323,17 @@ static int ms_play_effect(struct input_dev *dev, void *data, return 0; } -static int ms_init_ff(struct hid_device *hdev) +static int ms_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { - struct hid_input *hidinput; - struct input_dev *input_dev; struct ms_data *ms = hid_get_drvdata(hdev); - - if (list_empty(&hdev->inputs)) { - hid_err(hdev, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_entry(hdev->inputs.next, struct hid_input, list); - input_dev = hidinput->input; + struct input_dev *input_dev = hidinput->input; if (!(ms->quirks & MS_QUIRK_FF)) return 0; + if (!list_is_first(&hidinput->list, &hdev->inputs)) + return 0; + ms->hdev = hdev; INIT_WORK(&ms->ff_worker, ms_ff_worker); @@ -352,16 +347,6 @@ static int ms_init_ff(struct hid_device *hdev) return input_ff_create_memless(input_dev, NULL, ms_play_effect); } -static void ms_remove_ff(struct hid_device *hdev) -{ - struct ms_data *ms = hid_get_drvdata(hdev); - - if (!(ms->quirks & MS_QUIRK_FF)) - return; - - cancel_work_sync(&ms->ff_worker); -} - static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) { unsigned long quirks = id->driver_data; @@ -385,29 +370,21 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err_free; + return ret; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ? HID_CONNECT_HIDINPUT_FORCE : 0)); if (ret) { hid_err(hdev, "hw start failed\n"); - goto err_free; + return ret; } - ret = ms_init_ff(hdev); - if (ret) - hid_err(hdev, "could not initialize ff, continuing anyway"); - return 0; -err_free: - return ret; } - static void ms_remove(struct hid_device *hdev) { hid_hw_stop(hdev); - ms_remove_ff(hdev); } static const struct hid_device_id ms_devices[] = { @@ -469,6 +446,7 @@ static struct hid_driver ms_driver = { .report_fixup = ms_report_fixup, .input_mapping = ms_input_mapping, .input_mapped = ms_input_mapped, + .input_configured = ms_input_configured, .event = ms_event, .probe = ms_probe, .remove = ms_remove, diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 571166a769b9..2c41bacab1ca 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -2189,16 +2189,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); - if (td->is_haptic_touchpad) { - if (hid_haptic_init(hdev, &td->haptic)) { - dev_warn(&hdev->dev, "Cannot allocate haptic for %s\n", - hdev->name); - td->is_haptic_touchpad = false; - devm_kfree(&hdev->dev, td->haptic); - } - } else { + if (!td->is_haptic_touchpad) devm_kfree(&hdev->dev, td->haptic); - } return 0; } diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 20a54f337220..d2ded6b08ce9 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -398,7 +398,7 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *hdev, * Re-apply our settings after this has been received. */ if (data[3] == OXP_EFFECT_MONO_TRUE) { - mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); + mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); return 0; } @@ -788,7 +788,7 @@ static ssize_t map_button_store(struct device *dev, default: return -EINVAL; } - mod_delayed_work(system_wq, &drvdata.oxp_btn_queue, msecs_to_jiffies(50)); + mod_delayed_work(system_dfl_wq, &drvdata.oxp_btn_queue, msecs_to_jiffies(50)); return count; } @@ -1349,7 +1349,7 @@ static void oxp_rgb_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { led_cdev->brightness = brightness; - mod_delayed_work(system_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(50)); + mod_delayed_work(system_dfl_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(50)); } static struct attribute *oxp_rgb_attrs[] = { @@ -1502,7 +1502,7 @@ skip_rgb: drvdata.rumble_intensity = 5; INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn); - mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); + mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); ret = devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group); if (ret) diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 8bba29ef6c7a..cae56a6c941b 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -62,15 +62,13 @@ static int hid_plff_play(struct input_dev *dev, void *data, return 0; } -static int plff_init(struct hid_device *hid) +static int pl_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct plff_device *plff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct list_head *report_ptr = report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; s32 maxval; s32 *strong; @@ -83,89 +81,80 @@ static int plff_init(struct hid_device *hid) The input reports also contain a field which contains 8 ff00.0001 usages and 8 boolean values. Their meaning is currently unknown. - + A version of the 0e8f:0003 exists that has all the values in separate fields and misses the extra input field, thus resembling Zeroplus (hid-zpff) devices. */ - if (list_empty(report_list)) { + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; + + report = list_first_entry_or_null(report_list, struct hid_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, struct hid_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"); - } else if (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(struct plff_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); - return error; - } - - 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"); + } else if (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(struct plff_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); + return error; + } + + 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); return 0; } #else -static inline int plff_init(struct hid_device *hid) +static inline int pl_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } @@ -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"); - goto err; + return ret; } - 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"); - goto err; + return ret; } - ret = plff_init(hdev); - if (ret) - goto stop; - return 0; - -stop: - hid_hw_stop(hdev); -err: - return ret; } - static const struct hid_device_id pl_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR), .driver_data = 1 }, /* Twin USB Joystick */ @@ -217,6 +196,7 @@ static struct hid_driver pl_driver = { .name = "pantherlord", .id_table = pl_devices, .probe = pl_probe, + .input_configured = pl_input_configured, }; module_hid_driver(pl_driver); diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index 963c45113204..193ab2a6146e 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -48,68 +48,56 @@ static int hid_sjoyff_play(struct input_dev *dev, void *data, return 0; } -static int sjoyff_init(struct hid_device *hid) +static int sjoy_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct sjoyff_device *sjoyff; struct hid_report *report; - struct hid_input *hidinput; struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; - struct list_head *report_ptr = report_list; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int error; - if (list_empty(report_list)) { + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; + + report = list_first_entry_or_null(report_list, struct hid_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, struct hid_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(struct sjoyff_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(struct sjoyff_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); - return error; - } + error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); + if (error) { + kfree(sjoyff); + return error; } - hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n"); - return 0; } #else -static inline int sjoyff_init(struct hid_device *hid) +static inline int sjoy_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } @@ -124,20 +112,16 @@ static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); - goto err; + return ret; } - 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"); - goto err; + return ret; } - sjoyff_init(hdev); - return 0; -err: - return ret; } static const struct hid_device_id sjoy_devices[] = { @@ -165,6 +149,7 @@ static struct hid_driver sjoy_driver = { .name = "smartjoyplus", .id_table = sjoy_devices, .probe = sjoy_probe, + .input_configured = sjoy_input_configured, }; module_hid_driver(sjoy_driver); diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 319e7b670384..303769b07628 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -116,22 +116,25 @@ static int tmff_play(struct input_dev *dev, void *data, return 0; } -static int tmff_init(struct hid_device *hid, const signed short *ff_bits) +static int tm_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct tmff_device *tmff; struct hid_report *report; struct list_head *report_list; - struct hid_input *hidinput; - struct input_dev *input_dev; + struct input_dev *input_dev = hidinput->input; + const struct hid_device_id *id; + const signed short *ff_bits; int error; int i; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; + + id = hid_match_device(hid, hid->driver); + if (!id) return -ENODEV; - } - hidinput = list_entry(hid->inputs.next, struct hid_input, list); - input_dev = hidinput->input; + + ff_bits = (void *)id->driver_data; tmff = kzalloc_obj(struct tmff_device); if (!tmff) @@ -205,35 +208,13 @@ fail: return error; } #else -static inline int tmff_init(struct hid_device *hid, const signed short *ff_bits) +static inline int tm_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } #endif -static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - tmff_init(hdev, (void *)id->driver_data); - - return 0; -err: - return ret; -} - static const struct hid_device_id tm_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300), .driver_data = (unsigned long)ff_rumble }, @@ -262,7 +243,7 @@ MODULE_DEVICE_TABLE(hid, tm_devices); static struct hid_driver tm_driver = { .name = "thrustmaster", .id_table = tm_devices, - .probe = tm_probe, + .input_configured = tm_input_configured, }; module_hid_driver(tm_driver); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index d8e023c8aa84..b565c59d3dfe 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -50,20 +50,15 @@ static int zpff_play(struct input_dev *dev, void *data, return 0; } -static int zpff_init(struct hid_device *hid) +static int zp_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct zpff_device *zpff; struct hid_report *report; - struct hid_input *hidinput; - struct input_dev *dev; + struct input_dev *dev = hidinput->input; int i, error; - if (list_empty(&hid->inputs)) { - hid_err(hid, "no inputs found\n"); - return -ENODEV; - } - hidinput = list_entry(hid->inputs.next, struct hid_input, list); - dev = hidinput->input; + if (!list_is_first(&hidinput->list, &hid->inputs)) + return 0; for (i = 0; i < 4; i++) { report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1); @@ -75,6 +70,7 @@ static int zpff_init(struct hid_device *hid) if (!zpff) return -ENOMEM; + zpff->report = report; set_bit(FF_RUMBLE, dev->ffbit); error = input_ff_create_memless(dev, zpff, zpff_play); @@ -83,7 +79,6 @@ static int zpff_init(struct hid_device *hid) return error; } - zpff->report = report; zpff->report->field[0]->value[0] = 0x00; zpff->report->field[1]->value[0] = 0x02; zpff->report->field[2]->value[0] = 0x00; @@ -95,35 +90,13 @@ static int zpff_init(struct hid_device *hid) return 0; } #else -static inline int zpff_init(struct hid_device *hid) +static inline int zp_input_configured(struct hid_device *hid, + struct hid_input *hidinput) { return 0; } #endif -static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int ret; - - ret = hid_parse(hdev); - if (ret) { - hid_err(hdev, "parse failed\n"); - goto err; - } - - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); - if (ret) { - hid_err(hdev, "hw start failed\n"); - goto err; - } - - zpff_init(hdev); - - return 0; -err: - return ret; -} - static const struct hid_device_id zp_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, @@ -134,7 +107,7 @@ MODULE_DEVICE_TABLE(hid, zp_devices); static struct hid_driver zp_driver = { .name = "zeroplus", .id_table = zp_devices, - .probe = zp_probe, + .input_configured = zp_input_configured, }; module_hid_driver(zp_driver); diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 8d36ae96a3ee..e7196e429c8b 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -232,10 +232,8 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ret = devm_request_irq(dev, pdev->irq, ish_irq_handler, irq_flag, KBUILD_MODNAME, ishtp); - if (ret) { - dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq); + if (ret) return ret; - } dev_set_drvdata(ishtp->devc, ishtp); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 93121dd42ee9..0d2ad7bc3648 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -698,11 +698,8 @@ static int quicki2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) quicki2c_irq_thread_handler, IRQF_ONESHOT, KBUILD_MODNAME, qcdev); - if (ret) { - dev_err_once(&pdev->dev, - "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); + if (ret) goto dev_deinit; - } ret = quicki2c_get_device_descriptor(qcdev); if (ret) { diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index e0ae04d043b8..89226f5ce45e 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -647,11 +647,8 @@ static int quickspi_probe(struct pci_dev *pdev, quickspi_irq_thread_handler, IRQF_ONESHOT, KBUILD_MODNAME, qsdev); - if (ret) { - dev_err(&pdev->dev, - "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); + if (ret) goto dev_deinit; - } ret = reset_tic(qsdev); if (ret) { diff --git a/include/linux/hid.h b/include/linux/hid.h index b240baa95ab5..8d17b741638c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -642,6 +642,7 @@ enum hid_battery_status { * @max: maximum battery value from HID descriptor * @report_type: HID report type (input/feature) * @report_id: HID report ID for this battery + * @report_offset: bit offset of the capacity field within its report * @charge_status: current charging status * @status: battery reporting status * @capacity: current battery capacity (0-100) @@ -657,6 +658,7 @@ struct hid_battery { __s32 max; __s32 report_type; __s32 report_id; + __s32 report_offset; __s32 charge_status; enum hid_battery_status status; __s32 capacity; @@ -1021,7 +1023,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); diff --git a/scripts/coccinelle/hid/ff_race.cocci b/scripts/coccinelle/hid/ff_race.cocci new file mode 100644 index 000000000000..479f5d1e3184 --- /dev/null +++ b/scripts/coccinelle/hid/ff_race.cocci @@ -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) |
