hid-for-linus-2026092202

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEL65usyKPHcrRDEicpmLzj2vtYEkFAmqyY7EACgkQpmLzj2vt
 YEnprxAAofmpQUorlic97WALU2CamXN/hdzLGlra2RPW6eJyxQUlwqR8xcUptx3e
 14OM5X4WDHfFD1lRFx7/flzn2J5h5CLXUWnNUp5DR7W0GLQSnSwOiuawfb5/Enkd
 aqhgsqC2nfEjxQ3iqdsY1MTASN/MEjolNU66tP31DQH5D+CGY6eZtivNwFHaHjJI
 Ps4/vr2nuddSCsXaWRKwdBQ9wMr8g+F7dIeI8vqAJlWg5IdN0tu3ClAuHUqMr6X3
 zMHJ7xkkrRg7S40xeQsajDiPGRRWPkC6G3lrJ1OSFOP1IEMKT5dLLg5HB2wLwfzI
 Xzw4CJZiSIaNKyMQLQJcik8wZZbc60A2ulDscbpk9PLlr9nweBc+7wzVZ0mOQ6Lo
 rD2iYG7ROG684Jt5HVd9SPrIgjCDnlOXDBT9JLyPCw38eEysEHOdSlwA0CSJyR21
 cD0UmfIUgFDXxR/omcPbZDVng8683eRR3W/Tj2XPBOyKZYF/JtnIsrbowIN2dmGe
 PKnsrtp2LW5/xiO/lTfEsiiHL8WoxtXj/Mwoq/NuZm4TvZ/C3nd3ZiEaL5UQeYYc
 AyTFOqBe4DoLauyS2M50gitvDpSBFS9i5xrjCukalpJGfTUlX8lQjU7kdH7O5rbh
 VlMjlYqPNSyd4Yo/nveqIS3ELrKGs5z3oDSxwSB05h1nCgn3dgo=
 =k0Hr
 -----END PGP SIGNATURE-----

Merge tag 'hid-for-linus-2026092202' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID fixes from Jiri Kosina:

 - new device IDs/quirks (Logitech G502X, Elecom M-XT4DRBK, Steelseries
   Arctis 7, Asus Rog Z13 Folio, Lenovo Yoga Slim Gen 11)

 - fixes for various code issues found by LLMs

* tag 'hid-for-linus-2026092202' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  selftests/hid: add unnumbered variant to the hid_bpf tests
  HID: bpf: fix __hid_bpf_hw_check_params report length
  selftests/hid: add define for commonly used buf size
  HID: amd_sfh: Validate PCI BAR size before mapping
  HID: elecom: fix bus type for M-XGL20DLBK
  HID: elecom: Add support for ELECOM M-XT4DRBK (018E)
  HID: corsair-void: Fix firmware event packet description
  HID: i2c-hid: Add i2c-hid-quirk-bad-input-size quirk for 0911:5288 device
  HID: hid-oxp: use cancel_delayed_work_sync() in remove
  HID: i2c-hid: add reset quirk for Lenovo Yoga Slim 7x Gen 11 keyboard
  HID: roccat: fix locking in roccat_connect() and roccat_disconnect()
  HID: steelseries: Add support for Arctis 7 (2018)
  HID: logitech-hidpp: Add support for G502 X Lightspeed USB mouse
  HID: fix semantic patch and improve its performance
  HID: alps: fix use-after-free on input2 registration failure
  HID: alps: unregister DualPoint Stick input device on remove
  HID: winwing: fix use-after-free in force feedback teardown
  HID: multitouch: Add report ID mismatch quirk for ASUS ROG Z13 Folio
  HID: wacom: fix OOB read in wacom_wac_pen_serial_enforce()
  HID: quirks: add ALWAYS_POLL quirk for SDINNOVATION gaming keyboard
This commit is contained in:
Linus Torvalds 2026-09-22 10:30:17 -07:00
commit fe2ec83746
21 changed files with 259 additions and 68 deletions

View File

@ -1210,7 +1210,8 @@ config HID_STEELSERIES
depends on USB_HID
help
Support for Steelseries SRW-S1 steering wheel, and the Steelseries
Arctis 1 Wireless for XBox headset.
Arctis 1 Wireless for XBox, Arctis 7 (2018), Arctis 9, and Arctis
Nova headsets.
config HID_SUNPLUS
tristate "Sunplus wireless desktop"

View File

@ -13,11 +13,15 @@
#include <linux/auxiliary_bus.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/sizes.h>
#include "amd_sfh_hid.h"
#define PCI_DEVICE_ID_AMD_MP2 0x15E4
#define PCI_DEVICE_ID_AMD_MP2_1_1 0x164A
/* The BAR 2 size must cover the highest register offset (0x10958) */
#define AMD_SFH_MIN_BAR_SIZE SZ_128K
#define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4))
#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4))

View File

@ -497,6 +497,16 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
if (rc)
return rc;
if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
dev_err(&pdev->dev, "BAR 2 is not IORESOURCE_MEM\n");
return -ENODEV;
}
if (pci_resource_len(pdev, 2) < AMD_SFH_MIN_BAR_SIZE) {
dev_err(&pdev->dev, "BAR 2 is too small\n");
return -EINVAL;
}
rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME);
if (rc)
return rc;

View File

@ -359,7 +359,7 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx)
static int
__hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,
enum hid_report_type rtype)
enum hid_report_type rtype, bool hw_request)
{
struct hid_report_enum *report_enum;
struct hid_report *report;
@ -388,6 +388,10 @@ __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz,
report_len = hid_report_len(report);
/* unnumbered reports need to have a report ID reserved in the first byte */
if (hw_request && report_enum->numbered == 0)
report_len += 1;
if (*buf__sz > report_len)
*buf__sz = report_len;
@ -420,7 +424,7 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
return -EDEADLOCK;
/* check arguments */
ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype);
ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype, true);
if (ret)
return ret;
@ -480,7 +484,7 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
return -EDEADLOCK;
/* check arguments */
ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT);
ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT, true);
if (ret)
return ret;
@ -506,7 +510,7 @@ __hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *b
return -EDEADLOCK;
/* check arguments */
ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
ret = __hid_bpf_hw_check_params(ctx, buf, &size, type, false);
if (ret)
return ret;

View File

@ -407,6 +407,8 @@ static int u1_raw_event(struct alps_dev *hdata, u8 *data, int size)
return 1;
case U1_SP_ABSOLUTE_REPORT_ID:
if (!hdata->input2)
return 0;
sp_x = get_unaligned_le16(data+2);
sp_y = get_unaligned_le16(data+4);
@ -738,7 +740,6 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
goto exit;
}
data->input2 = input2;
input2->phys = input->phys;
input2->name = "DualPoint Stick";
input2->id.bustype = BUS_I2C;
@ -762,11 +763,12 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
__set_bit(INPUT_PROP_POINTER, input2->propbit);
__set_bit(INPUT_PROP_POINTING_STICK, input2->propbit);
if (input_register_device(data->input2)) {
if (input_register_device(input2)) {
input_free_device(input2);
ret = -ENOENT;
goto exit;
}
data->input2 = input2;
}
exit:
@ -823,6 +825,24 @@ static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
}
static void alps_remove(struct hid_device *hdev)
{
struct alps_dev *data = hid_get_drvdata(hdev);
/*
* input2 ("DualPoint Stick") is allocated separately and is not
* tracked in hdev->inputs, so the default remove path
* (hid_hw_stop -> hidinput_disconnect) does not unregister it.
*
* Stop the device first so that no URB callback can touch input2
* while it is being unregistered, then drop it explicitly.
*/
hid_hw_stop(hdev);
if (data->input2)
input_unregister_device(data->input2);
}
static const struct hid_device_id alps_id[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) },
@ -845,6 +865,7 @@ static struct hid_driver alps_driver = {
.input_configured = alps_input_configured,
.resume = pm_ptr(alps_post_resume),
.reset_resume = pm_ptr(alps_post_reset),
.remove = alps_remove,
};
module_hid_driver(alps_driver);

View File

