diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index feb8f368f834e..bf7fcad6022e8 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -28,6 +28,7 @@ */ #include +#include #include #include #include @@ -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,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 }, @@ -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); @@ -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) @@ -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); @@ -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 */ @@ -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; + + 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 "); MODULE_DESCRIPTION("Xbox pad driver");