From f0e47f8ee07410e29dcba8a13c91f3e2ef3c92b1 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 27 Nov 2023 12:09:31 -0800
Subject: [PATCH] Added support for the NACON Revolution 5 Pro controller
---
src/joystick/controller_list.h | 4 ++
src/joystick/hidapi/SDL_hidapi_ps4.c | 27 ++++++++++--
src/joystick/hidapi/SDL_hidapi_ps5.c | 52 +++++++++++++++++++++++-
src/joystick/hidapi/SDL_hidapijoystick.c | 1 +
src/joystick/usb_ids.h | 5 +++
5 files changed, 84 insertions(+), 5 deletions(-)
diff --git a/src/joystick/controller_list.h b/src/joystick/controller_list.h
index 331bf171588e..d8342f680907 100644
--- a/src/joystick/controller_list.h
+++ b/src/joystick/controller_list.h
@@ -141,6 +141,8 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x2c22, 0x2303 ), k_eControllerType_XInputPS4Controller, NULL }, // Qanba Obsidian Arcade Joystick
{ MAKE_CONTROLLER_ID( 0x2c22, 0x2500 ), k_eControllerType_PS4Controller, NULL }, // Qanba Dragon
{ MAKE_CONTROLLER_ID( 0x2c22, 0x2503 ), k_eControllerType_XInputPS4Controller, NULL }, // Qanba Dragon Arcade Joystick
+ { MAKE_CONTROLLER_ID( 0x3285, 0x0d16 ), k_eControllerType_PS4Controller, NULL }, // NACON Revolution 5 Pro (PS4 mode with dongle)
+ { MAKE_CONTROLLER_ID( 0x3285, 0x0d17 ), k_eControllerType_PS4Controller, NULL }, // NACON Revolution 5 Pro (PS4 mode wired)
{ MAKE_CONTROLLER_ID( 0x7545, 0x0104 ), k_eControllerType_PS4Controller, NULL }, // Armor 3 or Level Up Cobra - At least one variant has gyro
{ MAKE_CONTROLLER_ID (0x9886, 0x0024 ), k_eControllerType_XInputPS4Controller, NULL }, // Astro C40 in Xbox 360 mode
{ MAKE_CONTROLLER_ID( 0x9886, 0x0025 ), k_eControllerType_PS4Controller, NULL }, // Astro C40
@@ -154,6 +156,8 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x0f0d, 0x0184 ), k_eControllerType_PS5Controller, NULL }, // Hori Fighting Stick α
{ MAKE_CONTROLLER_ID( 0x1532, 0x100b ), k_eControllerType_PS5Controller, NULL }, // Razer Wolverine V2 Pro (Wired)
{ MAKE_CONTROLLER_ID( 0x1532, 0x100c ), k_eControllerType_PS5Controller, NULL }, // Razer Wolverine V2 Pro (Wireless)
+ { MAKE_CONTROLLER_ID( 0x3285, 0x0d18 ), k_eControllerType_PS5Controller, NULL }, // NACON Revolution 5 Pro (PS5 mode with dongle)
+ { MAKE_CONTROLLER_ID( 0x3285, 0x0d19 ), k_eControllerType_PS5Controller, NULL }, // NACON Revolution 5 Pro (PS5 mode wired)
{ MAKE_CONTROLLER_ID( 0x358a, 0x0104 ), k_eControllerType_PS5Controller, NULL }, // Backbone One PlayStation Edition for iOS
{ 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
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 33a262dcd2f4..a5e9cb0a33c9 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -142,6 +142,7 @@ typedef struct
SDL_HIDAPI_Device *device;
SDL_Joystick *joystick;
SDL_bool is_dongle;
+ SDL_bool is_nacon_dongle;
SDL_bool official_controller;
SDL_bool sensors_supported;
SDL_bool lightbar_supported;
@@ -413,6 +414,11 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
}
ctx->effects_supported = (ctx->lightbar_supported || ctx->vibration_supported);
+ if (device->vendor_id == USB_VENDOR_NACON_ALT &&
+ device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRELESS) {
+ ctx->is_nacon_dongle = SDL_TRUE;
+ }
+
if (device->vendor_id == USB_VENDOR_PDP &&
(device->product_id == USB_PRODUCT_VICTRIX_FS_PRO ||
device->product_id == USB_PRODUCT_VICTRIX_FS_PRO_V2)) {
@@ -438,7 +444,7 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
} else {
HIDAPI_DisconnectBluetoothDevice(device->serial);
}
- if (ctx->is_dongle && serial[0] == '\0') {
+ if ((ctx->is_dongle || ctx->is_nacon_dongle) && serial[0] == '\0') {
/* Not yet connected */
return SDL_TRUE;
}
@@ -1147,6 +1153,21 @@ static SDL_bool HIDAPI_DriverPS4_IsPacketValid(SDL_DriverPS4_Context *ctx, Uint8
return SDL_TRUE;
}
+ if (ctx->is_nacon_dongle && size >= (1 + sizeof(PS4StatePacket_t))) {
+ /* The report timestamp doesn't change when the controller isn't connected */
+ PS4StatePacket_t *packet = (PS4StatePacket_t *)&data[1];
+ if (SDL_memcmp(packet->rgucTimestamp, ctx->last_state.rgucTimestamp, sizeof(packet->rgucTimestamp)) == 0) {
+ return SDL_FALSE;
+ }
+ if (ctx->last_state.rgucAccelX[0] == 0 && ctx->last_state.rgucAccelX[1] == 0 &&
+ ctx->last_state.rgucAccelY[0] == 0 && ctx->last_state.rgucAccelY[1] == 0 &&
+ ctx->last_state.rgucAccelZ[0] == 0 && ctx->last_state.rgucAccelZ[1] == 0) {
+ /* We don't have any state to compare yet, go ahead and copy it */
+ SDL_memcpy(&ctx->last_state, &data[1], sizeof(PS4StatePacket_t));
+ return SDL_FALSE;
+ }
+ }
+
/* In the case of a DS4 USB dongle, bit[2] of byte 31 indicates if a DS4 is actually connected (indicated by '0').
* For non-dongle, this bit is always 0 (connected).
* This is usually the ID over USB, but the DS4v2 that started shipping with the PS4 Slim will also send this
@@ -1258,7 +1279,7 @@ static SDL_bool HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
}
}
- if (ctx->is_dongle) {
+ if (ctx->is_dongle || ctx->is_nacon_dongle) {
if (packet_count == 0) {
if (device->num_joysticks > 0) {
/* Check to see if it looks like the device disconnected */
@@ -1280,7 +1301,7 @@ static SDL_bool HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
}
}
- if (size < 0 && device->num_joysticks > 0) {
+ if (packet_count == 0 && size < 0 && device->num_joysticks > 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
}
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index 37b15991b6df..4e71f95c61b9 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -231,6 +231,7 @@ typedef struct
{
SDL_HIDAPI_Device *device;
SDL_Joystick *joystick;
+ SDL_bool is_nacon_dongle;
SDL_bool use_alternate_report;
SDL_bool sensors_supported;
SDL_bool lightbar_supported;
@@ -264,6 +265,7 @@ typedef struct
{
PS5SimpleStatePacket_t simple;
PS5StatePacketCommon_t state;
+ PS5StatePacketAlt_t alt_state;
PS5StatePacket_t full_state;
Uint8 data[64];
} last_state;
@@ -491,10 +493,17 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
}
ctx->use_alternate_report = SDL_TRUE;
+
+ if (device->vendor_id == USB_VENDOR_NACON_ALT &&
+ (device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRED ||
+ device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS)) {
+ /* This doesn't report vibration capability, but it can do rumble */
+ ctx->vibration_supported = SDL_TRUE;
+ }
} else if (device->vendor_id == USB_VENDOR_RAZER &&
(device->product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_PS5_WIRED ||
device->product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_PS5_WIRELESS)) {
- /* The Razer Wolverine V2 Pro doesn't respond to the detection protocol, but has a touchpad and sensors, but no vibration */
+ /* The Razer Wolverine V2 Pro doesn't respond to the detection protocol, but has a touchpad and sensors and no vibration */
ctx->sensors_supported = SDL_TRUE;
ctx->touchpad_supported = SDL_TRUE;
ctx->use_alternate_report = SDL_TRUE;
@@ -502,6 +511,11 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
}
ctx->effects_supported = (ctx->lightbar_supported || ctx->vibration_supported || ctx->playerled_supported);
+ if (device->vendor_id == USB_VENDOR_NACON_ALT &&
+ device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS) {
+ ctx->is_nacon_dongle = SDL_TRUE;
+ }
+
device->joystick_type = joystick_type;
device->type = SDL_GAMEPAD_TYPE_PS5;
if (device->vendor_id == USB_VENDOR_SONY) {
@@ -513,6 +527,11 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
}
HIDAPI_SetDeviceSerial(device, serial);
+ if (ctx->is_nacon_dongle) {
+ /* We don't know if this is connected yet, wait for reports */
+ return SDL_TRUE;
+ }
+
/* Prefer the USB device over the Bluetooth device */
if (device->is_bluetooth) {
if (HIDAPI_HasConnectedUSBDevice(device->serial)) {
@@ -1437,6 +1456,20 @@ static SDL_bool HIDAPI_DriverPS5_IsPacketValid(SDL_DriverPS5_Context *ctx, Uint8
{
switch (data[0]) {
case k_EPS5ReportIdState:
+ if (ctx->is_nacon_dongle && size >= (1 + sizeof(PS5StatePacketAlt_t))) {
+ /* The report timestamp doesn't change when the controller isn't connected */
+ PS5StatePacketAlt_t *packet = (PS5StatePacketAlt_t *)&data[1];
+ if (SDL_memcmp(packet->rgucPacketSequence, ctx->last_state.state.rgucPacketSequence, sizeof(packet->rgucPacketSequence)) == 0) {
+ return SDL_FALSE;
+ }
+ if (ctx->last_state.alt_state.rgucAccelX[0] == 0 && ctx->last_state.alt_state.rgucAccelX[1] == 0 &&
+ ctx->last_state.alt_state.rgucAccelY[0] == 0 && ctx->last_state.alt_state.rgucAccelY[1] == 0 &&
+ ctx->last_state.alt_state.rgucAccelZ[0] == 0 && ctx->last_state.alt_state.rgucAccelZ[1] == 0) {
+ /* We don't have any state to compare yet, go ahead and copy it */
+ SDL_memcpy(&ctx->last_state, &data[1], sizeof(PS5StatePacketAlt_t));
+ return SDL_FALSE;
+ }
+ }
return SDL_TRUE;
case k_EPS5ReportIdBluetoothState:
@@ -1531,7 +1564,22 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
}
}
- if (size < 0 && device->num_joysticks > 0) {
+ if (ctx->is_nacon_dongle) {
+ if (packet_count == 0) {
+ if (device->num_joysticks > 0) {
+ /* Check to see if it looks like the device disconnected */
+ if (now >= (ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) {
+ HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+ }
+ }
+ } else {
+ if (device->num_joysticks == 0) {
+ HIDAPI_JoystickConnected(device, NULL);
+ }
+ }
+ }
+
+ if (packet_count == 0 && size < 0 && device->num_joysticks > 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
}
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 01e44286407a..be3eb2e92d41 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -157,6 +157,7 @@ SDL_bool HIDAPI_SupportsPlaystationDetection(Uint16 vendor, Uint16 product)
case USB_VENDOR_MADCATZ:
return SDL_TRUE;
case USB_VENDOR_NACON:
+ case USB_VENDOR_NACON_ALT:
return SDL_TRUE;
case USB_VENDOR_PDP:
return SDL_TRUE;
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index 08bd21bca52c..a582512ad87d 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -39,6 +39,7 @@
#define USB_VENDOR_MADCATZ 0x0738
#define USB_VENDOR_MICROSOFT 0x045e
#define USB_VENDOR_NACON 0x146b
+#define USB_VENDOR_NACON_ALT 0x3285
#define USB_VENDOR_NINTENDO 0x057e
#define USB_VENDOR_NVIDIA 0x0955
#define USB_VENDOR_PDP 0x0e6f
@@ -72,6 +73,10 @@
#define USB_PRODUCT_HORI_FIGHTING_STICK_ALPHA_PS5 0x0184
#define USB_PRODUCT_LOGITECH_F310 0xc216
#define USB_PRODUCT_LOGITECH_CHILLSTREAM 0xcad1
+#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRELESS 0x0d16
+#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRED 0x0d17
+#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS 0x0d18
+#define USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRED 0x0d19
#define USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER 0x0337
#define USB_PRODUCT_NINTENDO_N64_CONTROLLER 0x2019
#define USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER 0x201e