@ -51,20 +51,23 @@
/* Receiver report information: (ID 102) */
/* -------------------------------------------------------------------------- */
/*
* When queried, the recevier responds with 4 bytes to describe the firmware
* The first 2 bytes are for the receiver, the second 2 are the headset
* When queried, the receiver responds with 5 bytes to describe the firmware
* The first byte is the ID, then 2 bytes for the receiver, then 2 for the headset
* The headset firmware version will be 0 if no headset is connected
*
* 0: Recevier firmware major version
* 0: Report ID
* 102 for the firmware packet
*
* 1: Receiver firmware major version
* Major version of the receiver's firmware
*
* 1: Recevier firmware minor version
* 2: Receiver firmware minor version
* Minor version of the receiver's firmware
*
* 2: Headset firmware major version
* 3: Headset firmware major version
* Major version of the headset's firmware
*
* 3: Headset firmware minor version
* 4: Headset firmware minor version
* Minor version of the headset's firmware
*/
/* -------------------------------------------------------------------------- */

View File

@ -79,7 +79,7 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB:
case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F:
case USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC:
case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
case USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD:
/*
* Report descriptor format:
* 12: button bit count
@ -104,6 +104,7 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8);
break;
case USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C:
case USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E:
/*
* Report descriptor format:
* 22: button bit count
@ -148,7 +149,8 @@ static const struct hid_device_id elecom_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) },

View File

@ -477,7 +477,8 @@
#define USB_DEVICE_ID_ELECOM_M_XT3URBK_018F 0x018f
#define USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC 0x00fc
#define USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C 0x018c
#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd
#define USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD 0x00fd
#define USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E 0x018e
#define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe
#define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff
#define USB_DEVICE_ID_ELECOM_M_DT2DRBK 0x018d
@ -765,6 +766,7 @@
#define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a
#define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396
#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD 0x8987
#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_G11_KEYBOARD 0x83db
#define USB_DEVICE_ID_ITE8595 0x8595
#define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50
@ -1296,6 +1298,9 @@
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_UNIVERSAL_KBD 0xa006
#define USB_DEVICE_ID_SAMSUNG_WIRELESS_MULTI_HOGP_KBD 0xa064
#define USB_VENDOR_ID_SDINNOVATION 0x36ae
#define USB_DEVICE_ID_SDINNOVATION_GAMING_KBD 0xfeab
#define USB_VENDOR_ID_SEMICO 0x1a2c
#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD 0x0023
#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2 0x0027
@ -1395,6 +1400,7 @@
#define USB_VENDOR_ID_STEELSERIES 0x1038
#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X 0x12b6
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_2018 0x12ad
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_9 0x12c2
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X 0x2253
#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7 0x2202

View File

@ -4924,6 +4924,8 @@ static const struct hid_device_id hidpp_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC08D) },
{ /* Logitech G502 X Plus Wireless Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC095) },
{ /* Logitech G502 X Lightspeed Wireless Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC098) },
{ /* Logitech G703 Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) },
{ /* Logitech G703 Hero Gaming Mouse over USB */

View File

@ -79,6 +79,7 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_APPLE_TOUCHBAR BIT(23)
#define MT_QUIRK_YOGABOOK9I BIT(24)
#define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25)
#define MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH BIT(26)
#define MT_INPUTMODE_TOUCHSCREEN 0x02
#define MT_INPUTMODE_TOUCHPAD 0x03
@ -235,6 +236,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app);
#define MT_CLS_APPLE_TOUCHBAR 0x0114
#define MT_CLS_YOGABOOK9I 0x0115
#define MT_CLS_EGALAX_P80H84 0x0116
#define MT_CLS_ASUS_ROG_Z13_FOLIO 0x0117
#define MT_CLS_SIS 0x0457
#define MT_DEFAULT_MAXCONTACT 10
@ -405,6 +407,16 @@ static const struct mt_class mt_classes[] = {
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_ASUS_CUSTOM_UP },
{ .name = MT_CLS_ASUS_ROG_Z13_FOLIO,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_IGNORE_DUPLICATES |
MT_QUIRK_HOVERING |
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_STICKY_FINGERS |
MT_QUIRK_WIN8_PTP_BUTTONS |
MT_QUIRK_CONFIDENCE |
MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH,
.export_all_inputs = true },
{ .name = MT_CLS_VTL,
.quirks = MT_QUIRK_ALWAYS_VALID |
MT_QUIRK_CONTACT_CNT_ACCURATE |
@ -507,6 +519,7 @@ static const struct attribute_group mt_attribute_group = {
static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
{
struct mt_device *td = hid_get_drvdata(hdev);
int ret;
u32 size = hid_report_len(report);
u8 *buf;
@ -528,8 +541,14 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
dev_warn(&hdev->dev, "failed to fetch feature %d\n",
report->id);
} else {
/* The report ID in the request and the response should match */
if (report->id != buf[0]) {
/*
* The report ID in the request and the response should match.
* Some firmware (e.g. the ASUS ROG Z13 Folio
* touchpad) returns a mismatched ID on this specific fetch;
* tolerate it only for devices explicitly flagged as such.
*/
if (report->id != buf[0] &&
!(td->mtclass.quirks & MT_QUIRK_IGNORE_FEATURE_ID_MISMATCH)) {
hid_err(hdev, "Returned feature report did not match the request\n");
goto free;
}
@ -2715,6 +2734,12 @@ static const struct hid_device_id mt_devices[] = {
HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288) },
/* Asus ROG Flow Z13 (2025) GZ302EA keyboard-cover touchpad */
{ .driver_data = MT_CLS_ASUS_ROG_Z13_FOLIO,
HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8,
USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO) },
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },

View File

@ -1552,9 +1552,9 @@ static int oxp_hid_probe(struct hid_device *hdev,
static void oxp_hid_remove(struct hid_device *hdev)
{
cancel_delayed_work(&drvdata.oxp_rgb_queue);
cancel_delayed_work(&drvdata.oxp_btn_queue);
cancel_delayed_work(&drvdata.oxp_mcu_init);
cancel_delayed_work_sync(&drvdata.oxp_rgb_queue);
cancel_delayed_work_sync(&drvdata.oxp_btn_queue);
cancel_delayed_work_sync(&drvdata.oxp_mcu_init);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}

View File

@ -186,6 +186,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X52_PRO), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_X65), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
{ HID_USB_DEVICE(USB_VENDOR_ID_SDINNOVATION, USB_DEVICE_ID_SDINNOVATION_GAMING_KBD), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET },
@ -425,13 +426,14 @@ static const struct hid_device_id hid_have_special_driver[] = {
#endif
#if IS_ENABLED(CONFIG_HID_ELECOM)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_00FD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK_018E) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) },
@ -751,6 +753,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_STEELSERIES)
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_7_2018) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_9) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7) },

View File

@ -344,8 +344,6 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report
return temp;
}
mutex_unlock(&devices_lock);
init_waitqueue_head(&device->wait);
INIT_LIST_HEAD(&device->readers);
mutex_init(&device->readers_lock);
@ -356,6 +354,7 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report
device->cbuf_end = 0;
device->report_size = report_size;
mutex_unlock(&devices_lock);
return minor;
}
EXPORT_SYMBOL_GPL(roccat_connect);
@ -369,15 +368,12 @@ void roccat_disconnect(int minor)
mutex_lock(&devices_lock);
device = devices[minor];
mutex_unlock(&devices_lock);
device->exist = 0; /* TODO exist maybe not needed */
device_destroy(device->dev->class, MKDEV(roccat_major, minor));
mutex_lock(&devices_lock);
devices[minor] = NULL;
mutex_unlock(&devices_lock);
if (device->open) {
hid_hw_close(device->hid);
@ -385,6 +381,8 @@ void roccat_disconnect(int minor)
} else {
roccat_free_device(device);
}
mutex_unlock(&devices_lock);
}
EXPORT_SYMBOL_GPL(roccat_disconnect);

View File

