SDL: Improved reliability of the Steam Controller driver

From 8657fa8b3b79f6f9a516ff4763dd235e03cd8d44 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 15 Nov 2024 15:55:13 -0800
Subject: [PATCH] Improved reliability of the Steam Controller driver

---
 src/joystick/hidapi/SDL_hidapi_steam.c | 70 +++++++++++++-------------
 1 file changed, 36 insertions(+), 34 deletions(-)

diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c
index 336bc2ff7d7bc..68fb01017b274 100644
--- a/src/joystick/hidapi/SDL_hidapi_steam.c
+++ b/src/joystick/hidapi/SDL_hidapi_steam.c
@@ -28,6 +28,9 @@
 
 #ifdef SDL_JOYSTICK_HIDAPI_STEAM
 
+// Define this if you want to log all packets from the controller
+// #define DEBUG_STEAM_PROTOCOL
+
 #define SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED    "SDL_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED"
 
 #if defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
@@ -210,11 +213,7 @@ static uint8_t GetSegmentHeader(int nSegmentNumber, bool bLastPacket)
 
 static void hexdump(const uint8_t *ptr, int len)
 {
-    int i;
-    for (i = 0; i < len; ++i) {
-        printf("%02x ", ptr[i]);
-    }
-    printf("\n");
+    HIDAPI_DumpPacket("Data", ptr, len);
 }
 
 static void ResetSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler)
@@ -418,22 +417,25 @@ static int GetFeatureReport(SDL_HIDAPI_Device *dev, unsigned char uBuffer[65])
 
 static int ReadResponse(SDL_HIDAPI_Device *dev, uint8_t uBuffer[65], int nExpectedResponse)
 {
-    int nRet = GetFeatureReport(dev, uBuffer);
+    for (int nRetries = 0; nRetries < 10; nRetries++) {
+        int nRet = GetFeatureReport(dev, uBuffer);
 
-    DPRINTF("ReadResponse( %p %p %d )\n", dev, uBuffer, nExpectedResponse);
+        DPRINTF("ReadResponse( %p %p 0x%x )\n", dev, uBuffer, nExpectedResponse);
 
-    if (nRet < 0) {
-        return nRet;
-    }
+        if (nRet < 0) {
+            continue;
+        }
 
-    DPRINTF("ReadResponse got %d bytes of data: ", nRet);
-    HEXDUMP(uBuffer, nRet);
+        DPRINTF("ReadResponse got %d bytes of data: ", nRet);
+        HEXDUMP(uBuffer, nRet);
 
-    if (uBuffer[1] != nExpectedResponse) {
-        return -1;
-    }
+        if (uBuffer[1] != nExpectedResponse) {
+            continue;
+        }
 
-    return nRet;
+        return nRet;
+    }
+    return -1;
 }
 
 //---------------------------------------------------------------------------
@@ -1223,15 +1225,23 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
         const Uint8 *pPacket;
 
         r = ReadSteamController(device->dev, data, sizeof(data));
-        if (r == 0) {
+        if (r <= 0) {
+            if (r < 0) {
+                // Failed to read from controller
+                if (ctx->connected) {
+                    HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+                    ctx->connected = false;
+                }
+                return false;
+            }
             break;
         }
 
-        nPacketLength = 0;
-        if (r > 0) {
-            nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r);
-        }
+#ifdef DEBUG_STEAM_PROTOCOL
+        HIDAPI_DumpPacket("Steam Controller packet: size = %d", data, r);
+#endif
 
+        nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r);
         pPacket = ctx->m_assembler.uBuffer;
 
         if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) {
@@ -1321,11 +1331,6 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
             }
 
             ctx->m_last_state = ctx->m_state;
-        } else if (ctx->connected && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) {
-            // Controller has disconnected from the wireless dongle
-            HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
-            joystick = NULL;
-            ctx->connected = false;
         } else if (!ctx->connected && D0G_IS_WIRELESS_CONNECT(pPacket, nPacketLength)) {
             // Controller has connected to the wireless dongle
             if (!HIDAPI_JoystickConnected(device, NULL)) {
@@ -1337,14 +1342,11 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device)
 
             joystick = SDL_GetJoystickFromID(device->joysticks[0]);
             ctx->connected = true;
-        }
-
-        if (r <= 0) {
-            // Failed to read from controller
-            if (ctx->connected) {
-                HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
-            }
-            return false;
+        } else if (ctx->connected && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) {
+            // Controller has disconnected from the wireless dongle
+            HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
+            joystick = NULL;
+            ctx->connected = false;
         }
     }
     return true;