SDL: Improved detection of third party PS4 and PS5 controllers

From fa2063fb44d42d4894fbab55d62ff8c09186b47a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 22 Sep 2022 23:42:25 -0700
Subject: [PATCH] Improved detection of third party PS4 and PS5 controllers

---
 src/joystick/SDL_gamecontroller.c          |  2 +-
 src/joystick/SDL_joystick.c                |  5 ++++
 src/joystick/hidapi/SDL_hidapi_gamecube.c  |  6 ++---
 src/joystick/hidapi/SDL_hidapi_luna.c      |  5 ++--
 src/joystick/hidapi/SDL_hidapi_ps3.c       | 22 ++++++++-------
 src/joystick/hidapi/SDL_hidapi_ps4.c       | 29 +++++++++++++-------
 src/joystick/hidapi/SDL_hidapi_ps5.c       | 29 +++++++++++++-------
 src/joystick/hidapi/SDL_hidapi_shield.c    |  5 ++--
 src/joystick/hidapi/SDL_hidapi_stadia.c    |  5 ++--
 src/joystick/hidapi/SDL_hidapi_steam.c     |  4 +--
 src/joystick/hidapi/SDL_hidapi_switch.c    |  8 ++++++
 src/joystick/hidapi/SDL_hidapi_xbox360.c   |  2 ++
 src/joystick/hidapi/SDL_hidapi_xbox360w.c  |  2 ++
 src/joystick/hidapi/SDL_hidapi_xboxone.c   |  2 ++
 src/joystick/hidapi/SDL_hidapijoystick.c   | 31 ++++++++++++++++------
 src/joystick/hidapi/SDL_hidapijoystick_c.h |  4 +++
 src/joystick/usb_ids.h                     | 22 ++++++++++++++-
 17 files changed, 132 insertions(+), 51 deletions(-)

diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 1565eaa10a62..204ae965b965 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -568,7 +568,7 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI
     SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
 
     if ((vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) ||
-        (vendor == USB_VENDOR_SHENZHEN && product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER)) {
+        (vendor == USB_VENDOR_DRAGONRISE && product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER)) {
         /* GameCube driver has 12 buttons and 6 axes */
         SDL_strlcat(mapping_string, "a:b0,b:b1,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,", sizeof(mapping_string));
     } else if (vendor == USB_VENDOR_NINTENDO &&
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 9c7c3de70f00..e445fe53ad7e 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -2167,6 +2167,11 @@ SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *nam
         if (SDL_IsJoystickVirtual(guid)) {
             return SDL_CONTROLLER_TYPE_VIRTUAL;
         }
+#ifdef SDL_JOYSTICK_HIDAPI
+        if (SDL_IsJoystickHIDAPI(guid)) {
+            return HIDAPI_GetGameControllerTypeFromGUID(guid);
+        }
+#endif /* SDL_JOYSTICK_HIDAPI */
     }
     return type;
 }
diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c
index d3bd2cb226ea..4e68f47b0fd5 100644
--- a/src/joystick/hidapi/SDL_hidapi_gamecube.c
+++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c
@@ -82,7 +82,7 @@ HIDAPI_DriverGameCube_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *n
         /* Nintendo Co., Ltd.  Wii U GameCube Controller Adapter */
         return SDL_TRUE;
     }
-    if (vendor_id == USB_VENDOR_SHENZHEN && product_id == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER) {
+    if (vendor_id == USB_VENDOR_DRAGONRISE && product_id == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER) {
         /* EVORETRO GameCube Controller Adapter */
         return SDL_TRUE;
     }
@@ -145,8 +145,6 @@ HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
     SDL_EnableGameCubeAdaptors();
 #endif
 
-    HIDAPI_SetDeviceName(device, "Nintendo GameCube Controller");
-
     ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -219,6 +217,8 @@ HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device)
     SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
                         SDL_GameControllerButtonReportingHintChanged, ctx);
 
+    HIDAPI_SetDeviceName(device, "Nintendo GameCube Controller");
+
     return SDL_TRUE;
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_luna.c b/src/joystick/hidapi/SDL_hidapi_luna.c
index 0f8cfd48850f..3bcf3d050757 100644
--- a/src/joystick/hidapi/SDL_hidapi_luna.c
+++ b/src/joystick/hidapi/SDL_hidapi_luna.c
@@ -77,8 +77,6 @@ HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device *device)
 {
     SDL_DriverLuna_Context *ctx;
 
-    HIDAPI_SetDeviceName(device, "Amazon Luna Controller");
-
     ctx = (SDL_DriverLuna_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -86,6 +84,9 @@ HIDAPI_DriverLuna_InitDevice(SDL_HIDAPI_Device *device)
     }
     device->context = ctx;
 
+    device->type = SDL_CONTROLLER_TYPE_AMAZON_LUNA;
+    HIDAPI_SetDeviceName(device, "Amazon Luna Controller");
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_ps3.c b/src/joystick/hidapi/SDL_hidapi_ps3.c
index 99bb613ceaa8..1b60c460f5d7 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps3.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps3.c
@@ -133,13 +133,15 @@ static SDL_bool
 HIDAPI_DriverPS3_InitDevice(SDL_HIDAPI_Device *device)
 {
     SDL_DriverPS3_Context *ctx;
+    SDL_bool is_shanwan = SDL_FALSE;
 
-    if (device->vendor_id == USB_VENDOR_SONY) {
-        if (SDL_strncasecmp(device->name, "ShanWan", 7) == 0) {
-            HIDAPI_SetDeviceName(device, "ShanWan PS3 Controller");
-        } else {
-            HIDAPI_SetDeviceName(device, "PS3 Controller");
-        }
+    if (device->vendor_id == USB_VENDOR_SONY &&
+        SDL_strncasecmp(device->name, "ShanWan", 7) == 0) {
+        is_shanwan = SDL_TRUE;
+    }
+    if (device->vendor_id == USB_VENDOR_SHANWAN ||
+        device->vendor_id == USB_VENDOR_SHANWAN_ALT) {
+        is_shanwan = SDL_TRUE;
     }
 
     ctx = (SDL_DriverPS3_Context *)SDL_calloc(1, sizeof(*ctx));
@@ -148,13 +150,10 @@ HIDAPI_DriverPS3_InitDevice(SDL_HIDAPI_Device *device)
         return SDL_FALSE;
     }
     ctx->device = device;
+    ctx->is_shanwan = is_shanwan;
 
     device->context = ctx;
 
-    if (SDL_strncasecmp(device->name, "ShanWan", 7) == 0) {
-        ctx->is_shanwan = SDL_TRUE;
-    }
-
     /* Set the controller into report mode over Bluetooth */
     {
         Uint8 data[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
@@ -189,6 +188,9 @@ HIDAPI_DriverPS3_InitDevice(SDL_HIDAPI_Device *device)
         }
     }
 
+    device->type = SDL_CONTROLLER_TYPE_PS3;
+    HIDAPI_SetDeviceName(device, "PS3 Controller");
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 1fe59375bd91..fcc4cca6b5a2 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -168,12 +168,6 @@ HIDAPI_DriverPS4_IsEnabled(void)
                    SDL_HIDAPI_DEFAULT));
 }
 
-static SDL_bool
-HIDAPI_DriverPS4_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
-{
-    return (type == SDL_CONTROLLER_TYPE_PS4) ? SDL_TRUE : SDL_FALSE;
-}
-
 static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
 {
     SDL_memset(report, 0, length);
@@ -181,6 +175,21 @@ static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report
     return SDL_hid_get_feature_report(dev, report, length);
 }
 
+static SDL_bool
+HIDAPI_DriverPS4_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
+{
+    Uint8 data[USB_PACKET_LENGTH];
+    int size;
+
+    if (device && SONY_THIRDPARTY_VENDOR(device->vendor_id) &&
+        (size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdCapabilities, data, sizeof(data))) == 48 &&
+        data[2] == 0x27) {
+        /* Supported third party controller */
+        return SDL_TRUE;
+    }
+    return (type == SDL_CONTROLLER_TYPE_PS4) ? SDL_TRUE : SDL_FALSE;
+}
+
 static void
 SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index)
 {
@@ -216,10 +225,6 @@ HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
     int size;
     char serial[18];
 
-    if (device->vendor_id == USB_VENDOR_SONY) {
-        HIDAPI_SetDeviceName(device, "PS4 Controller");
-    }
-
     ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -309,6 +314,10 @@ HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
         }
     }
 
+    device->type = SDL_CONTROLLER_TYPE_PS4;
+    if (device->vendor_id == USB_VENDOR_SONY) {
+        HIDAPI_SetDeviceName(device, "PS4 Controller");
+    }
     HIDAPI_SetDeviceSerial(device, serial);
 
     return HIDAPI_JoystickConnected(device, NULL);
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index 638bc44911c1..e128eda16fa6 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -250,12 +250,6 @@ HIDAPI_DriverPS5_IsEnabled(void)
                    SDL_HIDAPI_DEFAULT));
 }
 
-static SDL_bool
-HIDAPI_DriverPS5_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
-{
-    return (type == SDL_CONTROLLER_TYPE_PS5) ? SDL_TRUE : SDL_FALSE;
-}
-
 static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
 {
     SDL_memset(report, 0, length);
@@ -263,6 +257,21 @@ static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report
     return SDL_hid_get_feature_report(dev, report, length);
 }
 
+static SDL_bool
+HIDAPI_DriverPS5_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
+{
+    Uint8 data[USB_PACKET_LENGTH];
+    int size;
+
+    if (device && SONY_THIRDPARTY_VENDOR(device->vendor_id) &&
+        (size = ReadFeatureReport(device->dev, k_EPS5FeatureReportIdCapabilities, data, sizeof(data))) == 48 &&
+        data[2] == 0x28) {
+        /* Supported third party controller */
+        return SDL_TRUE;
+    }
+    return (type == SDL_CONTROLLER_TYPE_PS5) ? SDL_TRUE : SDL_FALSE;
+}
+
 static void
 SetLedsForPlayerIndex(DS5EffectsState_t *effects, int player_index)
 {
@@ -317,10 +326,6 @@ HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
     int size;
     char serial[18];
 
-    if (device->vendor_id == USB_VENDOR_SONY) {
-        HIDAPI_SetDeviceName(device, "PS5 Controller");
-    }
-
     ctx = (SDL_DriverPS5_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -416,6 +421,10 @@ HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
         ctx->use_alternate_report = SDL_TRUE;
     }
 
+    device->type = SDL_CONTROLLER_TYPE_PS5;
+    if (device->vendor_id == USB_VENDOR_SONY) {
+        HIDAPI_SetDeviceName(device, "PS5 Controller");
+    }
     HIDAPI_SetDeviceSerial(device, serial);
 
     return HIDAPI_JoystickConnected(device, NULL);
diff --git a/src/joystick/hidapi/SDL_hidapi_shield.c b/src/joystick/hidapi/SDL_hidapi_shield.c
index 42cfea1c8042..1fa75b74a010 100644
--- a/src/joystick/hidapi/SDL_hidapi_shield.c
+++ b/src/joystick/hidapi/SDL_hidapi_shield.c
@@ -112,8 +112,6 @@ HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device)
 {
     SDL_DriverShield_Context *ctx;
 
-    HIDAPI_SetDeviceName(device, "NVIDIA SHIELD Controller");
-
     ctx = (SDL_DriverShield_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -121,6 +119,9 @@ HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device)
     }
     device->context = ctx;
 
+    device->type = SDL_CONTROLLER_TYPE_NVIDIA_SHIELD;
+    HIDAPI_SetDeviceName(device, "NVIDIA SHIELD Controller");
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_stadia.c b/src/joystick/hidapi/SDL_hidapi_stadia.c
index 8b20d75ab7d4..a6d54b2a533c 100644
--- a/src/joystick/hidapi/SDL_hidapi_stadia.c
+++ b/src/joystick/hidapi/SDL_hidapi_stadia.c
@@ -78,8 +78,6 @@ HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device *device)
 {
     SDL_DriverStadia_Context *ctx;
 
-    HIDAPI_SetDeviceName(device, "Google Stadia Controller");
-
     ctx = (SDL_DriverStadia_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -87,6 +85,9 @@ HIDAPI_DriverStadia_InitDevice(SDL_HIDAPI_Device *device)
     }
     device->context = ctx;
 
+    device->type = SDL_CONTROLLER_TYPE_GOOGLE_STADIA;
+    HIDAPI_SetDeviceName(device, "Google Stadia Controller");
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c
index 64ce13adeb0c..6df6512b3573 100644
--- a/src/joystick/hidapi/SDL_hidapi_steam.c
+++ b/src/joystick/hidapi/SDL_hidapi_steam.c
@@ -1027,8 +1027,6 @@ HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device)
 {
     SDL_DriverSteam_Context *ctx;
 
-    HIDAPI_SetDeviceName(device, "Steam Controller");
-
     ctx = (SDL_DriverSteam_Context *)SDL_calloc(1, sizeof(*ctx));
     if (!ctx) {
         SDL_OutOfMemory();
@@ -1036,6 +1034,8 @@ HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device)
     }
     device->context = ctx;
 
+    HIDAPI_SetDeviceName(device, "Steam Controller");
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index e05fdc391343..d2e84040087a 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -1161,32 +1161,40 @@ UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
     case k_eSwitchDeviceInfoControllerType_JoyConLeft:
         HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
         HIDAPI_SetDeviceProduct(device, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT);
+        device->type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
         break;
     case k_eSwitchDeviceInfoControllerType_JoyConRight:
         HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
         HIDAPI_SetDeviceProduct(device, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT);
+        device->type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
         break;
     case k_eSwitchDeviceInfoControllerType_ProController:
         HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
         HIDAPI_SetDeviceProduct(device, USB_PRODUCT_NINTENDO_SWITCH_PRO);
+        device->type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
         break;
     case k_eSwitchDeviceInfoControllerType_NESLeft:
         HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)");
+        device->type = SDL_CONTROLLER_TYPE_UNKNOWN;
         break;
     case k_eSwitchDeviceInfoControllerType_NESRight:
         HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)");
+        device->type = SDL_CONTROLLER_TYPE_UNKNOWN;
         break;
     case k_eSwitchDeviceInfoControllerType_SNES:
         HIDAPI_SetDeviceName(device, "Nintendo SNES Controller");
         HIDAPI_SetDeviceProduct(device, USB_PRODUCT_NINTENDO_SNES_CONTROLLER);
+        device->type = SDL_CONTROLLER_TYPE_UNKNOWN;
         break;
     case k_eSwitchDeviceInfoControllerType_N64:
         HIDAPI_SetDeviceName(device, "Nintendo N64 Controller");
         HIDAPI_SetDeviceProduct(device, USB_PRODUCT_NINTENDO_N64_CONTROLLER);
+        device->type = SDL_CONTROLLER_TYPE_UNKNOWN;
         break;
     case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
         HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller");
         HIDAPI_SetDeviceProduct(device, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER);
+        device->type = SDL_CONTROLLER_TYPE_UNKNOWN;
         break;
     default:
         break;
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c
index a73071b94707..191ccb97ff1d 100644
--- a/src/joystick/hidapi/SDL_hidapi_xbox360.c
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c
@@ -157,6 +157,8 @@ HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
 
     device->context = ctx;
 
+    device->type = SDL_CONTROLLER_TYPE_XBOX360;
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c
index 5869aff49d2d..f0a1d2931b13 100644
--- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c
@@ -159,6 +159,8 @@ HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device)
         return SDL_FALSE;
     }
 
+    device->type = SDL_CONTROLLER_TYPE_XBOX360;
+
     return SDL_TRUE;
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index 987fb698764f..5be605917305 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -340,6 +340,8 @@ HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device)
     SDL_Log("Controller version: %d (0x%.4x)\n", device->version, device->version);
 #endif
 
+    device->type = SDL_CONTROLLER_TYPE_XBOXONE;
+
     return HIDAPI_JoystickConnected(device, NULL);
 }
 
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 0a3873a71ba1..fd2ce71405f8 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -235,7 +235,6 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
     const Uint16 USAGE_GAMEPAD = 0x0005;
     const Uint16 USAGE_MULTIAXISCONTROLLER = 0x0008;
     int i;
-    SDL_GameControllerType type;
 
     if (device->num_children > 0) {
         return &SDL_HIDAPI_DriverCombined;
@@ -254,10 +253,9 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device)
         }
     }
 
-    type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
     for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
         SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
-        if (driver->enabled && driver->IsSupportedDevice(device, device->name, type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
+        if (driver->enabled && driver->IsSupportedDevice(device, device->name, device->type, device->vendor_id, device->product_id, device->version, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol)) {
             return driver;
         }
     }
@@ -690,6 +688,7 @@ HIDAPI_AddDevice(const struct SDL_hid_device_info *info, int num_children, SDL_H
 
     /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
     device->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, device->vendor_id, device->product_id, device->version, device->name, 'h', 0);
+    device->type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
 
     if (num_children > 0) {
         int i;
@@ -914,14 +913,13 @@ HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Devi
 
         /* If we're looking for the raw input Xbox One controller, match it against any other Xbox One controller */
         if (product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER &&
-            SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == SDL_CONTROLLER_TYPE_XBOXONE) {
+            device->type == SDL_CONTROLLER_TYPE_XBOXONE) {
             return SDL_TRUE;
         }
 
         /* If we're looking for an XInput controller, match it against any other Xbox controller */
         if (product_id == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
-            SDL_GameControllerType type = SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol);
-            if (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) {
+            if (device->type == SDL_CONTROLLER_TYPE_XBOX360 || device->type == SDL_CONTROLLER_TYPE_XBOXONE) {
                 return SDL_TRUE;
             }
         }
@@ -947,8 +945,7 @@ HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type)
 
     SDL_LockJoysticks();
     for (device = SDL_HIDAPI_devices; device; device = device->next) {
-        if (device->driver &&
-            SDL_GetJoystickGameControllerProtocol(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == type) {
+        if (device->driver && device->type == type) {
             result = SDL_TRUE;
             break;
         }
@@ -1013,6 +1010,24 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons
     return result;
 }
 
+SDL_GameControllerType
+HIDAPI_GetGameControllerTypeFromGUID(SDL_JoystickGUID guid)
+{
+    SDL_HIDAPI_Device *device;
+    SDL_GameControllerType type = SDL_CONTROLLER_TYPE_UNKNOWN;
+
+    SDL_LockJoysticks();
+    for (device = SDL_HIDAPI_devices; device; device = device->next) {
+        if (SDL_memcmp(&guid, &device->guid, sizeof(guid)) == 0) {
+            type = device->type;
+            break;
+        }
+    }
+    SDL_UnlockJoysticks();
+
+    return type;
+}
+
 static void
 HIDAPI_JoystickDetect(void)
 {
diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h
index 3b574d6572ad..c653d15bea4c 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick_c.h
+++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -73,6 +73,7 @@ typedef struct _SDL_HIDAPI_Device
     int interface_protocol;
     Uint16 usage_page;      /* Available on Windows and Mac OS X */
     Uint16 usage;           /* Available on Windows and Mac OS X */
+    SDL_GameControllerType type;
 
     struct _SDL_HIDAPI_DeviceDriver *driver;
     void *context;
@@ -144,6 +145,9 @@ extern SDL_bool HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type);
 /* Return true if a HID device is present and supported as a joystick */
 extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
 
+/* Return the type of a game controller if it's present and supported */
+extern SDL_GameControllerType HIDAPI_GetGameControllerTypeFromGUID(SDL_JoystickGUID guid);
+
 extern void HIDAPI_UpdateDevices(void);
 extern void HIDAPI_SetDeviceName(SDL_HIDAPI_Device *device, const char *name);
 extern void HIDAPI_SetDeviceProduct(SDL_HIDAPI_Device *device, Uint16 product_id);
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index 3a62bc462595..e9f27fb4c2a0 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -27,21 +27,41 @@
 #define USB_VENDOR_8BITDO       0x2dc8
 #define USB_VENDOR_AMAZON       0x1949
 #define USB_VENDOR_APPLE        0x05ac
+#define USB_VENDOR_DRAGONRISE   0x0079
 #define USB_VENDOR_GOOGLE       0x18d1
 #define USB_VENDOR_HORI         0x0f0d
 #define USB_VENDOR_HYPERKIN     0x2e24
+#define USB_VENDOR_MADCATZ      0x0738
 #define USB_VENDOR_MICROSOFT    0x045e
+#define USB_VENDOR_NACON        0x146b
 #define USB_VENDOR_NINTENDO     0x057e
 #define USB_VENDOR_NVIDIA       0x0955
 #define USB_VENDOR_PDP          0x0e6f
 #define USB_VENDOR_POWERA       0x24c6
 #define USB_VENDOR_POWERA_ALT   0x20d6
+#define USB_VENDOR_QANBA        0x2c22
 #define USB_VENDOR_RAZER        0x1532
 #define USB_VENDOR_SHANWAN      0x2563
-#define USB_VENDOR_SHENZHEN     0x0079
+#define USB_VENDOR_SHANWAN_ALT  0x20bc
 #define USB_VENDOR_SONY         0x054c
+#define USB_VENDOR_THRUSTMASTER 0x044f
 #define USB_VENDOR_VALVE        0x28de
 
+#define SONY_THIRDPARTY_VENDOR(X)       \
+    (X == USB_VENDOR_DRAGONRISE     ||  \
+     X == USB_VENDOR_HORI           ||  \
+     X == USB_VENDOR_MADCATZ        ||  \
+     X == USB_VENDOR_NACON          ||  \
+     X == USB_VENDOR_PDP            ||  \
+     X == USB_VENDOR_POWERA         ||  \
+     X == USB_VENDOR_POWERA_ALT     ||  \
+     X == USB_VENDOR_QANBA          ||  \
+     X == USB_VENDOR_RAZER          ||  \
+     X == USB_VENDOR_SHANWAN        ||  \
+     X == USB_VENDOR_SHANWAN_ALT    ||  \
+     X == USB_VENDOR_THRUSTMASTER   ||  \
+     X == 0x7545 /* SZ-MYPOWER */)
+
 #define USB_PRODUCT_8BITDO_XBOX_CONTROLLER                  0x2002
 #define USB_PRODUCT_AMAZON_LUNA_CONTROLLER                  0x0419
 #define USB_PRODUCT_GOOGLE_STADIA_CONTROLLER                0x9400