@ -101,6 +101,19 @@ static int steelseries_arctis_1_request_status(struct hid_device *hdev)
return steelseries_send_output_report(hdev, data, sizeof(data));
}
static int steelseries_arctis_7_2018_request_status(struct hid_device *hdev)
{
const u8 connection[] = { 0x06, 0x14 };
const u8 battery[] = { 0x06, 0x18 };
int ret;
ret = steelseries_send_output_report(hdev, connection, sizeof(connection));
if (ret)
return ret;
return steelseries_send_output_report(hdev, battery, sizeof(battery));
}
static int steelseries_arctis_9_request_status(struct hid_device *hdev)
{
const u8 data[] = { 0x00, 0x20 };
@ -152,6 +165,27 @@ static void steelseries_arctis_1_parse_status(struct steelseries_device *sd,
sd->battery_capacity = data[3];
}
static void steelseries_arctis_7_2018_parse_status(struct steelseries_device *sd,
u8 *data, int size)
{
if (size < 3 || data[0] != 0x06)
return;
switch (data[1]) {
case 0x14:
/* 0x03 means that the headset is connected to the transmitter. */
sd->headset_connected = data[2] == 0x03;
break;
case 0x18:
if (!sd->headset_connected)
break;
/* The Arctis 7 sometimes overreports battery. Cap to 100. */
sd->battery_capacity = steelseries_map_capacity(data[2], 0, 100);
break;
}
}
static void steelseries_arctis_9_parse_status(struct steelseries_device *sd,
u8 *data, int size)
{
@ -232,6 +266,13 @@ static const struct steelseries_device_info arctis_1_info = {
.parse_status = steelseries_arctis_1_parse_status,
};
static const struct steelseries_device_info arctis_7_2018_info = {
.sync_interface = 5,
.capabilities = SS_CAP_BATTERY,
.request_status = steelseries_arctis_7_2018_request_status,
.parse_status = steelseries_arctis_7_2018_parse_status,
};
static const struct steelseries_device_info arctis_9_info = {
.sync_interface = 0,
.capabilities = SS_CAP_BATTERY,
@ -653,6 +694,9 @@ static const struct hid_device_id steelseries_arctis_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X),
.driver_data = (unsigned long)&arctis_1_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_7_2018),
.driver_data = (unsigned long)&arctis_7_2018_info },
{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES,
USB_DEVICE_ID_STEELSERIES_ARCTIS_9),
.driver_data = (unsigned long)&arctis_9_info },

View File

@ -315,7 +315,8 @@ static void winwing_haptic_rumble_cb(struct work_struct *work)
static int winwing_play_effect(struct input_dev *dev, void *context,
struct ff_effect *effect)
{
struct winwing_drv_data *data = (struct winwing_drv_data *) context;
struct hid_device *hdev = input_get_drvdata(dev);
struct winwing_drv_data *data = hid_get_drvdata(hdev);
if (effect->type != FF_RUMBLE)
return 0;
@ -342,7 +343,12 @@ static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput)
input_set_capability(hidinput->input, EV_FF, FF_RUMBLE);
return input_ff_create_memless(hidinput->input, data,
/*
* input_ff_create_memless() takes ownership of the context pointer
* and frees it on teardown; do not hand it the devm-managed drvdata.
* winwing_play_effect() fetches it from the input device instead.
*/
return input_ff_create_memless(hidinput->input, NULL,
winwing_play_effect);
}

View File

