SDL: Added support for the Hori Fighting Stick Alpha to the HIDAPI driver

From b2ac758f6173c6b0bced935c082f63d0e448d227 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 29 Aug 2022 17:33:00 -0700
Subject: [PATCH] Added support for the Hori Fighting Stick Alpha to the HIDAPI
 driver

---
 src/hidapi/libusb/hid.c              |   3 +-
 src/joystick/SDL_joystick.c          |   3 +
 src/joystick/controller_type.c       |   3 +
 src/joystick/hidapi/SDL_hidapi_ps4.c |  60 +++++--
 src/joystick/hidapi/SDL_hidapi_ps5.c | 244 +++++++++++++++++++++------
 src/joystick/usb_ids.h               |   3 +
 6 files changed, 255 insertions(+), 61 deletions(-)

diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c
index 37d01e34dab..6ee1fbe673b 100644
--- a/src/hidapi/libusb/hid.c
+++ b/src/hidapi/libusb/hid.c
@@ -1123,7 +1123,8 @@ static int SDLCALL read_thread(void *param)
 static void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, struct libusb_config_descriptor *conf_desc)
 {
 	if ((idVendor == 0x05ac && idProduct == 0x055b) /* Gamesir-G3w */ ||
-	    (idVendor == 0x0f0d && idProduct == 0x00dc) /* HORIPAD */) {
+	    (idVendor == 0x0f0d && idProduct == 0x00dc) /* HORIPAD */ ||
+	    (idVendor == 0x0f0d && idProduct == 0x011e) /* Hori Fighting Stick α */) {
 		unsigned char data[20];
 
 		/* The HORIPAD FPS for Nintendo Switch requires this to enable input reports.
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 117bf4c6851..c855f36d9e3 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -2381,6 +2381,9 @@ static SDL_bool SDL_IsJoystickProductArcadeStick(Uint32 vidpid)
         MAKE_VIDPID(0x0f0d, 0x008a),    /* HORI Real Arcade Pro 4 */
         MAKE_VIDPID(0x0f0d, 0x008c),    /* Hori Real Arcade Pro 4 */
         MAKE_VIDPID(0x0f0d, 0x00aa),    /* HORI Real Arcade Pro V Hayabusa in Switch Mode */
+        MAKE_VIDPID(0x0f0d, 0x011c),    /* Hori Fighting Stick α in PS4 Mode */
+        MAKE_VIDPID(0x0f0d, 0x011e),    /* Hori Fighting Stick α in PC Mode  */
+        MAKE_VIDPID(0x0f0d, 0x0184),    /* Hori Fighting Stick α in PS5 Mode */
         MAKE_VIDPID(0x1532, 0x0a00),    /* Razer Atrox Arcade Stick */
         MAKE_VIDPID(0x1bad, 0xf03d),    /* Street Fighter IV Arcade Stick TE - Chun Li */
         MAKE_VIDPID(0x1bad, 0xf502),    /* Hori Real Arcade Pro.VX SA */
diff --git a/src/joystick/controller_type.c b/src/joystick/controller_type.c
index aba5be33a81..5029da09fd3 100644
--- a/src/joystick/controller_type.c
+++ b/src/joystick/controller_type.c
@@ -108,6 +108,7 @@ static const ControllerDescription_t arrControllers[] = {
 	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x009c ), k_eControllerType_PS4Controller, NULL },	// HORI TAC PRO mousething
 	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00a0 ), k_eControllerType_PS4Controller, NULL },	// HORI TAC4 mousething
 	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00ee ), k_eControllerType_PS4Controller, NULL },	// Hori mini wired https://www.playstation.com/en-us/explore/accessories/gaming-controllers/mini-wired-gamepad/
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x011c ), k_eControllerType_PS4Controller, NULL },	// Hori Fighting Stick α
 	{ MAKE_CONTROLLER_ID( 0x11c0, 0x4001 ), k_eControllerType_PS4Controller, NULL },	// "PS4 Fun Controller" added from user log
 	{ MAKE_CONTROLLER_ID( 0x146b, 0x0d01 ), k_eControllerType_PS4Controller, NULL },	// Nacon Revolution Pro Controller - has gyro
 	{ MAKE_CONTROLLER_ID( 0x146b, 0x0d02 ), k_eControllerType_PS4Controller, NULL },	// Nacon Revolution Pro Controller v2 - has gyro
