Re: [PATCH] Input: xpad - add support for Beitong KP series controllers

Shengyu Qu <[email protected]> Mon, 3 Aug 2026 17:53:34 +0800
Newsgroups org.kernel.vger.linux-input,org.kernel.vger.linux-kernel
Message-ID <TY4PR01MB1443203E77BE22E6D0E2E85CF98D52@TY4PR01MB14432.jpnprd01.prod.outlook.com>

在 2026/7/27 01:39, VegetablCat 写道:

> @@ -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 },

? Where does the ID come from? It's not same as the existing ID below.
Have you confirmed that those IDs are all consistent with real devices?
Or those just from some random AI slop.... Add ID only for tested
device, don't just add IDs without testing. Besides, it's already tested
that KP50B/C doesn't need such quirk and still works well.

Also I would say don't use "Controller" in device name since those are
dongles, also controller itself uses different VID from dongles
(confirmed on KP50B/C).

> +	{ 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_REACHABLE(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_REACHABLE(CONFIG_HID)
> +	error = hid_register_driver(&xpad_receiver_driver);
> +	if (error)
> +		return error;
> +#endif
> +
> +	error = usb_register(&xpad_driver);
> +	if (error) {
> +#if IS_REACHABLE(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_REACHABLE(CONFIG_HID)
> +	hid_unregister_driver(&xpad_receiver_driver);
> +#endif
> +}
> +
> +module_init(xpad_init);
> +module_exit(xpad_exit);
>   
>   MODULE_AUTHOR("Marko Friedemann <[email protected]>");
>   MODULE_DESCRIPTION("Xbox pad driver");
OpenPGP_signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEET/yoGP3p5Zl+RKVuX75KBAfJsRkFAmpwZR4ACgkQX75KBAfJ
sRnlKg//XrsU1NsrBe3fDHQZTnys0PfeveuPN5D/7wM4OjaEPhu2ixddmPbKv43c
YzL9+cv+ZOF/w1rLmDMJLs51+iQrAY1e+r+jDy3KtMyseN660jaHX7WDHFG94PCR
GOrM0Y36eFrIRGkm6oWOd+4fUNTnykPC76tR8KFV/lHqZ1TusRsRjQu55xNwzmkt
Y/rcAkEtRqfZkhzvEInmQchHiPLZsupYlIxovnPy663LoN4YR0Hmu6vUcV9eN5nB
BP+vFo4NAaFQJDz+yiTV3rrhn+qwQGy+SBT+fjMQotqXtjPVw2G8uV/0GuWhb2sW
1Mj1wClmECJ8SSJgAqDLw3A/wdyaDUuFjxvh32gETaEdPvIO0yF/B5mFJ7+KkaTg
FLJiv4IRC1Uf2bjDFin/iZnz47EARJuAUhF4kPQmdIGq9YuIMT0dh1IQqJmRgd/P
O2ATyTEISmTrRL2aCg5HKhXab5pXFdTeGP09dIqnHpZIQXD8QKNAk0Cpl17Sxh/N
W91jtvxY46GPhG5a36qKDCrtd7HaGoxz6aiz3lRBycCFNGco2141lAbt4BWOpaHR
xx3E8foJChDV2AbwUfJ1f7CBRKNosD1ucpNLxBxCmU0AvUNP2fe6KFjGXuRbcXM2
+/YNhDxspJlvkm1QVnxZRAlxEp4q1xscKUSL7OtMx6nyONDNT/k=
=QsUY
-----END PGP SIGNATURE-----