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)
This commit is contained in:
Jiri Kosina 2026-08-19 09:27:26 +02:00
commit d89f04ac41
31 changed files with 555 additions and 774 deletions

View File

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

View File

@ -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)

View File

@ -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));
}

View File

@ -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 @@ static int axff_init(struct hid_device *hid)
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);

View File

@ -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);

View File

@ -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;
return -ENODEV;
}
if (list_empty(&hid->inputs)) {
hid_err(hid, "no inputs found\n");
error = -ENODEV;
goto error_hw_stop;
}
set_bit(FF_RUMBLE, input_dev->ffbit);
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);
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);

View File

@ -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)
{

View File

@ -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 (list_empty(report_list)) {
if (hid->product != 0x0006)
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;
}
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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
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");
return 0;
}
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;
}
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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

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

View File

@ -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,32 +68,17 @@ static int mwctrl_init(struct hid_device *hid)
mwctrl->strong = &report->field[2]->value[0];
mwctrl->weak = &report->field[3]->value[0];
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, mwctrl, mwctrl_play);
if (error) {
kfree(mwctrl);
return error;
}
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;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (ret) {
hid_err(hdev, "hw start failed\n");
return ret;
}
ret = mwctrl_init(hdev);
if (ret)
hid_hw_stop(hdev);
return ret;
}
static const struct hid_device_id mwctrl_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_MEGAWORLD,
USB_DEVICE_ID_MEGAWORLD_GAMEPAD) },
@ -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);

View File

@ -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);
mf = kzalloc_obj(struct mf_device);
if (!mf)
return -ENOMEM;
dev = input->input;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, mf, mf_play);
if (error) {
kfree(mf);
return error;
}
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);
report = list_first_entry_or_null(report_list, struct hid_report, list);
if (!report) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
}
hid_info(hid, "Force feedback for HJZ Mayflash game controller "
"adapters by Marcel Hasler <mahasler@gmail.com>\n");
if (report->maxfield < 1 || report->field[0]->report_count < 2) {
hid_err(hid, "Invalid report, this should never happen!\n");
return -ENODEV;
}
mf = kzalloc_obj(struct mf_device);
if (!mf)
return -ENOMEM;
mf->report = report;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, mf, mf_play);
if (error) {
kfree(mf);
return error;
}
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);

View File

@ -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,

View File

@ -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;
}

View File

@ -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 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16 up)
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)

View File

@ -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;
}
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);
if (report->maxfield < 1) {
hid_err(hid, "no fields in the report\n");
return -ENODEV;
}
hid_info(hid, "Force feedback for PantherLord/GreenAsia devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
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);
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);

View File

@ -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;
}
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;
dev = hidinput->input;
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);
error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
if (error) {
kfree(sjoyff);
return error;
}
if (report->maxfield < 1) {
hid_err(hid, "no fields in the report\n");
return -ENODEV;
}
hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n");
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;
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);
error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
if (error) {
kfree(sjoyff);
return error;
}
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);

View File

@ -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 @@ static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
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);

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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);

View File

@ -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)