Skip to content

Commit

Permalink
Improved Xbox One controller initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
slouken committed Jan 16, 2020
1 parent 981e0d3 commit 4e1cc12
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 17 deletions.
3 changes: 3 additions & 0 deletions src/hidapi/SDL_hidapi.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
41 changes: 41 additions & 0 deletions src/hidapi/libusb/hid.c
Expand Up @@ -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)
{
Expand All @@ -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);
Expand All @@ -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 */
Expand All @@ -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);
Expand All @@ -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;
Expand Down
47 changes: 30 additions & 17 deletions src/joystick/hidapi/SDL_hidapi_xboxone.c
Expand Up @@ -86,20 +86,25 @@ 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];
} SDL_DriverXboxOne_InitPacket;


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 {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -177,24 +178,28 @@ 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,
and the Hyperkin Duke plays an Xbox startup animation, so we want
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
Expand All @@ -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++;
Expand Down

0 comments on commit 4e1cc12

Please sign in to comment.