From 43aa1fa9e7ebd716b51087032556850ff675efd8 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 18 Jan 2020 11:21:14 -0800 Subject: [PATCH] Added support for detecting previously unknown Xbox 360 and Xbox One controllers using the HIDAPI driver with libusb and Android --- .../app/HIDDeviceBLESteamController.java | 2 +- .../java/org/libsdl/app/HIDDeviceManager.java | 25 +--- src/hidapi/SDL_hidapi.c | 6 + src/hidapi/android/hid.cpp | 7 +- src/hidapi/hidapi/hidapi.h | 6 + src/hidapi/libusb/hid.c | 3 + src/hidapi/mac/hid.c | 45 +++--- src/joystick/SDL_joystick.c | 135 ++++++++++++++---- src/joystick/SDL_joystick_c.h | 2 +- src/joystick/hidapi/SDL_hidapi_gamecube.c | 4 +- src/joystick/hidapi/SDL_hidapi_ps4.c | 33 ++--- src/joystick/hidapi/SDL_hidapi_switch.c | 8 +- src/joystick/hidapi/SDL_hidapi_xbox360.c | 28 ++-- src/joystick/hidapi/SDL_hidapi_xbox360w.c | 15 +- src/joystick/hidapi/SDL_hidapi_xboxone.c | 19 +-- src/joystick/hidapi/SDL_hidapijoystick.c | 39 +++-- src/joystick/hidapi/SDL_hidapijoystick_c.h | 6 +- src/joystick/windows/SDL_dinputjoystick.c | 2 +- 18 files changed, 244 insertions(+), 141 deletions(-) diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java index 7e104b698..dcc4ffe0d 100644 --- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java @@ -470,7 +470,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe // Only register controller with the native side once it has been fully configured if (!isRegistered()) { Log.v(TAG, "Registering Steam Controller with ID: " + getId()); - mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0); + mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0); setRegistered(); } } diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index 38ed44458..56a65de11 100644 --- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -241,17 +241,7 @@ public class HIDDeviceManager { } } - private boolean isHIDDeviceUSB(UsbDevice usbDevice) { - for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); ++interface_number) { - if (isHIDDeviceInterface(usbDevice, interface_number)) { - return true; - } - } - return false; - } - - private boolean isHIDDeviceInterface(UsbDevice usbDevice, int interface_number) { - UsbInterface usbInterface = usbDevice.getInterface(interface_number); + private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) { if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { return true; } @@ -331,9 +321,7 @@ public class HIDDeviceManager { } private void handleUsbDeviceAttached(UsbDevice usbDevice) { - if (isHIDDeviceUSB(usbDevice)) { - connectHIDDeviceUSB(usbDevice); - } + connectHIDDeviceUSB(usbDevice); } private void handleUsbDeviceDetached(UsbDevice usbDevice) { @@ -366,11 +354,12 @@ public class HIDDeviceManager { private void connectHIDDeviceUSB(UsbDevice usbDevice) { synchronized (this) { for (int interface_number = 0; interface_number < usbDevice.getInterfaceCount(); interface_number++) { - if (isHIDDeviceInterface(usbDevice, interface_number)) { - HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_number); + UsbInterface usbInterface = usbDevice.getInterface(interface_number); + if (isHIDDeviceInterface(usbDevice, usbInterface)) { + HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, usbInterface.getId()); int id = device.getId(); mDevicesById.put(id, device); - HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), interface_number); + HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol()); } } } @@ -670,7 +659,7 @@ public class HIDDeviceManager { private native void HIDDeviceRegisterCallback(); private native void HIDDeviceReleaseCallback(); - native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number); + native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol); native void HIDDeviceOpenPending(int deviceID); native void HIDDeviceOpenResult(int deviceID, boolean opened); native void HIDDeviceDisconnected(int deviceID); diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c index 1cf3e1bc9..247de2ebc 100644 --- a/src/hidapi/SDL_hidapi.c +++ b/src/hidapi/SDL_hidapi.c @@ -419,6 +419,9 @@ LIBUSB_CopyHIDDeviceInfo(struct LIBUSB_hid_device_info *pSrc, pDst->usage_page = pSrc->usage_page; pDst->usage = pSrc->usage; pDst->interface_number = pSrc->interface_number; + pDst->interface_class = pSrc->interface_class; + pDst->interface_subclass = pSrc->interface_subclass; + pDst->interface_protocol = pSrc->interface_protocol; pDst->next = NULL; } #endif /* SDL_LIBUSB_DYNAMIC */ @@ -438,6 +441,9 @@ PLATFORM_CopyHIDDeviceInfo(struct PLATFORM_hid_device_info *pSrc, pDst->usage_page = pSrc->usage_page; pDst->usage = pSrc->usage; pDst->interface_number = pSrc->interface_number; + pDst->interface_class = pSrc->interface_class; + pDst->interface_subclass = pSrc->interface_subclass; + pDst->interface_protocol = pSrc->interface_protocol; pDst->next = NULL; } #endif /* HAVE_PLATFORM_BACKEND */ diff --git a/src/hidapi/android/hid.cpp b/src/hidapi/android/hid.cpp index df82dcc97..dd0edf1c6 100644 --- a/src/hidapi/android/hid.cpp +++ b/src/hidapi/android/hid.cpp @@ -739,7 +739,7 @@ extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallback)(JNIEnv *env, jobject thiz); extern "C" -JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface ); +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol ); extern "C" JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceOpenPending)(JNIEnv *env, jobject thiz, int nDeviceID); @@ -828,7 +828,7 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceReleaseCallbac } extern "C" -JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface ) +JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNIEnv *env, jobject thiz, int nDeviceID, jstring sIdentifier, int nVendorId, int nProductId, jstring sSerialNumber, int nReleaseNumber, jstring sManufacturer, jstring sProduct, int nInterface, int nInterfaceClass, int nInterfaceSubclass, int nInterfaceProtocol ) { LOGV( "HIDDeviceConnected() id=%d VID/PID = %.4x/%.4x, interface %d\n", nDeviceID, nVendorId, nProductId, nInterface ); @@ -842,6 +842,9 @@ JNIEXPORT void JNICALL HID_DEVICE_MANAGER_JAVA_INTERFACE(HIDDeviceConnected)(JNI pInfo->manufacturer_string = CreateWStringFromJString( env, sManufacturer ); pInfo->product_string = CreateWStringFromJString( env, sProduct ); pInfo->interface_number = nInterface; + pInfo->interface_class = nInterfaceClass; + pInfo->interface_subclass = nInterfaceSubclass; + pInfo->interface_protocol = nInterfaceProtocol; hid_device_ref pDevice( new CHIDDevice( nDeviceID, pInfo ) ); diff --git a/src/hidapi/hidapi/hidapi.h b/src/hidapi/hidapi/hidapi.h index 0a36e4293..de3e57260 100644 --- a/src/hidapi/hidapi/hidapi.h +++ b/src/hidapi/hidapi/hidapi.h @@ -78,6 +78,12 @@ namespace NAMESPACE { only if the device contains more than one interface. */ int interface_number; + /** Additional information about the USB interface. + Valid on libusb and Android implementations. */ + int interface_class; + int interface_subclass; + int interface_protocol; + /** Pointer to the next device */ struct hid_device_info *next; }; diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index 967c86f73..bcf51ea2a 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -714,6 +714,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, /* Interface Number */ cur_dev->interface_number = interface_num; + cur_dev->interface_class = intf_desc->bInterfaceClass; + cur_dev->interface_subclass = intf_desc->bInterfaceSubClass; + cur_dev->interface_protocol = intf_desc->bInterfaceProtocol; } } } /* altsettings */ diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c index f6420e187..70a2fb0d3 100644 --- a/src/hidapi/mac/hid.c +++ b/src/hidapi/mac/hid.c @@ -41,10 +41,10 @@ StackOverflow. It is used with his permission. */ typedef int pthread_barrierattr_t; typedef struct pthread_barrier { - pthread_mutex_t mutex; - pthread_cond_t cond; - int count; - int trip_count; + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; } pthread_barrier_t; static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) @@ -178,22 +178,22 @@ static void free_hid_device(hid_device *dev) free(dev->input_report_buf); if (device_list) { - if (device_list->dev == dev) { - device_list = device_list->next; - } - else { - struct hid_device_list_node *node = device_list; - while (node) { - if (node->next && node->next->dev == dev) { - struct hid_device_list_node *new_next = node->next->next; - free(node->next); - node->next = new_next; - break; - } + if (device_list->dev == dev) { + device_list = device_list->next; + } + else { + struct hid_device_list_node *node = device_list; + while (node) { + if (node->next && node->next->dev == dev) { + struct hid_device_list_node *new_next = node->next->next; + free(node->next); + node->next = new_next; + break; + } - node = node->next; - } - } + node = node->next; + } + } } /* Clean up the thread objects */ @@ -478,9 +478,10 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, IOHIDDeviceRef dev = device_array[i]; - if (!dev) { - continue; - } + if (!dev) { + continue; + } + dev_vid = get_vendor_id(dev); dev_pid = get_product_id(dev); diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index dd1f76e1d..263965f37 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -751,17 +751,17 @@ SDL_JoystickSetPlayerIndex(SDL_Joystick * joystick, int player_index) int SDL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { - int result; + int result; if (!SDL_PrivateJoystickValid(joystick)) { return -1; } - SDL_LockJoysticks(); + SDL_LockJoysticks(); result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); - SDL_UnlockJoysticks(); + SDL_UnlockJoysticks(); - return result; + return result; } /* @@ -1352,7 +1352,7 @@ SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *nam Uint16 vendor, product; SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL); - type = SDL_GetJoystickGameControllerType(vendor, product, name); + type = SDL_GetJoystickGameControllerType(name, vendor, product, -1, 0, 0, 0); if (type == SDL_CONTROLLER_TYPE_UNKNOWN) { if (SDL_IsJoystickXInput(guid)) { /* This is probably an Xbox One controller */ @@ -1363,37 +1363,120 @@ SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *nam } SDL_GameControllerType -SDL_GetJoystickGameControllerType(Uint16 vendor, Uint16 product, const char *name) +SDL_GetJoystickGameControllerType(const char *name, Uint16 vendor, Uint16 product, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { + SDL_GameControllerType type = SDL_CONTROLLER_TYPE_UNKNOWN; + if (vendor == 0x0000 && product == 0x0000) { /* Some devices are only identifiable by their name */ if (SDL_strcmp(name, "Lic Pro Controller") == 0 || SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 || SDL_strcmp(name, "Wireless Gamepad") == 0) { /* HORI or PowerA Switch Pro Controller clone */ - return SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; + type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; + } else { + type = SDL_CONTROLLER_TYPE_UNKNOWN; + } + + } else if (vendor == 0x0001 && product == 0x0001) { + type = SDL_CONTROLLER_TYPE_UNKNOWN; + + } else { + switch (GuessControllerType(vendor, product)) { + case k_eControllerType_XBox360Controller: + type = SDL_CONTROLLER_TYPE_XBOX360; + break; + case k_eControllerType_XBoxOneController: + type = SDL_CONTROLLER_TYPE_XBOXONE; + break; + case k_eControllerType_PS3Controller: + type = SDL_CONTROLLER_TYPE_PS3; + break; + case k_eControllerType_PS4Controller: + type = SDL_CONTROLLER_TYPE_PS4; + break; + case k_eControllerType_SwitchProController: + case k_eControllerType_SwitchInputOnlyController: + type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; + break; + default: + type = SDL_CONTROLLER_TYPE_UNKNOWN; + break; } - return SDL_CONTROLLER_TYPE_UNKNOWN; - } - if (vendor == 0x0001 && product == 0x0001) { - return SDL_CONTROLLER_TYPE_UNKNOWN; } - switch (GuessControllerType(vendor, product)) { - case k_eControllerType_XBox360Controller: - return SDL_CONTROLLER_TYPE_XBOX360; - case k_eControllerType_XBoxOneController: - return SDL_CONTROLLER_TYPE_XBOXONE; - case k_eControllerType_PS3Controller: - return SDL_CONTROLLER_TYPE_PS3; - case k_eControllerType_PS4Controller: - return SDL_CONTROLLER_TYPE_PS4; - case k_eControllerType_SwitchProController: - case k_eControllerType_SwitchInputOnlyController: - return SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; - default: - return SDL_CONTROLLER_TYPE_UNKNOWN; + if (type == SDL_CONTROLLER_TYPE_UNKNOWN) { + /* This code should match the checks in libusb/hid.c and HIDDeviceManager.java */ + static const int LIBUSB_CLASS_VENDOR_SPEC = 0xFF; + 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 XBONE_IFACE_SUBCLASS = 71; + static const int XBONE_IFACE_PROTOCOL = 208; + + if (interface_class == LIBUSB_CLASS_VENDOR_SPEC && + interface_subclass == XB360_IFACE_SUBCLASS && + (interface_protocol == XB360_IFACE_PROTOCOL || + interface_protocol == XB360W_IFACE_PROTOCOL)) { + + 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 */ + 0x1bad, /* Harmonix */ + 0x24c6, /* PowerA */ + }; + + int i; + for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) { + if (vendor == SUPPORTED_VENDORS[i]) { + type = SDL_CONTROLLER_TYPE_XBOX360; + break; + } + } + } + + if (interface_number == 0 && + interface_class == LIBUSB_CLASS_VENDOR_SPEC && + interface_subclass == XBONE_IFACE_SUBCLASS && + interface_protocol == XBONE_IFACE_PROTOCOL) { + + static const int SUPPORTED_VENDORS[] = { + 0x045e, /* Microsoft */ + 0x0738, /* Mad Catz */ + 0x0e6f, /* PDP */ + 0x0f0d, /* Hori */ + 0x1532, /* Razer Wildcat */ + 0x24c6, /* PowerA */ + 0x2e24, /* Hyperkin */ + }; + + int i; + for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) { + if (vendor == SUPPORTED_VENDORS[i]) { + type = SDL_CONTROLLER_TYPE_XBOXONE; + break; + } + } + } } + return type; } SDL_bool @@ -1686,7 +1769,7 @@ SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid) } } - if (SDL_GetJoystickGameControllerType(vendor, product, name) == SDL_CONTROLLER_TYPE_PS4 && SDL_IsPS4RemapperRunning()) { + if (SDL_GetJoystickGameControllerType(name, vendor, product, -1, 0, 0, 0) == SDL_CONTROLLER_TYPE_PS4 && SDL_IsPS4RemapperRunning()) { return SDL_TRUE; } diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 024dffc8f..1ba4dee44 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -60,7 +60,7 @@ extern const char *SDL_GetCustomJoystickName(Uint16 vendor, Uint16 product); /* Function to return the type of a controller */ extern SDL_GameControllerType SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *name); -extern SDL_GameControllerType SDL_GetJoystickGameControllerType(Uint16 vendor, Uint16 product, const char *name); +extern SDL_GameControllerType SDL_GetJoystickGameControllerType(const char *name, Uint16 vendor, Uint16 product, int interface_number, int interface_class, int interface_subclass, int interface_protocol); /* Function to return whether a joystick is a Nintendo Switch Pro controller */ extern SDL_bool SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id); diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c index 402ba8b54..dbf410fd8 100644 --- a/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -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; } diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 2a71a0771..92d9df1b4 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -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; diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 6dfe02687..e66f0c5b1 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -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); diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index 144057a7c..903641e33 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -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) { diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c index e9b1bff46..5d0319d83 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c @@ -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 diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index 260464815..a0d78bcc8 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -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 * diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 2ae57e687..d05377cdb 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -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 diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 899e187c8..5aa99a686 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -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); diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c index 184900cbb..922580918 100644 --- a/src/joystick/windows/SDL_dinputjoystick.c +++ b/src/joystick/windows/SDL_dinputjoystick.c @@ -374,7 +374,7 @@ SDL_IsXInputDevice(const GUID* pGuidProductFromDirectInput) if (SDL_memcmp(&pGuidProductFromDirectInput->Data4[2], "PIDVID", 6) == 0) { Uint16 vendor_id = (Uint16)LOWORD(pGuidProductFromDirectInput->Data1); Uint16 product_id = (Uint16)HIWORD(pGuidProductFromDirectInput->Data1); - SDL_GameControllerType type = SDL_GetJoystickGameControllerType(vendor_id, product_id, ""); + SDL_GameControllerType type = SDL_GetJoystickGameControllerType("", vendor_id, product_id, -1, 0, 0, 0); if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE || (vendor_id == 0x28DE && product_id == 0x11FF)) {