From 3b06d64dc39cc09a64513628c6ef1770c1589523 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 24 May 2023 21:48:20 -0700 Subject: [PATCH] hidapi/libusb: enable support for Xbox 360 and Xbox One controllers Signed-off-by: Sam Lantinga --- src/hidapi/libusb/hid.c | 177 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 172 insertions(+), 5 deletions(-) diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index c0907c4ab..567d803e9 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -864,6 +864,105 @@ static uint16_t get_report_descriptor_size_from_interface_descriptors(const stru return result; } +static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc) +{ + static const int XB360_IFACE_SUBCLASS = 93; + static const int XB360_IFACE_PROTOCOL = 1; /* Wired */ + static const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */ + static const int SUPPORTED_VENDORS[] = { + 0x0079, /* GPD Win 2 */ + 0x044f, /* Thrustmaster */ + 0x045e, /* Microsoft */ + 0x046d, /* Logitech */ + 0x056e, /* Elecom */ + 0x06a3, /* Saitek */ + 0x0738, /* Mad Catz */ + 0x07ff, /* Mad Catz */ + 0x0e6f, /* PDP */ + 0x0f0d, /* Hori */ + 0x1038, /* SteelSeries */ + 0x11c9, /* Nacon */ + 0x12ab, /* Unknown */ + 0x1430, /* RedOctane */ + 0x146b, /* BigBen */ + 0x1532, /* Razer Sabertooth */ + 0x15e4, /* Numark */ + 0x162e, /* Joytech */ + 0x1689, /* Razer Onza */ + 0x1949, /* Lab126, Inc. */ + 0x1bad, /* Harmonix */ + 0x20d6, /* PowerA */ + 0x24c6, /* PowerA */ + 0x2c22, /* Qanba */ + 0x2dc8, /* 8BitDo */ + 0x9886, /* ASTRO Gaming */ + }; + + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == XB360_IFACE_SUBCLASS && + (intf_desc->bInterfaceProtocol == XB360_IFACE_PROTOCOL || + intf_desc->bInterfaceProtocol == XB360W_IFACE_PROTOCOL)) { + int i; + for (i = 0; i < sizeof(SUPPORTED_VENDORS)/sizeof(SUPPORTED_VENDORS[0]); ++i) { + if (vendor_id == SUPPORTED_VENDORS[i]) { + return 1; + } + } + } + return 0; +} + +static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc) +{ + static const int XB1_IFACE_SUBCLASS = 71; + static const int XB1_IFACE_PROTOCOL = 208; + static const int SUPPORTED_VENDORS[] = { + 0x044f, /* Thrustmaster */ + 0x045e, /* Microsoft */ + 0x0738, /* Mad Catz */ + 0x0e6f, /* PDP */ + 0x0f0d, /* Hori */ + 0x10f5, /* Turtle Beach */ + 0x1532, /* Razer Wildcat */ + 0x20d6, /* PowerA */ + 0x24c6, /* PowerA */ + 0x2dc8, /* 8BitDo */ + 0x2e24, /* Hyperkin */ + 0x3537, /* GameSir */ + }; + + if (intf_desc->bInterfaceNumber == 0 && + intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS && + intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) { + int i; + for (i = 0; i < sizeof(SUPPORTED_VENDORS)/sizeof(SUPPORTED_VENDORS[0]); ++i) { + if (vendor_id == SUPPORTED_VENDORS[i]) { + return 1; + } + } + } + return 0; +} + +static int should_enumerate_interface(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc) +{ + //printf("Checking interface 0x%x %d/%d/%d/%d\n", vendor_id, intf_desc->bInterfaceNumber, intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol); + + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) + return 1; + + /* Also enumerate Xbox 360 controllers */ + if (is_xbox360(vendor_id, intf_desc)) + return 1; + + /* Also enumerate Xbox One controllers */ + if (is_xboxone(vendor_id, intf_desc)) + return 1; + + return 0; +} + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { libusb_device **devs; @@ -907,7 +1006,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, for (k = 0; k < intf->num_altsetting; k++) { const struct libusb_interface_descriptor *intf_desc; intf_desc = &intf->altsetting[k]; - if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + if (should_enumerate_interface(dev_vid, intf_desc)) { struct hid_device_info *tmp; res = libusb_open(dev, &handle); @@ -1171,8 +1270,66 @@ static void *read_thread(void *param) return NULL; } +static void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, const struct libusb_config_descriptor *conf_desc) +{ + if ((idVendor == 0x05ac && idProduct == 0x055b) /* Gamesir-G3w */ || + idVendor == 0x0f0d /* Hori Xbox controllers */) { + unsigned char data[20]; -static int hidapi_initialize_device(hid_device *dev, int config_number, const struct libusb_interface_descriptor *intf_desc) + /* The HORIPAD FPS for Nintendo Switch requires this to enable input reports. + This VID/PID is also shared with other HORI controllers, but they all seem + to be fine with this as well. + */ + libusb_control_transfer(device_handle, 0xC1, 0x01, 0x100, 0x0, data, sizeof(data), 100); + } +} + +static void init_xboxone(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, const struct libusb_config_descriptor *conf_desc) +{ + static const int VENDOR_MICROSOFT = 0x045e; + static const int XB1_IFACE_SUBCLASS = 71; + static const int XB1_IFACE_PROTOCOL = 208; + int j, k, res; + + for (j = 0; j < conf_desc->bNumInterfaces; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS && + intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) { + int bSetAlternateSetting = 0; + + /* Newer Microsoft Xbox One controllers have a high speed alternate setting */ + if (idVendor == VENDOR_MICROSOFT && + intf_desc->bInterfaceNumber == 0 && intf_desc->bAlternateSetting == 1) { + bSetAlternateSetting = 1; + } else if (intf_desc->bInterfaceNumber != 0 && intf_desc->bAlternateSetting == 0) { + bSetAlternateSetting = 1; + } + + if (bSetAlternateSetting) { + res = libusb_claim_interface(device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + continue; + } + + LOG("Setting alternate setting for VID/PID 0x%x/0x%x interface %d to %d\n", idVendor, idProduct, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting); + + res = libusb_set_interface_alt_setting(device_handle, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting); + if (res < 0) { + LOG("xbox init: can't set alt setting %d: %d\n", intf_desc->bInterfaceNumber, res); + } + + libusb_release_interface(device_handle, intf_desc->bInterfaceNumber); + } + } + } + } +} + +static int hidapi_initialize_device(hid_device *dev, int config_number, const struct libusb_interface_descriptor *intf_desc, const struct libusb_config_descriptor *conf_desc) { int i =0; int res = 0; @@ -1209,6 +1366,16 @@ static int hidapi_initialize_device(hid_device *dev, int config_number, const st return 0; } + /* Initialize XBox 360 controllers */ + if (is_xbox360(desc.idVendor, intf_desc)) { + init_xbox360(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc); + } + + /* Initialize XBox One controllers */ + if (is_xboxone(desc.idVendor, intf_desc)) { + init_xboxone(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc); + } + /* Store off the string descriptor indexes */ dev->manufacturer_index = desc.iManufacturer; dev->product_index = desc.iProduct; @@ -1302,7 +1469,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) const struct libusb_interface *intf = &conf_desc->interface[j]; for (k = 0; k < intf->num_altsetting && !good_open; k++) { const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; - if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + if (should_enumerate_interface(desc.idVendor, intf_desc)) { char dev_path[64]; get_path(&dev_path, usb_dev, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); if (!strcmp(dev_path, path)) { @@ -1314,7 +1481,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) LOG("can't open device\n"); break; } - good_open = hidapi_initialize_device(dev, conf_desc->bConfigurationValue, intf_desc); + good_open = hidapi_initialize_device(dev, conf_desc->bConfigurationValue, intf_desc, conf_desc); if (!good_open) libusb_close(dev->device_handle); } @@ -1392,7 +1559,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys goto err; } - if (!hidapi_initialize_device(dev, conf_desc->bConfigurationValue, selected_intf_desc)) + if (!hidapi_initialize_device(dev, conf_desc->bConfigurationValue, selected_intf_desc, conf_desc)) goto err; return dev;