Improved Xbox One controller initialization

This commit is contained in:
Sam Lantinga 2020-01-16 15:32:39 -08:00
parent 981e0d367c
commit 4e1cc124d2
3 changed files with 74 additions and 17 deletions

View File

@ -165,6 +165,7 @@ static struct
int (*release_interface)(libusb_device_handle *dev_handle, int interface_number); int (*release_interface)(libusb_device_handle *dev_handle, int interface_number);
int (*kernel_driver_active)(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 (*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); struct libusb_transfer * (*alloc_transfer)(int iso_packets);
int (*submit_transfer)(struct libusb_transfer *transfer); int (*submit_transfer)(struct libusb_transfer *transfer);
int (*cancel_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_release_interface libusb_ctx.release_interface
#define libusb_kernel_driver_active libusb_ctx.kernel_driver_active #define libusb_kernel_driver_active libusb_ctx.kernel_driver_active
#define libusb_detach_kernel_driver libusb_ctx.detach_kernel_driver #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_alloc_transfer libusb_ctx.alloc_transfer
#define libusb_submit_transfer libusb_ctx.submit_transfer #define libusb_submit_transfer libusb_ctx.submit_transfer
#define libusb_cancel_transfer libusb_ctx.cancel_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(release_interface)
LOAD_LIBUSB_SYMBOL(kernel_driver_active) LOAD_LIBUSB_SYMBOL(kernel_driver_active)
LOAD_LIBUSB_SYMBOL(detach_kernel_driver) LOAD_LIBUSB_SYMBOL(detach_kernel_driver)
LOAD_LIBUSB_SYMBOL(set_interface_alt_setting)
LOAD_LIBUSB_SYMBOL(alloc_transfer) LOAD_LIBUSB_SYMBOL(alloc_transfer)
LOAD_LIBUSB_SYMBOL(submit_transfer) LOAD_LIBUSB_SYMBOL(submit_transfer)
LOAD_LIBUSB_SYMBOL(cancel_transfer) LOAD_LIBUSB_SYMBOL(cancel_transfer)

View File

@ -913,6 +913,39 @@ static int read_thread(void *param)
return 0; 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) 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_device_descriptor desc;
struct libusb_config_descriptor *conf_desc = NULL; struct libusb_config_descriptor *conf_desc = NULL;
int i,j,k; int i,j,k;
libusb_get_device_descriptor(usb_dev, &desc); libusb_get_device_descriptor(usb_dev, &desc);
res = libusb_get_active_config_descriptor(usb_dev, &conf_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; break;
} }
good_open = 1; good_open = 1;
#ifdef DETACH_KERNEL_DRIVER #ifdef DETACH_KERNEL_DRIVER
/* Detach the kernel driver, but only if the /* Detach the kernel driver, but only if the
device is managed by the kernel */ device is managed by the kernel */
@ -973,6 +1008,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path, int bExclusive)
} }
} }
#endif #endif
res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
if (res < 0) { if (res < 0) {
LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); 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; 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 */ /* Store off the string descriptor indexes */
dev->manufacturer_index = desc.iManufacturer; dev->manufacturer_index = desc.iManufacturer;
dev->product_index = desc.iProduct; dev->product_index = desc.iProduct;

View File

@ -86,6 +86,8 @@ static const Uint8 xboxone_rumble_reset[] = {
typedef struct { typedef struct {
Uint16 vendor_id; Uint16 vendor_id;
Uint16 product_id; Uint16 product_id;
Uint16 exclude_vendor_id;
Uint16 exclude_product_id;
const Uint8 *data; const Uint8 *data;
int size; int size;
const Uint8 response[2]; const Uint8 response[2];
@ -93,13 +95,16 @@ typedef struct {
static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = { static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
{ 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xf0 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init0, sizeof(xboxone_init0), { 0x04, 0xf0 } },
{ 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x04, 0xb0 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init1, sizeof(xboxone_init1), { 0x04, 0xb0 } },
{ 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init2, sizeof(xboxone_init2), { 0x00, 0x00 } },
{ 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init3, sizeof(xboxone_init3), { 0x00, 0x00 } },
{ 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } }, { 0x0000, 0x0000, 0x0000, 0x0000, xboxone_init4, sizeof(xboxone_init4), { 0x00, 0x00 } },
{ 0x0000, 0x0000, xboxone_init5, sizeof(xboxone_init5), { 0x00, 0x00 } }, /* These next packets are required for third party controllers (PowerA, PDP, HORI),
{ 0x0000, 0x0000, xboxone_init6, sizeof(xboxone_init6), { 0x00, 0x00 } }, 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 { typedef struct {
@ -137,15 +142,11 @@ static SDL_bool
ControllerNeedsRumbleSequenceSynchronized(Uint16 vendor_id, Uint16 product_id) ControllerNeedsRumbleSequenceSynchronized(Uint16 vendor_id, Uint16 product_id)
{ {
const Uint16 USB_VENDOR_MICROSOFT = 0x045e; 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 (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_MODEL_1708 || /* All Xbox One controllers, from model 1537 through Elite Series 2, appear to need this */
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES2) {
return SDL_TRUE; return SDL_TRUE;
} }
}
return SDL_FALSE; return SDL_FALSE;
} }
@ -177,12 +178,12 @@ SynchronizeRumbleSequence(hid_device *dev, SDL_DriverXboxOne_Context *ctx)
return SDL_TRUE; return SDL_TRUE;
} }
/* Return true if this controller sends the 0x02 "waiting for init" packet */ /* Return true if this controller sends the 0x02 "waiting for init" packet */
static SDL_bool static SDL_bool
ControllerSendsWaitingForInit(Uint16 vendor_id, Uint16 product_id) ControllerSendsWaitingForInit(Uint16 vendor_id, Uint16 product_id)
{ {
const Uint16 USB_VENDOR_HYPERKIN = 0x2e24; const Uint16 USB_VENDOR_HYPERKIN = 0x2e24;
const Uint16 USB_VENDOR_PDP = 0x0e6f;
if (vendor_id == USB_VENDOR_HYPERKIN) { if (vendor_id == USB_VENDOR_HYPERKIN) {
/* The Hyperkin controllers always send 0x02 when waiting for init, /* 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. to make sure we don't send the init sequence if it isn't needed.
*/ */
return SDL_TRUE; return SDL_TRUE;
} else { }
/* Other controllers may or may not send 0x02, but it doesn't hurt to reinit */ if (vendor_id == USB_VENDOR_PDP) {
/* The PDP and PowerA controllers don't always send 0x02 when plugged in on Linux */ /* The PDP Rock Candy (PID 0x0246) doesn't send 0x02 on Linux for some reason */
return SDL_FALSE; 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 static SDL_bool
@ -218,6 +223,14 @@ SendControllerInit(hid_device *dev, SDL_DriverXboxOne_Context *ctx)
continue; 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); SDL_memcpy(init_packet, packet->data, packet->size);
if (init_packet[0] != 0x01) { if (init_packet[0] != 0x01) {
init_packet[2] = ctx->sequence++; init_packet[2] = ctx->sequence++;