@@ -140,6 +141,7 @@ static const ControllerDescription_t arrControllers[] = {
 	{ MAKE_CONTROLLER_ID( 0x0c12, 0x0e20 ), k_eControllerType_PS4Controller, NULL },	// Brook Mars Controller - needs FW update to show up as Ps4 controller on PC. Has Gyro but touchpad is a single button.
 
 	{ MAKE_CONTROLLER_ID( 0x054c, 0x0ce6 ), k_eControllerType_PS5Controller, NULL },	// Sony PS5 Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0184 ), k_eControllerType_PS5Controller, NULL },	// Hori Fighting Stick α
 
 	{ MAKE_CONTROLLER_ID( 0x0079, 0x0006 ), k_eControllerType_UnknownNonSteamController, NULL },	// DragonRise Generic USB PCB, sometimes configured as a PC Twin Shock Controller - looks like a DS3 but the face buttons are 1-4 instead of symbols
 
@@ -199,6 +201,7 @@ static const ControllerDescription_t arrControllers[] = {
 	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x001b ), k_eControllerType_XBox360Controller, NULL },	// Hori Real Arcade Pro VX
 	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x008c ), k_eControllerType_XBox360Controller, NULL },	// Hori Real Arcade Pro 4
 	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x00db ), k_eControllerType_XBox360Controller, "HORI Slime Controller" },	// Hori Dragon Quest Slime Controller
+	{ MAKE_CONTROLLER_ID( 0x0f0d, 0x011e ), k_eControllerType_XBox360Controller, NULL },	// Hori Fighting Stick α
 	{ MAKE_CONTROLLER_ID( 0x1038, 0x1430 ), k_eControllerType_XBox360Controller, "SteelSeries Stratus Duo" },	// SteelSeries Stratus Duo
 	{ MAKE_CONTROLLER_ID( 0x1038, 0x1431 ), k_eControllerType_XBox360Controller, "SteelSeries Stratus Duo" },	// SteelSeries Stratus Duo
 	{ MAKE_CONTROLLER_ID( 0x1038, 0xb360 ), k_eControllerType_XBox360Controller, NULL },	// SteelSeries Nimbus/Stratus XL
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 3d3b617d872..cdabd60bf8c 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -127,8 +127,11 @@ typedef struct {
     SDL_bool official_controller;
     SDL_bool audio_supported;
     SDL_bool effects_supported;
+    SDL_bool sensors_supported;
+    SDL_bool touchpad_supported;
     SDL_bool enhanced_mode;
     SDL_bool report_sensors;
+    SDL_bool report_touchpad;
     SDL_bool hardware_calibration;
     IMUCalibrationData calibration[6];
     Uint32 last_packet;
@@ -202,6 +205,26 @@ static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id)
         return SDL_FALSE;
     }
 
+    /* The Hori Fighting Stick Alpha doesn't have any rumble hardware */
+    if (vendor_id == USB_VENDOR_HORI && product_id == USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS4) {
+        return SDL_FALSE;
+    }
+
+    return SDL_TRUE;
+}
+
+static SDL_bool HIDAPI_DriverPS4_HasSensors(Uint16 vendor_id, Uint16 product_id)
+{
+    /* The Hori Fighting Stick Alpha doesn't have any gyro or accelerometer */
+    if (vendor_id == USB_VENDOR_HORI && product_id == USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS4) {
+        return SDL_FALSE;
+    }
+
+    return SDL_TRUE;
+}
+
+static SDL_bool HIDAPI_DriverPS4_HasTouchpad(Uint16 vendor_id, Uint16 product_id)
+{
     return SDL_TRUE;
 }
 
