From 68c2cf84f6f27504f8607f0b6c36d90a58562ea6 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 14 Nov 2024 00:36:06 -0600 Subject: [PATCH] hidapi: Add support for the Steam Controller wireless dongle --- src/joystick/hidapi/SDL_hidapi_steam.c | 65 ++++++++++++++++++++++---- src/joystick/usb_ids.h | 1 + 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c index 9612dd7bfc..d5d12edf1e 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/src/joystick/hidapi/SDL_hidapi_steam.c @@ -136,6 +136,8 @@ typedef struct SteamControllerStateInternal_t #define D0G_WIRELESS_NEWLYPAIRED 3 #define D0G_IS_WIRELESS_DISCONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) == D0G_WIRELESS_DISCONNECTED) +#define D0G_IS_WIRELESS_CONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) != D0G_WIRELESS_DISCONNECTED) + #define MAX_REPORT_SEGMENT_PAYLOAD_SIZE 18 /* @@ -317,7 +319,14 @@ static int SetFeatureReport(SDL_HIDAPI_Device *dev, unsigned char uBuffer[65], i nRet = SDL_hid_send_feature_report(dev->dev, uPacketBuffer, sizeof(uPacketBuffer)); } } else { - nRet = SDL_hid_send_feature_report(dev->dev, uBuffer, 65); + for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) { + nRet = SDL_hid_send_feature_report(dev->dev, uBuffer, 65); + if (nRet >= 0) { + break; + } + + SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000); + } } DPRINTF("SetFeatureReport() ret = %d\n", nRet); @@ -380,7 +389,15 @@ static int GetFeatureReport(SDL_HIDAPI_Device *dev, unsigned char uBuffer[65]) return -1; } else { SDL_memset(uBuffer, 0, 65); - nRet = SDL_hid_get_feature_report(dev->dev, uBuffer, 65); + + for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) { + nRet = SDL_hid_get_feature_report(dev->dev, uBuffer, 65); + if (nRet >= 0) { + break; + } + + SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000); + } DPRINTF("GetFeatureReport USB ret=%d\n", nRet); HEXDUMP(uBuffer, nRet); @@ -992,7 +1009,24 @@ static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) HIDAPI_SetDeviceName(device, "Steam Controller"); - return HIDAPI_JoystickConnected(device, NULL); + // If this is a wireless dongle, request a wireless state update + if (device->product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE) { + unsigned char buf[65]; + int res; + + buf[0] = 0; + buf[1] = ID_DONGLE_GET_WIRELESS_STATE; + res = SetFeatureReport(device, buf, 2); + if (res < 0) { + return SDL_SetError("Failed to send ID_DONGLE_GET_WIRELESS_STATE request"); + } + + // We will enumerate any attached controllers in UpdateDevices() + return true; + } else { + // Wired and BLE controllers are always connected if HIDAPI can see them + return HIDAPI_JoystickConnected(device, NULL); + } } static int HIDAPI_DriverSteam_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) @@ -1095,8 +1129,6 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) if (device->num_joysticks > 0) { joystick = SDL_GetJoystickFromID(device->joysticks[0]); - } else { - return false; } for (;;) { @@ -1109,10 +1141,6 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) break; } - if (!joystick) { - continue; - } - nPacketLength = 0; if (r > 0) { nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r); @@ -1123,6 +1151,10 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) { Uint64 timestamp = SDL_GetTicksNS(); + if (!joystick) { + continue; + } + if (ctx->m_state.ulButtons != ctx->m_last_state.ulButtons) { Uint8 hat = 0; @@ -1203,11 +1235,24 @@ static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) } ctx->m_last_state = ctx->m_state; + } else if (joystick && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) { + // Controller has disconnected from the wireless dongle + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); + joystick = NULL; + } else if (!joystick && D0G_IS_WIRELESS_CONNECT(pPacket, nPacketLength)) { + // Controller has connected to the wireless dongle + if (!HIDAPI_JoystickConnected(device, NULL)) { + return false; + } + + joystick = SDL_GetJoystickFromID(device->joysticks[0]); } if (r <= 0) { // Failed to read from controller - HIDAPI_JoystickDisconnected(device, device->joysticks[0]); + if (joystick) { + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); + } return false; } } diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h index 06efe0f6f3..623ffcf49d 100644 --- a/src/joystick/usb_ids.h +++ b/src/joystick/usb_ids.h @@ -125,6 +125,7 @@ #define USB_PRODUCT_THRUSTMASTER_ESWAPX_PRO 0xd012 #define USB_PRODUCT_TURTLE_BEACH_SERIES_X_REACT_R 0x7013 #define USB_PRODUCT_TURTLE_BEACH_SERIES_X_RECON 0x7009 +#define USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE 0x1142 #define USB_PRODUCT_VICTRIX_FS_PRO 0x0203 #define USB_PRODUCT_VICTRIX_FS_PRO_V2 0x0207 #define USB_PRODUCT_XBOX360_XUSB_CONTROLLER 0x02a1 // XUSB driver software PID