Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion drivers/input/joystick/xpad.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
*/

#include <linux/bits.h>
#include <linux/hid.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/rcupdate.h>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -332,8 +334,36 @@ 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, "BEITONG BTP-KP20A/BTP-KP40A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5126, "BEITONG BTP-KP20A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5127, "BEITONG BTP-KP20A/BTP-KP40A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5128, "BEITONG BTP-KP20A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x512f, "BEITONG BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5130, "BEITONG BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5133, "BEITONG BTP-KP50B Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5134, "BETOP BTP-KP50B Xinput Dongle", 0, XTYPE_XBOX360 },
{ 0x20bc, 0x5145, "BEITONG BTP-KP40A/BTP-KP40B Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5146, "BEITONG BTP-KP40A/BTP-KP40B Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5149, "BEITONG BTP-KP50C Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x514a, "BETOP BTP-KP50C Xinput Dongle", 0, XTYPE_XBOX360 },
{ 0x20bc, 0x5150, "BEITONG BTP-KP50D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5151, "BEITONG BTP-KP50D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5152, "BEITONG BTP-KP50E Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5153, "BEITONG BTP-KP50E Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5154, "BEITONG BTP-KP40D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5155, "BEITONG BTP-KP40D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5158, "BEITONG BTP-KP20D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5159, "BEITONG BTP-KP20D Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x515b, "BEITONG BTP-KP40D Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x515c, "BEITONG BTP-KP40D Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x515d, "BEITONG BTP-KP40F Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x515e, "BEITONG BTP-KP40F Controller (White)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x515f, "BEITONG BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5160, "BEITONG BTP-KP70A Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x5169, "BEITONG BTP-KP40F Controller (Black)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x516a, "BEITONG BTP-KP40F Controller (Black)", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x518a, "BEITONG BTP-KP70C NTE Edition Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 0x20bc, 0x518b, "BEITONG BTP-KP70C NTE Edition Wireless Controller", 0, XTYPE_XBOX360, FLAG_READ_XUSB10 },
{ 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 },
Expand Down Expand Up @@ -778,6 +808,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);
Expand Down Expand Up @@ -2044,6 +2075,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)
Expand Down Expand Up @@ -2082,6 +2114,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);
Expand Down Expand Up @@ -2148,6 +2182,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 */
Expand Down Expand Up @@ -2312,7 +2359,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;
Comment on lines +2367 to +2373

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): Double-check the HID receiver VID/PID for consistency with the USB IDs above

The HID receiver entry uses VID 0x20dd / PID 0x515c, but the related BEITONG/BETOP USB xpad entries use VID 0x20bc with 0x515x PIDs. Please verify this VID against the actual receiver descriptor; if it was meant to be 0x20bc, correcting it now will prevent a runtime mismatch where the dongle is not bound correctly.

{ }
};
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 <mfr@bmx-chemnitz.de>");
MODULE_DESCRIPTION("Xbox pad driver");
Expand Down