@@ -456,9 +479,14 @@ HIDAPI_DriverPS4_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
     if (!ctx->enhanced_mode) {
         ctx->enhanced_mode = SDL_TRUE;
 
-        SDL_PrivateJoystickAddTouchpad(joystick, 2);
-        SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
-        SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
+        if (ctx->touchpad_supported) {
+            SDL_PrivateJoystickAddTouchpad(joystick, 2);
+            ctx->report_touchpad = SDL_TRUE;
+        }
+        if (ctx->sensors_supported) {
+            SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
+            SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
+        }
 
         HIDAPI_DriverPS4_UpdateEffects(device);
     }
@@ -570,6 +598,14 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
         ctx->effects_supported = SDL_TRUE;
     }
 
+    if (HIDAPI_DriverPS4_HasSensors(device->vendor_id, device->product_id)) {
+        ctx->sensors_supported = SDL_TRUE;
+    }
+
+    if (HIDAPI_DriverPS4_HasTouchpad(device->vendor_id, device->product_id)) {
+        ctx->touchpad_supported = SDL_TRUE;
+    }
+
     if (!joystick->serial && device->serial && SDL_strlen(device->serial) == 12) {
         int i, j;
         char serial[18];
@@ -835,15 +871,17 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev,
         }
     }
 
-    touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
-    touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
-    touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
-    SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+    if (ctx->report_touchpad) {
+        touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
+        touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
+        touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
+        SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
 
-    touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
-    touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
-    touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
-    SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+        touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
+        touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
+        touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
+        SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+    }
 
     if (ctx->report_sensors) {
         float data[3];
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index 8a199f2e7fa..fa8a8ff0fee 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -81,8 +81,53 @@ typedef struct
     Uint8 ucTriggerLeft;                /* 4 */
     Uint8 ucTriggerRight;               /* 5 */
     Uint8 ucCounter;                    /* 6 */
-    Uint8 rgucButtonsAndHat[3];         /* 7 */
-    Uint8 ucZero;                       /* 10 */
+    Uint8 rgucButtonsAndHat[4];         /* 7 */
+    Uint8 rgucPacketSequence[4];        /* 11 - 32 bit little endian */
+    Uint8 rgucGyroX[2];                 /* 15 */
+    Uint8 rgucGyroY[2];                 /* 17 */
+    Uint8 rgucGyroZ[2];                 /* 19 */
+    Uint8 rgucAccelX[2];                /* 21 */
+    Uint8 rgucAccelY[2];                /* 23 */
+    Uint8 rgucAccelZ[2];                /* 25 */
+
+} PS5StatePacketCommon_t;
+
+typedef struct
+{
+    Uint8 ucLeftJoystickX;              /* 0 */
+    Uint8 ucLeftJoystickY;              /* 1 */
+    Uint8 ucRightJoystickX;             /* 2 */
+    Uint8 ucRightJoystickY;             /* 3 */
+    Uint8 ucTriggerLeft;                /* 4 */
+    Uint8 ucTriggerRight;               /* 5 */
+    Uint8 ucCounter;                    /* 6 */
+    Uint8 rgucButtonsAndHat[4];         /* 7 */
+    Uint8 rgucPacketSequence[4];        /* 11 - 32 bit little endian */
+    Uint8 rgucGyroX[2];                 /* 15 */
+    Uint8 rgucGyroY[2];                 /* 17 */
+    Uint8 rgucGyroZ[2];                 /* 19 */
+    Uint8 rgucAccelX[2];                /* 21 */
+    Uint8 rgucAccelY[2];                /* 23 */
+    Uint8 rgucAccelZ[2];                /* 25 */
+    Uint8 rgucUnknown1[4];              /* 27 */
+    Uint8 ucTouchpadCounter1;           /* 31 - high bit clear + counter */
+    Uint8 rgucTouchpadData1[3];         /* 32 - X/Y, 12 bits per axis */
+    Uint8 ucTouchpadCounter2;           /* 35 - high bit clear + counter */
+    Uint8 rgucTouchpadData2[3];         /* 36 - X/Y, 12 bits per axis */
+
+    /* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */
+} PS5StatePacketV1_t;
+
+typedef struct
+{
+    Uint8 ucLeftJoystickX;              /* 0 */
+    Uint8 ucLeftJoystickY;              /* 1 */
+    Uint8 ucRightJoystickX;             /* 2 */
+    Uint8 ucRightJoystickY;             /* 3 */
+    Uint8 ucTriggerLeft;                /* 4 */
+    Uint8 ucTriggerRight;               /* 5 */
+    Uint8 ucCounter;                    /* 6 */
+    Uint8 rgucButtonsAndHat[4];         /* 7 */
     Uint8 rgucPacketSequence[4];        /* 11 - 32 bit little endian */
     Uint8 rgucGyroX[2];                 /* 15 */
     Uint8 rgucGyroY[2];                 /* 17 */
@@ -102,7 +147,7 @@ typedef struct
     Uint8 ucConnectState;               /* 53 - 0x08 = USB, 0x01 = headphone */
 
     /* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */
-} PS5StatePacket_t;
+} PS5StatePacketV2_t;
 
 typedef struct
 {
@@ -153,8 +198,12 @@ typedef struct {
     SDL_HIDAPI_Device *device;
     SDL_Joystick *joystick;
     SDL_bool is_bluetooth;
+    SDL_bool effects_supported;
+    SDL_bool sensors_supported;
+    SDL_bool touchpad_supported;
     SDL_bool enhanced_mode;
     SDL_bool report_sensors;
+    SDL_bool report_touchpad;
     SDL_bool hardware_calibration;
     IMUCalibrationData calibration[6];
     Uint16 firmware_version;
@@ -171,10 +220,35 @@ typedef struct {
     union
     {
         PS5SimpleStatePacket_t simple;
-        PS5StatePacket_t state;
+        PS5StatePacketCommon_t state;
+        Uint8 data[64];
     } last_state;
 } SDL_DriverPS5_Context;
 
+static SDL_bool HIDAPI_DriverPS5_CanRumble(Uint16 vendor_id, Uint16 product_id)
+{
+    /* The Hori Fighting Stick Alpha doesn't have any rumble hardware */
+    if (vendor_id == USB_VENDOR_HORI && product_id == USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5) {
+        return SDL_FALSE;
+    }
+
+    return SDL_TRUE;
+}
+
+static SDL_bool HIDAPI_DriverPS5_HasSensors(Uint16 vendor_id, Uint16 product_id)
+{
+    /* The Hori Fighting Stick Alpha doesn't have any gyro or accelerometer */
+    if (vendor_id == USB_VENDOR_HORI && product_id == USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5) {
+        return SDL_FALSE;
+    }
+
+    return SDL_TRUE;
+}
+
+static SDL_bool HIDAPI_DriverPS5_HasTouchpad(Uint16 vendor_id, Uint16 product_id)
+{
+    return SDL_TRUE;
+}
 
 static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size);
 
@@ -477,15 +551,26 @@ static void
 HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
 {
     SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
-    const PS5StatePacket_t *packet = &ctx->last_state.state;
-
-    /* Check the timer to make sure the Bluetooth connection LED animation is complete */
-    const Uint32 connection_complete = 10200000;
-    Uint32 timer = ((Uint32)packet->rgucTimer1[0] <<  0) |
-                   ((Uint32)packet->rgucTimer1[1] <<  8) |
-                   ((Uint32)packet->rgucTimer1[2] << 16) |
-                   ((Uint32)packet->rgucTimer1[3] << 24);
-    if (SDL_TICKS_PASSED(timer, connection_complete)) {
+    SDL_bool led_reset_complete = SDL_FALSE;
+
+    if (ctx->firmware_version <= 0x204) {
+        /* We don't know how to check the timer, just assume it's complete for now */
+        led_reset_complete = SDL_TRUE;
+    } else {
+        const PS5StatePacketV2_t *packet = (PS5StatePacketV2_t *)ctx->last_state.data;
+
+        /* Check the timer to make sure the Bluetooth connection LED animation is complete */
+        const Uint32 connection_complete = 10200000;
+        Uint32 timer = ((Uint32)packet->rgucTimer1[0] <<  0) |
+                       ((Uint32)packet->rgucTimer1[1] <<  8) |
+                       ((Uint32)packet->rgucTimer1[2] << 16) |
+                       ((Uint32)packet->rgucTimer1[3] << 24);
+        if (SDL_TICKS_PASSED(timer, connection_complete)) {
+            led_reset_complete = SDL_TRUE;
+        }
+    }
+
+    if (led_reset_complete) {
         HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLEDReset);
 
         ctx->led_reset_state = k_EDS5LEDResetStateComplete;
@@ -516,9 +601,14 @@ HIDAPI_DriverPS5_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joysti
     if (!ctx->enhanced_mode) {
         ctx->enhanced_mode = SDL_TRUE;
 
-        SDL_PrivateJoystickAddTouchpad(joystick, 2);
-        SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
-        SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
+        if (ctx->touchpad_supported) {
+            SDL_PrivateJoystickAddTouchpad(joystick, 2);
+            ctx->report_touchpad = SDL_TRUE;
+        }
+        if (ctx->sensors_supported) {
+            SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
+            SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
+        }
 
         /* Switch into enhanced report mode */
         HIDAPI_DriverPS5_UpdateEffects(device, 0);
@@ -640,6 +730,18 @@ HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
         }
     }
 
+    if (HIDAPI_DriverPS5_CanRumble(device->vendor_id, device->product_id)) {
+        ctx->effects_supported = SDL_TRUE;
+    }
+
+    if (HIDAPI_DriverPS5_HasSensors(device->vendor_id, device->product_id)) {
+        ctx->sensors_supported = SDL_TRUE;
+    }
+
+    if (HIDAPI_DriverPS5_HasTouchpad(device->vendor_id, device->product_id)) {
+        ctx->touchpad_supported = SDL_TRUE;
+    }
+
     if (!joystick->serial && device->serial && SDL_strlen(device->serial) == 12) {
         int i, j;
         char serial[18];
@@ -706,7 +808,7 @@ HIDAPI_DriverPS5_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick
     SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
     Uint32 result = 0;
 
-    if (ctx->enhanced_mode) {
+    if (ctx->enhanced_mode && ctx->effects_supported) {
         result |= SDL_JOYCAP_LED | SDL_JOYCAP_RUMBLE;
     }
 
@@ -736,6 +838,10 @@ HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joy
     int *pending_size;
     int maximum_size;
 
+    if (!ctx->effects_supported) {
+        return SDL_Unsupported();
+    }
+
     if (!ctx->enhanced_mode) {
         HIDAPI_DriverPS5_SetEnhancedMode(device, joystick);
     }
@@ -903,13 +1009,9 @@ HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL_hid_device
 }
 
 static void
-HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet)
+HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketCommon_t *packet)
 {
-    static const float TOUCHPAD_SCALEX = 1.0f / 1920;
-    static const float TOUCHPAD_SCALEY = 1.0f / 1070;
     Sint16 axis;
-    Uint8 touchpad_state;
-    int touchpad_x, touchpad_y;
 
     if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) {
         {
@@ -998,6 +1100,64 @@ HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev,
     axis = ((int)packet->ucRightJoystickY * 257) - 32768;
     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
 
+    if (ctx->report_sensors) {
+        float data[3];
+
+        data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
+        data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
+        data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1]));
+        SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3);
+
+        data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1]));
+        data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1]));
+        data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1]));
+        SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3);
+    }
+}
+
+static void
+HIDAPI_DriverPS5_HandleStatePacketV1(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketV1_t *packet)
+{
+    static const float TOUCHPAD_SCALEX = 1.0f / 1920;
+    static const float TOUCHPAD_SCALEY = 1.0f / 1070;
+    Uint8 touchpad_state;
+    int touchpad_x, touchpad_y;
+
+    if (ctx->report_touchpad) {
+        touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
+        touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
+        touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
+        SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+
+        touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
+        touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
+        touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
+        SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+    }
+
+    SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
+}
+
+static void
+HIDAPI_DriverPS5_HandleStatePacketV2(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketV2_t *packet)
+{
+    static const float TOUCHPAD_SCALEX = 1.0f / 1920;
+    static const float TOUCHPAD_SCALEY = 1.0f / 1070;
+    Uint8 touchpad_state;
+    int touchpad_x, touchpad_y;
+
+    if (ctx->report_touchpad) {
+        touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
+        touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
+        touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
+        SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+
+        touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
+        touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
+        touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
+        SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
+    }
+
     /* A check of packet->ucBatteryLevel & 0x10 should work as a check for BT vs USB but doesn't
      * seem to always work. Possibly related to being 100% charged?
      */
@@ -1018,31 +1178,7 @@ HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev,
         }
     }
 
