Added support for detecting previously unknown Xbox 360 and Xbox One controllers using the HIDAPI driver with libusb and Android

This commit is contained in:
Sam Lantinga
2020-01-18 11:21:14 -08:00
parent 27035425e0
commit 43aa1fa9e7
18 changed files with 244 additions and 141 deletions

View File

@@ -50,9 +50,9 @@ typedef struct {
} SDL_DriverGameCube_Context;
static SDL_bool
HIDAPI_DriverGameCube_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
HIDAPI_DriverGameCube_IsSupportedDevice(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
if (vendor_id == 0x057e && product_id == 0x0337) {
if (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) {
/* Nintendo Co., Ltd. Wii U GameCube Controller Adapter */
return SDL_TRUE;
}

View File

@@ -37,15 +37,6 @@
#ifdef SDL_JOYSTICK_HIDAPI_PS4
#define SONY_USB_VID 0x054C
#define SONY_DS4_PID 0x05C4
#define SONY_DS4_DONGLE_PID 0x0BA0
#define SONY_DS4_SLIM_PID 0x09CC
#define RAZER_USB_VID 0x1532
#define RAZER_PANTHERA_PID 0X0401
#define RAZER_PANTHERA_EVO_PID 0x1008
#define USB_PACKET_LENGTH 64
typedef enum
@@ -139,15 +130,15 @@ static Uint32 crc32(Uint32 crc, const void *data, int count)
}
static SDL_bool
HIDAPI_DriverPS4_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
HIDAPI_DriverPS4_IsSupportedDevice(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
return (SDL_GetJoystickGameControllerType(vendor_id, product_id, name) == SDL_CONTROLLER_TYPE_PS4);
return (SDL_GetJoystickGameControllerType(name, vendor_id, product_id, interface_number, interface_class, interface_subclass, interface_protocol) == SDL_CONTROLLER_TYPE_PS4);
}
static const char *
HIDAPI_DriverPS4_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == SONY_USB_VID) {
if (vendor_id == USB_VENDOR_SONY) {
return "PS4 Controller";
}
return NULL;
@@ -186,8 +177,8 @@ static SDL_bool CheckUSBConnected(hid_device *dev)
static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
{
/* The Razer Panthera fight stick hangs when trying to rumble */
if (vendor_id == RAZER_USB_VID &&
(product_id == RAZER_PANTHERA_PID || product_id == RAZER_PANTHERA_EVO_PID)) {
if (vendor_id == USB_VENDOR_RAZER &&
(product_id == USB_PRODUCT_RAZER_PANTHERA || product_id == USB_PRODUCT_RAZER_PANTHERA_EVO)) {
return SDL_FALSE;
}
return SDL_TRUE;
@@ -232,10 +223,10 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
device->context = ctx;
/* Check for type of connection */
ctx->is_dongle = (device->vendor_id == SONY_USB_VID && device->product_id == SONY_DS4_DONGLE_PID);
ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE);
if (ctx->is_dongle) {
ctx->is_bluetooth = SDL_FALSE;
} else if (device->vendor_id == SONY_USB_VID) {
} else if (device->vendor_id == USB_VENDOR_SONY) {
ctx->is_bluetooth = !CheckUSBConnected(device->dev);
} else {
/* Third party controllers appear to all be wired */
@@ -246,8 +237,8 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
#endif
/* Check to see if audio is supported */
if (device->vendor_id == SONY_USB_VID &&
(device->product_id == SONY_DS4_SLIM_PID || device->product_id == SONY_DS4_DONGLE_PID )) {
if (device->vendor_id == USB_VENDOR_SONY &&
(device->product_id == USB_PRODUCT_SONY_DS4_SLIM || device->product_id == USB_PRODUCT_SONY_DS4_DONGLE)) {
ctx->audio_supported = SDL_TRUE;
}
@@ -491,6 +482,12 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
static void
HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
if (ctx->rumble_expiration) {
HIDAPI_DriverPS4_RumbleJoystick(device, joystick, 0, 0, 0);
}
hid_close(device->dev);
device->dev = NULL;

View File

@@ -241,9 +241,9 @@ static SDL_bool IsGameCubeFormFactor(int vendor_id, int product_id)
}
static SDL_bool
HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
HIDAPI_DriverSwitch_IsSupportedDevice(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
return (SDL_GetJoystickGameControllerType(vendor_id, product_id, name) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
return (SDL_GetJoystickGameControllerType(name, vendor_id, product_id, interface_number, interface_class, interface_subclass, interface_protocol) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
}
static const char *
@@ -1094,6 +1094,10 @@ HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst
{
SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
if (ctx->m_nRumbleExpiration) {
HIDAPI_DriverSwitch_RumbleJoystick(device, joystick, 0, 0, 0);
}
if (!ctx->m_bInputOnly) {
/* Restore simple input mode for other applications */
SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);

View File

@@ -247,32 +247,30 @@ HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx)
#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */
static SDL_bool
HIDAPI_DriverXbox360_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
SDL_GameControllerType type = SDL_GetJoystickGameControllerType(vendor_id, product_id, name);
const Uint16 MICROSOFT_USB_VID = 0x045e;
const Uint16 NVIDIA_USB_VID = 0x0955;
const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, interface_number, interface_class, interface_subclass, interface_protocol);
if (vendor_id == NVIDIA_USB_VID) {
if (vendor_id == USB_VENDOR_NVIDIA) {
/* This is the NVIDIA Shield controller which doesn't talk Xbox controller protocol */
return SDL_FALSE;
}
if (vendor_id == MICROSOFT_USB_VID) {
if (product_id == 0x0291 || product_id == 0x0719) {
/* This is the wireless dongle, which talks a different protocol */
return SDL_FALSE;
}
if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
(type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
/* This is the wireless dongle, which talks a different protocol */
return SDL_FALSE;
}
if (interface_number > 0) {
/* This is the chatpad or other input interface, not the Xbox 360 interface */
return SDL_FALSE;
}
#if defined(__MACOSX__) || defined(__WIN32__)
if (vendor_id == 0x045e && product_id == 0x028e && version == 1) {
if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x028e && version == 1) {
/* This is the Steam Virtual Gamepad, which isn't supported by this driver */
return SDL_FALSE;
}
if (vendor_id == 0x045e && product_id == 0x02e0) {
if (vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02e0) {
/* This is the old Bluetooth Xbox One S firmware, which isn't supported by this driver */
return SDL_FALSE;
}
@@ -822,9 +820,11 @@ HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
static void
HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
#endif
if (ctx->rumble_expiration) {
HIDAPI_DriverXbox360_RumbleJoystick(device, joystick, 0, 0, 0);
}
#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
if (ctx->xinput_enabled) {

View File

@@ -45,12 +45,14 @@ typedef struct {
static SDL_bool
HIDAPI_DriverXbox360W_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
HIDAPI_DriverXbox360W_IsSupportedDevice(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
const Uint16 MICROSOFT_USB_VID = 0x045e;
const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */
SDL_GameControllerType type = SDL_GetJoystickGameControllerType(name, vendor_id, product_id, interface_number, interface_class, interface_subclass, interface_protocol);
if (vendor_id == MICROSOFT_USB_VID) {
return (product_id == 0x0291 || product_id == 0x0719);
if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x0719)) ||
(type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
return SDL_TRUE;
}
return SDL_FALSE;
}
@@ -290,6 +292,11 @@ HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
static void
HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
if (ctx->rumble_expiration) {
HIDAPI_DriverXbox360W_RumbleJoystick(device, joystick, 0, 0, 0);
}
}
static void

View File

@@ -147,11 +147,6 @@ static SDL_bool
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
{
/* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
const Uint16 USB_VENDOR_MICROSOFT = 0x045e;
const Uint16 USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH = 0x02e0;
const Uint16 USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH = 0x02fd;
const Uint16 USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH = 0x0b05;
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
@@ -165,9 +160,6 @@ IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
static SDL_bool
ControllerHasPaddles(Uint16 vendor_id, Uint16 product_id)
{
const Uint16 USB_VENDOR_MICROSOFT = 0x045e;
const Uint16 USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 = 0x0b00;
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2) {
return SDL_TRUE;
@@ -180,8 +172,6 @@ ControllerHasPaddles(Uint16 vendor_id, Uint16 product_id)
static SDL_bool
ControllerNeedsRumbleSequenceSynchronized(Uint16 vendor_id, Uint16 product_id)
{
const Uint16 USB_VENDOR_MICROSOFT = 0x045e;
if (vendor_id == USB_VENDOR_MICROSOFT) {
/* All Xbox One controllers, from model 1537 through Elite Series 2, appear to need this */
return SDL_TRUE;
@@ -221,9 +211,6 @@ SynchronizeRumbleSequence(hid_device *dev, SDL_DriverXboxOne_Context *ctx)
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
@@ -310,19 +297,19 @@ SendControllerInit(hid_device *dev, SDL_DriverXboxOne_Context *ctx)
}
static SDL_bool
HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
HIDAPI_DriverXboxOne_IsSupportedDevice(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
{
#ifdef __LINUX__
if (IsBluetoothXboxOneController(vendor_id, product_id)) {
/* We can't do rumble on this device, hid_write() fails, so don't try to open it here */
return SDL_FALSE;
}
if (vendor_id == 0x24c6 && product_id == 0x541a) {
if (vendor_id == USB_VENDOR_POWERA && product_id == 0x541a) {
/* The PowerA Mini controller, model 1240245-01, blocks while writing feature reports */
return SDL_FALSE;
}
#endif
return (SDL_GetJoystickGameControllerType(vendor_id, product_id, name) == SDL_CONTROLLER_TYPE_XBOXONE);
return (SDL_GetJoystickGameControllerType(name, vendor_id, product_id, interface_number, interface_class, interface_subclass, interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE);
}
static const char *

View File

@@ -389,7 +389,7 @@ HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, co
for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
if (driver->enabled && driver->IsSupportedDevice(vendor_id, product_id, version, -1, name)) {
if (driver->enabled && driver->IsSupportedDevice(name, vendor_id, product_id, version, -1, 0, 0, 0)) {
return SDL_TRUE;
}
}
@@ -418,7 +418,7 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
if (driver->enabled && driver->IsSupportedDevice(device->vendor_id, device->product_id, device->version, device->interface_number, device->name)) {
if (driver->enabled && driver->IsSupportedDevice(device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
return driver;
}
}
@@ -647,6 +647,9 @@ HIDAPI_AddDevice(struct hid_device_info *info)
device->product_id = info->product_id;
device->version = info->release_number;
device->interface_number = info->interface_number;
device->interface_class = info->interface_class;
device->interface_subclass = info->interface_subclass;
device->interface_protocol = info->interface_protocol;
device->usage_page = info->usage_page;
device->usage = info->usage;
{
@@ -736,7 +739,7 @@ HIDAPI_AddDevice(struct hid_device_info *info)
HIDAPI_SetupDeviceDriver(device);
#ifdef DEBUG_HIDAPI
SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE");
SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, interface_class %d, interface_subclass %d, interface_protocol %d, usage page 0x%.4x, usage 0x%.4x, path = %s, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol, device->usage_page, device->usage, device->path, device->driver ? device->driver->hint : "NONE");
#endif
}
@@ -812,6 +815,7 @@ SDL_bool
HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
SDL_HIDAPI_Device *device;
SDL_bool supported = SDL_FALSE;
SDL_bool result = SDL_FALSE;
/* Make sure we're initialized, as this could be called from other drivers during startup */
@@ -819,15 +823,24 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons
return SDL_FALSE;
}
/* Don't update the device list for devices we know aren't supported */
if (!HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name)) {
return SDL_FALSE;
/* Only update the device list for devices we know might be supported.
If we did this for every device, it would hit the USB driver too hard and potentially
lock up the system. This won't catch devices that we support but can only detect using
USB interface details, like Xbox controllers, but hopefully the device list update is
responsive enough to catch those.
*/
supported = HIDAPI_IsDeviceSupported(vendor_id, product_id, version, name);
#if defined(SDL_JOYSTICK_HIDAPI_XBOX360) || defined(SDL_JOYSTICK_HIDAPI_XBOXONE)
if (!supported &&
(SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box") || SDL_strstr(name, "XBOX"))) {
supported = SDL_TRUE;
}
/* Make sure the device list is completely up to date when we check for device presence */
if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
HIDAPI_UpdateDeviceList();
SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
#endif /* SDL_JOYSTICK_HIDAPI_XBOX360 || SDL_JOYSTICK_HIDAPI_XBOXONE */
if (supported) {
if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
HIDAPI_UpdateDeviceList();
SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
}
}
/* Note that this isn't a perfect check - there may be multiple devices with 0 VID/PID,
@@ -845,8 +858,8 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons
SDL_UnlockJoysticks();
/* If we're looking for the wireless XBox 360 controller, also look for the dongle */
if (!result && vendor_id == 0x045e && product_id == 0x02a1) {
return HIDAPI_IsDevicePresent(0x045e, 0x0719, version, name);
if (!result && vendor_id == USB_VENDOR_MICROSOFT && product_id == 0x02a1) {
return HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, 0x0719, version, name);
}
#ifdef DEBUG_HIDAPI

View File

@@ -24,6 +24,7 @@
#define SDL_JOYSTICK_HIDAPI_H
#include "../../hidapi/hidapi/hidapi.h"
#include "../usb_ids.h"
/* This is the full set of HIDAPI drivers available */
#define SDL_JOYSTICK_HIDAPI_PS4
@@ -59,6 +60,9 @@ typedef struct _SDL_HIDAPI_Device
Uint16 version;
SDL_JoystickGUID guid;
int interface_number; /* Available on Windows and Linux */
int interface_class;
int interface_subclass;
int interface_protocol;
Uint16 usage_page; /* Available on Windows and Mac OS X */
Uint16 usage; /* Available on Windows and Mac OS X */
@@ -78,7 +82,7 @@ typedef struct _SDL_HIDAPI_DeviceDriver
{
const char *hint;
SDL_bool enabled;
SDL_bool (*IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name);
SDL_bool (*IsSupportedDevice)(const char *name, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
const char *(*GetDeviceName)(Uint16 vendor_id, Uint16 product_id);
SDL_bool (*InitDevice)(SDL_HIDAPI_Device *device);
int (*GetDevicePlayerIndex)(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id);