@ -123,9 +123,11 @@ static const struct i2c_hid_quirks {
__u32 quirks;
} i2c_hid_quirks[] = {
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET | I2C_HID_QUIRK_BAD_INPUT_SIZE },
{ I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_G11_KEYBOARD,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ USB_VENDOR_ID_ALPS_JP, HID_ANY_ID,

View File

@ -113,8 +113,9 @@ static int wacom_wac_pen_serial_enforce(struct hid_device *hdev,
/* Queue events which have invalid tool type or serial number */
for (i = 0; i < report->maxfield; i++) {
for (j = 0; j < report->field[i]->maxusage; j++) {
struct hid_field *field = report->field[i];
struct hid_field *field = report->field[i];
for (j = 0; j < field->report_count; j++) {
struct hid_usage *usage = &field->usage[j];
unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
unsigned int offset;

View File

@ -7,18 +7,19 @@
virtual report
@r@
@r exists@
identifier probe_fn;
expression hdev, flags;
identifier hdev;
expression flags;
position p1, p2;
@@
probe_fn(struct hid_device *hdev, ...) {
<...
... when any
hid_hw_start@p1(hdev, flags)
...
\(input_ff_create\|input_ff_create_memless\)@p2(...)
...>
\(input_ff_create@p2\|input_ff_create_memless@p2\)(...)
... when any
}
@script:python depends on report@

View File

@ -5,7 +5,7 @@
#include <bpf/bpf.h>
struct hid_hw_request_syscall_args {
__u8 data[10];
__u8 data[MAX_BUF_SIZE];
unsigned int hid;
int retval;
size_t size;
@ -54,11 +54,27 @@ FIXTURE_TEARDOWN(hid_bpf) {
hid_bpf_teardown(_metadata, self, variant); \
} while (0)
FIXTURE_VARIANT(hid_bpf) {
__u8 *rdesc;
size_t rdesc_size;
};
FIXTURE_VARIANT_ADD(hid_bpf, numbered) {
.rdesc = rdesc,
.rdesc_size = sizeof(rdesc),
};
FIXTURE_VARIANT_ADD(hid_bpf, unnumbered) {
.rdesc = fido2_rdesc,
.rdesc_size = sizeof(fido2_rdesc),
};
FIXTURE_SETUP(hid_bpf)
{
int err;
err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36, rdesc, sizeof(rdesc));
err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36,
variant->rdesc, variant->rdesc_size);
ASSERT_OK(err);
}
@ -175,7 +191,7 @@ TEST_F(hid_bpf, raw_event)
const struct test_program progs[] = {
{ .name = "hid_first_event" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -226,7 +242,7 @@ TEST_F(hid_bpf, subprog_raw_event)
const struct test_program progs[] = {
{ .name = "hid_subprog_first_event" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -284,7 +300,7 @@ TEST_F(hid_bpf, test_attach_detach)
{ .name = "hid_second_event" },
};
struct bpf_link *link;
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err, link_fd;
LOAD_PROGRAMS(progs);
@ -369,7 +385,7 @@ TEST_F(hid_bpf, test_hid_change_report)
const struct test_program progs[] = {
{ .name = "hid_change_report_id" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -396,21 +412,24 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
{
struct hid_hw_request_syscall_args args = {
.retval = -1,
.size = 10,
.size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
.ctx_size_in = sizeof(args),
);
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err, prog_fd;
LOAD_BPF;
args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */
args.data[1] = 2; /* report ID */
args.data[2] = 42; /* report ID */
args.data[1] = 2;
args.data[2] = 42;
if (variant->rdesc == fido2_rdesc)
args.data[0] = 0;
prog_fd = bpf_program__fd(self->skel->progs.hid_user_input_report);
@ -428,8 +447,13 @@ TEST_F(hid_bpf, test_hid_user_input_report_call)
/* read the data from hidraw */
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
ASSERT_EQ(buf[0], 1);
if (variant->rdesc == rdesc) {
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
} else {
ASSERT_EQ(err, 64)
TH_LOG("read_hidraw");
}
ASSERT_EQ(buf[0], args.data[0]);
ASSERT_EQ(buf[1], 2);
ASSERT_EQ(buf[2], 42);
}
@ -442,7 +466,7 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
{
struct hid_hw_request_syscall_args args = {
.retval = -1,
.size = 10,
.size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
@ -455,8 +479,11 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
args.hid = self->hid.hid_id;
args.data[0] = 1; /* report ID */
args.data[1] = 2; /* report ID */
args.data[2] = 42; /* report ID */
args.data[1] = 2;
args.data[2] = 42;
if (variant->rdesc == fido2_rdesc)
args.data[0] = 0;
prog_fd = bpf_program__fd(self->skel->progs.hid_user_output_report);
@ -472,9 +499,14 @@ TEST_F(hid_bpf, test_hid_user_output_report_call)
ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
ASSERT_OK(cond_err) TH_LOG("error while calling waiting for the condition");
ASSERT_EQ(args.retval, 3);
if (variant->rdesc == rdesc) {
ASSERT_EQ(args.retval, 3);
} else if (variant->rdesc == fido2_rdesc) {
ASSERT_EQ(args.retval, 65)
TH_LOG("report size error, should have 64 + 1 extra byte for the report ID 0");
}
ASSERT_EQ(output_report[0], 1);
ASSERT_EQ(output_report[0], args.data[0]);
ASSERT_EQ(output_report[1], 2);
ASSERT_EQ(output_report[2], 42);
@ -491,7 +523,7 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
.retval = -1,
.type = HID_FEATURE_REPORT,
.request_type = HID_REQ_GET_REPORT,
.size = 10,
.size = MAX_BUF_SIZE,
};
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
.ctx_in = &args,
@ -524,7 +556,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_filter_raw_request" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -577,7 +609,7 @@ TEST_F(hid_bpf, test_hid_change_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_hidraw_raw_request" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -603,7 +635,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_raw_request_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_raw_request" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -626,7 +658,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_filter_output_report" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -679,7 +711,7 @@ TEST_F(hid_bpf, test_hid_change_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_hidraw_output_report" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -703,7 +735,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_output_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_output_report" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -729,7 +761,7 @@ TEST_F(hid_bpf, test_multiply_events_wq)
const struct test_program progs[] = {
{ .name = "hid_test_multiply_events_wq" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -767,7 +799,7 @@ TEST_F(hid_bpf, test_multiply_events)
const struct test_program progs[] = {
{ .name = "hid_test_multiply_events" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -801,7 +833,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call)
const struct test_program progs[] = {
{ .name = "hid_test_infinite_loop_input_report" },
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -855,7 +887,7 @@ TEST_F(hid_bpf, test_hid_attach_flags)
.insert_head = 0,
},
};
__u8 buf[10] = {0};
__u8 buf[MAX_BUF_SIZE] = {0};
int err;
LOAD_PROGRAMS(progs);
@ -886,6 +918,9 @@ TEST_F(hid_bpf, test_rdesc_fixup)
};
int err, desc_size;
if (variant->rdesc != rdesc)
SKIP(return, "not compatible report descriptor");
LOAD_PROGRAMS(progs);
/* check that hid_rdesc_fixup() was executed */

View File

@ -13,6 +13,7 @@
#include <linux/uhid.h>
#define SHOW_UHID_DEBUG 0
#define MAX_BUF_SIZE 128
#define min(a, b) \
({ __typeof__(a) _a = (a); \
@ -97,6 +98,28 @@ static unsigned char rdesc[] = {
static __u8 feature_data[] = { 1, 2 };
static __maybe_unused unsigned char fido2_rdesc[] = {
0x06, 0xd0, 0xf1, /* Usage Page (FIDO Alliance) */
0x09, 0x01, /* Usage (U2F Authenticator Device) */
0xa1, 0x01, /* Collection (Application) */
0x09, 0x20, /* Usage (Input Report Data) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x00, /* Logical Maximum (255) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x40, /* Report Count (64) */
0x81, 0x02, /* Input (Data,Var,Abs) */
0x09, 0x21, /* Usage (Output Report Data) */
0x15, 0x00, /* Logical Minimum (0) */
0x26, 0xff, 0x00, /* Logical Maximum (255) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x40, /* Report Count (64) */
0x91, 0x02, /* Output (Data,Var,Abs) */
0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
0x09, 0x22, /* Usage (Vendor Usage 0x22) */
0xb1, 0x02, /* Feature (Data,Var,Abs) */
0xc0, /* End Collection */
};
#define ASSERT_OK(data) ASSERT_FALSE(data)
#define ASSERT_OK_PTR(ptr) ASSERT_NE(NULL, ptr)
@ -110,7 +133,7 @@ static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t uhid_output_mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t uhid_output_cond = PTHREAD_COND_INITIALIZER;
static unsigned char output_report[10];
static unsigned char output_report[MAX_BUF_SIZE];
/* no need to protect uhid_stopped, only one thread accesses it */
static bool uhid_stopped;

View File

@ -98,7 +98,7 @@ struct hid_bpf_ops change_report_id = {
struct hid_hw_request_syscall_args {
/* data needs to come at offset 0 so we can use it in calls */
__u8 data[10];
__u8 data[128];
unsigned int hid;
int retval;
size_t size;