* [PATCH v2] Input: xpad - add support for Beitong KP series controllers
@ 2026-07-26 8:21 VegetablCat
2026-07-26 8:29 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: VegetablCat @ 2026-07-26 8:21 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, vegetablecat
From: vegetablecat <vegetablecat@foxmail.com>
Beitong KP series controllers (0x20bc:*) enumerate as standard
Xbox 360 devices but require a Microsoft OS Extended Compat ID
descriptor read (XUSB10) to lock into XInput mode. Without this
request the controller re-enumerates as a Nintendo Switch device
(057e:2069) after approximately 1.9 seconds and stops reporting
valid input.
Add FLAG_READ_XUSB10 and device table entries for KP20A/D,
KP40A/B/D/F, KP50B/C/D/E, and KP70A. Devices with this flag
trigger a usb_control_msg_recv() during probe to read the XUSB10
descriptor, which is the sole condition required to keep the
controllers in XInput mode.
The flag follows the same pattern as FLAG_DELAY_INIT used for
the ByoWave Proteus Controller.
Additionally, add an optional HID receiver driver for the KP40D
receiver dongle (0x20dd:0x515c) that binds to its HID interface
to prevent hid-generic from interfering. The receiver dongle is
a separate USB device with its own vendor ID (0x20dd), distinct
from the controller (0x20bc). The HID code is guarded with
IS_ENABLED(CONFIG_HID) to avoid build failures when HID is
disabled or built as a module.
Tested on 20bc:5126 (KP20A) and 20bc:515b (KP40D).
Signed-off-by: vegetablecat <vegetablecat@foxmail.com>
---
drivers/input/joystick/xpad.c | 119 +++++++++++++++++++++++++++++++++-
1 file changed, 118 insertions(+), 1 deletion(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index feb8f36..13b6168 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -28,6 +28,7 @@
*/
#include <linux/bits.h>
+#include <linux/hid.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/rcupdate.h>
@@ -72,6 +73,7 @@
#define PKT_XBE2_FW_5_11 4
#define FLAG_DELAY_INIT BIT(0)
+#define FLAG_READ_XUSB10 BIT(1)
static bool dpad_to_buttons;
module_param(dpad_to_buttons, bool, S_IRUGO);
@@ -332,6 +334,32 @@ static const struct xpad_device {
{ 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 },
{ 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 },
{ 0x1ee9, 0x1590, "ZOTAC Gaming Zone", 0, XTYPE_XBOX360 },
+ { 0x20bc, 0x5125, "BETOP BTP-KP20A/BTP-KP40A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5126, "BETOP BTP-KP20A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5127, "BETOP BTP-KP20A/BTP-KP40A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5128, "BETOP BTP-KP20A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x512f, "BETOP BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5130, "BETOP BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5133, "BETOP BTP-KP50B Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5145, "BETOP BTP-KP40A/BTP-KP40B Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5146, "BETOP BTP-KP40A/BTP-KP40B Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5149, "BETOP BTP-KP50C Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5150, "BETOP BTP-KP50D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5151, "BETOP BTP-KP50D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5152, "BETOP BTP-KP50E Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5153, "BETOP BTP-KP50E Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5154, "BETOP BTP-KP40D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5155, "BETOP BTP-KP40D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5158, "BETOP BTP-KP20D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5159, "BETOP BTP-KP20D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x515b, "BETOP BTP-KP40D Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x515c, "BETOP BTP-KP40D Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x515d, "BETOP BTP-KP40F Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x515e, "BETOP BTP-KP40F Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x515f, "BETOP BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5160, "BETOP BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x5169, "BETOP BTP-KP40F Controller (Black)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
+ { 0x20bc, 0x516a, "BETOP BTP-KP40F Controller (Black)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5134, "BETOP BTP-KP50B Xinput Dongle", 0, XTYPE_XBOX360 },
{ 0x20bc, 0x514a, "BETOP BTP-KP50C Xinput Dongle", 0, XTYPE_XBOX360 },
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
@@ -778,6 +806,7 @@ struct usb_xpad {
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
+ bool read_xusb10; /* read XUSB10 descriptor during probe */
};
static int xpad_init_input(struct usb_xpad *xpad);
@@ -2044,6 +2073,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_xpad *xpad;
struct usb_endpoint_descriptor *ep_irq_in, *ep_irq_out;
+ u8 ms_feature_descriptor[40];
int i, error;
if (intf->cur_altsetting->desc.bNumEndpoints != 2)
@@ -2082,6 +2112,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->name = xpad_device[i].name;
if (xpad_device[i].flags & FLAG_DELAY_INIT)
xpad->delay_init = true;
+ if (xpad_device[i].flags & FLAG_READ_XUSB10)
+ xpad->read_xusb10 = true;
xpad->packet_type = PKT_XB;
INIT_WORK(&xpad->work, xpad_presence_work);
@@ -2148,6 +2180,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_set_intfdata(intf, xpad);
+ if (xpad->read_xusb10) {
+ error = usb_control_msg_recv(udev, 0, 0xee,
+ USB_TYPE_VENDOR | USB_DIR_IN |
+ USB_RECIP_DEVICE,
+ 0x0000, 0x0004,
+ ms_feature_descriptor,
+ sizeof(ms_feature_descriptor), 25,
+ GFP_KERNEL);
+ if (error)
+ dev_warn(&intf->dev,
+ "unable to read Microsoft feature descriptor: %d\n",
+ error);
+ }
/* Packet type detection */
if (le16_to_cpu(udev->descriptor.idVendor) == 0x045e) { /* Microsoft controllers */
@@ -2312,7 +2357,79 @@ static struct usb_driver xpad_driver = {
.id_table = xpad_table,
};
-module_usb_driver(xpad_driver);
+#if IS_ENABLED(CONFIG_HID)
+static int xpad_receiver_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int error;
+
+ error = hid_parse(hdev);
+ if (error)
+ return error;
+
+ error = hid_hw_start(hdev, 0);
+ if (error)
+ return error;
+
+ error = hid_hw_open(hdev);
+ if (error) {
+ hid_hw_stop(hdev);
+ return error;
+ }
+
+ return 0;
+}
+
+static void xpad_receiver_remove(struct hid_device *hdev)
+{
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id xpad_receiver_devices[] = {
+ { HID_USB_DEVICE(0x20dd, 0x515c) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, xpad_receiver_devices);
+
+static struct hid_driver xpad_receiver_driver = {
+ .name = "xpad-kp40d-receiver",
+ .id_table = xpad_receiver_devices,
+ .probe = xpad_receiver_probe,
+ .remove = xpad_receiver_remove,
+};
+#endif
+
+static int __init xpad_init(void)
+{
+ int error;
+
+#if IS_ENABLED(CONFIG_HID)
+ error = hid_register_driver(&xpad_receiver_driver);
+ if (error)
+ return error;
+#endif
+
+ error = usb_register(&xpad_driver);
+ if (error) {
+#if IS_ENABLED(CONFIG_HID)
+ hid_unregister_driver(&xpad_receiver_driver);
+#endif
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit xpad_exit(void)
+{
+ usb_deregister(&xpad_driver);
+#if IS_ENABLED(CONFIG_HID)
+ hid_unregister_driver(&xpad_receiver_driver);
+#endif
+}
+
+module_init(xpad_init);
+module_exit(xpad_exit);
MODULE_AUTHOR("Marko Friedemann <mfr@bmx-chemnitz.de>");
MODULE_DESCRIPTION("Xbox pad driver");
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] Input: xpad - add support for Beitong KP series controllers
2026-07-26 8:21 [PATCH v2] Input: xpad - add support for Beitong KP series controllers VegetablCat
@ 2026-07-26 8:29 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-26 8:29 UTC (permalink / raw)
To: VegetablCat; +Cc: linux-input, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Using `IS_ENABLED(CONFIG_HID)` causes undefined reference linker errors when `CONFIG_JOYSTICK_XPAD=y` and `CONFIG_HID=m`.
--
--- Patch [1]: [PATCH v2] Input: xpad - add support for Beitong KP series controllers ---
commit aae02dfc97d563806266aab4cd52d4aaca3c3e89
Author: vegetablecat <vegetablecat@foxmail.com>
Subject: Input: xpad - add support for Beitong KP series controllers
This commit adds support for Beitong KP series controllers to the xpad driver,
triggering an XUSB10 descriptor read during probe to keep the devices in XInput
mode. It also introduces an optional HID receiver driver for the KP40D receiver
dongle to prevent interference from hid-generic.
> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
> index feb8f368f834e..13b61681d6ec9 100644
> --- a/drivers/input/joystick/xpad.c
> +++ b/drivers/input/joystick/xpad.c
[ ... ]
> @@ -2312,7 +2357,79 @@ static struct usb_driver xpad_driver = {
> .id_table = xpad_table,
> };
>
> -module_usb_driver(xpad_driver);
> +#if IS_ENABLED(CONFIG_HID)
[Severity: Low]
If the kernel is built with CONFIG_JOYSTICK_XPAD=y and CONFIG_HID=m,
IS_ENABLED(CONFIG_HID) evaluates to true.
Will this cause undefined reference linker errors in xpad_receiver_probe()
and xpad_init() since vmlinux cannot link against exported HID symbols like
hid_parse and hid_register_driver which would be built into a loadable module?
Would it be better to use IS_REACHABLE(CONFIG_HID) here, or enforce this via
a Kconfig dependency like "depends on HID || HID=n" instead?
> +static int xpad_receiver_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> + int error;
> +
> + error = hid_parse(hdev);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/tencent_CD9DBF098D1B3BB565A1DD5761C1D34F4805@qq.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-26 8:29 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-26 8:21 [PATCH v2] Input: xpad - add support for Beitong KP series controllers VegetablCat
2026-07-26 8:29 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox