فهرست منبع

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

Sam Lantinga 5 سال پیش
والد
کامیت
43aa1fa9e7

+ 1 - 1
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();
             }
         }

+ 7 - 18
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);

+ 6 - 0
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 */

+ 5 - 2
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<CHIDDevice> pDevice( new CHIDDevice( nDeviceID, pInfo ) );
 

+ 6 - 0
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;
 		};

+ 3 - 0
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 */

+ 24 - 23
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;
-    			}
-
-    			node = node->next;
-    		}
-    	}
+		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;
+			}
+		}
 	}
 	
 	/* 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);
 		

+ 111 - 28
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;
     }
 

+ 1 - 1
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);

+ 2 - 2
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;
     }

+ 15 - 18
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;
 

+ 6 - 2
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);

+ 14 - 14
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) {

+ 11 - 4
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

+ 3 - 16
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 *

+ 27 - 14
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;
-    }
-
-    /* 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);
+    /* 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;
+    }
+#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

+ 5 - 1
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);

+ 1 - 1
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)) {