From 4e1cc124d2bd68134cfb48e36f34b32077c4a3a7 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 16 Jan 2020 15:32:39 -0800 Subject: [PATCH] Improved Xbox One controller initialization --- src/hidapi/SDL_hidapi.c | 3 ++ src/hidapi/libusb/hid.c | 41 +++++++++++++++++++++ src/joystick/hidapi/SDL_hidapi_xboxone.c | 47 +++++++++++++++--------- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c index f320c76a5..2d30f2805 100644 --- a/src/hidapi/SDL_hidapi.c +++ b/src/hidapi/SDL_hidapi.c @@ -165,6 +165,7 @@ static struct int (*release_interface)(libusb_device_handle *dev_handle, int interface_number); int (*kernel_driver_active)(libusb_device_handle *dev_handle, int interface_number); int (*detach_kernel_driver)(libusb_device_handle *dev_handle, int interface_number); + int (*set_interface_alt_setting)(libusb_device_handle *dev, int interface_number, int alternate_setting); struct libusb_transfer * (*alloc_transfer)(int iso_packets); int (*submit_transfer)(struct libusb_transfer *transfer); int (*cancel_transfer)(struct libusb_transfer *transfer); @@ -207,6 +208,7 @@ static struct #define libusb_release_interface libusb_ctx.release_interface #define libusb_kernel_driver_active libusb_ctx.kernel_driver_active #define libusb_detach_kernel_driver libusb_ctx.detach_kernel_driver +#define libusb_set_interface_alt_setting libusb_ctx.set_interface_alt_setting #define libusb_alloc_transfer libusb_ctx.alloc_transfer #define libusb_submit_transfer libusb_ctx.submit_transfer #define libusb_cancel_transfer libusb_ctx.cancel_transfer @@ -472,6 +474,7 @@ int HID_API_EXPORT HID_API_CALL hid_init(void) LOAD_LIBUSB_SYMBOL(release_interface) LOAD_LIBUSB_SYMBOL(kernel_driver_active) LOAD_LIBUSB_SYMBOL(detach_kernel_driver) + LOAD_LIBUSB_SYMBOL(set_interface_alt_setting) LOAD_LIBUSB_SYMBOL(alloc_transfer) LOAD_LIBUSB_SYMBOL(submit_transfer) LOAD_LIBUSB_SYMBOL(cancel_transfer) diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index ce847bd27..d6dfaf38f 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -913,6 +913,39 @@ static int read_thread(void *param) return 0; } +static void init_xboxone(libusb_device_handle *device_handle, struct libusb_config_descriptor *conf_desc) +{ + 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_desc = &intf->altsetting[k]; + + if (intf_desc->bInterfaceNumber != 0 && + intf_desc->bAlternateSetting == 0 && + intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == XB1_IFACE_SUBCLASS && + intf_desc->bInterfaceProtocol == XB1_IFACE_PROTOCOL) { + 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; + } + + 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); + } + } + } +} hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive) { @@ -934,6 +967,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive) struct libusb_device_descriptor desc; struct libusb_config_descriptor *conf_desc = NULL; int i,j,k; + libusb_get_device_descriptor(usb_dev, &desc); res = libusb_get_active_config_descriptor(usb_dev, &conf_desc); @@ -959,6 +993,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive) break; } good_open = 1; + #ifdef DETACH_KERNEL_DRIVER /* Detach the kernel driver, but only if the device is managed by the kernel */ @@ -973,6 +1008,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive) } } #endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); if (res < 0) { LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); @@ -982,6 +1018,11 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive) break; } + /* Initialize XBox One controllers */ + if (is_xboxone(desc.idVendor, intf_desc)) { + init_xboxone(dev->device_handle, conf_desc); + } + /* Store off the string descriptor indexes */ dev->manufacturer_index = desc.iManufacturer; dev->product_index = desc.iProduct; diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index 3f4c7f4c6..af09f6e10 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -86,6 +86,8 @@ static const Uint8 xboxone_rumble_reset[] = { typedef struct { Uint16 vendor_id; Uint16 product_id; + Uint16 exclude_vendor_id; + Uint16 exclude_product_id; const Uint8 *data; int size; const Uint8 response[2]; @@ -93,13 +95,16 @@ typedef struct { static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = { - { 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xf0 } }, - { 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x04, 0xb0 } }, - { 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } }, - { 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } }, - { 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } }, - { 0x0000, 0x0000, xboxone_init5, sizeof(xboxone_init5), { 0x00, 0x00 } }, - { 0x0000, 0x0000, xboxone_init6, sizeof(xboxone_init6), { 0x00, 0x00 } }, + { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xf0 } }, + { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x04, 0xb0 } }, + { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } }, + { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } }, + { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } }, + /* These next packets are required for third party controllers (PowerA, PDP, HORI), + but are the wrong protocol for Microsoft Xbox controllers. + */ + { 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init5, sizeof(xboxone_init5), { 0x00, 0x00 } }, + { 0x0000, 0x0000, 0x045e, 0x0000, xboxone_init6, sizeof(xboxone_init6), { 0x00, 0x00 } }, }; typedef struct { @@ -137,14 +142,10 @@ static SDL_bool ControllerNeedsRumbleSequenceSynchronized(Uint16 vendor_id, Uint16 product_id) { const Uint16 USB_VENDOR_MICROSOFT = 0x045e; - const Uint16 USB_PRODUCT_XBOX_ONE_MODEL_1708 = 0x02ea; /* Needed with the latest firmware */ - const Uint16 USB_PRODUCT_XBOX_ONE_ELITE_SERIES2 = 0x0b00; if (vendor_id == USB_VENDOR_MICROSOFT) { - if (product_id == USB_PRODUCT_XBOX_ONE_MODEL_1708 || - product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES2) { - return SDL_TRUE; - } + /* All Xbox One controllers, from model 1537 through Elite Series 2, appear to need this */ + return SDL_TRUE; } return SDL_FALSE; } @@ -177,12 +178,12 @@ SynchronizeRumbleSequence(hid_device *dev, SDL_DriverXboxOne_Context *ctx) return SDL_TRUE; } - /* Return true if this controller sends the 0x02 "waiting for init" packet */ static SDL_bool ControllerSendsWaitingForInit(Uint16 vendor_id, Uint16 product_id) { const Uint16 USB_VENDOR_HYPERKIN = 0x2e24; + const Uint16 USB_VENDOR_PDP = 0x0e6f; if (vendor_id == USB_VENDOR_HYPERKIN) { /* The Hyperkin controllers always send 0x02 when waiting for init, @@ -190,11 +191,15 @@ ControllerSendsWaitingForInit(Uint16 vendor_id, Uint16 product_id) to make sure we don't send the init sequence if it isn't needed. */ return SDL_TRUE; - } else { - /* Other controllers may or may not send 0x02, but it doesn't hurt to reinit */ - /* The PDP and PowerA controllers don't always send 0x02 when plugged in on Linux */ + } + if (vendor_id == USB_VENDOR_PDP) { + /* The PDP Rock Candy (PID 0x0246) doesn't send 0x02 on Linux for some reason */ return SDL_FALSE; } + + /* It doesn't hurt to reinit, especially if a driver has misconfigured the controller */ + /*return SDL_TRUE;*/ + return SDL_FALSE; } static SDL_bool @@ -218,6 +223,14 @@ SendControllerInit(hid_device *dev, SDL_DriverXboxOne_Context *ctx) continue; } + if (packet->exclude_vendor_id && (vendor_id == packet->exclude_vendor_id)) { + continue; + } + + if (packet->exclude_product_id && (product_id == packet->exclude_product_id)) { + continue; + } + SDL_memcpy(init_packet, packet->data, packet->size); if (init_packet[0] != 0x01) { init_packet[2] = ctx->sequence++;