mirror of
https://github.com/torvalds/linux.git
synced 2026-09-13 15:40:03 +02:00
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:
commit
d89f04ac41
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
34
scripts/coccinelle/hid/ff_race.cocci
Normal file
34
scripts/coccinelle/hid/ff_race.cocci
Normal 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)
|
||||
Loading…
Reference in New Issue
Block a user