* [PATCH v2] HID: quirks: update hid-sony supported devices
From: Rosalie Wanders @ 2026-04-07 19:53 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Rosalie Wanders, linux-input, linux-kernel
hid-sony has been updated with new device support, update the
hid_have_special_driver list accordingly.
Signed-off-by: Rosalie Wanders <rosalie@mailbox.org>
---
changes:
v2: rebase on v2 of 'HID: sony: add support for more instruments'
drivers/hid/hid-quirks.c | 46 ++++++++++++++++++++++++++++++++++------
1 file changed, 39 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index edc4339adb50..7c411e59fa6b 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -690,22 +690,54 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
#endif
#if IS_ENABLED(CONFIG_HID_SONY)
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR) },
#endif
#if IS_ENABLED(CONFIG_HID_SPEEDLINK)
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
--
2.53.0
^ permalink raw reply related
* Re: [RFC PATCH 3/5] Input: pc110pad - remove driver
From: Bjorn Helgaas @ 2026-04-07 19:51 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-input, linux-kernel, Vojtech Pavlik, Jiri Kosina,
Benjamin Tissoires, Maciej W. Rozycki
In-Reply-To: <20240808172733.1194442-4-dmitry.torokhov@gmail.com>
[+cc Maciej]
On Thu, Aug 08, 2024 at 10:27:29AM -0700, Dmitry Torokhov wrote:
> Palm Top PC 110 is a handheld personal computer with 80486SX CPU that
> was released exclusively in Japan in September 1995.
>
> While the kernel still supports 486 CPU it is highly unlikely that
> anyone is using this device with the latest kernel.
>
> Remove the driver.
>
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
I applied this patch only to remove pc110pad to pci/enumeration for
v7.1, since "x86/cpu: Remove M486/M486SX/ELAN support" has been queued
for v7.1:
https://lore.kernel.org/all/20251214084710.3606385-2-mingo@kernel.org/
I put this in the PCI tree because pc110pad was the only user of
no_pci_devices(), which we can now remove as well.
> ---
> drivers/input/mouse/Kconfig | 10 ---
> drivers/input/mouse/Makefile | 1 -
> drivers/input/mouse/pc110pad.c | 160 ---------------------------------
> 3 files changed, 171 deletions(-)
> delete mode 100644 drivers/input/mouse/pc110pad.c
>
> diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
> index f660e6ba24c2..7b7053c57588 100644
> --- a/drivers/input/mouse/Kconfig
> +++ b/drivers/input/mouse/Kconfig
> @@ -312,16 +312,6 @@ config MOUSE_ELAN_I2C_SMBUS
>
> If unsure, say Y.
>
> -config MOUSE_PC110PAD
> - tristate "IBM PC110 touchpad"
> - depends on ISA
> - help
> - Say Y if you have the IBM PC-110 micro-notebook and want its
> - touchpad supported.
> -
> - To compile this driver as a module, choose M here: the
> - module will be called pc110pad.
> -
> config MOUSE_AMIGA
> tristate "Amiga mouse"
> depends on AMIGA
> diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
> index e745b64fed49..4f9fb7d87a37 100644
> --- a/drivers/input/mouse/Makefile
> +++ b/drivers/input/mouse/Makefile
> @@ -13,7 +13,6 @@ obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o
> obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
> obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
> obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
> -obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
> obj-$(CONFIG_MOUSE_PS2) += psmouse.o
> obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
> obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
> diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
> deleted file mode 100644
> index efa58049f746..000000000000
> --- a/drivers/input/mouse/pc110pad.c
> +++ /dev/null
> @@ -1,160 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0-or-later
> -/*
> - * Copyright (c) 2000-2001 Vojtech Pavlik
> - *
> - * Based on the work of:
> - * Alan Cox Robin O'Leary
> - */
> -
> -/*
> - * IBM PC110 touchpad driver for Linux
> - */
> -
> -#include <linux/module.h>
> -#include <linux/kernel.h>
> -#include <linux/errno.h>
> -#include <linux/ioport.h>
> -#include <linux/input.h>
> -#include <linux/init.h>
> -#include <linux/interrupt.h>
> -#include <linux/pci.h>
> -#include <linux/delay.h>
> -
> -#include <asm/io.h>
> -#include <asm/irq.h>
> -
> -MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
> -MODULE_DESCRIPTION("IBM PC110 touchpad driver");
> -MODULE_LICENSE("GPL");
> -
> -#define PC110PAD_OFF 0x30
> -#define PC110PAD_ON 0x38
> -
> -static int pc110pad_irq = 10;
> -static int pc110pad_io = 0x15e0;
> -
> -static struct input_dev *pc110pad_dev;
> -static int pc110pad_data[3];
> -static int pc110pad_count;
> -
> -static irqreturn_t pc110pad_interrupt(int irq, void *ptr)
> -{
> - int value = inb_p(pc110pad_io);
> - int handshake = inb_p(pc110pad_io + 2);
> -
> - outb(handshake | 1, pc110pad_io + 2);
> - udelay(2);
> - outb(handshake & ~1, pc110pad_io + 2);
> - udelay(2);
> - inb_p(0x64);
> -
> - pc110pad_data[pc110pad_count++] = value;
> -
> - if (pc110pad_count < 3)
> - return IRQ_HANDLED;
> -
> - input_report_key(pc110pad_dev, BTN_TOUCH,
> - pc110pad_data[0] & 0x01);
> - input_report_abs(pc110pad_dev, ABS_X,
> - pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
> - input_report_abs(pc110pad_dev, ABS_Y,
> - pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
> - input_sync(pc110pad_dev);
> -
> - pc110pad_count = 0;
> - return IRQ_HANDLED;
> -}
> -
> -static void pc110pad_close(struct input_dev *dev)
> -{
> - outb(PC110PAD_OFF, pc110pad_io + 2);
> -}
> -
> -static int pc110pad_open(struct input_dev *dev)
> -{
> - pc110pad_interrupt(0, NULL);
> - pc110pad_interrupt(0, NULL);
> - pc110pad_interrupt(0, NULL);
> - outb(PC110PAD_ON, pc110pad_io + 2);
> - pc110pad_count = 0;
> -
> - return 0;
> -}
> -
> -/*
> - * We try to avoid enabling the hardware if it's not
> - * there, but we don't know how to test. But we do know
> - * that the PC110 is not a PCI system. So if we find any
> - * PCI devices in the machine, we don't have a PC110.
> - */
> -static int __init pc110pad_init(void)
> -{
> - int err;
> -
> - if (!no_pci_devices())
> - return -ENODEV;
> -
> - if (!request_region(pc110pad_io, 4, "pc110pad")) {
> - printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
> - pc110pad_io, pc110pad_io + 4);
> - return -EBUSY;
> - }
> -
> - outb(PC110PAD_OFF, pc110pad_io + 2);
> -
> - if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) {
> - printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
> - err = -EBUSY;
> - goto err_release_region;
> - }
> -
> - pc110pad_dev = input_allocate_device();
> - if (!pc110pad_dev) {
> - printk(KERN_ERR "pc110pad: Not enough memory.\n");
> - err = -ENOMEM;
> - goto err_free_irq;
> - }
> -
> - pc110pad_dev->name = "IBM PC110 TouchPad";
> - pc110pad_dev->phys = "isa15e0/input0";
> - pc110pad_dev->id.bustype = BUS_ISA;
> - pc110pad_dev->id.vendor = 0x0003;
> - pc110pad_dev->id.product = 0x0001;
> - pc110pad_dev->id.version = 0x0100;
> -
> - pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> - pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
> - pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
> -
> - input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff);
> - input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff);
> -
> - pc110pad_dev->open = pc110pad_open;
> - pc110pad_dev->close = pc110pad_close;
> -
> - err = input_register_device(pc110pad_dev);
> - if (err)
> - goto err_free_dev;
> -
> - return 0;
> -
> - err_free_dev:
> - input_free_device(pc110pad_dev);
> - err_free_irq:
> - free_irq(pc110pad_irq, NULL);
> - err_release_region:
> - release_region(pc110pad_io, 4);
> -
> - return err;
> -}
> -
> -static void __exit pc110pad_exit(void)
> -{
> - outb(PC110PAD_OFF, pc110pad_io + 2);
> - free_irq(pc110pad_irq, NULL);
> - input_unregister_device(pc110pad_dev);
> - release_region(pc110pad_io, 4);
> -}
> -
> -module_init(pc110pad_init);
> -module_exit(pc110pad_exit);
> --
> 2.46.0.76.ge559c4bf1a-goog
>
^ permalink raw reply
* [PATCH v2] HID: sony: add support for more instruments
From: Rosalie Wanders @ 2026-04-07 19:46 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Rosalie Wanders, Sanjay Govind, Brenton Simpson, linux-input,
linux-kernel
This patch adds support for the following instruments:
* Rock Band 1/2/3 Wii/PS3 instruments
* Rock Band 3 PS3 Pro instruments
* DJ Hero Turntable
Wii and PS3 instruments are the same besides the vendor and product ID.
This patch also fixes the mappings for the existing Guitar Hero
instruments.
Co-developed-by: Sanjay Govind <sanjay.govind9@gmail.com>
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
Co-developed-by: Brenton Simpson <appsforartists@google.com>
Signed-off-by: Brenton Simpson <appsforartists@google.com>
Signed-off-by: Rosalie Wanders <rosalie@mailbox.org>
---
changes:
v2: correct squier device name and remove non-existent squier usb device
drivers/hid/hid-ids.h | 26 +++-
drivers/hid/hid-sony.c | 274 ++++++++++++++++++++++++++++++++++-------
2 files changed, 253 insertions(+), 47 deletions(-)
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3e299a30dcde..3dab8b910831 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -664,6 +664,18 @@
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
+#define USB_VENDOR_ID_HARMONIX 0x1bad
+#define USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR 0x0004
+#define USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR 0x3010
+#define USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS 0x0005
+#define USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS 0x3110
+#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE 0x3138
+#define USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR 0x3430
+#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE 0x3438
+#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE 0x3538
+#define USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD 0x3330
+#define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE 0x3338
+
#define USB_VENDOR_ID_HP 0x03f0
#define USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A 0x464a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a
@@ -1298,8 +1310,18 @@
#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000
#define USB_VENDOR_ID_SONY_RHYTHM 0x12ba
-#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE 0x074b
-#define USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE 0x0100
+#define USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE 0x074b
+#define USB_DEVICE_ID_SONY_PS3_GH_GUITAR 0x0100
+#define USB_DEVICE_ID_SONY_PS3_GH_DRUMS 0x0120
+#define USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE 0x0140
+#define USB_DEVICE_ID_SONY_PS3_RB_GUITAR 0x0200
+#define USB_DEVICE_ID_SONY_PS3_RB_DRUMS 0x0210
+#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE 0x0218
+#define USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR 0x2430
+#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE 0x2438
+#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE 0x2538
+#define USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD 0x2330
+#define USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE 0x2338
#define USB_VENDOR_ID_SINO_LITE 0x1345
#define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index a89af14e4acc..a14e730318ce 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * HID driver for Sony / PS2 / PS3 / PS4 BD devices.
+ * HID driver for Sony / PS2 / PS3 / PS4 / PS5 BD devices.
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
@@ -12,9 +12,10 @@
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
* Copyright (c) 2018 Todd Kelner
* Copyright (c) 2020-2021 Pascal Giard <pascal.giard@etsmtl.ca>
- * Copyright (c) 2020 Sanjay Govind <sanjay.govind9@gmail.com>
+ * Copyright (c) 2020-2026 Sanjay Govind <sanjay.govind9@gmail.com>
* Copyright (c) 2021 Daniel Nguyen <daniel.nguyen.1@ens.etsmtl.ca>
* Copyright (c) 2026 Rosalie Wanders <rosalie@mailbox.org>
+ * Copyright (c) 2026 Brenton Simpson <appsforartists@google.com>
*/
/*
@@ -59,12 +60,15 @@
#define NSG_MR5U_REMOTE_BT BIT(11)
#define NSG_MR7U_REMOTE_BT BIT(12)
#define SHANWAN_GAMEPAD BIT(13)
-#define GH_GUITAR_CONTROLLER BIT(14)
-#define GHL_GUITAR_PS3WIIU BIT(15)
-#define GHL_GUITAR_PS4 BIT(16)
-#define RB4_GUITAR_PS4_USB BIT(17)
-#define RB4_GUITAR_PS4_BT BIT(18)
-#define RB4_GUITAR_PS5 BIT(19)
+#define INSTRUMENT BIT(14)
+#define GH_GUITAR_TILT BIT(15)
+#define GHL_GUITAR_PS3WIIU BIT(16)
+#define GHL_GUITAR_PS4 BIT(17)
+#define RB4_GUITAR_PS4_USB BIT(18)
+#define RB4_GUITAR_PS4_BT BIT(19)
+#define RB4_GUITAR_PS5 BIT(20)
+#define RB3_PS3_PRO_INSTRUMENT BIT(21)
+#define PS3_DJH_TURNTABLE BIT(22)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -87,6 +91,10 @@
#define GHL_GUITAR_POKE_INTERVAL 8 /* In seconds */
#define GUITAR_TILT_USAGE 44
+#define TURNTABLE_EFFECTS_KNOB_USAGE 44
+#define TURNTABLE_PLATTER_BUTTONS_USAGE 45
+#define TURNTABLE_CROSS_FADER_USAGE 46
+
/* Magic data taken from GHLtarUtility:
* https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs
* Note: The Wii U and PS3 dongles happen to share the same!
@@ -102,6 +110,13 @@ static const char ghl_ps4_magic_data[] = {
0x30, 0x02, 0x08, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00
};
+/* Rock Band 3 PS3 Pro Instruments require sending a report
+ * once an instrument is connected to its dongle.
+ * We need to retry sending these reports,
+ * but to avoid doing this too often we delay the retries
+ */
+#define RB3_PRO_INSTRUMENT_POKE_RETRY_INTERVAL 8 /* In seconds */
+
/* PS/3 Motion controller */
static const u8 motion_rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
@@ -427,20 +442,25 @@ static const unsigned int rb4_absmap[] = {
[0x31] = ABS_Y,
};
-static const unsigned int rb4_keymap[] = {
- [0x1] = BTN_WEST, /* Square */
- [0x2] = BTN_SOUTH, /* Cross */
- [0x3] = BTN_EAST, /* Circle */
- [0x4] = BTN_NORTH, /* Triangle */
- [0x5] = BTN_TL, /* L1 */
- [0x6] = BTN_TR, /* R1 */
- [0x7] = BTN_TL2, /* L2 */
- [0x8] = BTN_TR2, /* R2 */
- [0x9] = BTN_SELECT, /* Share */
- [0xa] = BTN_START, /* Options */
- [0xb] = BTN_THUMBL, /* L3 */
- [0xc] = BTN_THUMBR, /* R3 */
- [0xd] = BTN_MODE, /* PS */
+static const unsigned int ps3_turntable_absmap[] = {
+ [0x32] = ABS_X,
+ [0x35] = ABS_Y,
+};
+
+static const unsigned int instrument_keymap[] = {
+ [0x1] = BTN_WEST,
+ [0x2] = BTN_SOUTH,
+ [0x3] = BTN_EAST,
+ [0x4] = BTN_NORTH,
+ [0x5] = BTN_TL,
+ [0x6] = BTN_TR,
+ [0x7] = BTN_TL2,
+ [0x8] = BTN_TR2,
+ [0x9] = BTN_SELECT,
+ [0xa] = BTN_START,
+ [0xb] = BTN_THUMBL,
+ [0xc] = BTN_THUMBR,
+ [0xd] = BTN_MODE,
};
static enum power_supply_property sony_battery_props[] = {
@@ -490,6 +510,7 @@ struct motion_output_report_02 {
#define SIXAXIS_REPORT_0xF2_SIZE 17
#define SIXAXIS_REPORT_0xF5_SIZE 8
#define MOTION_REPORT_0x02_SIZE 49
+#define PRO_INSTRUMENT_0x00_SIZE 8
#define SENSOR_SUFFIX " Motion Sensors"
#define TOUCHPAD_SUFFIX " Touchpad"
@@ -539,6 +560,9 @@ struct sony_sc {
/* GH Live */
struct urb *ghl_urb;
struct timer_list ghl_poke_timer;
+
+ /* Rock Band 3 Pro Instruments */
+ unsigned long rb3_pro_poke_jiffies;
};
static void sony_set_leds(struct sony_sc *sc);
@@ -610,35 +634,108 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev,
return 0;
}
-static int gh_guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
+
+
+/*
+ * Sending HID_REQ_SET_REPORT enables the full report. Without this
+ * Rock Band 3 Pro instruments only report navigation events
+ */
+static int rb3_pro_instrument_enable_full_report(struct sony_sc *sc)
+{
+ struct hid_device *hdev = sc->hdev;
+ static const u8 report[] = { 0x00, 0xE9, 0x00, 0x89, 0x1B,
+ 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x89, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xE9, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00 };
+ u8 *buf;
+ int ret;
+
+ buf = kmemdup(report, sizeof(report), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(report),
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int djh_turntable_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) {
unsigned int abs = usage->hid & HID_USAGE;
- if (abs == GUITAR_TILT_USAGE) {
+ if (abs == TURNTABLE_CROSS_FADER_USAGE) {
+ hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RX);
+ return 1;
+ } else if (abs == TURNTABLE_EFFECTS_KNOB_USAGE) {
hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY);
return 1;
+ } else if (abs == TURNTABLE_PLATTER_BUTTONS_USAGE) {
+ hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RZ);
+ return 1;
}
+ } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
+ unsigned int abs = usage->hid & HID_USAGE;
+
+ if (abs >= ARRAY_SIZE(ps3_turntable_absmap))
+ return -1;
+
+ abs = ps3_turntable_absmap[abs];
+
+ hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs);
+ return 1;
}
return 0;
}
-static int rb4_guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
+static int instrument_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
unsigned int key = usage->hid & HID_USAGE;
- if (key >= ARRAY_SIZE(rb4_keymap))
+ if (key >= ARRAY_SIZE(instrument_keymap))
return 0;
- key = rb4_keymap[key];
+ key = instrument_keymap[key];
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
return 1;
- } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
+ }
+
+ return 0;
+}
+
+static int gh_guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) {
+ unsigned int abs = usage->hid & HID_USAGE;
+
+ if (abs == GUITAR_TILT_USAGE) {
+ hid_map_usage_clear(hi, usage, bit, max, EV_ABS, ABS_RY);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int rb4_guitar_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
unsigned int abs = usage->hid & HID_USAGE;
/* Let the HID parser deal with the HAT. */
@@ -1052,6 +1149,18 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
return 1;
}
+ /* Rock Band 3 PS3 Pro instruments set rd[24] to 0xE0 when they're
+ * sending full reports, and 0x02 when only sending navigation.
+ */
+ if ((sc->quirks & RB3_PS3_PRO_INSTRUMENT) && rd[24] == 0x02) {
+ /* Only attempt to enable report every 8 seconds */
+ if (time_after(jiffies, sc->rb3_pro_poke_jiffies)) {
+ sc->rb3_pro_poke_jiffies = jiffies +
+ (RB3_PRO_INSTRUMENT_POKE_RETRY_INTERVAL * HZ);
+ rb3_pro_instrument_enable_full_report(sc);
+ }
+ }
+
if (sc->defer_initialization) {
sc->defer_initialization = 0;
sony_schedule_work(sc, SONY_WORKER_STATE);
@@ -1065,6 +1174,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
unsigned long **bit, int *max)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
+ int ret;
if (sc->quirks & BUZZ_CONTROLLER) {
unsigned int key = usage->hid & HID_USAGE;
@@ -1098,9 +1208,19 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
if (sc->quirks & SIXAXIS_CONTROLLER)
return sixaxis_mapping(hdev, hi, field, usage, bit, max);
- if (sc->quirks & GH_GUITAR_CONTROLLER)
+ /* INSTRUMENT quirk is used as a base mapping for instruments */
+ if (sc->quirks & INSTRUMENT) {
+ ret = instrument_mapping(hdev, hi, field, usage, bit, max);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (sc->quirks & GH_GUITAR_TILT)
return gh_guitar_mapping(hdev, hi, field, usage, bit, max);
+ if (sc->quirks & PS3_DJH_TURNTABLE)
+ return djh_turntable_mapping(hdev, hi, field, usage, bit, max);
+
if (sc->quirks & (RB4_GUITAR_PS4_USB | RB4_GUITAR_PS4_BT))
return rb4_guitar_mapping(hdev, hi, field, usage, bit, max);
@@ -2060,6 +2180,19 @@ static int sony_input_configured(struct hid_device *hdev,
}
sony_init_output_report(sc, sixaxis_send_output_report);
+ } else if (sc->quirks & RB3_PS3_PRO_INSTRUMENT) {
+ /*
+ * Rock Band 3 PS3 Pro Instruments also do not handle HID Output
+ * Reports on the interrupt EP like they should, so we need to force
+ * HID output reports to use HID_REQ_SET_REPORT on the Control EP.
+ *
+ * There is also another issue about HID Output Reports via USB,
+ * these instruments do not want the report_id as part of the data
+ * packet, so we have to discard buf[0] when sending the actual
+ * control message, even for numbered reports.
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
+ hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
@@ -2227,6 +2360,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err;
}
+ if (sc->quirks & RB3_PS3_PRO_INSTRUMENT) {
+ sc->rb3_pro_poke_jiffies = 0;
+ }
+
if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
if (!hid_is_usb(hdev)) {
ret = -EINVAL;
@@ -2364,35 +2501,82 @@ static const struct hid_device_id sony_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
.driver_data = NSG_MR7U_REMOTE_BT },
/* Guitar Hero Live PS3 and Wii U guitar dongles */
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
- .driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_CONTROLLER },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE),
+ .driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_TILT | INSTRUMENT },
/* Guitar Hero PC Guitar Dongle */
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE),
- .driver_data = GH_GUITAR_CONTROLLER },
- /* Guitar Hero PS3 World Tour Guitar Dongle */
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE),
- .driver_data = GH_GUITAR_CONTROLLER },
+ .driver_data = GH_GUITAR_TILT | INSTRUMENT },
+ /* Guitar Hero PS3 Guitar Dongle */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR),
+ .driver_data = GH_GUITAR_TILT | INSTRUMENT },
+ /* Guitar Hero PS3 Drum Dongle */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS),
+ .driver_data = INSTRUMENT },
+ /* DJ Hero PS3 Guitar Dongle */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE),
+ .driver_data = PS3_DJH_TURNTABLE | INSTRUMENT },
/* Guitar Hero Live PS4 guitar dongles */
{ HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE),
- .driver_data = GHL_GUITAR_PS4 | GH_GUITAR_CONTROLLER },
+ .driver_data = GHL_GUITAR_PS4 | GH_GUITAR_TILT | INSTRUMENT },
+ /* Rock Band 1 Wii instruments */
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS),
+ .driver_data = INSTRUMENT },
+ /* Rock Band 2 Wii instruments */
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS),
+ .driver_data = INSTRUMENT },
+ /* Rock Band 3 Wii instruments */
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIER_MODE),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE),
+ .driver_data = INSTRUMENT },
+ /* Rock Band 3 PS3 instruments */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS),
+ .driver_data = INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE),
+ .driver_data = INSTRUMENT },
+ /* Rock Band 3 PS3 Pro instruments */
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR),
+ .driver_data = INSTRUMENT | RB3_PS3_PRO_INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE),
+ .driver_data = INSTRUMENT | RB3_PS3_PRO_INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIER_MODE),
+ .driver_data = INSTRUMENT | RB3_PS3_PRO_INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD),
+ .driver_data = INSTRUMENT | RB3_PS3_PRO_INSTRUMENT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE),
+ .driver_data = INSTRUMENT | RB3_PS3_PRO_INSTRUMENT },
/* Rock Band 4 PS4 guitars */
{ HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER),
- .driver_data = RB4_GUITAR_PS4_USB },
+ .driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT },
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG),
- .driver_data = RB4_GUITAR_PS4_USB },
+ .driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT },
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE),
- .driver_data = RB4_GUITAR_PS4_USB },
+ .driver_data = RB4_GUITAR_PS4_USB | INSTRUMENT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR),
- .driver_data = RB4_GUITAR_PS4_BT },
+ .driver_data = RB4_GUITAR_PS4_BT | INSTRUMENT },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER),
- .driver_data = RB4_GUITAR_PS4_BT },
+ .driver_data = RB4_GUITAR_PS4_BT | INSTRUMENT },
/* Rock Band 4 PS5 guitars */
{ HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER),
- .driver_data = RB4_GUITAR_PS5 },
+ .driver_data = RB4_GUITAR_PS5 | INSTRUMENT },
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG),
- .driver_data = RB4_GUITAR_PS5 },
+ .driver_data = RB4_GUITAR_PS5 | INSTRUMENT },
{ HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE),
- .driver_data = RB4_GUITAR_PS5 },
+ .driver_data = RB4_GUITAR_PS5 | INSTRUMENT },
{ }
};
MODULE_DEVICE_TABLE(hid, sony_devices);
@@ -2428,5 +2612,5 @@ static void __exit sony_exit(void)
module_init(sony_init);
module_exit(sony_exit);
-MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 / PS4 BD devices");
+MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 / PS4 / PS5 BD devices");
MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 0/2] PCI: Remove no_pci_devices
From: Bjorn Helgaas @ 2026-04-07 19:48 UTC (permalink / raw)
To: Heiner Kallweit
Cc: Dmitry Torokhov, Bjorn Helgaas, open list:HID CORE LAYER,
linux-pci@vger.kernel.org, Maciej W. Rozycki
In-Reply-To: <dd9ea7c1-44f5-4364-a3d5-885b1c99159f@gmail.com>
[+cc Maciej]
On Fri, Apr 03, 2026 at 12:15:34AM +0200, Heiner Kallweit wrote:
> Remove the last user of no_pci_devices(), and then the function itself.
>
> Heiner Kallweit (2):
> input: pc110pad: change PCI check to get rid of orphaned
> no_pci_devices
> PCI: Remove no_pci_devices
Applied Dmitry's patch to remove pc110pad and your patch to remove
no_pci_devices() to pci/enumeration for v7.1, thanks!
> drivers/input/mouse/pc110pad.c | 2 +-
> drivers/pci/probe.c | 17 -----------------
> include/linux/pci.h | 3 ---
> 3 files changed, 1 insertion(+), 21 deletions(-)
>
> --
> 2.53.0
>
^ permalink raw reply
* Re: [PATCH v2] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 19:22 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Antheas Kapenekakis, Pierre-Loup A. Griffais, linux-input,
linux-kernel
In-Reply-To: <CALQgdA0oYzWqFtyG2eZKgwKr-QLYz5dLbsFbpY8qDrpxK7vj8Q@mail.gmail.com>
On Tue, Apr 7, 2026 at 4:46 PM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Sashuko correctly identified issues areoud re-scheduling work that
already completed, please see:
Is there a way to test a patch against sashiko without submitting it
to the mailing list?
^ permalink raw reply
* [PATCH v7] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 19:17 UTC (permalink / raw)
To: Dmitry Torokhov, Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Sanjay Govind, Lode Willems
Cc: Antheas Kapenekakis, linux-input, linux-kernel
Xbox 360 wireless controllers expose information in the link and
capabilities reports.
Extract and use the vendor id for wireless controllers, and use
the subtype to build a nicer device name and product id.
Some xbox 360 controllers put a vid and pid into the stick capability
data, so check if this was done, and pull the vid, pid and revision from
there.
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
v2: Delay marking device as present until after capabilities or timeout
v3: Fix issues when receiving incorrect or missing link and capabilities reports
v4: Clear wireless state when processing device prescence change
v5: Fix typo, fix some potential race conditions with work scheduling
v6: Fix typo, address potential time-of-check time-of-use issues with presence work
v7: Explicitly check if the input device has been initialized already when processing presence changes
drivers/input/joystick/xpad.c | 209 ++++++++++++++++++++++++++++++----
1 file changed, 188 insertions(+), 21 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..018c186f78ef 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -68,6 +68,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
+#include <linux/unaligned.h>
#include <linux/usb/input.h>
#include <linux/usb/quirks.h>
@@ -94,6 +95,22 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+#define FLAG_FORCE_FEEDBACK 0x01
+
+#define SUBTYPE_GAMEPAD 0x01
+#define SUBTYPE_WHEEL 0x02
+#define SUBTYPE_ARCADE_STICK 0x03
+#define SUBTYPE_FLIGHT_STICK 0x04
+#define SUBTYPE_DANCE_PAD 0x05
+#define SUBTYPE_GUITAR 0x06
+#define SUBTYPE_GUITAR_ALTERNATE 0x07
+#define SUBTYPE_DRUM_KIT 0x08
+#define SUBTYPE_GUITAR_BASS 0x0B
+#define SUBTYPE_RB_KEYBOARD 0x0F
+#define SUBTYPE_ARCADE_PAD 0x13
+#define SUBTYPE_TURNTABLE 0x17
+#define SUBTYPE_PRO_GUITAR 0x19
+
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
@@ -795,8 +812,13 @@ struct usb_xpad {
int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
+ u8 sub_type;
+ u16 flags;
+ u16 wireless_vid;
+ u16 wireless_pid;
+ u16 wireless_version;
const char *name; /* name of the device */
- struct work_struct work; /* init/remove device from callback */
+ struct delayed_work work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
@@ -807,6 +829,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
static int xpad_start_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
+
/*
* xpad_process_packet
@@ -980,19 +1004,12 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
static void xpad_presence_work(struct work_struct *work)
{
- struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
int error;
-
- if (xpad->pad_present) {
- error = xpad_init_input(xpad);
- if (error) {
- /* complain only, not much else we can do here */
- dev_err(&xpad->dev->dev,
- "unable to init device: %d\n", error);
- } else {
- rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
- }
- } else {
+ /* Check if the pad presence has changed */
+ if (xpad->pad_present == xpad->input_created)
+ return;
+ if (xpad->input_created) {
RCU_INIT_POINTER(xpad->x360w_dev, NULL);
synchronize_rcu();
/*
@@ -1000,6 +1017,15 @@ static void xpad_presence_work(struct work_struct *work)
* using input device we can get rid of it.
*/
xpad_deinit_input(xpad);
+ } else {
+ error = xpad_init_input(xpad);
+ if (error) {
+ /* complain only, not much else we can do here */
+ dev_err(&xpad->intf->dev,
+ "unable to init device: %d\n", error);
+ } else {
+ rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
+ }
}
}
@@ -1017,10 +1043,11 @@ static void xpad_presence_work(struct work_struct *work)
* 01.1 - Pad state (Bytes 4+) valid
*
*/
-static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, u32 len)
{
struct input_dev *dev;
bool present;
+ u16 parsed_vid;
/* Presence change */
if (data[0] & 0x08) {
@@ -1028,7 +1055,65 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (xpad->pad_present != present) {
xpad->pad_present = present;
- schedule_work(&xpad->work);
+ if (present) {
+ /*
+ * Delay marking device as present, so we can make sure
+ * we have received all the information from the capabilities
+ * report. Some devices don't send one, so the delay
+ * guarantees that these devices are still initialized.
+ */
+ mod_delayed_work(system_percpu_wq,
+ &xpad->work, msecs_to_jiffies(500));
+ } else {
+ mod_delayed_work(system_percpu_wq, &xpad->work, 0);
+ }
+ }
+ }
+
+ /* Link report */
+ if (len >= 26 && data[0] == 0x00 && data[1] == 0x0F) {
+ xpad->sub_type = data[25] & 0x7f;
+
+ /* Decode vendor id from link report */
+ parsed_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
+
+ /*
+ * If the link report doesn't provide a proper vid, it sets the vid to 1.
+ * In that case we zero out wireless_vid, so that we fall back to the vid
+ * from the receiver instead.
+ */
+ if (parsed_vid == 1)
+ parsed_vid = 0;
+
+ /*
+ * x360w controllers on windows put the subtype into the product
+ * for wheels and gamepads, but it makes sense to do it for all
+ * subtypes. This will be used if the capabilities report
+ * doesn't provide us with a product id later.
+ */
+ xpad->wireless_vid = parsed_vid;
+ xpad->wireless_pid = 0x02a0 + xpad->sub_type;
+ xpad->wireless_version = 0;
+
+ if ((data[25] & 0x80) != 0)
+ xpad->flags |= FLAG_FORCE_FEEDBACK;
+
+ xpad_inquiry_pad_capabilities(xpad);
+ }
+
+ /* Capabilities report */
+ if (len >= 21 && data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
+ xpad->flags |= data[20];
+ /*
+ * A bunch of vendors started putting vids and pids
+ * into capabilities data because they can't be
+ * retrieved by xinput easliy.
+ * Not all of them do though, so check the vids match
+ * before extracting that info.
+ */
+ if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
+ xpad->wireless_pid = get_unaligned_le16(data + 12);
+ xpad->wireless_version = get_unaligned_le16(data + 14);
}
}
@@ -1254,7 +1339,7 @@ static void xpad_irq_in(struct urb *urb)
xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
break;
case XTYPE_XBOX360W:
- xpad360w_process_packet(xpad, 0, xpad->idata);
+ xpad360w_process_packet(xpad, 0, xpad->idata, urb->actual_length);
break;
case XTYPE_XBOXONE:
xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length);
@@ -1495,6 +1580,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
return xpad_try_sending_next_out_packet(xpad);
}
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ guard(spinlock_irqsave)(&xpad->odata_lock);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x02;
+ packet->data[3] = 0x80;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ return xpad_try_sending_next_out_packet(xpad);
+}
+
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
int error;
@@ -1892,8 +2002,8 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
{
usb_kill_urb(xpad->irq_in);
- /* Make sure we are done with presence work if it was scheduled */
- flush_work(&xpad->work);
+ /* Cancel any pending presence work */
+ cancel_delayed_work_sync(&xpad->work);
}
static int xpad_open(struct input_dev *dev)
@@ -1945,6 +2055,11 @@ static void xpad_deinit_input(struct usb_xpad *xpad)
{
if (xpad->input_created) {
xpad->input_created = false;
+ xpad->wireless_vid = 0;
+ xpad->wireless_pid = 0;
+ xpad->wireless_version = 0;
+ xpad->flags = 0;
+ xpad->sub_type = 0;
xpad_led_disconnect(xpad);
input_unregister_device(xpad->dev);
}
@@ -1965,8 +2080,60 @@ static int xpad_init_input(struct usb_xpad *xpad)
usb_to_input_id(xpad->udev, &input_dev->id);
if (xpad->xtype == XTYPE_XBOX360W) {
- /* x360w controllers and the receiver have different ids */
- input_dev->id.product = 0x02a1;
+ if (xpad->wireless_vid)
+ input_dev->id.vendor = xpad->wireless_vid;
+ if (xpad->wireless_pid)
+ input_dev->id.product = xpad->wireless_pid;
+ else
+ /* Default product id for x360w controllers */
+ input_dev->id.product = 0x02a1;
+ if (xpad->wireless_version)
+ input_dev->id.version = xpad->wireless_version;
+ switch (xpad->sub_type) {
+ case SUBTYPE_GAMEPAD:
+ input_dev->name = "Xbox 360 Wireless Controller";
+ break;
+ case SUBTYPE_WHEEL:
+ input_dev->name = "Xbox 360 Wireless Wheel";
+ break;
+ case SUBTYPE_ARCADE_STICK:
+ input_dev->name = "Xbox 360 Wireless Arcade Stick";
+ break;
+ case SUBTYPE_FLIGHT_STICK:
+ input_dev->name = "Xbox 360 Wireless Flight Stick";
+ break;
+ case SUBTYPE_DANCE_PAD:
+ input_dev->name = "Xbox 360 Wireless Dance Pad";
+ break;
+ case SUBTYPE_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Guitar";
+ break;
+ case SUBTYPE_GUITAR_ALTERNATE:
+ input_dev->name = "Xbox 360 Wireless Alternate Guitar";
+ break;
+ case SUBTYPE_GUITAR_BASS:
+ input_dev->name = "Xbox 360 Wireless Bass Guitar";
+ break;
+ case SUBTYPE_DRUM_KIT:
+ /* Vendors used force feedback flag to differentiate these */
+ if (xpad->flags & FLAG_FORCE_FEEDBACK)
+ input_dev->name = "Xbox 360 Wireless Guitar Hero Drum Kit";
+ else
+ input_dev->name = "Xbox 360 Wireless Rock Band Drum Kit";
+ break;
+ case SUBTYPE_RB_KEYBOARD:
+ input_dev->name = "Xbox 360 Wireless Rock Band Keyboard";
+ break;
+ case SUBTYPE_ARCADE_PAD:
+ input_dev->name = "Xbox 360 Wireless Arcade Pad";
+ break;
+ case SUBTYPE_TURNTABLE:
+ input_dev->name = "Xbox 360 Wireless DJ Hero Turntable";
+ break;
+ case SUBTYPE_PRO_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Rock Band Pro Guitar";
+ break;
+ }
}
input_dev->dev.parent = &xpad->intf->dev;
@@ -2106,7 +2273,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->delay_init = true;
xpad->packet_type = PKT_XB;
- INIT_WORK(&xpad->work, xpad_presence_work);
+ INIT_DELAYED_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) {
if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
--
2.53.0
^ permalink raw reply related
* [PATCH v6] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 18:20 UTC (permalink / raw)
To: Dmitry Torokhov, Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Sanjay Govind, Pierre-Loup A. Griffais
Cc: linux-input, linux-kernel
Xbox 360 wireless controllers expose information in the link and
capabilities reports.
Extract and use the vendor id for wireless controllers, and use
the subtype to build a nicer device name and product id.
Some xbox 360 controllers put a vid and pid into the stick capability
data, so check if this was done, and pull the vid, pid and revision from
there.
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
v2: Delay marking device as present until after capabilities or timeout
v3: Fix issues when receiving incorrect or missing link and capabilities reports
v4: Clear wireless state when processing device prescence change
v5: Fix typo, fix some potential race conditions with work scheduling
v6: Fix typo, address potential time-of-check time-of-use issues with presence work
drivers/input/joystick/xpad.c | 197 +++++++++++++++++++++++++++++++---
1 file changed, 183 insertions(+), 14 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..90f531bed3db 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -68,6 +68,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
+#include <linux/unaligned.h>
#include <linux/usb/input.h>
#include <linux/usb/quirks.h>
@@ -94,6 +95,22 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+#define FLAG_FORCE_FEEDBACK 0x01
+
+#define SUBTYPE_GAMEPAD 0x01
+#define SUBTYPE_WHEEL 0x02
+#define SUBTYPE_ARCADE_STICK 0x03
+#define SUBTYPE_FLIGHT_STICK 0x04
+#define SUBTYPE_DANCE_PAD 0x05
+#define SUBTYPE_GUITAR 0x06
+#define SUBTYPE_GUITAR_ALTERNATE 0x07
+#define SUBTYPE_DRUM_KIT 0x08
+#define SUBTYPE_GUITAR_BASS 0x0B
+#define SUBTYPE_RB_KEYBOARD 0x0F
+#define SUBTYPE_ARCADE_PAD 0x13
+#define SUBTYPE_TURNTABLE 0x17
+#define SUBTYPE_PRO_GUITAR 0x19
+
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
@@ -766,6 +783,7 @@ struct usb_xpad {
struct usb_device *udev; /* usb device */
struct usb_interface *intf; /* usb interface */
+ bool pending_pad_present;
bool pad_present;
bool input_created;
@@ -795,8 +813,13 @@ struct usb_xpad {
int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
+ u8 sub_type;
+ u16 flags;
+ u16 wireless_vid;
+ u16 wireless_pid;
+ u16 wireless_version;
const char *name; /* name of the device */
- struct work_struct work; /* init/remove device from callback */
+ struct delayed_work work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
@@ -807,6 +830,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
static int xpad_start_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
+
/*
* xpad_process_packet
@@ -980,15 +1005,18 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
static void xpad_presence_work(struct work_struct *work)
{
- struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
int error;
-
+ if (xpad->pad_present == xpad->pending_pad_present)
+ return;
+ xpad->pad_present = xpad->pending_pad_present;
if (xpad->pad_present) {
error = xpad_init_input(xpad);
if (error) {
/* complain only, not much else we can do here */
- dev_err(&xpad->dev->dev,
- "unable to init device: %d\n", error);
+ if (xpad->dev)
+ dev_err(&xpad->dev->dev,
+ "unable to init device: %d\n", error);
} else {
rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
}
@@ -1017,18 +1045,77 @@ static void xpad_presence_work(struct work_struct *work)
* 01.1 - Pad state (Bytes 4+) valid
*
*/
-static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, u32 len)
{
struct input_dev *dev;
bool present;
+ u16 parsed_vid;
/* Presence change */
if (data[0] & 0x08) {
present = (data[1] & 0x80) != 0;
- if (xpad->pad_present != present) {
- xpad->pad_present = present;
- schedule_work(&xpad->work);
+ if (xpad->pending_pad_present != present) {
+ xpad->pending_pad_present = present;
+ if (present) {
+ /*
+ * Delay marking device as present, so we can make sure
+ * we have received all the information from the capabilities
+ * report. Some devices don't send one, so the delay
+ * guarantees that these devices are still initialized.
+ */
+ mod_delayed_work(system_percpu_wq,
+ &xpad->work, msecs_to_jiffies(500));
+ } else {
+ mod_delayed_work(system_percpu_wq, &xpad->work, 0);
+ }
+ }
+ }
+
+ /* Link report */
+ if (len >= 26 && data[0] == 0x00 && data[1] == 0x0F) {
+ xpad->sub_type = data[25] & 0x7f;
+
+ /* Decode vendor id from link report */
+ parsed_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
+
+ /*
+ * If the link report doesn't provide a proper vid, it sets the vid to 1.
+ * In that case we zero out wireless_vid, so that we fall back to the vid
+ * from the receiver instead.
+ */
+ if (parsed_vid == 1)
+ parsed_vid = 0;
+
+ /*
+ * x360w controllers on windows put the subtype into the product
+ * for wheels and gamepads, but it makes sense to do it for all
+ * subtypes. This will be used if the capabilities report
+ * doesn't provide us with a product id later.
+ */
+ xpad->wireless_vid = parsed_vid;
+ xpad->wireless_pid = 0x02a0 + xpad->sub_type;
+ xpad->wireless_version = 0;
+
+ if ((data[25] & 0x80) != 0)
+ xpad->flags |= FLAG_FORCE_FEEDBACK;
+
+ xpad_inquiry_pad_capabilities(xpad);
+ }
+
+ /* Capabilities report */
+ if (len >= 21 && data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
+ xpad->flags |= data[20];
+ /*
+ * A bunch of vendors started putting vids and pids
+ * into capabilities data because they can't be
+ * retrieved by xinput easliy.
+ * Not all of them do though, so check the vids match
+ * before extracting that info.
+ */
+ if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
+ xpad->wireless_pid = get_unaligned_le16(data + 12);
+ xpad->wireless_version = get_unaligned_le16(data + 14);
}
}
@@ -1254,7 +1341,7 @@ static void xpad_irq_in(struct urb *urb)
xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
break;
case XTYPE_XBOX360W:
- xpad360w_process_packet(xpad, 0, xpad->idata);
+ xpad360w_process_packet(xpad, 0, xpad->idata, urb->actual_length);
break;
case XTYPE_XBOXONE:
xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length);
@@ -1495,6 +1582,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
return xpad_try_sending_next_out_packet(xpad);
}
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ guard(spinlock_irqsave)(&xpad->odata_lock);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x02;
+ packet->data[3] = 0x80;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ return xpad_try_sending_next_out_packet(xpad);
+}
+
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
int error;
@@ -1893,7 +2005,7 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
usb_kill_urb(xpad->irq_in);
/* Make sure we are done with presence work if it was scheduled */
- flush_work(&xpad->work);
+ cancel_delayed_work_sync(&xpad->work);
}
static int xpad_open(struct input_dev *dev)
@@ -1945,6 +2057,11 @@ static void xpad_deinit_input(struct usb_xpad *xpad)
{
if (xpad->input_created) {
xpad->input_created = false;
+ xpad->wireless_vid = 0;
+ xpad->wireless_pid = 0;
+ xpad->wireless_version = 0;
+ xpad->flags = 0;
+ xpad->sub_type = 0;
xpad_led_disconnect(xpad);
input_unregister_device(xpad->dev);
}
@@ -1965,8 +2082,60 @@ static int xpad_init_input(struct usb_xpad *xpad)
usb_to_input_id(xpad->udev, &input_dev->id);
if (xpad->xtype == XTYPE_XBOX360W) {
- /* x360w controllers and the receiver have different ids */
- input_dev->id.product = 0x02a1;
+ if (xpad->wireless_vid)
+ input_dev->id.vendor = xpad->wireless_vid;
+ if (xpad->wireless_pid)
+ input_dev->id.product = xpad->wireless_pid;
+ else
+ /* Default product id for x360w controllers */
+ input_dev->id.product = 0x02a1;
+ if (xpad->wireless_version)
+ input_dev->id.version = xpad->wireless_version;
+ switch (xpad->sub_type) {
+ case SUBTYPE_GAMEPAD:
+ input_dev->name = "Xbox 360 Wireless Controller";
+ break;
+ case SUBTYPE_WHEEL:
+ input_dev->name = "Xbox 360 Wireless Wheel";
+ break;
+ case SUBTYPE_ARCADE_STICK:
+ input_dev->name = "Xbox 360 Wireless Arcade Stick";
+ break;
+ case SUBTYPE_FLIGHT_STICK:
+ input_dev->name = "Xbox 360 Wireless Flight Stick";
+ break;
+ case SUBTYPE_DANCE_PAD:
+ input_dev->name = "Xbox 360 Wireless Dance Pad";
+ break;
+ case SUBTYPE_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Guitar";
+ break;
+ case SUBTYPE_GUITAR_ALTERNATE:
+ input_dev->name = "Xbox 360 Wireless Alternate Guitar";
+ break;
+ case SUBTYPE_GUITAR_BASS:
+ input_dev->name = "Xbox 360 Wireless Bass Guitar";
+ break;
+ case SUBTYPE_DRUM_KIT:
+ /* Vendors used force feedback flag to differentiate these */
+ if (xpad->flags & FLAG_FORCE_FEEDBACK)
+ input_dev->name = "Xbox 360 Wireless Guitar Hero Drum Kit";
+ else
+ input_dev->name = "Xbox 360 Wireless Rock Band Drum Kit";
+ break;
+ case SUBTYPE_RB_KEYBOARD:
+ input_dev->name = "Xbox 360 Wireless Rock Band Keyboard";
+ break;
+ case SUBTYPE_ARCADE_PAD:
+ input_dev->name = "Xbox 360 Wireless Arcade Pad";
+ break;
+ case SUBTYPE_TURNTABLE:
+ input_dev->name = "Xbox 360 Wireless DJ Hero Turntable";
+ break;
+ case SUBTYPE_PRO_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Rock Band Pro Guitar";
+ break;
+ }
}
input_dev->dev.parent = &xpad->intf->dev;
@@ -2106,7 +2275,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->delay_init = true;
xpad->packet_type = PKT_XB;
- INIT_WORK(&xpad->work, xpad_presence_work);
+ INIT_DELAYED_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) {
if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
--
2.53.0
^ permalink raw reply related
* [PATCH] HID: core: add short report quirk and use it for GPD Win (2f24:0137)
From: Zhouwang Huang @ 2026-04-07 17:32 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires, honjow
Cc: denis.benato, linux-kernel, linux-input
Commit 9e2a17d2e808 ("HID: gpd: fix report descriptor on GPD Win
handheld (2f24:0137)") used report_fixup to shrink Report Count from
63 to 11 so that short reports from firmware <= 1.09 would not be
rejected by hid_report_raw_event().
However, firmware 1.10 fixed the report length and now sends the full
63 bytes. Because report_fixup already shrank the descriptor,
usbhid allocates a 12-byte URB buffer — far too small for the 64-byte
transfer — causing continuous -EOVERFLOW on every interrupt-in URB.
The HID report descriptor and bcdDevice are identical across firmware
versions, so report_fixup cannot conditionally apply.
Replace the report_fixup driver with a new per-device quirk
HID_QUIRK_ALLOW_SHORT_REPORTS. When set, hid_report_raw_event()
zero-pads short reports instead of rejecting them — the same behaviour
the core had before commit 0a3fe972a7cb ("HID: core: Mitigate
potential OOB by removing bogus memset()"). The descriptor is left
unmodified so the URB buffer matches the declared report size and works
with any firmware version.
Remove hid-gpd.c, its Kconfig entry and Makefile line; the device is
now handled by hid-generic with the quirk applied from hid-quirks.c.
Fixes: 9e2a17d2e808 ("HID: gpd: fix report descriptor on GPD Win handheld (2f24:0137)")
Signed-off-by: Zhouwang Huang <honjow311@gmail.com>
---
drivers/hid/Kconfig | 10 --------
drivers/hid/Makefile | 1 -
drivers/hid/hid-core.c | 11 +++++----
drivers/hid/hid-gpd.c | 52 ----------------------------------------
drivers/hid/hid-quirks.c | 2 ++
include/linux/hid.h | 2 ++
6 files changed, 11 insertions(+), 67 deletions(-)
delete mode 100644 drivers/hid/hid-gpd.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 2159d0fb7020..c1d9f7c6a5f2 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -419,16 +419,6 @@ config HID_GLORIOUS
Support for Glorious PC Gaming Race mice such as
the Glorious Model O, O- and D.
-config HID_GPD
- tristate "GPD Win handheld OEM HID support"
- depends on USB_HID
- help
- Report descriptor fix for the OEM USB HID interface (GameSir
- 2f24:0137) found on GPD Win handhelds. The firmware declares 63-byte
- reports but only sends 11 bytes, which the HID core rejects.
-
- Say Y or M here if you use a GPD Win handheld with this interface.
-
config HID_HOLTEK
tristate "Holtek HID devices"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index f69cd6015465..e01838239ae6 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -53,7 +53,6 @@ obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EVISION) += hid-evision.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_FT260) += hid-ft260.o
-obj-$(CONFIG_HID_GPD) += hid-gpd.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f5587b786f87..52e86f927a38 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2057,10 +2057,13 @@ int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *
rsize = max_buffer_size;
if (csize < rsize) {
- hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
- report->id, rsize, csize);
- ret = -EINVAL;
- goto out;
+ if (!(hid->quirks & HID_QUIRK_ALLOW_SHORT_REPORTS)) {
+ hid_warn_ratelimited(hid, "Event data for report %d was too short (%d vs %d)\n",
+ report->id, rsize, csize);
+ ret = -EINVAL;
+ goto out;
+ }
+ memset(cdata + csize, 0, rsize - csize);
}
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
diff --git a/drivers/hid/hid-gpd.c b/drivers/hid/hid-gpd.c
deleted file mode 100644
index 5b4d203e2499..000000000000
--- a/drivers/hid/hid-gpd.c
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * HID report descriptor fixup for GPD Win handhelds.
- *
- * The OEM HID interface (VID 2f24 / GameSir, PID 0137) declares Report ID 1
- * with Report Count 63 (8-bit fields) for both Input and Feature, but the
- * firmware only sends 11 bytes of payload after the report ID.
- */
-
-#include <linux/hid.h>
-#include <linux/module.h>
-
-#include "hid-ids.h"
-
-#define RDESC_LEN 38
-#define RPT_COUNT_INPUT_OFF 21
-#define RPT_COUNT_FEATURE_OFF 34
-
-static const __u8 *gpd_report_fixup(struct hid_device *hdev, __u8 *rdesc,
- unsigned int *rsize)
-{
- if (*rsize != RDESC_LEN)
- return rdesc;
-
- if (rdesc[RPT_COUNT_INPUT_OFF - 1] == 0x95 &&
- rdesc[RPT_COUNT_INPUT_OFF] == 0x3f &&
- rdesc[RPT_COUNT_FEATURE_OFF - 1] == 0x95 &&
- rdesc[RPT_COUNT_FEATURE_OFF] == 0x3f) {
- hid_info(hdev, "fixing report counts (63 -> 11 bytes)\n");
- rdesc[RPT_COUNT_INPUT_OFF] = 11;
- rdesc[RPT_COUNT_FEATURE_OFF] = 11;
- }
-
- return rdesc;
-}
-
-static const struct hid_device_id gpd_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_GAMESIR, USB_DEVICE_ID_GAMESIR_0137) },
- { }
-};
-MODULE_DEVICE_TABLE(hid, gpd_devices);
-
-static struct hid_driver gpd_driver = {
- .name = "gpd",
- .id_table = gpd_devices,
- .report_fixup = gpd_report_fixup,
-};
-
-module_hid_driver(gpd_driver);
-
-MODULE_DESCRIPTION("HID report descriptor fix for GPD Win handheld (GameSir 2f24:0137)");
-MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index f6be3ffee023..b9ae1442eba9 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -97,6 +97,8 @@ static const struct hid_device_id hid_quirks[] = {
HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMEVICE, USB_DEVICE_ID_GAMEVICE_KISHI),
HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GAMESIR, USB_DEVICE_ID_GAMESIR_0137),
+ HID_QUIRK_ALLOW_SHORT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING), HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING), HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING), HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 31324609af4d..212dd13bfcfa 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -381,6 +381,7 @@ struct hid_item {
* | @HID_QUIRK_X_INVERT:
* | @HID_QUIRK_Y_INVERT:
* | @HID_QUIRK_IGNORE_MOUSE:
+ * | @HID_QUIRK_ALLOW_SHORT_REPORTS: accept shorter-than-expected reports, zero-pad
* | @HID_QUIRK_SKIP_OUTPUT_REPORTS:
* | @HID_QUIRK_SKIP_OUTPUT_REPORT_ID:
* | @HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP:
@@ -408,6 +409,7 @@ struct hid_item {
#define HID_QUIRK_X_INVERT BIT(12)
#define HID_QUIRK_Y_INVERT BIT(13)
#define HID_QUIRK_IGNORE_MOUSE BIT(14)
+#define HID_QUIRK_ALLOW_SHORT_REPORTS BIT(15)
#define HID_QUIRK_SKIP_OUTPUT_REPORTS BIT(16)
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID BIT(17)
#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP BIT(18)
--
2.53.0
^ permalink raw reply related
* [PATCH] HID: quirks: update hid-sony supported devices
From: Rosalie Wanders @ 2026-04-07 14:42 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Rosalie Wanders, linux-input, linux-kernel
hid-sony has been updated with new device support, update the
hid_have_special_driver table accordingly.
Signed-off-by: Rosalie Wanders <rosalie@mailbox.org>
---
drivers/hid/hid-quirks.c | 48 ++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index edc4339adb50..17639d340efc 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -690,22 +690,56 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
#endif
#if IS_ENABLED(CONFIG_HID_SONY)
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS4_GIBSON_SG_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_CRKD, USB_DEVICE_ID_CRKD_PS5_GIBSON_SG_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB1_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB2_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_DRUMS_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_MUSTANG_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_SQUIRE_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_MUSTANG_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_HARMONIX, USB_DEVICE_ID_HARMONIX_WII_RB3_SQUIRE_GUITAR) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_PS4_STRATOCASTER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_JAGUAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS4_RIFFMASTER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PDP, USB_DEVICE_ID_PDP_PS5_RIFFMASTER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_DJH_TURNTABLE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GH_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_KEYBOARD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_DRUMS_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_KEYBOARD_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_MUSTANG_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MPA_SQUIRE_MODE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_MUSTANG_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB3_SQUIRE_GUITAR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_DRUMS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_RB_GUITAR) },
#endif
#if IS_ENABLED(CONFIG_HID_SPEEDLINK)
{ HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2 1/2] HID: logitech-dj: Standardise hid_report_enum variable nomenclature
From: Lee Jones @ 2026-04-07 13:59 UTC (permalink / raw)
To: Filipe Laíns, Jiri Kosina, Benjamin Tissoires, linux-input,
linux-kernel
In-Reply-To: <20260324143651.342273-1-lee@kernel.org>
On Tue, 24 Mar 2026, Lee Jones wrote:
> Since we will need to differentiate between the two report_enum types
> soon, let's unify the naming conventions now to save confusion and/or
> unnecessary/unrelated changes in upcoming commits.
>
> {input,output}_report_enum is used in other places to let's conform.
>
> Signed-off-by: Lee Jones <lee@kernel.org>
> ---
> v1 => v2: New patch
>
> drivers/hid/hid-logitech-dj.c | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
During a previous submission you indicated that you preferred pings over
[RESEND]s - so this is it.
This submission was posted 2 weeks ago. Could someone take a look please?
> diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
> index 44b716697510..32139b2561c0 100644
> --- a/drivers/hid/hid-logitech-dj.c
> +++ b/drivers/hid/hid-logitech-dj.c
> @@ -1858,7 +1858,7 @@ static int logi_dj_raw_event(struct hid_device *hdev,
> static int logi_dj_probe(struct hid_device *hdev,
> const struct hid_device_id *id)
> {
> - struct hid_report_enum *rep_enum;
> + struct hid_report_enum *input_report_enum;
> struct hid_report *rep;
> struct dj_receiver_dev *djrcv_dev;
> struct usb_interface *intf;
> @@ -1903,10 +1903,10 @@ static int logi_dj_probe(struct hid_device *hdev,
> }
> }
>
> - rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
> + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT];
>
> /* no input reports, bail out */
> - if (list_empty(&rep_enum->report_list))
> + if (list_empty(&input_report_enum->report_list))
> return -ENODEV;
>
> /*
> @@ -1914,7 +1914,7 @@ static int logi_dj_probe(struct hid_device *hdev,
> * Note: we should theoretically check for HID++ and DJ
> * collections, but this will do.
> */
> - list_for_each_entry(rep, &rep_enum->report_list, list) {
> + list_for_each_entry(rep, &input_report_enum->report_list, list) {
> if (rep->application == 0xff000001)
> has_hidpp = true;
> }
> @@ -1927,7 +1927,7 @@ static int logi_dj_probe(struct hid_device *hdev,
> return -ENODEV;
>
> /* get the current application attached to the node */
> - rep = list_first_entry(&rep_enum->report_list, struct hid_report, list);
> + rep = list_first_entry(&input_report_enum->report_list, struct hid_report, list);
> djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data,
> rep->application, has_hidpp);
> if (!djrcv_dev) {
> @@ -1935,7 +1935,7 @@ static int logi_dj_probe(struct hid_device *hdev,
> return -ENOMEM;
> }
>
> - if (!rep_enum->numbered)
> + if (!input_report_enum->numbered)
> djrcv_dev->unnumbered_application = rep->application;
>
> /* Starts the usb device and connects to upper interfaces hiddev and
> --
> 2.53.0.983.g0bb29b3bc5-goog
>
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH WIP v3 11/11] arm64: dts: qcom: sdm845-google: Add STM FTS touchscreen support
From: Konrad Dybcio @ 2026-04-07 10:40 UTC (permalink / raw)
To: david, Dmitry Torokhov, Maxime Coquelin, Alexandre Torgue,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Henrik Rydberg,
Bjorn Andersson, Konrad Dybcio
Cc: Petr Hodina, linux-input, linux-stm32, linux-arm-kernel,
linux-kernel, Krzysztof Kozlowski, devicetree, linux-arm-msm,
phone-devel
In-Reply-To: <20260403-stmfts5-v3-11-5da768cfd201@ixit.cz>
On 4/3/26 7:08 PM, David Heidelberg via B4 Relay wrote:
> From: Petr Hodina <petr.hodina@protonmail.com>
>
> Basic touchscreen connected to second i2c bus.
I was really hoping for an advanced touchscreen!
> Signed-off-by: Petr Hodina <petr.hodina@protonmail.com>
> Co-developed-by: David Heidelberg <david@ixit.cz>
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Konrad
^ permalink raw reply
* [PATCH v5] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 9:51 UTC (permalink / raw)
To: Dmitry Torokhov, Vicki Pfau, Mario Limonciello, Sanjay Govind,
Nilton Perim Neto, Lode Willems
Cc: Antheas Kapenekakis, linux-input, linux-kernel
Xbox 360 wireless controllers expose information in the link and
capabilities reports.
Extract and use the vendor id for wireless controllers, and use
the subtype to build a nicer device name and product id.
Some xbox 360 controllers put a vid and pid into the stick capability
data, so check if this was done, and pull the vid, pid and revision from
there.
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
v2: Delay marking device as present until after capabilities or timeout
v3: Fix issues when receiving incorrect or missing link and capabilities reports
v4: Clear wireless state when processing device prescence change
v5: Fix typo, fix some potential race conditions with work scheduling
drivers/input/joystick/xpad.c | 180 ++++++++++++++++++++++++++++++++--
1 file changed, 171 insertions(+), 9 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..6f8d87ce08a0 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -68,6 +68,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
+#include <linux/unaligned.h>
#include <linux/usb/input.h>
#include <linux/usb/quirks.h>
@@ -94,6 +95,22 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+#define FLAG_FORCE_FEEDBACK 0x01
+
+#define SUBTYPE_GAMEPAD 0x01
+#define SUBTYPE_WHEEL 0x02
+#define SUBTYPE_ARCADE_STICK 0x03
+#define SUBTYPE_FLIGHT_STICK 0x04
+#define SUBTYPE_DANCE_PAD 0x05
+#define SUBTYPE_GUITAR 0x06
+#define SUBTYPE_GUITAR_ALTERNATE 0x07
+#define SUBTYPE_DRUM_KIT 0x08
+#define SUBTYPE_GUITAR_BASS 0x0B
+#define SUBTYPE_RB_KEYBOARD 0x0F
+#define SUBTYPE_ARCADE_PAD 0x13
+#define SUBTYPE_TURNTABLE 0x17
+#define SUBTYPE_PRO_GUITAR 0x19
+
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
@@ -795,8 +812,13 @@ struct usb_xpad {
int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
+ u8 sub_type;
+ u16 flags;
+ u16 wireless_vid;
+ u16 wireless_pid;
+ u16 wireless_version;
const char *name; /* name of the device */
- struct work_struct work; /* init/remove device from callback */
+ struct delayed_work work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
@@ -807,6 +829,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
static int xpad_start_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
+
/*
* xpad_process_packet
@@ -980,7 +1004,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
static void xpad_presence_work(struct work_struct *work)
{
- struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
int error;
if (xpad->pad_present) {
@@ -1017,7 +1041,7 @@ static void xpad_presence_work(struct work_struct *work)
* 01.1 - Pad state (Bytes 4+) valid
*
*/
-static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, u32 len)
{
struct input_dev *dev;
bool present;
@@ -1028,7 +1052,68 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (xpad->pad_present != present) {
xpad->pad_present = present;
- schedule_work(&xpad->work);
+ xpad->wireless_vid = 0;
+ xpad->wireless_pid = 0;
+ xpad->wireless_version = 0;
+ xpad->flags = 0;
+ xpad->sub_type = 0;
+ if (present) {
+ /*
+ * Delay marking device as present, so we can make sure
+ * we have received all the information from the capabilities
+ * report. Some devices don't send one, so the delay
+ * guarantees that these devices are still initialized.
+ */
+ mod_delayed_work(system_percpu_wq,
+ &xpad->work, msecs_to_jiffies(500));
+ } else {
+ mod_delayed_work(system_percpu_wq, &xpad->work, 0);
+ }
+ }
+ }
+
+ /* Link report */
+ if (len >= 26 && data[0] == 0x00 && data[1] == 0x0F) {
+ xpad->sub_type = data[25] & 0x7f;
+
+ /* Decode vendor id from link report */
+ xpad->wireless_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
+
+ /*
+ * If the link report doesn't provide a proper vid, it sets the vid to 1.
+ * In that case we zero out wireless_vid, so that we fall back to the vid
+ * from the receiver instead.
+ */
+ if (xpad->wireless_vid == 1)
+ xpad->wireless_vid = 0;
+ /*
+ * x360w controllers on windows put the subtype into the product
+ * for wheels and gamepads, but it makes sense to do it for all
+ * subtypes. This will be used if the capabilities report
+ * doesn't provide us with a product id later.
+ */
+ xpad->wireless_pid = 0x02a0 + xpad->sub_type;
+ xpad->wireless_version = 0;
+
+ if ((data[25] & 0x80) != 0)
+ xpad->flags |= FLAG_FORCE_FEEDBACK;
+
+ xpad_inquiry_pad_capabilities(xpad);
+ }
+
+ /* Capabilities report */
+ if (len >= 21 && data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
+ xpad->flags |= data[20];
+ /*
+ * A bunch of vendors started putting vids and pids
+ * into capabilities data because they can't be
+ * retrieved by xinput easliy.
+ * Not all of them do though, so check the vids match
+ * before extracting that info.
+ */
+ if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
+ xpad->wireless_pid = get_unaligned_le16(data + 12);
+ xpad->wireless_version = get_unaligned_le16(data + 14);
}
}
@@ -1254,7 +1339,7 @@ static void xpad_irq_in(struct urb *urb)
xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
break;
case XTYPE_XBOX360W:
- xpad360w_process_packet(xpad, 0, xpad->idata);
+ xpad360w_process_packet(xpad, 0, xpad->idata, urb->actual_length);
break;
case XTYPE_XBOXONE:
xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length);
@@ -1495,6 +1580,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
return xpad_try_sending_next_out_packet(xpad);
}
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ guard(spinlock_irqsave)(&xpad->odata_lock);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x02;
+ packet->data[3] = 0x80;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ return xpad_try_sending_next_out_packet(xpad);
+}
+
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
int error;
@@ -1893,7 +2003,7 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
usb_kill_urb(xpad->irq_in);
/* Make sure we are done with presence work if it was scheduled */
- flush_work(&xpad->work);
+ cancel_delayed_work_sync(&xpad->work);
}
static int xpad_open(struct input_dev *dev)
@@ -1965,8 +2075,60 @@ static int xpad_init_input(struct usb_xpad *xpad)
usb_to_input_id(xpad->udev, &input_dev->id);
if (xpad->xtype == XTYPE_XBOX360W) {
- /* x360w controllers and the receiver have different ids */
- input_dev->id.product = 0x02a1;
+ if (xpad->wireless_vid)
+ input_dev->id.vendor = xpad->wireless_vid;
+ if (xpad->wireless_pid)
+ input_dev->id.product = xpad->wireless_pid;
+ else
+ /* Default product id for x360w controllers */
+ input_dev->id.product = 0x02a1;
+ if (xpad->wireless_version)
+ input_dev->id.version = xpad->wireless_version;
+ switch (xpad->sub_type) {
+ case SUBTYPE_GAMEPAD:
+ input_dev->name = "Xbox 360 Wireless Controller";
+ break;
+ case SUBTYPE_WHEEL:
+ input_dev->name = "Xbox 360 Wireless Wheel";
+ break;
+ case SUBTYPE_ARCADE_STICK:
+ input_dev->name = "Xbox 360 Wireless Arcade Stick";
+ break;
+ case SUBTYPE_FLIGHT_SICK:
+ input_dev->name = "Xbox 360 Wireless Flight Stick";
+ break;
+ case SUBTYPE_DANCE_PAD:
+ input_dev->name = "Xbox 360 Wireless Dance Pad";
+ break;
+ case SUBTYPE_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Guitar";
+ break;
+ case SUBTYPE_GUITAR_ALTERNATE:
+ input_dev->name = "Xbox 360 Wireless Alternate Guitar";
+ break;
+ case SUBTYPE_GUITAR_BASS:
+ input_dev->name = "Xbox 360 Wireless Bass Guitar";
+ break;
+ case SUBTYPE_DRUM_KIT:
+ /* Vendors used force feedback flag to differentiate these */
+ if (xpad->flags & FLAG_FORCE_FEEDBACK)
+ input_dev->name = "Xbox 360 Wireless Guitar Hero Drum Kit";
+ else
+ input_dev->name = "Xbox 360 Wireless Rock Band Drum Kit";
+ break;
+ case SUBTYPE_RB_KEYBOARD:
+ input_dev->name = "Xbox 360 Wireless Rock Band Keyboard";
+ break;
+ case SUBTYPE_ARCADE_PAD:
+ input_dev->name = "Xbox 360 Wireless Arcade Pad";
+ break;
+ case SUBTYPE_TURNTABLE:
+ input_dev->name = "Xbox 360 Wireless DJ Hero Turntable";
+ break;
+ case SUBTYPE_PRO_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Rock Band Pro Guitar";
+ break;
+ }
}
input_dev->dev.parent = &xpad->intf->dev;
@@ -2106,7 +2268,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->delay_init = true;
xpad->packet_type = PKT_XB;
- INIT_WORK(&xpad->work, xpad_presence_work);
+ INIT_DELAYED_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) {
if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH] Input: uinput - fix circular locking dependency with ff-core
From: Mikhail Gavrilov @ 2026-04-07 8:39 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <adSSoVZBLs8b6I0J@google.com>
On Tue, Apr 7, 2026 at 10:19 AM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
>
> I was talking about taking this new lock in both uinput_request_send()
> as well as in uinput_destroy_device() when updating the state. With that
> requests_lock will be taken only in uinput_request_alloc_id(),
> uinput_request_release_slot(), and uinput_flush_requests().
Hi Dmitry,
I've sent v2 as a separate thread with a dedicated state_lock
spinlock as you suggested, along with Fixes and Cc: stable tags.
https://lore.kernel.org/all/20260407075031.38351-1-mikhail.v.gavrilov@gmail.com/
--
Best Regards,
Mike Gavrilov.
^ permalink raw reply
* [PATCH v4] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 8:13 UTC (permalink / raw)
To: Dmitry Torokhov, Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Sanjay Govind
Cc: Antheas Kapenekakis, linux-input, linux-kernel
Xbox 360 wireless controllers expose information in the link and
capabilities reports.
Extract and use the vendor id for wireless controllers, and use
the subtype to build a nicer device name and product id.
Some xbox 360 controllers put a vid and pid into the stick capability
data, so check if this was done, and pull the vid, pid and revision from
there.
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
v2: Delay marking device as present until after capabilities or timeout
v3: Fix issues when receiving incorrect or missing link and capabilities reports
v4: Clear wireless state when processing device prescence change
drivers/input/joystick/xpad.c | 179 ++++++++++++++++++++++++++++++++--
1 file changed, 170 insertions(+), 9 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..3538c7e55c6d 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -68,6 +68,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
+#include <linux/unaligned.h>
#include <linux/usb/input.h>
#include <linux/usb/quirks.h>
@@ -94,6 +95,22 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+#define FLAG_FORCE_FEEDBACK 0x01
+
+#define SUBTYPE_GAMEPAD 0x01
+#define SUBTYPE_WHEEL 0x02
+#define SUBTYPE_ARCADE_STICK 0x03
+#define SUBTYPE_FLIGHT_SICK 0x04
+#define SUBTYPE_DANCE_PAD 0x05
+#define SUBTYPE_GUITAR 0x06
+#define SUBTYPE_GUITAR_ALTERNATE 0x07
+#define SUBTYPE_DRUM_KIT 0x08
+#define SUBTYPE_GUITAR_BASS 0x0B
+#define SUBTYPE_RB_KEYBOARD 0x0F
+#define SUBTYPE_ARCADE_PAD 0x13
+#define SUBTYPE_TURNTABLE 0x17
+#define SUBTYPE_PRO_GUITAR 0x19
+
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
@@ -795,8 +812,13 @@ struct usb_xpad {
int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
+ u8 sub_type;
+ u16 flags;
+ u16 wireless_vid;
+ u16 wireless_pid;
+ u16 wireless_version;
const char *name; /* name of the device */
- struct work_struct work; /* init/remove device from callback */
+ struct delayed_work work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
@@ -807,6 +829,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
static int xpad_start_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
+
/*
* xpad_process_packet
@@ -980,7 +1004,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
static void xpad_presence_work(struct work_struct *work)
{
- struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
int error;
if (xpad->pad_present) {
@@ -1017,7 +1041,7 @@ static void xpad_presence_work(struct work_struct *work)
* 01.1 - Pad state (Bytes 4+) valid
*
*/
-static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, u32 len)
{
struct input_dev *dev;
bool present;
@@ -1028,7 +1052,67 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (xpad->pad_present != present) {
xpad->pad_present = present;
- schedule_work(&xpad->work);
+ xpad->wireless_vid = 0;
+ xpad->wireless_pid = 0;
+ xpad->wireless_version = 0;
+ xpad->flags = 0;
+ xpad->sub_type = 0;
+ if (present) {
+ /*
+ * Delay marking device as present, so we can make sure
+ * we have received all the information from the capabilities
+ * report. Some devices don't send one, so the delay
+ * guarantees that these devices are still initialized.
+ */
+ schedule_delayed_work(&xpad->work, msecs_to_jiffies(500));
+ } else {
+ schedule_delayed_work(&xpad->work, 0);
+ }
+ }
+ }
+
+ /* Link report */
+ if (data[0] == 0x00 && data[1] == 0x0F && len >= 26) {
+ xpad->sub_type = data[25] & 0x7f;
+
+ /* Decode vendor id from link report */
+ xpad->wireless_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
+
+ /*
+ * If the link report doesn't provide a proper vid, it sets the vid to 1.
+ * In that case we zero out wireless_vid, so that we fall back to the vid
+ * from the receiver instead.
+ */
+ if (xpad->wireless_vid == 1)
+ xpad->wireless_vid = 0;
+ /*
+ * x360w controllers on windows put the subtype into the product
+ * for wheels and gamepads, but it makes sense to do it for all
+ * subtypes. This will be used if the capabilities report
+ * doesn't provide us with a product id later.
+ */
+ xpad->wireless_pid = 0x02a0 + xpad->sub_type;
+ xpad->wireless_version = 0;
+
+ if ((data[25] & 0x80) != 0)
+ xpad->flags |= FLAG_FORCE_FEEDBACK;
+
+ xpad_inquiry_pad_capabilities(xpad);
+ }
+
+ /* Capabilities report */
+ if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12 && len >= 21) {
+ xpad->flags |= data[20];
+ /*
+ * A bunch of vendors started putting vids and pids
+ * into capabilities data because they can't be
+ * retrieved by xinput easliy.
+ * Not all of them do though, so check the vids match
+ * before extracting that info.
+ */
+ if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
+ xpad->wireless_pid = get_unaligned_le16(data + 12);
+ xpad->wireless_version = get_unaligned_le16(data + 14);
}
}
@@ -1254,7 +1338,7 @@ static void xpad_irq_in(struct urb *urb)
xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
break;
case XTYPE_XBOX360W:
- xpad360w_process_packet(xpad, 0, xpad->idata);
+ xpad360w_process_packet(xpad, 0, xpad->idata, urb->actual_length);
break;
case XTYPE_XBOXONE:
xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length);
@@ -1495,6 +1579,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
return xpad_try_sending_next_out_packet(xpad);
}
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ guard(spinlock_irqsave)(&xpad->odata_lock);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x02;
+ packet->data[3] = 0x80;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ return xpad_try_sending_next_out_packet(xpad);
+}
+
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
int error;
@@ -1893,7 +2002,7 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
usb_kill_urb(xpad->irq_in);
/* Make sure we are done with presence work if it was scheduled */
- flush_work(&xpad->work);
+ flush_delayed_work(&xpad->work);
}
static int xpad_open(struct input_dev *dev)
@@ -1965,8 +2074,60 @@ static int xpad_init_input(struct usb_xpad *xpad)
usb_to_input_id(xpad->udev, &input_dev->id);
if (xpad->xtype == XTYPE_XBOX360W) {
- /* x360w controllers and the receiver have different ids */
- input_dev->id.product = 0x02a1;
+ if (xpad->wireless_vid)
+ input_dev->id.vendor = xpad->wireless_vid;
+ if (xpad->wireless_pid)
+ input_dev->id.product = xpad->wireless_pid;
+ else
+ /* Default product id for x360w controllers */
+ input_dev->id.product = 0x02a1;
+ if (xpad->wireless_version)
+ input_dev->id.version = xpad->wireless_version;
+ switch (xpad->sub_type) {
+ case SUBTYPE_GAMEPAD:
+ input_dev->name = "Xbox 360 Wireless Controller";
+ break;
+ case SUBTYPE_WHEEL:
+ input_dev->name = "Xbox 360 Wireless Wheel";
+ break;
+ case SUBTYPE_ARCADE_STICK:
+ input_dev->name = "Xbox 360 Wireless Arcade Stick";
+ break;
+ case SUBTYPE_FLIGHT_SICK:
+ input_dev->name = "Xbox 360 Wireless Flight Stick";
+ break;
+ case SUBTYPE_DANCE_PAD:
+ input_dev->name = "Xbox 360 Wireless Dance Pad";
+ break;
+ case SUBTYPE_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Guitar";
+ break;
+ case SUBTYPE_GUITAR_ALTERNATE:
+ input_dev->name = "Xbox 360 Wireless Alternate Guitar";
+ break;
+ case SUBTYPE_GUITAR_BASS:
+ input_dev->name = "Xbox 360 Wireless Bass Guitar";
+ break;
+ case SUBTYPE_DRUM_KIT:
+ /* Vendors used force feedback flag to differentiate these */
+ if (xpad->flags & FLAG_FORCE_FEEDBACK)
+ input_dev->name = "Xbox 360 Wireless Guitar Hero Drum Kit";
+ else
+ input_dev->name = "Xbox 360 Wireless Rock Band Drum Kit";
+ break;
+ case SUBTYPE_RB_KEYBOARD:
+ input_dev->name = "Xbox 360 Wireless Rock Band Keyboard";
+ break;
+ case SUBTYPE_ARCADE_PAD:
+ input_dev->name = "Xbox 360 Wireless Arcade Pad";
+ break;
+ case SUBTYPE_TURNTABLE:
+ input_dev->name = "Xbox 360 Wireless DJ Hero Turntable";
+ break;
+ case SUBTYPE_PRO_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Rock Band Pro Guitar";
+ break;
+ }
}
input_dev->dev.parent = &xpad->intf->dev;
@@ -2106,7 +2267,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->delay_init = true;
xpad->packet_type = PKT_XB;
- INIT_WORK(&xpad->work, xpad_presence_work);
+ INIT_DELAYED_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) {
if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2 1/1] HID: add malicious HID device detection driver
From: Benjamin Tissoires @ 2026-04-07 7:59 UTC (permalink / raw)
To: Zubeyr Almaho; +Cc: Jiri Kosina, linux-input, linux-kernel, security
In-Reply-To: <20260404133746.80914-2-zybo1000@gmail.com>
Hi,
On Apr 04 2026, Zubeyr Almaho wrote:
> Add a passive HID driver that computes a suspicion score for keyboard-like
> USB devices based on:
>
> - low keystroke timing entropy,
> - immediate post-enumeration typing,
> - known suspicious VID/PID and descriptor anomalies.
>
> If the score exceeds a tunable threshold, the driver emits a warning and
> suggests userspace blocking (e.g. usbguard). The module never blocks or
> modifies HID events.
>
> Signed-off-by: Zubeyr Almaho <zybo1000@gmail.com>
As Greg said, a HID-BPF program would be far better in terms of scope
because there are numerous pitfal in your implementation.
The question will be how to notify userspace of the suspicious HID
device in HID-BPF, but here, you are also just emitting a dmesg warning,
so it's not doing much either.
Anyway, comments inline, but please reach out to the end where I explain
why the implementation can't work in it's current state.
> ---
> drivers/hid/hid-omg-detect.c | 435 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 435 insertions(+)
> create mode 100644 drivers/hid/hid-omg-detect.c
>
> diff --git a/drivers/hid/hid-omg-detect.c b/drivers/hid/hid-omg-detect.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/hid/hid-omg-detect.c
> @@ -0,0 +1,435 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * hid-omg-detect.c - Malicious HID Device Detection Kernel Module
> + *
> + * Detects O.MG Cable and similar BadUSB devices by combining:
> + * 1. Keystroke timing entropy analysis (human vs machine rhythm)
> + * 2. Plug-and-type detection (typing immediately after connect)
> + * 3. USB descriptor fingerprinting (known bad VID/PIDs + anomalies)
> + *
> + * When suspicion score >= threshold, logs a kernel warning and suggests
> + * blocking the device with usbguard.
> + *
> + * The driver is purely passive — it does not drop, modify, or delay any
> + * HID events.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/hid.h>
> +#include <linux/usb.h>
> +#include <linux/ktime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/math64.h>
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Zübeyr Almaho <zybo1000@gmail.com>");
> +MODULE_DESCRIPTION("O.MG Cable / Malicious HID Device Detector");
> +
> +/* ============================================================
> + * Tuneable parameters (pass as insmod args)
> + * ============================================================ */
I'm pretty sure checkpatch.pl would complain about those multiline
comments.
> +static int score_threshold = 70;
> +module_param(score_threshold, int, 0644);
> +MODULE_PARM_DESC(score_threshold,
> + "Suspicion score (0-100) to trigger alert (default: 70)");
> +
> +static int plug_type_ms = 500;
> +module_param(plug_type_ms, int, 0644);
> +MODULE_PARM_DESC(plug_type_ms,
> + "Max ms after connect before first key = plug-and-type (default: 500)");
> +
> +static int entropy_variance_low = 500;
> +module_param(entropy_variance_low, int, 0644);
> +MODULE_PARM_DESC(entropy_variance_low,
> + "Variance (us^2) below which typing is machine-like (default: 500)");
> +
> +/* ============================================================
> + * Constants
> + * ============================================================ */
> +#define DRIVER_NAME "hid_omg_detect"
> +#define MAX_TIMING_SAMPLES 64
> +#define MIN_SAMPLES 10
> +
> +/* ============================================================
> + * Known suspicious VID/PID pairs (O.MG Cable, Rubber Ducky, etc.)
> + * ============================================================ */
> +static const struct {
> + u16 vid;
> + u16 pid;
> + const char *name;
> +} suspicious_ids[] = {
> + { 0x16c0, 0x27db, "O.MG Cable (classic)" },
> + { 0x1209, 0xa000, "O.MG Cable (Elite)" },
> + { 0x16c0, 0x27dc, "USB Rubber Ducky" },
> + { 0x1b4f, 0x9208, "LilyPad Arduino BadUSB" },
> +};
> +
> +/* ============================================================
> + * Per-device state (allocated on probe, freed on remove)
> + * ============================================================ */
> +struct omg_device_state {
> + struct hid_device *hdev;
> +
> + /* --- connection timing --- */
> + ktime_t connect_time;
> + ktime_t first_key_time;
> + bool first_key_seen;
> +
> + /* --- keystroke interval circular buffer --- */
> + ktime_t last_key_time;
> + s64 intervals_us[MAX_TIMING_SAMPLES];
> + unsigned int sample_count;
> + unsigned int sample_head;
> +
> + /* --- USB info (immutable after probe) --- */
> + u16 vid;
> + u16 pid;
> + const char *vid_pid_match_name; /* non-NULL if VID/PID matched */
> + bool descriptor_anomaly;
> +
> + /* --- verdict --- */
> + int score;
> + bool alerted;
> +
> + spinlock_t lock;
> +};
> +
> +/* ============================================================
> + * Math helpers
> + * ============================================================ */
> +static void timing_stats(struct omg_device_state *s, s64 *mean, s64 *variance)
> +{
> + unsigned int i, n;
> + s64 sum = 0, sq = 0, m;
> +
> + n = min(s->sample_count, (unsigned int)MAX_TIMING_SAMPLES);
> +
> + if (n < 2) {
> + *mean = 0;
> + *variance = 0;
> + return;
> + }
> +
> + for (i = 0; i < n; i++)
> + sum += s->intervals_us[i];
> + m = div_s64(sum, n);
> +
> + for (i = 0; i < n; i++) {
> + s64 d = s->intervals_us[i] - m;
> +
> + sq += d * d;
> + }
> +
> + *mean = m;
> + *variance = div_s64(sq, n);
> +}
> +
> +/* ============================================================
> + * Signal 1: Timing entropy
> + *
> + * Humans: high variance (pauses, rhythm changes)
> + * Machines: low variance (constant clock)
> + *
> + * Also penalises extremely fast consistent typing (mean < 15 ms).
> + * ============================================================ */
> +static int score_entropy(struct omg_device_state *s)
> +{
> + s64 mean, var;
> + int pts = 0;
> +
> + if (s->sample_count < MIN_SAMPLES)
> + return 0;
> +
> + timing_stats(s, &mean, &var);
> +
> + if (var < (s64)entropy_variance_low)
> + pts += 35;
> + else if (var < (s64)entropy_variance_low * 10)
> + pts += 20;
> + else if (var < (s64)entropy_variance_low * 40)
> + pts += 8;
> +
> + if (mean > 0 && mean < 5000)
> + pts += 25;
> + else if (mean > 0 && mean < 15000)
> + pts += 10;
> +
> + return min(pts, 60);
> +}
> +
> +/* ============================================================
> + * Signal 2: Plug-and-type
> + *
> + * A legitimate keyboard sits idle for a while after connect.
> + * A script device starts injecting within milliseconds.
> + * ============================================================ */
> +static int score_plug_type(struct omg_device_state *s)
> +{
> + s64 delta_ms;
> +
> + if (!s->first_key_seen)
> + return 0;
> +
> + delta_ms = ktime_to_ms(ktime_sub(s->first_key_time, s->connect_time));
> +
> + if (delta_ms < 100)
> + return 30;
> + if (delta_ms < (s64)plug_type_ms)
> + return 15;
> + if (delta_ms < 2000)
> + return 5;
> +
> + return 0;
> +}
> +
> +/* ============================================================
> + * Signal 3: USB descriptor fingerprint
> + * ============================================================ */
> +static int score_descriptor(struct omg_device_state *s)
> +{
> + int pts = 0;
> +
> + if (s->vid_pid_match_name)
> + pts += 50;
> +
> + if (s->descriptor_anomaly)
> + pts += 20;
> +
> + return min(pts, 50);
> +}
> +
> +/* ============================================================
> + * Combine all signals and return per-signal breakdown.
> + * Caller is responsible for alerting outside any lock.
> + * ============================================================ */
> +struct omg_score_result {
> + int entropy;
> + int plug_type;
> + int descriptor;
> + int total;
> + bool newly_alerted;
> +};
> +
> +static void compute_score(struct omg_device_state *s,
> + struct omg_score_result *res)
> +{
> + res->entropy = score_entropy(s);
> + res->plug_type = score_plug_type(s);
> + res->descriptor = score_descriptor(s);
> + res->total = min(res->entropy + res->plug_type + res->descriptor,
> + 100);
> +
> + s->score = res->total;
> + res->newly_alerted = false;
> +
> + if (res->total >= score_threshold && !s->alerted) {
> + s->alerted = true;
> + res->newly_alerted = true;
> + }
> +}
> +
> +/* Emit alert outside of spinlock */
> +static void emit_alert(struct hid_device *hdev,
> + struct omg_device_state *s,
> + const struct omg_score_result *res)
> +{
> + hid_warn(hdev, "=============================================\n");
> + hid_warn(hdev, "!! SUSPICIOUS HID DEVICE DETECTED !!\n");
> + hid_warn(hdev, "Device : %s\n", hdev->name);
> + hid_warn(hdev, "VID/PID: %04x:%04x\n", s->vid, s->pid);
> + if (s->vid_pid_match_name)
> + hid_warn(hdev, "Match : %s\n", s->vid_pid_match_name);
> + hid_warn(hdev, "Score : %d/100 (threshold=%d)\n",
> + res->total, score_threshold);
> + hid_warn(hdev, " Entropy : %d pts\n", res->entropy);
> + hid_warn(hdev, " Plug-and-type: %d pts\n", res->plug_type);
> + hid_warn(hdev, " Descriptor : %d pts\n", res->descriptor);
> + hid_warn(hdev, "Action : sudo usbguard block-device <id>\n");
> + hid_warn(hdev, "=============================================\n");
> +}
> +
> +/* ============================================================
> + * HID raw_event hook — called for every incoming HID report
> + *
> + * This runs in softirq/BH context — no sleeping locks allowed.
> + * ============================================================ */
> +static int omg_raw_event(struct hid_device *hdev,
> + struct hid_report *report,
> + u8 *data, int size)
> +{
> + struct omg_device_state *s = hid_get_drvdata(hdev);
> + struct omg_score_result res;
> + ktime_t now;
> + unsigned long flags;
> + bool keystroke = false;
> + int i;
> +
> + if (!s)
> + return 0;
> +
> + if (report->type != HID_INPUT_REPORT)
> + return 0;
> +
> + /* Byte 0 = modifier, byte 1 = reserved, bytes 2-7 = keycodes */
That's a strong assumption. This actually depend on the report
descriptor of the device and what you said is generally true for USB
keyboards, but not all of HID devices behave like that:
- bluetooth keyboards usually have a report ID in byte 0
- some devices are reporting gyro data, so they are sending a stream of
events which would likely be tagged as "suspicious"
- some devices are reporting touch information, and as such they are
also reporting a constant stream of events as long as at least one
finger is touching the sensor
- yubikeys, other security keys and gaming devices are devices
specifically tailored to send a stream of events, being a keyboard
macro, or a one time password, or a stored passwored in the key
- not all devices are keyboards :)
> + for (i = 2; i < size && i < 8; i++) {
> + if (data[i] != 0) {
> + keystroke = true;
> + break;
> + }
> + }
> + if (size > 0 && data[0] != 0)
> + keystroke = true;
> +
> + if (!keystroke)
> + return 0;
> +
> + now = ktime_get();
> +
> + spin_lock_irqsave(&s->lock, flags);
Why spin_locking?
> +
> + if (!s->first_key_seen) {
> + s->first_key_seen = true;
> + s->first_key_time = now;
> + s->last_key_time = now;
> + } else {
> + s64 interval = ktime_to_us(ktime_sub(now, s->last_key_time));
> +
> + /* ignore idle gaps > 10 s */
> + if (interval < 10000000LL) {
> + s->intervals_us[s->sample_head] = interval;
> + s->sample_head =
> + (s->sample_head + 1) % MAX_TIMING_SAMPLES;
> + if (s->sample_count < MAX_TIMING_SAMPLES)
> + s->sample_count++;
> + }
> + s->last_key_time = now;
> + }
> +
> + compute_score(s, &res);
> +
> + spin_unlock_irqrestore(&s->lock, flags);
> +
> + /* Alert outside the spinlock — hid_warn may sleep */
> + if (res.newly_alerted)
> + emit_alert(hdev, s, &res);
> +
> + return 0;
> +}
> +
> +/* ============================================================
> + * HID probe — device connected
> + * ============================================================ */
> +static int omg_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> + struct omg_device_state *s;
> + struct usb_interface *intf;
> + struct usb_device *udev;
> + struct omg_score_result res;
> + int ret, i;
> +
> + if (hdev->bus != BUS_USB)
> + return -ENODEV;
Why restricting to BUS_USB only? Bluetooth could be a strong acttack
vector as well.
> +
> + s = kzalloc(sizeof(*s), GFP_KERNEL);
devm_kzalloc?
> + if (!s)
> + return -ENOMEM;
> +
> + spin_lock_init(&s->lock);
> + s->hdev = hdev;
> + s->connect_time = ktime_get();
> +
> + /* Extract USB descriptor info */
> + intf = to_usb_interface(hdev->dev.parent);
You can't call that function unless you are sure that you are talking to
ta USB device. If the user is using a uhid device (because they are
replaying another), the transport driver is not usbhid, and you'll end
up with a lot of issues.
There is a function for checking if the transport is usb: hid_is_usb()
> + udev = interface_to_usbdev(intf);
> + s->vid = le16_to_cpu(udev->descriptor.idVendor);
> + s->pid = le16_to_cpu(udev->descriptor.idProduct);
> +
> + /* Check known bad VID/PID table (once, at probe time) */
> + for (i = 0; i < ARRAY_SIZE(suspicious_ids); i++) {
> + if (s->vid == suspicious_ids[i].vid &&
> + s->pid == suspicious_ids[i].pid) {
> + s->vid_pid_match_name = suspicious_ids[i].name;
> + break;
> + }
> + }
> +
> + /*
> + * Anomaly: legitimate HID keyboards use bDeviceClass = 0x00
> + * (class defined at interface level). Any other value here
> + * for a device presenting as a keyboard is suspicious.
> + */
> + s->descriptor_anomaly =
> + (udev->descriptor.bDeviceClass != 0x00 &&
> + udev->descriptor.bDeviceClass != 0x03);
> +
> + hid_set_drvdata(hdev, s);
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "hid_parse failed: %d\n", ret);
> + goto err_free;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> + if (ret) {
> + hid_err(hdev, "hid_hw_start failed: %d\n", ret);
> + goto err_free;
> + }
> +
> + hid_info(hdev, "Monitoring %s (VID=%04x PID=%04x%s%s)\n",
> + hdev->name, s->vid, s->pid,
> + s->vid_pid_match_name ? " KNOWN-BAD:" : "",
> + s->vid_pid_match_name ? s->vid_pid_match_name : "");
> +
> + if (s->descriptor_anomaly)
> + hid_warn(hdev, "Descriptor anomaly: bDeviceClass=0x%02x\n",
> + udev->descriptor.bDeviceClass);
> +
> + /* Run initial descriptor-only score */
> + compute_score(s, &res);
> + if (res.newly_alerted)
> + emit_alert(hdev, s, &res);
> +
> + return 0;
> +
> +err_free:
> + hid_set_drvdata(hdev, NULL);
> + kfree(s);
no need to free when using devm_kzalloc.
> + return ret;
> +}
> +
> +/* ============================================================
> + * HID remove — device disconnected
> + * ============================================================ */
> +static void omg_remove(struct hid_device *hdev)
> +{
> + struct omg_device_state *s = hid_get_drvdata(hdev);
> +
> + hid_hw_stop(hdev);
> +
> + if (s) {
> + hid_info(hdev, "Removed %s (final score: %d/100)\n",
> + hdev->name, s->score);
> + hid_set_drvdata(hdev, NULL);
> + kfree(s);
> + }
> +}
this omg_remove() function can be removed entirely when using
devm_kzalloc.
> +
> +/* Match all USB HID devices — we inspect and score them all */
> +static const struct hid_device_id omg_table[] = {
> + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) },
ouch. Very much ouch:
You are binding to any USB device, but you don't have implemented a
.match callback, meaning that you are racing with any other driver to
take the ownership of a USB device.
This is where your plan falls completely. There can only be one HID
driver attached to a specific HID device. And we have multiple HID
drivers in the subsystem. So either they get their fixes (the original
HID driver), either they'll get the new security driver but they can not
have both. And even worse, there is no guarantee one driver will be
loaded before the other.
hid-generic is a special case as this is a fallback driver. So
hid-generic is capable of unbinding itself to let device specific
drivers to take over the device. But here, that driver just takes
ownership and can't release it (otherwise it's pointless).
So definitively, addressing that problem at the driver level is the
wrong place:
- either make it a HID core feature, but it'll be harder to sell IMO
- either make it a separate module that HID core calls in: slightly
easier to sell, but still hard
- either work at the HID-BPF level: much better because you'll need some
userspace eventually and this way you control from userpsace both the
HID-BPF program and your userspace part
I've recently added the parsed report descriptor injection during probe
in the HID-BPF program (assuming you use udev-hid-bpf to load HID-BPF).
So in theory, you can create a HID-BPF which would matches on keyboards,
chose which bytes are interesting to look at and then bind on those *in
addition* to any kernel driver. It happens before any kernel processing
so you've got the raw event stream and can decide to block or not the
stream.
Anyway, even with the various issues I mentioned in the code, this
present version can't be merged, as long as you are using a HID driver.
Cheers,
Benjamin
> + { }
> +};
> +MODULE_DEVICE_TABLE(hid, omg_table);
> +
> +static struct hid_driver omg_driver = {
> + .name = DRIVER_NAME,
> + .id_table = omg_table,
> + .probe = omg_probe,
> + .remove = omg_remove,
> + .raw_event = omg_raw_event,
> +};
> +
> +module_hid_driver(omg_driver);
>
^ permalink raw reply
* [PATCH v2] Input: uinput - fix circular locking dependency with ff-core
From: Mikhail Gavrilov @ 2026-04-07 7:50 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, stable, Mikhail Gavrilov
A lockdep circular locking dependency warning can be triggered
reproducibly when using a force-feedback gamepad with uinput (for
example, playing ELDEN RING under Wine with a Flydigi Vader 5
controller):
ff->mutex -> udev->mutex -> input_mutex -> dev->mutex -> ff->mutex
The cycle is caused by four lock acquisition paths:
1. ff upload: input_ff_upload() holds ff->mutex and calls
uinput_dev_upload_effect() -> uinput_request_submit() ->
uinput_request_send(), which acquires udev->mutex.
2. device create: uinput_ioctl_handler() holds udev->mutex and calls
uinput_create_device() -> input_register_device(), which acquires
input_mutex.
3. device register: input_register_device() holds input_mutex and
calls kbd_connect() -> input_register_handle(), which acquires
dev->mutex.
4. evdev release: evdev_release() calls input_flush_device() under
dev->mutex, which calls input_ff_flush() acquiring ff->mutex.
Fix this by introducing a new state_lock spinlock to protect
udev->state and udev->dev access in uinput_request_send() instead of
acquiring udev->mutex. The function only needs to atomically check
device state and queue an input event into the ring buffer via
uinput_dev_event() -- both operations are safe under a spinlock
(ktime_get_ts64() and wake_up_interruptible() do not sleep). This
breaks the ff->mutex -> udev->mutex link since a spinlock is a leaf in
the lock ordering and cannot form cycles with mutexes.
To keep state transitions visible to uinput_request_send(), protect
writes to udev->state in uinput_create_device() and
uinput_destroy_device() with the same state_lock spinlock.
Additionally, move init_completion(&request->done) from
uinput_request_send() to uinput_request_submit() before
uinput_request_reserve_slot(). Once the slot is allocated,
uinput_flush_requests() may call complete() on it at any time from
the destroy path, so the completion must be initialised before the
request becomes visible.
Lock ordering after the fix:
ff->mutex -> state_lock (spinlock, leaf)
udev->mutex -> state_lock (spinlock, leaf)
udev->mutex -> input_mutex -> dev->mutex -> ff->mutex (no back-edge)
Fixes: ff462551235d ("Input: uinput - switch to the new FF interface")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/CABXGCsMoxag+kEwHhb7KqhuyxfmGGd0P=tHZyb1uKE0pLr8Hkg@mail.gmail.com/
Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
---
Changes since v1:
https://lore.kernel.org/all/20260228223628.472208-1-mikhail.v.gavrilov@gmail.com/
- Use a dedicated state_lock spinlock instead of reusing requests_lock,
as suggested by Dmitry Torokhov
- Add Fixes and Cc: stable tags
drivers/input/misc/uinput.c | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index e589060db280..e24caf6fc8e8 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -57,6 +57,7 @@ struct uinput_device {
struct input_dev *dev;
struct mutex mutex;
enum uinput_state state;
+ spinlock_t state_lock;
wait_queue_head_t waitq;
unsigned char ready;
unsigned char head;
@@ -146,19 +147,15 @@ static void uinput_request_release_slot(struct uinput_device *udev,
static int uinput_request_send(struct uinput_device *udev,
struct uinput_request *request)
{
- int retval;
+ int retval = 0;
- retval = mutex_lock_interruptible(&udev->mutex);
- if (retval)
- return retval;
+ spin_lock(&udev->state_lock);
if (udev->state != UIST_CREATED) {
retval = -ENODEV;
goto out;
}
- init_completion(&request->done);
-
/*
* Tell our userspace application about this new request
* by queueing an input event.
@@ -166,7 +163,7 @@ static int uinput_request_send(struct uinput_device *udev,
uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
out:
- mutex_unlock(&udev->mutex);
+ spin_unlock(&udev->state_lock);
return retval;
}
@@ -175,6 +172,13 @@ static int uinput_request_submit(struct uinput_device *udev,
{
int retval;
+ /*
+ * Initialize completion before allocating the request slot.
+ * Once the slot is allocated, uinput_flush_requests() may
+ * complete it at any time, so it must be initialized first.
+ */
+ init_completion(&request->done);
+
retval = uinput_request_reserve_slot(udev, request);
if (retval)
return retval;
@@ -289,7 +293,14 @@ static void uinput_destroy_device(struct uinput_device *udev)
struct input_dev *dev = udev->dev;
enum uinput_state old_state = udev->state;
+ /*
+ * Update state under state_lock so that concurrent
+ * uinput_request_send() sees the state change before we
+ * flush pending requests and tear down the device.
+ */
+ spin_lock(&udev->state_lock);
udev->state = UIST_NEW_DEVICE;
+ spin_unlock(&udev->state_lock);
if (dev) {
name = dev->name;
@@ -366,7 +377,9 @@ static int uinput_create_device(struct uinput_device *udev)
if (error)
goto fail2;
+ spin_lock(&udev->state_lock);
udev->state = UIST_CREATED;
+ spin_unlock(&udev->state_lock);
return 0;
@@ -384,6 +397,7 @@ static int uinput_open(struct inode *inode, struct file *file)
return -ENOMEM;
mutex_init(&newdev->mutex);
+ spin_lock_init(&newdev->state_lock);
spin_lock_init(&newdev->requests_lock);
init_waitqueue_head(&newdev->requests_waitq);
init_waitqueue_head(&newdev->waitq);
--
2.53.0
^ permalink raw reply related
* [PATCH v3] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 6:57 UTC (permalink / raw)
To: Dmitry Torokhov, Vicki Pfau, Sanjay Govind, Mario Limonciello,
Nilton Perim Neto, Kees Cook
Cc: Antheas Kapenekakis, linux-input, linux-kernel
Xbox 360 wireless controllers expose information in the link and
capabilities reports.
Extract and use the vendor id for wireless controllers, and use
the subtype to build a nicer device name and product id.
Some xbox 360 controllers put a vid and pid into the stick capability
data, so check if this was done, and pull the vid, pid and revision from
there.
Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
---
v2: Delay marking device as present until after capabilities or timeout
v3: Fix issues when receiving incorrect or missing link and capabilities reports
drivers/input/joystick/xpad.c | 174 ++++++++++++++++++++++++++++++++--
1 file changed, 165 insertions(+), 9 deletions(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index bf4accf3f581..18c355aff80f 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -68,6 +68,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
+#include <linux/unaligned.h>
#include <linux/usb/input.h>
#include <linux/usb/quirks.h>
@@ -94,6 +95,22 @@
#define XTYPE_XBOXONE 3
#define XTYPE_UNKNOWN 4
+#define FLAG_FORCE_FEEDBACK 0x01
+
+#define SUBTYPE_GAMEPAD 0x01
+#define SUBTYPE_WHEEL 0x02
+#define SUBTYPE_ARCADE_STICK 0x03
+#define SUBTYPE_FLIGHT_SICK 0x04
+#define SUBTYPE_DANCE_PAD 0x05
+#define SUBTYPE_GUITAR 0x06
+#define SUBTYPE_GUITAR_ALTERNATE 0x07
+#define SUBTYPE_DRUM_KIT 0x08
+#define SUBTYPE_GUITAR_BASS 0x0B
+#define SUBTYPE_RB_KEYBOARD 0x0F
+#define SUBTYPE_ARCADE_PAD 0x13
+#define SUBTYPE_TURNTABLE 0x17
+#define SUBTYPE_PRO_GUITAR 0x19
+
/* Send power-off packet to xpad360w after holding the mode button for this many
* seconds
*/
@@ -795,8 +812,13 @@ struct usb_xpad {
int xtype; /* type of xbox device */
int packet_type; /* type of the extended packet */
int pad_nr; /* the order x360 pads were attached */
+ u8 sub_type;
+ u16 flags;
+ u16 wireless_vid;
+ u16 wireless_pid;
+ u16 wireless_version;
const char *name; /* name of the device */
- struct work_struct work; /* init/remove device from callback */
+ struct delayed_work work; /* init/remove device from callback */
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
@@ -807,6 +829,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
static int xpad_start_input(struct usb_xpad *xpad);
static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
+
/*
* xpad_process_packet
@@ -980,7 +1004,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
static void xpad_presence_work(struct work_struct *work)
{
- struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
int error;
if (xpad->pad_present) {
@@ -1017,7 +1041,7 @@ static void xpad_presence_work(struct work_struct *work)
* 01.1 - Pad state (Bytes 4+) valid
*
*/
-static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, u32 len)
{
struct input_dev *dev;
bool present;
@@ -1028,7 +1052,62 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
if (xpad->pad_present != present) {
xpad->pad_present = present;
- schedule_work(&xpad->work);
+ if (present) {
+ /*
+ * Delay marking device as present, so we can make sure
+ * we have received all the information from the capabilities
+ * report. Some devices don't send one, so the delay
+ * guarantees that these devices are still initialized.
+ */
+ schedule_delayed_work(&xpad->work, msecs_to_jiffies(500));
+ } else {
+ schedule_delayed_work(&xpad->work, 0);
+ }
+ }
+ }
+
+ /* Link report */
+ if (data[0] == 0x00 && data[1] == 0x0F && len >= 26) {
+ xpad->sub_type = data[25] & 0x7f;
+
+ /* Decode vendor id from link report */
+ xpad->wireless_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
+
+ /*
+ * If the link report doesn't provide a proper vid, it sets the vid to 1.
+ * In that case we zero out wireless_vid, so that we fall back to the vid
+ * from the receiver instead.
+ */
+ if (xpad->wireless_vid == 1)
+ xpad->wireless_vid = 0;
+ /*
+ * x360w controllers on windows put the subtype into the product
+ * for wheels and gamepads, but it makes sense to do it for all
+ * subtypes. This will be used if the capabilities report
+ * doesn't provide us with a product id later.
+ */
+ xpad->wireless_pid = 0x02a0 + xpad->sub_type;
+ xpad->wireless_version = 0;
+
+ if ((data[25] & 0x80) != 0)
+ xpad->flags |= FLAG_FORCE_FEEDBACK;
+
+ xpad_inquiry_pad_capabilities(xpad);
+ }
+
+ /* Capabilities report */
+ if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12 && len >= 20) {
+ xpad->flags |= data[20];
+ /*
+ * A bunch of vendors started putting vids and pids
+ * into capabilities data because they can't be
+ * retrieved by xinput easliy.
+ * Not all of them do though, so check the vids match
+ * before extracting that info.
+ */
+ if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
+ xpad->wireless_pid = get_unaligned_le16(data + 12);
+ xpad->wireless_version = get_unaligned_le16(data + 14);
}
}
@@ -1254,7 +1333,7 @@ static void xpad_irq_in(struct urb *urb)
xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
break;
case XTYPE_XBOX360W:
- xpad360w_process_packet(xpad, 0, xpad->idata);
+ xpad360w_process_packet(xpad, 0, xpad->idata, urb->actual_length);
break;
case XTYPE_XBOXONE:
xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length);
@@ -1495,6 +1574,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
return xpad_try_sending_next_out_packet(xpad);
}
+static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ guard(spinlock_irqsave)(&xpad->odata_lock);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x02;
+ packet->data[3] = 0x80;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ return xpad_try_sending_next_out_packet(xpad);
+}
+
static int xpad_start_xbox_one(struct usb_xpad *xpad)
{
int error;
@@ -1893,7 +1997,7 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
usb_kill_urb(xpad->irq_in);
/* Make sure we are done with presence work if it was scheduled */
- flush_work(&xpad->work);
+ flush_delayed_work(&xpad->work);
}
static int xpad_open(struct input_dev *dev)
@@ -1965,8 +2069,60 @@ static int xpad_init_input(struct usb_xpad *xpad)
usb_to_input_id(xpad->udev, &input_dev->id);
if (xpad->xtype == XTYPE_XBOX360W) {
- /* x360w controllers and the receiver have different ids */
- input_dev->id.product = 0x02a1;
+ if (xpad->wireless_vid)
+ input_dev->id.vendor = xpad->wireless_vid;
+ if (xpad->wireless_pid)
+ input_dev->id.product = xpad->wireless_pid;
+ else
+ /* Default product id for x360w controllers */
+ input_dev->id.product = 0x02a1;
+ if (xpad->wireless_version)
+ input_dev->id.version = xpad->wireless_version;
+ switch (xpad->sub_type) {
+ case SUBTYPE_GAMEPAD:
+ input_dev->name = "Xbox 360 Wireless Controller";
+ break;
+ case SUBTYPE_WHEEL:
+ input_dev->name = "Xbox 360 Wireless Wheel";
+ break;
+ case SUBTYPE_ARCADE_STICK:
+ input_dev->name = "Xbox 360 Wireless Arcade Stick";
+ break;
+ case SUBTYPE_FLIGHT_SICK:
+ input_dev->name = "Xbox 360 Wireless Flight Stick";
+ break;
+ case SUBTYPE_DANCE_PAD:
+ input_dev->name = "Xbox 360 Wireless Dance Pad";
+ break;
+ case SUBTYPE_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Guitar";
+ break;
+ case SUBTYPE_GUITAR_ALTERNATE:
+ input_dev->name = "Xbox 360 Wireless Alternate Guitar";
+ break;
+ case SUBTYPE_GUITAR_BASS:
+ input_dev->name = "Xbox 360 Wireless Bass Guitar";
+ break;
+ case SUBTYPE_DRUM_KIT:
+ /* Vendors used force feedback flag to differentiate these */
+ if (xpad->flags & FLAG_FORCE_FEEDBACK)
+ input_dev->name = "Xbox 360 Wireless Guitar Hero Drum Kit";
+ else
+ input_dev->name = "Xbox 360 Wireless Rock Band Drum Kit";
+ break;
+ case SUBTYPE_RB_KEYBOARD:
+ input_dev->name = "Xbox 360 Wireless Rock Band Keyboard";
+ break;
+ case SUBTYPE_ARCADE_PAD:
+ input_dev->name = "Xbox 360 Wireless Arcade Pad";
+ break;
+ case SUBTYPE_TURNTABLE:
+ input_dev->name = "Xbox 360 Wireless DJ Hero Turntable";
+ break;
+ case SUBTYPE_PRO_GUITAR:
+ input_dev->name = "Xbox 360 Wireless Rock Band Pro Guitar";
+ break;
+ }
}
input_dev->dev.parent = &xpad->intf->dev;
@@ -2106,7 +2262,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->delay_init = true;
xpad->packet_type = PKT_XB;
- INIT_WORK(&xpad->work, xpad_presence_work);
+ INIT_DELAYED_WORK(&xpad->work, xpad_presence_work);
if (xpad->xtype == XTYPE_UNKNOWN) {
if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v2] xpad: Overhaul device data for wireless devices
From: Sanjay Govind @ 2026-04-07 6:36 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Antheas Kapenekakis, Pierre-Loup A. Griffais, linux-input,
linux-kernel
In-Reply-To: <adSJp5U47g1JnImj@google.com>
On Tue, Apr 7, 2026 at 4:46 PM Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> When will it be set to 1?
The link report itself sets it to 1 in some cases, but i think i will
just change this so that if we get a link report and the vid == 1, we
set wireless_vid to 0, so we can rely on it being a non zero value
instead
> Maybe this should be disable_delayed_work() instead...
For simplicity I'll just get rid of that, and we can just always wait
for it to expire instead of trying to shortcut it if we get the
capabilities report faster than 500ms.
> Can this also read stale data for the same reason if the packet is too short?
I'll add in length checks.
^ permalink raw reply
* Re: [PATCH] Input: uinput - fix circular locking dependency with ff-core
From: Dmitry Torokhov @ 2026-04-07 5:19 UTC (permalink / raw)
To: Mikhail Gavrilov; +Cc: linux-input, linux-kernel
In-Reply-To: <CABXGCsNwLTAKFdRDHnGBdF-h3fnKY0_ysQJevMTv-N7+MNwS1A@mail.gmail.com>
Sorry for disappearing again...
On Mon, Mar 23, 2026 at 10:39:29AM +0500, Mikhail Gavrilov wrote:
> On Mon, Mar 23, 2026 at 10:34 AM Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
> >
> > Hmm, I am not sure I see the issue. We are not going to change state
> > back to UIST_CREATED until after uinput_destroy_device() returns so we
> > will not submit more requests...
> >
> > What am I missing?
>
> You are right, there is no lock ordering issue since the state
> transition is one-way.
>
> The reason I reused requests_lock is that uinput_request_send()
> needs to atomically check state and access udev->dev. If we use a
> separate state_lock and release it before calling
> uinput_dev_event(), uinput_destroy_device() could run in between,
> unregister the device, and we'd hit a use-after-free on udev->dev.
>
> A separate lock would need to be held across the same scope,
> making it functionally equivalent to reusing requests_lock.
I was talking about taking this new lock in both uinput_request_send()
as well as in uinput_destroy_device() when updating the state. With that
requests_lock will be taken only in uinput_request_alloc_id(),
uinput_request_release_slot(), and uinput_flush_requests().
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2] Input: aiptek: validate raw macro indices before updating state
From: Dmitry Torokhov @ 2026-04-07 5:12 UTC (permalink / raw)
To: Pengpeng Hou; +Cc: andriy.shevchenko, kees, linux-input, linux-kernel
In-Reply-To: <20260329001711.88076-1-pengpeng@iscas.ac.cn>
On Sun, Mar 29, 2026 at 08:17:11AM +0800, Pengpeng Hou wrote:
> aiptek_irq() derives macro key indices directly from tablet reports and
> then uses them to index macroKeyEvents[]. Report types 4 and 5 also save
> the derived value in aiptek->lastMacro and later use that state to
> release the previous key.
>
> Validate the raw macro index once before it enters that state machine, so
> lastMacro only ever stores an in-range macro key. Keep direct bounds
> checks for report type 6, which reads the macro number from the packet
> body and uses it immediately.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Applied, thank you.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2] Input: gf2k: skip invalid hat lookup values
From: Dmitry Torokhov @ 2026-04-07 5:12 UTC (permalink / raw)
To: Pengpeng Hou; +Cc: linux-input, linux-kernel, kees
In-Reply-To: <20260407120001.1-gf2k-v2-pengpeng@iscas.ac.cn>
On Tue, Apr 07, 2026 at 09:56:52AM +0800, Pengpeng Hou wrote:
> gf2k_read() decodes the hat position from a 4-bit field and uses it
> directly to index gf2k_hat_to_axis[]. The lookup table only has nine
> entries, so malformed packets can read past the end of the fixed table.
>
> Skip hat reporting when the decoded value falls outside the lookup
> table instead of forcing it to the neutral position. This keeps the
> fix local and avoids reporting a made-up axis state for malformed
> packets.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Applied, thank you.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v8 1/7] dt-bindings: input: syna,rmi4: Document syna,rmi4-s3706b
From: Dmitry Torokhov @ 2026-04-07 4:48 UTC (permalink / raw)
To: David Heidelberg
Cc: Kaustabh Chakraborty, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jason A. Donenfeld, Matthias Schiffer,
Vincent Huang, Casey Connolly, linux-input, devicetree,
linux-kernel, phone-devel, Krzysztof Kozlowski
In-Reply-To: <5630a4af-e18f-4daf-9b04-ea61091d9e51@ixit.cz>
On Wed, Mar 25, 2026 at 12:33:23PM +0100, David Heidelberg wrote:
> On 24/03/2026 20:42, Dmitry Torokhov wrote:
> > On Tue, Mar 24, 2026 at 08:40:34PM +0100, David Heidelberg via B4 Relay wrote:
> > > From: David Heidelberg <david@ixit.cz>
> > >
> > > Mostly irrelevant for authentic Synaptics touchscreens, but very important
> > > for applying workarounds to cheap TS knockoffs.
> > >
> > > These knockoffs work well with the downstream driver, and since the user
> > > has no way to distinguish them, later in this patch set, we introduce
> > > workarounds to ensure they function as well as possible.
> > >
> > > Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > > Signed-off-by: David Heidelberg <david@ixit.cz>
> > > ---
> > > Documentation/devicetree/bindings/input/syna,rmi4.yaml | 11 ++++++++---
> > > 1 file changed, 8 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/Documentation/devicetree/bindings/input/syna,rmi4.yaml b/Documentation/devicetree/bindings/input/syna,rmi4.yaml
> > > index 8685ef4481f4a..fb4804ac3544d 100644
> > > --- a/Documentation/devicetree/bindings/input/syna,rmi4.yaml
> > > +++ b/Documentation/devicetree/bindings/input/syna,rmi4.yaml
> > > @@ -18,9 +18,14 @@ description: |
> > > properties:
> > > compatible:
> > > - enum:
> > > - - syna,rmi4-i2c
> > > - - syna,rmi4-spi
> > > + oneOf:
> > > + - enum:
> > > + - syna,rmi4-i2c
> > > + - syna,rmi4-spi
> > > + - items:
> > > + - enum:
> > > + - syna,rmi4-s3706b # OnePlus 6/6T
> >
> > I thought that all the workarounds will be keyed off this new
> > compatible, but I do not see that. What am I missing?
>
> The compatible is used for sequence in the
>
> Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes
>
> where it is used to provide values missing for OP6 (and possible others in
> the future, when added).
>
> From my understanding the series, only two patches (1st and last) are
> specific for the OP6, rest will likely benefit various TS not implementing
> full Synaptics set. All measures apply only when touchscreen reports
> something wrong.
If the sensor does not implement RMI4 protocol properly it should not
use rmi4 compatibility. I will not apply any patches that work around
incomplete implementations unless they are triggered by a dedicated
compatible.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2] xpad: Overhaul device data for wireless devices
From: Dmitry Torokhov @ 2026-04-07 4:46 UTC (permalink / raw)
To: Sanjay Govind
Cc: Vicki Pfau, Nilton Perim Neto, Mario Limonciello,
Antheas Kapenekakis, Pierre-Loup A. Griffais, linux-input,
linux-kernel
In-Reply-To: <20260404083928.489966-3-sanjay.govind9@gmail.com>
Hi Sanjay,
On Sat, Apr 04, 2026 at 09:39:27PM +1300, Sanjay Govind wrote:
> Xbox 360 wireless controllers expose information in the link and
> capabilities reports.
>
> Extract and use the vendor id for wireless controllers, and use
> the subtype to build a nicer device name and product id.
>
> Some xbox 360 controllers put a vid and pid into the stick capability
> data, so check if this was done, and pull the vid, pid and revision from
> there.
Sashuko correctly identified issues areoud re-scheduling work that
already completed, please see:
https://sashiko.dev/#/patchset/20260404083928.489966-3-sanjay.govind9@gmail.com
>
> Signed-off-by: Sanjay Govind <sanjay.govind9@gmail.com>
> ---
> v2: Delay marking device as present until after capabilities or timeout
> drivers/input/joystick/xpad.c | 162 ++++++++++++++++++++++++++++++++--
> 1 file changed, 155 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
> index bf4accf3f581..c35512c7c199 100644
> --- a/drivers/input/joystick/xpad.c
> +++ b/drivers/input/joystick/xpad.c
> @@ -68,6 +68,7 @@
> #include <linux/slab.h>
> #include <linux/stat.h>
> #include <linux/module.h>
> +#include <linux/unaligned.h>
> #include <linux/usb/input.h>
> #include <linux/usb/quirks.h>
>
> @@ -94,6 +95,22 @@
> #define XTYPE_XBOXONE 3
> #define XTYPE_UNKNOWN 4
>
> +#define FLAG_FORCE_FEEDBACK 0x01
> +
> +#define SUBTYPE_GAMEPAD 0x01
> +#define SUBTYPE_WHEEL 0x02
> +#define SUBTYPE_ARCADE_STICK 0x03
> +#define SUBTYPE_FLIGHT_SICK 0x04
> +#define SUBTYPE_DANCE_PAD 0x05
> +#define SUBTYPE_GUITAR 0x06
> +#define SUBTYPE_GUITAR_ALTERNATE 0x07
> +#define SUBTYPE_DRUM_KIT 0x08
> +#define SUBTYPE_GUITAR_BASS 0x0B
> +#define SUBTYPE_RB_KEYBOARD 0x0F
> +#define SUBTYPE_ARCADE_PAD 0x13
> +#define SUBTYPE_TURNTABLE 0x17
> +#define SUBTYPE_PRO_GUITAR 0x19
> +
> /* Send power-off packet to xpad360w after holding the mode button for this many
> * seconds
> */
> @@ -795,8 +812,13 @@ struct usb_xpad {
> int xtype; /* type of xbox device */
> int packet_type; /* type of the extended packet */
> int pad_nr; /* the order x360 pads were attached */
> + u8 sub_type;
> + u16 flags;
> + u16 wireless_vid;
> + u16 wireless_pid;
> + u16 wireless_version;
> const char *name; /* name of the device */
> - struct work_struct work; /* init/remove device from callback */
> + struct delayed_work work; /* init/remove device from callback */
> time64_t mode_btn_down_ts;
> bool delay_init; /* init packets should be delayed */
> bool delayed_init_done;
> @@ -807,6 +829,8 @@ static void xpad_deinit_input(struct usb_xpad *xpad);
> static int xpad_start_input(struct usb_xpad *xpad);
> static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
> static void xpad360w_poweroff_controller(struct usb_xpad *xpad);
> +static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad);
> +
>
> /*
> * xpad_process_packet
> @@ -980,7 +1004,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
>
> static void xpad_presence_work(struct work_struct *work)
> {
> - struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
> + struct usb_xpad *xpad = container_of(work, struct usb_xpad, work.work);
> int error;
>
> if (xpad->pad_present) {
> @@ -1028,10 +1052,60 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha
>
> if (xpad->pad_present != present) {
> xpad->pad_present = present;
> - schedule_work(&xpad->work);
> + if (present) {
> + /*
> + * Delay marking device as present, so we can make sure
> + * we have received all the information from the capabilities
> + * report. Some devices don't send one, so the delay
> + * guarantees that these devices are still initialized.
> + */
> + schedule_delayed_work(&xpad->work, msecs_to_jiffies(500));
> + } else {
> + schedule_delayed_work(&xpad->work, 0);
schedule_delayed_work(&xpad->work,
present ? msecs_to_jiffies(500) : 0);
> + }
> }
> }
>
> + /* Link report */
> + if (data[0] == 0x00 && data[1] == 0x0F) {
> + xpad->sub_type = data[25] & 0x7f;
> +
> + /* Decode vendor id from link report */
> + xpad->wireless_vid = ((data[0x16] & 0xf) | data[0x18] << 4) << 8 | data[0x17];
> + /*
> + * x360w controllers on windows put the subtype into the product
> + * for wheels and gamepads, but it makes sense to do it for all
> + * subtypes. This will be used if the capabilities report
> + * doesn't provide us with a product id later.
> + */
> + xpad->wireless_pid = 0x02a0 + xpad->sub_type;
> + xpad->wireless_version = 0;
> +
> + if ((data[25] & 0x80) != 0)
> + xpad->flags |= FLAG_FORCE_FEEDBACK;
> +
> + xpad_inquiry_pad_capabilities(xpad);
> + }
> +
> + /* Capabilities report */
> + if (data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
> + xpad->flags |= data[20];
> + /*
> + * A bunch of vendors started putting vids and pids
> + * into capabilities data because they can't be
> + * retrieved by xinput easliy.
> + * Not all of them do though, so check the vids match
> + * before extracting that info.
> + */
> + if (get_unaligned_le16(data + 10) == xpad->wireless_vid) {
> + xpad->wireless_pid = get_unaligned_le16(data + 12);
> + xpad->wireless_version = get_unaligned_le16(data + 14);
> + }
> + /* We got the capabilities report, so mark the device present now */
> + cancel_delayed_work(&xpad->work);
> + schedule_delayed_work(&xpad->work, 0);
mod_delayed_work(&xpad->work, 0);
> + }
> +
> /* Valid pad data */
> if (data[1] != 0x1)
> return;
> @@ -1495,6 +1569,31 @@ static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
> return xpad_try_sending_next_out_packet(xpad);
> }
>
> +static int xpad_inquiry_pad_capabilities(struct usb_xpad *xpad)
> +{
> + struct xpad_output_packet *packet =
> + &xpad->out_packets[XPAD_OUT_CMD_IDX];
> +
> + guard(spinlock_irqsave)(&xpad->odata_lock);
> +
> + packet->data[0] = 0x00;
> + packet->data[1] = 0x00;
> + packet->data[2] = 0x02;
> + packet->data[3] = 0x80;
> + packet->data[4] = 0x00;
> + packet->data[5] = 0x00;
> + packet->data[6] = 0x00;
> + packet->data[7] = 0x00;
> + packet->data[8] = 0x00;
> + packet->data[9] = 0x00;
> + packet->data[10] = 0x00;
> + packet->data[11] = 0x00;
> + packet->len = 12;
> + packet->pending = true;
> +
> + return xpad_try_sending_next_out_packet(xpad);
> +}
> +
> static int xpad_start_xbox_one(struct usb_xpad *xpad)
> {
> int error;
> @@ -1893,7 +1992,7 @@ static void xpad360w_stop_input(struct usb_xpad *xpad)
> usb_kill_urb(xpad->irq_in);
>
> /* Make sure we are done with presence work if it was scheduled */
> - flush_work(&xpad->work);
> + flush_delayed_work(&xpad->work);
Maybe this should be disable_delayed_work() instead...
> }
>
> static int xpad_open(struct input_dev *dev)
> @@ -1965,8 +2064,57 @@ static int xpad_init_input(struct usb_xpad *xpad)
> usb_to_input_id(xpad->udev, &input_dev->id);
>
> if (xpad->xtype == XTYPE_XBOX360W) {
> - /* x360w controllers and the receiver have different ids */
> - input_dev->id.product = 0x02a1;
> + /* If the Link report has provided a vid, it won't be set to 1 */
> + if (xpad->wireless_vid != 1)
When will it be set to 1?
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH] HID: playstation: Add DualSense Edge extra button support
From: Aaron Webster @ 2026-04-07 4:40 UTC (permalink / raw)
To: Roderick Colenbrander, Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Aaron Webster
The DualSense Edge controller (product ID 0x0df2) has four additional
buttons compared to the standard DualSense: two front function buttons
(Fn1 and Fn2) and two rear paddles (left and right). These are reported
in bits 4-7 of buttons[2] in the input report.
Map them to BTN_TRIGGER_HAPPY1 through BTN_TRIGGER_HAPPY4 so that
userspace applications can use these extra inputs. An is_edge flag
gates the extra button handling based on the product ID.
Signed-off-by: Aaron Webster <awebster@gmail.com>
---
Tested with a DualSense Edge controller (Hardware: 1000208, Firmware:
1000087 type 3, Fw version: 20 131082 6, Sw series: 68, Update version:
0213, build date Jul 4 2025) on Debian 13 (trixie) with kernel
6.12.74+deb13+1-amd64 (x86_64) via Bluetooth.
drivers/hid/hid-playstation.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 3c0db8f93..962159239 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -112,6 +112,12 @@ struct ps_led_info {
#define DS_BUTTONS2_TOUCHPAD BIT(1)
#define DS_BUTTONS2_MIC_MUTE BIT(2)
+/* DualSense Edge extra buttons in buttons[2], bits 4-7. */
+#define DS_EDGE_BUTTONS_FN1 BIT(4)
+#define DS_EDGE_BUTTONS_FN2 BIT(5)
+#define DS_EDGE_BUTTONS_LEFT_PADDLE BIT(6)
+#define DS_EDGE_BUTTONS_RIGHT_PADDLE BIT(7)
+
/* Status fields of DualSense input report. */
#define DS_STATUS0_BATTERY_CAPACITY GENMASK(3, 0)
#define DS_STATUS0_CHARGING GENMASK(7, 4)
@@ -178,6 +184,9 @@ struct dualsense {
struct input_dev *touchpad;
struct input_dev *jack;
+ /* True if this is a DualSense Edge (product 0x0df2). */
+ bool is_edge;
+
/* Update version is used as a feature/capability version. */
u16 update_version;
@@ -1486,6 +1495,18 @@ static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *r
input_report_key(ds->gamepad, BTN_THUMBL, ds_report->buttons[1] & DS_BUTTONS1_L3);
input_report_key(ds->gamepad, BTN_THUMBR, ds_report->buttons[1] & DS_BUTTONS1_R3);
input_report_key(ds->gamepad, BTN_MODE, ds_report->buttons[2] & DS_BUTTONS2_PS_HOME);
+
+ if (ds->is_edge) {
+ input_report_key(ds->gamepad, BTN_TRIGGER_HAPPY1,
+ ds_report->buttons[2] & DS_EDGE_BUTTONS_FN1);
+ input_report_key(ds->gamepad, BTN_TRIGGER_HAPPY2,
+ ds_report->buttons[2] & DS_EDGE_BUTTONS_FN2);
+ input_report_key(ds->gamepad, BTN_TRIGGER_HAPPY3,
+ ds_report->buttons[2] & DS_EDGE_BUTTONS_LEFT_PADDLE);
+ input_report_key(ds->gamepad, BTN_TRIGGER_HAPPY4,
+ ds_report->buttons[2] & DS_EDGE_BUTTONS_RIGHT_PADDLE);
+ }
+
input_sync(ds->gamepad);
/*
@@ -1785,6 +1806,7 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
ds->use_vibration_v2 = ds->update_version >= DS_FEATURE_VERSION(2, 21);
} else if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) {
ds->use_vibration_v2 = true;
+ ds->is_edge = true;
}
ret = ps_devices_list_add(ps_dev);
@@ -1802,6 +1824,15 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
ret = PTR_ERR(ds->gamepad);
goto err;
}
+
+ /* Register DualSense Edge back paddle and Fn buttons. */
+ if (ds->is_edge) {
+ input_set_capability(ds->gamepad, EV_KEY, BTN_TRIGGER_HAPPY1);
+ input_set_capability(ds->gamepad, EV_KEY, BTN_TRIGGER_HAPPY2);
+ input_set_capability(ds->gamepad, EV_KEY, BTN_TRIGGER_HAPPY3);
+ input_set_capability(ds->gamepad, EV_KEY, BTN_TRIGGER_HAPPY4);
+ }
+
/* Use gamepad input device name as primary device name for e.g. LEDs */
ps_dev->input_dev_name = dev_name(&ds->gamepad->dev);
--
2.47.3
^ permalink raw reply related
* Re: [PATCH] Input: gpio-keys - add hibernation support
From: Dmitry Torokhov @ 2026-04-07 4:31 UTC (permalink / raw)
To: Armando De Leon; +Cc: Armando De Leon, linux-input, linux-kernel
In-Reply-To: <20260406203932.3391600-1-learmand@amazon.com>
Hi Armando,
On Mon, Apr 06, 2026 at 01:39:10PM -0700, Armando De Leon wrote:
> Hi Dmitry,
>
> Thanks for the guidance. I investigated further:
>
> The upstream GICv3 driver (irq-gic-v3.c) has no suspend/resume or
> syscore_ops for the distributor - it does not save or restore any
> per-IRQ configuration across power cycles. On platforms where the
> GIC is re-initialized by firmware during hibernate restore, all
> trigger type configurations set by consumer drivers during probe()
> are lost.
>
> The IRQ core does preserve the trigger type in irq_data
> (irqd_get_trigger_type), but resume_irq() in kernel/irq/pm.c only
> re-enables the interrupt without re-applying the trigger type to
> hardware.
>
> A fix in the IRQ PM resume path (having resume_irq() call
> irq_set_irq_type() using the saved trigger type) would fix all
> consumer drivers generically. However, such a change would have
> broad consequences and impact across all platforms and interrupt
> controllers, and would need very careful review and testing.
>
> Given that the gpio-keys fix is a single irq_set_irq_type() call
> in the .restore callback - minimal, self-contained, and low risk -
> would it be acceptable to handle it at the driver level for now?
> This avoids the risk of a sweeping IRQ core change while solving
> the immediate problem for gpio-keys users with hibernation support.
I am sorry but as I mentioned, the proper fix is in IRQ/irqchip code,
which will ensure that not only gpio-keys but also the rest of the
consumer drivers have consistent state post hibernate.
I understand that you might need your change for a product; I recommend
applying the patch to your internal tree while you are sorting out a
generic solution.
Thanks.
--
Dmitry
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox