Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 1/2] Input: xpad - query MS OS descriptor for BTP-KP20D
@ 2026-07-25  7:28 Aaron Ma
  2026-07-25  7:28 ` [PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support Aaron Ma
  0 siblings, 1 reply; 3+ messages in thread
From: Aaron Ma @ 2026-07-25  7:28 UTC (permalink / raw)
  To: Dmitry Torokhov,
	open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,
	linux-kernel
  Cc: Sanjay Govind, Vicki Pfau, Shengyu Qu, Aaron Ma, Qbeliw Tanaka,
	Elliot Tester, Kees Cook, Dmitriy Zharov

The Beitong BTP-KP20D dongle cycles through its operating modes and
never becomes usable unless the host requests the Microsoft OS string
descriptor at index 0xee. Windows makes this request while discovering
the XUSB10 compatible ID, but Linux USB core does not query Microsoft
OS descriptors.

Add a device-specific quirk that performs the request during probe,
allowing the dongle to remain in Xbox 360 mode.

Signed-off-by: Aaron Ma <mapengyu@gmail.com>
---
 drivers/input/joystick/xpad.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index feb8f368f834e..1616047e65f5e 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -72,6 +72,7 @@
 #define PKT_XBE2_FW_5_11    4
 
 #define FLAG_DELAY_INIT BIT(0)
+#define FLAG_MS_OS_DESC BIT(1)
 
 static bool dpad_to_buttons;
 module_param(dpad_to_buttons, bool, S_IRUGO);
@@ -334,6 +335,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, 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 },
 	{ 0x20d6, 0x2064, "PowerA Wired Controller for Xbox", MAP_SHARE_BUTTON, XTYPE_XBOXONE },
@@ -786,6 +788,24 @@ 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 void xpad_query_ms_os_descriptor(struct usb_device *udev)
+{
+	u8 *descriptor;
+	int error;
+
+	descriptor = kmalloc(18, GFP_KERNEL);
+	if (!descriptor)
+		return;
+
+	error = usb_get_descriptor(udev, USB_DT_STRING, 0xee,
+				   descriptor, 18);
+	if (error != 18)
+		dev_warn(&udev->dev,
+			 "unable to query MS OS descriptor: %d\n", error);
+
+	kfree(descriptor);
+}
+
 /*
  *	xpad_process_packet
  *
@@ -2055,6 +2075,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
 			break;
 	}
 
+	if (xpad_device[i].flags & FLAG_MS_OS_DESC)
+		xpad_query_ms_os_descriptor(udev);
+
 	xpad = kzalloc_obj(*xpad);
 	if (!xpad)
 		return -ENOMEM;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support
  2026-07-25  7:28 [PATCH 1/2] Input: xpad - query MS OS descriptor for BTP-KP20D Aaron Ma
@ 2026-07-25  7:28 ` Aaron Ma
  2026-07-25  7:48   ` sashiko-bot
  0 siblings, 1 reply; 3+ messages in thread
From: Aaron Ma @ 2026-07-25  7:28 UTC (permalink / raw)
  To: Dmitry Torokhov,
	open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,
	linux-kernel
  Cc: Sanjay Govind, Vicki Pfau, Shengyu Qu, Aaron Ma, Qbeliw Tanaka,
	Elliot Tester, Kees Cook, Dmitriy Zharov

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@gmail.com>
---
 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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support
  2026-07-25  7:28 ` [PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support Aaron Ma
@ 2026-07-25  7:48   ` sashiko-bot
  0 siblings, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-07-25  7:48 UTC (permalink / raw)
  To: Aaron Ma; +Cc: dmitry.torokhov, linux-input

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] Probe error path after input_register_device() causes a Use-After-Free risk because xpad driver data is unconditionally freed while the input device is visible to userspace.

Pre-existing issues:
- [Critical] The probe error path frees the output URB (irq_out) and its DMA buffer without killing the active URB, causing memory corruption.
--

--- Patch [2]: [PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support ---
commit ec428f1a8b8e609c623920e0839f0af31cf70bb0
Author: Aaron Ma <mapengyu@gmail.com>

Input: xpad - add wired Beitong BTP-KP20D support

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.

> 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

[ ... ]

> @@ -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;

[Severity: High]
If xpad_start_input() fails here, we jump to err_deinit_input which 
unregisters the input device, and eventually falls through to kfree(xpad) 
in xpad_probe(). 

Because xpad_init_input() has already successfully called 
input_register_device(), the device is visible to userspace.

Could a local user open the evdev node immediately after registration and 
issue an EVIOCSFF ioctl concurrently? If so, input_ff_upload() would call 
xpad_play_effect(), which might dereference xpad after it has been freed 
since input_unregister_device() does not block concurrent ioctls that 
have already passed the evdev->exist check.

> +		}
>  	}
>  	return 0;
>  
> +err_deinit_input:
> +	xpad_deinit_input(xpad);
>  err_deinit_output:
>  	xpad_deinit_output(xpad);

[Severity: Critical]
This is a pre-existing issue, but this new fallible xpad_start_input() 
call creates a new way to trigger it.

If we take the error path to err_deinit_output after xpad_init_input() 
succeeds (which calls xpad_led_probe() and submits the irq_out URB for 
Xbox 360 controllers), xpad_deinit_output() will free the irq_out URB 
and its odata DMA buffer.

However, it does not appear to call xpad_stop_output() or usb_kill_urb(). 
Can this lead to memory corruption if the USB host controller continues 
writing to the freed odata DMA buffer, and the URB completion handler 
xpad_irq_out() executes and dereferences the freed xpad structure?

>  err_free_in_urb:
>  	usb_free_urb(xpad->irq_in);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260725072813.339112-1-mapengyu@gmail.com?part=2

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-25  7:48 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-25  7:28 [PATCH 1/2] Input: xpad - query MS OS descriptor for BTP-KP20D Aaron Ma
2026-07-25  7:28 ` [PATCH 2/2] Input: xpad - add wired Beitong BTP-KP20D support Aaron Ma
2026-07-25  7:48   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox