SDL: Added support for the Xbox One S Controller with 5.x series firmware

From 907943a2366bc1f633da5ccc805a77f78ceee475 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 12 Aug 2021 17:51:08 -0700
Subject: [PATCH] Added support for the Xbox One S Controller with 5.x series
 firmware

---
 src/joystick/SDL_joystick.c              |  3 ++-
 src/joystick/controller_type.h           |  1 +
 src/joystick/hidapi/SDL_hidapi_xboxone.c | 24 +++++++++++++-----------
 src/joystick/usb_ids.h                   |  3 ++-
 4 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 7e58357043..baa511b181 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1988,8 +1988,9 @@ SDL_IsJoystickBluetoothXboxOne(Uint16 vendor_id, Uint16 product_id)
     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 ||
+            product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH_V5 ||
             product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
-            product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_REV2_BLUETOOTH ||
+            product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH_V5 ||
             product_id == USB_PRODUCT_XBOX_SERIES_X_BLUETOOTH) {
             return SDL_TRUE;
         }
diff --git a/src/joystick/controller_type.h b/src/joystick/controller_type.h
index 0488cfc5a8..64d925d9be 100644
--- a/src/joystick/controller_type.h
+++ b/src/joystick/controller_type.h
@@ -328,6 +328,7 @@ static const ControllerDescription_t arrControllers[] = {
 	{ MAKE_CONTROLLER_ID( 0x045e, 0x0b05 ), k_eControllerType_XBoxOneController, "Xbox One Elite 2 Controller" },	// Microsoft X-Box One Elite Series 2 pad (Bluetooth)
 	{ MAKE_CONTROLLER_ID( 0x045e, 0x0b12 ), k_eControllerType_XBoxOneController, "Xbox Series X Controller" },	// Microsoft X-Box Series X pad
 	{ MAKE_CONTROLLER_ID( 0x045e, 0x0b13 ), k_eControllerType_XBoxOneController, "Xbox Series X Controller" },	// Microsoft X-Box Series X pad (Bluetooth)
+	{ MAKE_CONTROLLER_ID( 0x045e, 0x0b20 ), k_eControllerType_XBoxOneController, "Xbox One S Controller" },	// Microsoft X-Box One S pad (Bluetooth) with 5.x firmware
 	{ MAKE_CONTROLLER_ID( 0x045e, 0x0b22 ), k_eControllerType_XBoxOneController, "Xbox One Elite 2 Controller" },	// Microsoft X-Box One Elite Series 2 pad (Bluetooth) with 5.x firmware
 	{ MAKE_CONTROLLER_ID( 0x0738, 0x4a01 ), k_eControllerType_XBoxOneController, NULL },	// Mad Catz FightStick TE 2
 	{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0139 ), k_eControllerType_XBoxOneController, "PDP Xbox One Afterglow" },	// PDP Afterglow Wired Controller for Xbox One
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index 7cf1fbfc9e..6da46b407d 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -661,6 +661,7 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons16(SDL_Joystick *joystick, SDL_Driver
 
 /*
  * Xbox One S with firmware 4.8.1923 uses a 17 byte packet with BACK button in byte 16 and the GUIDE button in a separate packet (on Windows), or in byte 15 (on Linux)
+ * Xbox One S with firmware 5.x uses a 17 byte packet with BACK and GUIDE buttons in byte 15
  * Xbox One Elite Series 2 with firmware 4.7.1872 uses a 55 byte packet with BACK button in byte 16, paddles starting at byte 33, and the GUIDE button in a separate packet
  * Xbox One Elite Series 2 with firmware 4.8.1908 uses a 33 byte packet with BACK button in byte 16, paddles starting at byte 17, and the GUIDE button in a separate packet
  * Xbox One Elite Series 2 with firmware 5.11.3112 uses a 19 byte packet with BACK and GUIDE buttons in byte 15
@@ -679,10 +680,7 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons(SDL_Joystick *joystick, SDL_DriverXb
     }
 
     if (ctx->last_state[15] != data[15]) {
-        if (ctx->has_share_button || size == 19) {
-            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[15] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
-            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[15] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
-        } else if (!ctx->has_guide_packet) {
+        if (!ctx->has_guide_packet) {
             SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[15] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
         }
         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[15] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
@@ -690,12 +688,11 @@ HIDAPI_DriverXboxOneBluetooth_HandleButtons(SDL_Joystick *joystick, SDL_DriverXb
         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[15] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
     }
 
-    if (ctx->last_state[16] != data[16]) {
-        if (ctx->has_share_button) {
-            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
-        } else {
-            SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
-        }
+    if (ctx->has_share_button) {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[15] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_MISC1, (data[16] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
+    } else {
+        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, ((data[15] & 0x04) || (data[16] & 0x01)) ? SDL_PRESSED : SDL_RELEASED);
     }
 
     /*
@@ -762,8 +759,13 @@ HIDAPI_DriverXboxOneBluetooth_HandleStatePacket(SDL_Joystick *joystick, SDL_Driv
     if (size == 16) {
         /* Original Xbox One S, with separate report for guide button */
         HIDAPI_DriverXboxOneBluetooth_HandleButtons16(joystick, ctx, data, size);
-    } else {
+    } else if (size > 16) {
         HIDAPI_DriverXboxOneBluetooth_HandleButtons(joystick, ctx, data, size);
+    } else {
+#ifdef DEBUG_XBOX_PROTOCOL
+        SDL_Log("Unknown Bluetooth state packet format\n");
+#endif
+        return;
     }
 
     if (ctx->last_state[13] != data[13]) {
diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h
index 8e363a60e6..6a4b26e391 100644
--- a/src/joystick/usb_ids.h
+++ b/src/joystick/usb_ids.h
@@ -60,10 +60,11 @@
 #define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1                 0x02e3
 #define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2                 0x0b00
 #define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH       0x0b05
-#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_REV2_BLUETOOTH  0x0b22
+#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH_V5    0x0b22
 #define USB_PRODUCT_XBOX_ONE_S                              0x02ea
 #define USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH               0x02e0
 #define USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH               0x02fd
+#define USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH_V5            0x0b20
 #define USB_PRODUCT_XBOX_SERIES_X                           0x0b12
 #define USB_PRODUCT_XBOX_SERIES_X_BLUETOOTH                 0x0b13
 #define USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT            0x02d6