[PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support
From: Aaron Ma
Date: Sat Jul 25 2026 - 03:33:02 EST
The wired BTP-KP20D resets itself when its interrupt-IN pipe is idle
for a few seconds, causing periodic re-enumeration whenever no
userspace client holds the input device open. Add a FLAG_KEEPALIVE
quirk that keeps the input URB running from probe to disconnect,
mirroring the XTYPE_XBOX360W URB lifecycle.
The device also needs the MS OS descriptor query to stay in Xbox 360
mode.
Signed-off-by: Aaron Ma <mapengyu@xxxxxxxxx>
---
drivers/input/joystick/xpad.c | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 1616047e65f5e..5369b880c6a08 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -73,6 +73,7 @@
#define FLAG_DELAY_INIT BIT(0)
#define FLAG_MS_OS_DESC BIT(1)
+#define FLAG_KEEPALIVE BIT(2)
static bool dpad_to_buttons;
module_param(dpad_to_buttons, bool, S_IRUGO);
@@ -335,6 +336,7 @@ static const struct xpad_device {
{ 0x1ee9, 0x1590, "ZOTAC Gaming Zone", 0, XTYPE_XBOX360 },
{ 0x20bc, 0x5134, "BETOP BTP-KP50B Xinput Dongle", 0, XTYPE_XBOX360 },
{ 0x20bc, 0x514a, "BETOP BTP-KP50C Xinput Dongle", 0, XTYPE_XBOX360 },
+ { 0x20bc, 0x5158, "Beitong BTP-KP20D Controller", 0, XTYPE_XBOX360, FLAG_MS_OS_DESC | FLAG_KEEPALIVE },
{ 0x20bc, 0x5159, "Beitong BTP-KP20D Controller", 0, XTYPE_XBOX360, FLAG_MS_OS_DESC },
{ 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
{ 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
@@ -780,6 +782,7 @@ struct usb_xpad {
time64_t mode_btn_down_ts;
bool delay_init; /* init packets should be delayed */
bool delayed_init_done;
+ bool keepalive; /* int URB must run from probe to disconnect */
};
static int xpad_init_input(struct usb_xpad *xpad);
@@ -1971,7 +1974,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
input_set_drvdata(input_dev, xpad);
- if (xpad->xtype != XTYPE_XBOX360W) {
+ if (xpad->xtype != XTYPE_XBOX360W && !xpad->keepalive) {
input_dev->open = xpad_open;
input_dev->close = xpad_close;
}
@@ -2105,6 +2108,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_KEEPALIVE)
+ xpad->keepalive = true;
xpad->packet_type = PKT_XB;
INIT_WORK(&xpad->work, xpad_presence_work);
@@ -2227,9 +2232,22 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
error = xpad_init_input(xpad);
if (error)
goto err_deinit_output;
+
+ if (xpad->keepalive) {
+ /*
+ * The device resets itself when the interrupt-IN pipe
+ * is idle for a few seconds, so keep the URB running
+ * from probe until disconnect.
+ */
+ error = xpad_start_input(xpad);
+ if (error)
+ goto err_deinit_input;
+ }
}
return 0;
+err_deinit_input:
+ xpad_deinit_input(xpad);
err_deinit_output:
xpad_deinit_output(xpad);
err_free_in_urb:
@@ -2247,6 +2265,8 @@ static void xpad_disconnect(struct usb_interface *intf)
if (xpad->xtype == XTYPE_XBOX360W)
xpad360w_stop_input(xpad);
+ else if (xpad->keepalive)
+ xpad_stop_input(xpad);
xpad_deinit_input(xpad);
@@ -2288,6 +2308,8 @@ static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
*/
if (auto_poweroff && xpad->pad_present)
xpad360w_poweroff_controller(xpad);
+ } else if (xpad->keepalive) {
+ xpad_stop_input(xpad);
} else {
guard(mutex)(&input->mutex);
@@ -2309,6 +2331,9 @@ static int xpad_resume(struct usb_interface *intf)
if (xpad->xtype == XTYPE_XBOX360W)
return xpad360w_start_input(xpad);
+ if (xpad->keepalive)
+ return xpad_start_input(xpad);
+
guard(mutex)(&input->mutex);
if (input_device_enabled(input))
--
2.53.0