-    touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
-    touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
-    touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
-    SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
-
-    touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
-    touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
-    touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
-    SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
-
-    if (ctx->report_sensors) {
-        float data[3];
-
-        data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
-        data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
-        data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1]));
-        SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3);
-
-        data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1]));
-        data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1]));
-        data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1]));
-        SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3);
-    }
-
-    SDL_memcpy(&ctx->last_state.state, packet, sizeof(ctx->last_state.state));
+    SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
 }
 
 static SDL_bool
@@ -1073,7 +1209,12 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
             if (size == 10 || size == 78) {
                 HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1]);
             } else {
-                HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1]);
+                HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[1]);
+                if (ctx->firmware_version <= 0x204) {
+                    HIDAPI_DriverPS5_HandleStatePacketV1(joystick, device->dev, ctx, (PS5StatePacketV1_t *)&data[1]);
+                } else {
+                    HIDAPI_DriverPS5_HandleStatePacketV2(joystick, device->dev, ctx, (PS5StatePacketV2_t *)&data[1]);
+                }
             }
             break;
         case k_EPS5ReportIdBluetoothState:
@@ -1084,7 +1225,12 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
             if (ctx->led_reset_state == k_EDS5LEDResetStatePending) {
                 HIDAPI_DriverPS5_CheckPendingLEDReset(device);
             }
-            HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]);
+            HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2]);
+            if (ctx->firmware_version <= 0x204) {
+                HIDAPI_DriverPS5_HandleStatePacketV1(joystick, device->dev, ctx, (PS5StatePacketV1_t *)&data[2]);
+            } else {
+                HIDAPI_DriverPS5_HandleStatePacketV2(joystick, device->dev, ctx, (PS5StatePacketV2_t *)&data[2]);
+            }
             break;
         default:
 #ifdef DEBUG_JOYSTICK
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index f1cd185dca6..752e9a543de 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -28,6 +28,7 @@
 #define USB_VENDOR_AMAZON       0x1949
 #define USB_VENDOR_APPLE        0x05ac
 #define USB_VENDOR_GOOGLE       0x18d1
+#define USB_VENDOR_HORI         0x0f0d
 #define USB_VENDOR_HYPERKIN     0x2e24
 #define USB_VENDOR_MICROSOFT    0x045e
 #define USB_VENDOR_NINTENDO     0x057e
@@ -44,6 +45,8 @@
 #define USB_PRODUCT_AMAZON_LUNA_CONTROLLER                  0x0419
 #define USB_PRODUCT_GOOGLE_STADIA_CONTROLLER                0x9400
 #define USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER               0x1846
+#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS4           0x011c
+#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5           0x0184
 #define USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER               0x0337
 #define USB_PRODUCT_NINTENDO_N64_CONTROLLER                 0x2019
 #define USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER        0x201e