Atlas - SDL_hidapi_steam.c
Home / ext / SDL / src / joystick / hidapi Lines: 22 | Size: 56001 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_JOYSTICK_HIDAPI 24 25#include "../../SDL_hints_c.h" 26#include "../SDL_sysjoystick.h" 27#include "SDL_hidapijoystick_c.h" 28 29#ifdef SDL_JOYSTICK_HIDAPI_STEAM 30 31// Define this if you want to log all packets from the controller 32// #define DEBUG_STEAM_PROTOCOL 33 34#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED "SDL_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED" 35 36#if defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS) 37// This requires prompting for Bluetooth permissions, so make sure the application really wants it 38#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT false 39#else 40#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT) 41#endif 42 43#define PAIRING_STATE_DURATION_SECONDS 60 44 45 46/*****************************************************************************************************/ 47 48#include "steam/controller_constants.h" 49#include "steam/controller_structs.h" 50 51enum 52{ 53 SDL_GAMEPAD_BUTTON_STEAM_RIGHT_PADDLE = 11, 54 SDL_GAMEPAD_BUTTON_STEAM_LEFT_PADDLE, 55 SDL_GAMEPAD_NUM_STEAM_BUTTONS, 56}; 57 58typedef struct SteamControllerStateInternal_t 59{ 60 // Controller Type for this Controller State 61 Uint32 eControllerType; 62 63 // If packet num matches that on your prior call, then the controller state hasn't been changed since 64 // your last call and there is no need to process it 65 Uint32 unPacketNum; 66 67 // bit flags for each of the buttons 68 Uint64 ulButtons; 69 70 // Left pad coordinates 71 short sLeftPadX; 72 short sLeftPadY; 73 74 // Right pad coordinates 75 short sRightPadX; 76 short sRightPadY; 77 78 // Center pad coordinates 79 short sCenterPadX; 80 short sCenterPadY; 81 82 // Left analog stick coordinates 83 short sLeftStickX; 84 short sLeftStickY; 85 86 // Right analog stick coordinates 87 short sRightStickX; 88 short sRightStickY; 89 90 unsigned short sTriggerL; 91 unsigned short sTriggerR; 92 93 short sAccelX; 94 short sAccelY; 95 short sAccelZ; 96 97 short sGyroX; 98 short sGyroY; 99 short sGyroZ; 100 101 float sGyroQuatW; 102 float sGyroQuatX; 103 float sGyroQuatY; 104 float sGyroQuatZ; 105 106 short sGyroSteeringAngle; 107 108 unsigned short sBatteryLevel; 109 110 // Pressure sensor data. 111 unsigned short sPressurePadLeft; 112 unsigned short sPressurePadRight; 113 114 unsigned short sPressureBumperLeft; 115 unsigned short sPressureBumperRight; 116 117 // Internal state data 118 short sPrevLeftPad[2]; 119 short sPrevLeftStick[2]; 120} SteamControllerStateInternal_t; 121 122// Defines for ulButtons in SteamControllerStateInternal_t 123#define STEAM_RIGHT_TRIGGER_MASK 0x00000001 124#define STEAM_LEFT_TRIGGER_MASK 0x00000002 125#define STEAM_RIGHT_BUMPER_MASK 0x00000004 126#define STEAM_LEFT_BUMPER_MASK 0x00000008 127#define STEAM_BUTTON_NORTH_MASK 0x00000010 // Y 128#define STEAM_BUTTON_EAST_MASK 0x00000020 // B 129#define STEAM_BUTTON_WEST_MASK 0x00000040 // X 130#define STEAM_BUTTON_SOUTH_MASK 0x00000080 // A 131#define STEAM_DPAD_UP_MASK 0x00000100 // DPAD UP 132#define STEAM_DPAD_RIGHT_MASK 0x00000200 // DPAD RIGHT 133#define STEAM_DPAD_LEFT_MASK 0x00000400 // DPAD LEFT 134#define STEAM_DPAD_DOWN_MASK 0x00000800 // DPAD DOWN 135#define STEAM_BUTTON_MENU_MASK 0x00001000 // SELECT 136#define STEAM_BUTTON_STEAM_MASK 0x00002000 // GUIDE 137#define STEAM_BUTTON_ESCAPE_MASK 0x00004000 // START 138#define STEAM_BUTTON_BACK_LEFT_MASK 0x00008000 139#define STEAM_BUTTON_BACK_RIGHT_MASK 0x00010000 140#define STEAM_BUTTON_LEFTPAD_CLICKED_MASK 0x00020000 141#define STEAM_BUTTON_RIGHTPAD_CLICKED_MASK 0x00040000 142#define STEAM_LEFTPAD_FINGERDOWN_MASK 0x00080000 143#define STEAM_RIGHTPAD_FINGERDOWN_MASK 0x00100000 144#define STEAM_JOYSTICK_BUTTON_MASK 0x00400000 145#define STEAM_LEFTPAD_AND_JOYSTICK_MASK 0x00800000 146 147// Look for report version 0x0001, type WIRELESS (3), length >= 1 byte 148#define D0G_IS_VALID_WIRELESS_EVENT(data, len) ((len) >= 5 && (data)[0] == 1 && (data)[1] == 0 && (data)[2] == 3 && (data)[3] >= 1) 149#define D0G_GET_WIRELESS_EVENT_TYPE(data) ((data)[4]) 150#define D0G_WIRELESS_DISCONNECTED 1 151#define D0G_WIRELESS_ESTABLISHED 2 152#define D0G_WIRELESS_NEWLYPAIRED 3 153 154#define D0G_IS_WIRELESS_DISCONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) == D0G_WIRELESS_DISCONNECTED) 155#define D0G_IS_WIRELESS_CONNECT(data, len) (D0G_IS_VALID_WIRELESS_EVENT(data, len) && D0G_GET_WIRELESS_EVENT_TYPE(data) != D0G_WIRELESS_DISCONNECTED) 156 157 158#define MAX_REPORT_SEGMENT_PAYLOAD_SIZE 18 159/* 160 * SteamControllerPacketAssembler has to be used when reading output repots from controllers. 161 */ 162typedef struct 163{ 164 uint8_t uBuffer[MAX_REPORT_SEGMENT_PAYLOAD_SIZE * 8 + 1]; 165 int nExpectedSegmentNumber; 166 bool bIsBle; 167} SteamControllerPacketAssembler; 168 169#undef clamp 170#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val))) 171 172#undef offsetof 173#define offsetof(s, m) (size_t) & (((s *)0)->m) 174 175#ifdef DEBUG_STEAM_CONTROLLER 176#define DPRINTF(format, ...) printf(format, ##__VA_ARGS__) 177#define HEXDUMP(ptr, len) hexdump(ptr, len) 178#else 179#define DPRINTF(format, ...) 180#define HEXDUMP(ptr, len) 181#endif 182#define printf SDL_Log 183 184#define MAX_REPORT_SEGMENT_SIZE (MAX_REPORT_SEGMENT_PAYLOAD_SIZE + 2) 185#define CALC_REPORT_SEGMENT_NUM(index) ((index / MAX_REPORT_SEGMENT_PAYLOAD_SIZE) & 0x07) 186#define REPORT_SEGMENT_DATA_FLAG 0x80 187#define REPORT_SEGMENT_LAST_FLAG 0x40 188#define BLE_REPORT_NUMBER 0x03 189 190#define STEAMCONTROLLER_TRIGGER_MAX_ANALOG 26000 191 192// Enable mouse mode when using the Steam Controller locally 193#undef ENABLE_MOUSE_MODE 194 195// Wireless firmware quirk: the firmware intentionally signals "failure" when performing 196// SET_FEATURE / GET_FEATURE when it actually means "pending radio roundtrip". The only 197// way to make SET_FEATURE / GET_FEATURE work is to loop several times with a sleep. If 198// it takes more than 50ms to get the response for SET_FEATURE / GET_FEATURE, we assume 199// that the controller has failed. 200#define RADIO_WORKAROUND_SLEEP_ATTEMPTS 50 201#define RADIO_WORKAROUND_SLEEP_DURATION_US 500 202 203// This was defined by experimentation. 2000 seemed to work but to give that extra bit of margin, set to 3ms. 204#define CONTROLLER_CONFIGURATION_DELAY_US 3000 205 206static uint8_t GetSegmentHeader(int nSegmentNumber, bool bLastPacket) 207{ 208 uint8_t header = REPORT_SEGMENT_DATA_FLAG; 209 header |= nSegmentNumber; 210 if (bLastPacket) { 211 header |= REPORT_SEGMENT_LAST_FLAG; 212 } 213 214 return header; 215} 216 217static void hexdump(const uint8_t *ptr, int len) 218{ 219 HIDAPI_DumpPacket("Data", ptr, len); 220} 221 222static void ResetSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler) 223{ 224 SDL_memset(pAssembler->uBuffer, 0, sizeof(pAssembler->uBuffer)); 225 pAssembler->nExpectedSegmentNumber = 0; 226} 227 228static void InitializeSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler, bool bIsBle) 229{ 230 pAssembler->bIsBle = bIsBle; 231 ResetSteamControllerPacketAssembler(pAssembler); 232} 233 234// Returns: 235// <0 on error 236// 0 on not ready 237// Complete packet size on completion 238static int WriteSegmentToSteamControllerPacketAssembler(SteamControllerPacketAssembler *pAssembler, const uint8_t *pSegment, int nSegmentLength) 239{ 240 if (pAssembler->bIsBle) { 241 uint8_t uSegmentHeader = pSegment[1]; 242 int nSegmentNumber = uSegmentHeader & 0x07; 243 244 HEXDUMP(pSegment, nSegmentLength); 245 246 if (pSegment[0] != BLE_REPORT_NUMBER) { 247 // We may get keyboard/mouse input events until controller stops sending them 248 return 0; 249 } 250 251 if (nSegmentLength != MAX_REPORT_SEGMENT_SIZE) { 252 printf("Bad segment size! %d\n", nSegmentLength); 253 hexdump(pSegment, nSegmentLength); 254 ResetSteamControllerPacketAssembler(pAssembler); 255 return -1; 256 } 257 258 DPRINTF("GOT PACKET HEADER = 0x%x\n", uSegmentHeader); 259 260 if (!(uSegmentHeader & REPORT_SEGMENT_DATA_FLAG)) { 261 // We get empty segments, just ignore them 262 return 0; 263 } 264 265 if (nSegmentNumber != pAssembler->nExpectedSegmentNumber) { 266 ResetSteamControllerPacketAssembler(pAssembler); 267 268 if (nSegmentNumber) { 269 // This happens occasionally 270 DPRINTF("Bad segment number, got %d, expected %d\n", 271 nSegmentNumber, pAssembler->nExpectedSegmentNumber); 272 return -1; 273 } 274 } 275 276 SDL_memcpy(pAssembler->uBuffer + nSegmentNumber * MAX_REPORT_SEGMENT_PAYLOAD_SIZE, 277 pSegment + 2, // ignore header and report number 278 MAX_REPORT_SEGMENT_PAYLOAD_SIZE); 279 280 if (uSegmentHeader & REPORT_SEGMENT_LAST_FLAG) { 281 pAssembler->nExpectedSegmentNumber = 0; 282 return (nSegmentNumber + 1) * MAX_REPORT_SEGMENT_PAYLOAD_SIZE; 283 } 284 285 pAssembler->nExpectedSegmentNumber++; 286 } else { 287 // Just pass through 288 SDL_memcpy(pAssembler->uBuffer, 289 pSegment, 290 nSegmentLength); 291 return nSegmentLength; 292 } 293 294 return 0; 295} 296 297#define BLE_MAX_READ_RETRIES 8 298 299static int SetFeatureReport(SDL_HIDAPI_Device *dev, const unsigned char uBuffer[65], int nActualDataLen) 300{ 301 int nRet = -1; 302 303 DPRINTF("SetFeatureReport %p %p %d\n", dev, uBuffer, nActualDataLen); 304 305 if (dev->is_bluetooth) { 306 int nSegmentNumber = 0; 307 uint8_t uPacketBuffer[MAX_REPORT_SEGMENT_SIZE]; 308 const unsigned char *pBufferPtr = uBuffer + 1; 309 310 if (nActualDataLen < 1) { 311 return -1; 312 } 313 314 // Skip report number in data 315 nActualDataLen--; 316 317 while (nActualDataLen > 0) { 318 int nBytesInPacket = nActualDataLen > MAX_REPORT_SEGMENT_PAYLOAD_SIZE ? MAX_REPORT_SEGMENT_PAYLOAD_SIZE : nActualDataLen; 319 320 nActualDataLen -= nBytesInPacket; 321 322 // Construct packet 323 SDL_memset(uPacketBuffer, 0, sizeof(uPacketBuffer)); 324 uPacketBuffer[0] = BLE_REPORT_NUMBER; 325 uPacketBuffer[1] = GetSegmentHeader(nSegmentNumber, nActualDataLen == 0); 326 SDL_memcpy(&uPacketBuffer[2], pBufferPtr, nBytesInPacket); 327 328 pBufferPtr += nBytesInPacket; 329 nSegmentNumber++; 330 331 nRet = SDL_hid_send_feature_report(dev->dev, uPacketBuffer, sizeof(uPacketBuffer)); 332 } 333 } else { 334 for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) { 335 nRet = SDL_hid_send_feature_report(dev->dev, uBuffer, 65); 336 if (nRet >= 0) { 337 break; 338 } 339 340 SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000); 341 } 342 } 343 344 DPRINTF("SetFeatureReport() ret = %d\n", nRet); 345 346 return nRet; 347} 348 349static int GetFeatureReport(SDL_HIDAPI_Device *dev, unsigned char uBuffer[65]) 350{ 351 int nRet = -1; 352 353 DPRINTF("GetFeatureReport( %p %p )\n", dev, uBuffer); 354 355 if (dev->is_bluetooth) { 356 int nRetries = 0; 357 uint8_t uSegmentBuffer[MAX_REPORT_SEGMENT_SIZE + 1]; 358 uint8_t ucBytesToRead = MAX_REPORT_SEGMENT_SIZE; 359 uint8_t ucDataStartOffset = 0; 360 361 SteamControllerPacketAssembler assembler; 362 InitializeSteamControllerPacketAssembler(&assembler, dev->is_bluetooth); 363 364 // On Windows and macOS, BLE devices get 2 copies of the feature report ID, one that is removed by ReadFeatureReport, 365 // and one that's included in the buffer we receive. We pad the bytes to read and skip over the report ID 366 // if necessary. 367#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_MACOS) 368 ++ucBytesToRead; 369 ++ucDataStartOffset; 370#endif 371 372 while (nRetries < BLE_MAX_READ_RETRIES) { 373 SDL_memset(uSegmentBuffer, 0, sizeof(uSegmentBuffer)); 374 uSegmentBuffer[0] = BLE_REPORT_NUMBER; 375 nRet = SDL_hid_get_feature_report(dev->dev, uSegmentBuffer, ucBytesToRead); 376 377 DPRINTF("GetFeatureReport ble ret=%d\n", nRet); 378 HEXDUMP(uSegmentBuffer, nRet); 379 380 // Zero retry counter if we got data 381 if (nRet > 2 && (uSegmentBuffer[ucDataStartOffset + 1] & REPORT_SEGMENT_DATA_FLAG)) { 382 nRetries = 0; 383 } else { 384 nRetries++; 385 } 386 387 if (nRet > 0) { 388 int nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&assembler, 389 uSegmentBuffer + ucDataStartOffset, 390 nRet - ucDataStartOffset); 391 392 if (nPacketLength > 0 && nPacketLength < 65) { 393 // Leave space for "report number" 394 uBuffer[0] = 0; 395 SDL_memcpy(uBuffer + 1, assembler.uBuffer, nPacketLength); 396 return nPacketLength; 397 } 398 } 399 } 400 printf("Could not get a full ble packet after %d retries\n", nRetries); 401 return -1; 402 } else { 403 SDL_memset(uBuffer, 0, 65); 404 405 for (int nRetries = 0; nRetries < RADIO_WORKAROUND_SLEEP_ATTEMPTS; nRetries++) { 406 nRet = SDL_hid_get_feature_report(dev->dev, uBuffer, 65); 407 if (nRet >= 0) { 408 break; 409 } 410 411 SDL_DelayNS(RADIO_WORKAROUND_SLEEP_DURATION_US * 1000); 412 } 413 414 DPRINTF("GetFeatureReport USB ret=%d\n", nRet); 415 HEXDUMP(uBuffer, nRet); 416 } 417 418 return nRet; 419} 420 421static int ReadResponse(SDL_HIDAPI_Device *dev, uint8_t uBuffer[65], int nExpectedResponse) 422{ 423 for (int nRetries = 0; nRetries < 10; nRetries++) { 424 int nRet = GetFeatureReport(dev, uBuffer); 425 426 DPRINTF("ReadResponse( %p %p 0x%x )\n", dev, uBuffer, nExpectedResponse); 427 428 if (nRet < 0) { 429 continue; 430 } 431 432 DPRINTF("ReadResponse got %d bytes of data: ", nRet); 433 HEXDUMP(uBuffer, nRet); 434 435 if (uBuffer[1] != nExpectedResponse) { 436 continue; 437 } 438 439 return nRet; 440 } 441 return -1; 442} 443 444//--------------------------------------------------------------------------- 445// Reset steam controller (unmap buttons and pads) and re-fetch capability bits 446//--------------------------------------------------------------------------- 447static bool ResetSteamController(SDL_HIDAPI_Device *dev, bool bSuppressErrorSpew, uint32_t *punUpdateRateUS) 448{ 449 // Firmware quirk: Set Feature and Get Feature requests always require a 65-byte buffer. 450 unsigned char buf[65]; 451 unsigned int i; 452 int res = -1; 453 int nSettings = 0; 454 int nAttributesLength; 455 FeatureReportMsg *msg; 456 uint32_t unUpdateRateUS = 9000; // Good default rate 457 458 DPRINTF("ResetSteamController hid=%p\n", dev); 459 460 SDL_zero(buf); 461 buf[0] = 0; 462 buf[1] = ID_GET_ATTRIBUTES_VALUES; 463 res = SetFeatureReport(dev, buf, 2); 464 if (res < 0) { 465 if (!bSuppressErrorSpew) { 466 printf("GET_ATTRIBUTES_VALUES failed for controller %p\n", dev); 467 } 468 return false; 469 } 470 471 // Retrieve GET_ATTRIBUTES_VALUES result 472 // Wireless controller endpoints without a connected controller will return nAttrs == 0 473 res = ReadResponse(dev, buf, ID_GET_ATTRIBUTES_VALUES); 474 if (res < 0 || buf[1] != ID_GET_ATTRIBUTES_VALUES) { 475 HEXDUMP(buf, res); 476 if (!bSuppressErrorSpew) { 477 printf("Bad GET_ATTRIBUTES_VALUES response for controller %p\n", dev); 478 } 479 return false; 480 } 481 482 nAttributesLength = buf[2]; 483 if (nAttributesLength > res) { 484 if (!bSuppressErrorSpew) { 485 printf("Bad GET_ATTRIBUTES_VALUES response for controller %p\n", dev); 486 } 487 return false; 488 } 489 490 msg = (FeatureReportMsg *)&buf[1]; 491 for (i = 0; i < (int)msg->header.length / sizeof(ControllerAttribute); ++i) { 492 uint8_t unAttribute = msg->payload.getAttributes.attributes[i].attributeTag; 493 uint32_t unValue = msg->payload.getAttributes.attributes[i].attributeValue; 494 495 switch (unAttribute) { 496 case ATTRIB_UNIQUE_ID: 497 break; 498 case ATTRIB_PRODUCT_ID: 499 break; 500 case ATTRIB_CAPABILITIES: 501 break; 502 case ATTRIB_CONNECTION_INTERVAL_IN_US: 503 unUpdateRateUS = unValue; 504 break; 505 default: 506 break; 507 } 508 } 509 if (punUpdateRateUS) { 510 *punUpdateRateUS = unUpdateRateUS; 511 } 512 513 // Clear digital button mappings 514 SDL_zero(buf); 515 buf[0] = 0; 516 buf[1] = ID_CLEAR_DIGITAL_MAPPINGS; 517 res = SetFeatureReport(dev, buf, 2); 518 if (res < 0) { 519 if (!bSuppressErrorSpew) { 520 printf("CLEAR_DIGITAL_MAPPINGS failed for controller %p\n", dev); 521 } 522 return false; 523 } 524 525 // Reset the default settings 526 SDL_zero(buf); 527 buf[1] = ID_LOAD_DEFAULT_SETTINGS; 528 buf[2] = 0; 529 res = SetFeatureReport(dev, buf, 3); 530 if (res < 0) { 531 if (!bSuppressErrorSpew) { 532 printf("LOAD_DEFAULT_SETTINGS failed for controller %p\n", dev); 533 } 534 return false; 535 } 536 537 // Apply custom settings - clear trackpad modes (cancel mouse emulation), etc 538#define ADD_SETTING(SETTING, VALUE) \ 539 buf[3 + nSettings * 3] = SETTING; \ 540 buf[3 + nSettings * 3 + 1] = ((uint16_t)VALUE) & 0xFF; \ 541 buf[3 + nSettings * 3 + 2] = ((uint16_t)VALUE) >> 8; \ 542 ++nSettings; 543 544 SDL_zero(buf); 545 buf[1] = ID_SET_SETTINGS_VALUES; 546 ADD_SETTING(SETTING_WIRELESS_PACKET_VERSION, 2); 547 ADD_SETTING(SETTING_LEFT_TRACKPAD_MODE, TRACKPAD_NONE); 548#ifdef ENABLE_MOUSE_MODE 549 ADD_SETTING(SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_ABSOLUTE_MOUSE); 550 ADD_SETTING(SETTING_SMOOTH_ABSOLUTE_MOUSE, 1); 551 ADD_SETTING(SETTING_MOMENTUM_MAXIMUM_VELOCITY, 20000); // [0-20000] default 8000 552 ADD_SETTING(SETTING_MOMENTUM_DECAY_AMOUNT, 50); // [0-50] default 5 553#else 554 ADD_SETTING(SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_NONE); 555 ADD_SETTING(SETTING_SMOOTH_ABSOLUTE_MOUSE, 0); 556#endif 557 buf[2] = (unsigned char)(nSettings * 3); 558 559 res = SetFeatureReport(dev, buf, 3 + nSettings * 3); 560 if (res < 0) { 561 if (!bSuppressErrorSpew) { 562 printf("SET_SETTINGS failed for controller %p\n", dev); 563 } 564 return false; 565 } 566 567#ifdef ENABLE_MOUSE_MODE 568 // Wait for ID_CLEAR_DIGITAL_MAPPINGS to be processed on the controller 569 bool bMappingsCleared = false; 570 int iRetry; 571 for (iRetry = 0; iRetry < 2; ++iRetry) { 572 SDL_zero(buf); 573 buf[1] = ID_GET_DIGITAL_MAPPINGS; 574 buf[2] = 1; // one byte - requesting from index 0 575 buf[3] = 0; 576 res = SetFeatureReport(dev, buf, 4); 577 if (res < 0) { 578 printf("GET_DIGITAL_MAPPINGS failed for controller %p\n", dev); 579 return false; 580 } 581 582 res = ReadResponse(dev, buf, ID_GET_DIGITAL_MAPPINGS); 583 if (res < 0 || buf[1] != ID_GET_DIGITAL_MAPPINGS) { 584 printf("Bad GET_DIGITAL_MAPPINGS response for controller %p\n", dev); 585 return false; 586 } 587 588 // If the length of the digital mappings result is not 1 (index byte, no mappings) then clearing hasn't executed 589 if (buf[2] == 1 && buf[3] == 0xFF) { 590 bMappingsCleared = true; 591 break; 592 } 593 usleep(CONTROLLER_CONFIGURATION_DELAY_US); 594 } 595 596 if (!bMappingsCleared && !bSuppressErrorSpew) { 597 printf("Warning: CLEAR_DIGITAL_MAPPINGS never completed for controller %p\n", dev); 598 } 599 600 // Set our new mappings 601 SDL_zero(buf); 602 buf[1] = ID_SET_DIGITAL_MAPPINGS; 603 buf[2] = 6; // 2 settings x 3 bytes 604 buf[3] = IO_DIGITAL_BUTTON_RIGHT_TRIGGER; 605 buf[4] = DEVICE_MOUSE; 606 buf[5] = MOUSE_BTN_LEFT; 607 buf[6] = IO_DIGITAL_BUTTON_LEFT_TRIGGER; 608 buf[7] = DEVICE_MOUSE; 609 buf[8] = MOUSE_BTN_RIGHT; 610 611 res = SetFeatureReport(dev, buf, 9); 612 if (res < 0) { 613 if (!bSuppressErrorSpew) { 614 printf("SET_DIGITAL_MAPPINGS failed for controller %p\n", dev); 615 } 616 return false; 617 } 618#endif // ENABLE_MOUSE_MODE 619 620 return true; 621} 622 623//--------------------------------------------------------------------------- 624// Read from a Steam Controller 625//--------------------------------------------------------------------------- 626static int ReadSteamController(SDL_hid_device *dev, uint8_t *pData, int nDataSize) 627{ 628 SDL_memset(pData, 0, nDataSize); 629 pData[0] = BLE_REPORT_NUMBER; // hid_read will also overwrite this with the same value, 0x03 630 return SDL_hid_read(dev, pData, nDataSize); 631} 632 633//--------------------------------------------------------------------------- 634// Set Steam Controller pairing state 635//--------------------------------------------------------------------------- 636static void SetPairingState(SDL_HIDAPI_Device *dev, bool bEnablePairing) 637{ 638 unsigned char buf[65]; 639 SDL_zero(buf); 640 buf[1] = ID_ENABLE_PAIRING; 641 buf[2] = 2; // 2 payload bytes: bool + timeout 642 buf[3] = bEnablePairing ? 1 : 0; 643 buf[4] = bEnablePairing ? PAIRING_STATE_DURATION_SECONDS : 0; 644 SetFeatureReport(dev, buf, 5); 645} 646 647//--------------------------------------------------------------------------- 648// Commit Steam Controller pairing 649//--------------------------------------------------------------------------- 650static void CommitPairing(SDL_HIDAPI_Device *dev) 651{ 652 unsigned char buf[65]; 653 SDL_zero(buf); 654 buf[1] = ID_DONGLE_COMMIT_DEVICE; 655 SetFeatureReport(dev, buf, 2); 656} 657 658//--------------------------------------------------------------------------- 659// Close a Steam Controller 660//--------------------------------------------------------------------------- 661static void CloseSteamController(SDL_HIDAPI_Device *dev) 662{ 663 // Switch the Steam Controller back to lizard mode so it works with the OS 664 unsigned char buf[65]; 665 int nSettings = 0; 666 667 // Reset digital button mappings 668 SDL_zero(buf); 669 buf[1] = ID_SET_DEFAULT_DIGITAL_MAPPINGS; 670 SetFeatureReport(dev, buf, 2); 671 672 // Reset the default settings 673 SDL_zero(buf); 674 buf[1] = ID_LOAD_DEFAULT_SETTINGS; 675 buf[2] = 0; 676 SetFeatureReport(dev, buf, 3); 677 678 // Reset mouse mode for lizard mode 679 SDL_zero(buf); 680 buf[1] = ID_SET_SETTINGS_VALUES; 681 ADD_SETTING(SETTING_RIGHT_TRACKPAD_MODE, TRACKPAD_ABSOLUTE_MOUSE); 682 buf[2] = (unsigned char)(nSettings * 3); 683 SetFeatureReport(dev, buf, 3 + nSettings * 3); 684} 685 686//--------------------------------------------------------------------------- 687// Scale and clamp values to a range 688//--------------------------------------------------------------------------- 689static float RemapValClamped(float val, float A, float B, float C, float D) 690{ 691 if (A == B) { 692 return (val - B) >= 0.0f ? D : C; 693 } else { 694 float cVal = (val - A) / (B - A); 695 cVal = clamp(cVal, 0.0f, 1.0f); 696 697 return C + (D - C) * cVal; 698 } 699} 700 701//--------------------------------------------------------------------------- 702// Rotate the pad coordinates 703//--------------------------------------------------------------------------- 704static void RotatePad(int *pX, int *pY, float flAngleInRad) 705{ 706 int origX = *pX, origY = *pY; 707 708 *pX = (int)(SDL_cosf(flAngleInRad) * origX - SDL_sinf(flAngleInRad) * origY); 709 *pY = (int)(SDL_sinf(flAngleInRad) * origX + SDL_cosf(flAngleInRad) * origY); 710} 711 712//--------------------------------------------------------------------------- 713// Format the first part of the state packet 714//--------------------------------------------------------------------------- 715static void FormatStatePacketUntilGyro(SteamControllerStateInternal_t *pState, ValveControllerStatePacket_t *pStatePacket) 716{ 717 int nLeftPadX; 718 int nLeftPadY; 719 int nRightPadX; 720 int nRightPadY; 721 int nPadOffset; 722 723 // 15 degrees in rad 724 const float flRotationAngle = 0.261799f; 725 726 SDL_memset(pState, 0, offsetof(SteamControllerStateInternal_t, sBatteryLevel)); 727 728 // pState->eControllerType = m_eControllerType; 729 pState->eControllerType = 2; // k_eControllerType_SteamController; 730 pState->unPacketNum = pStatePacket->unPacketNum; 731 732 // We have a chunk of trigger data in the packet format here, so zero it out afterwards 733 SDL_memcpy(&pState->ulButtons, &pStatePacket->ButtonTriggerData.ulButtons, 8); 734 pState->ulButtons &= ~0xFFFF000000LL; 735 736 // The firmware uses this bit to tell us what kind of data is packed into the left two axes 737 if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) { 738 // Finger-down bit not set; "left pad" is actually trackpad 739 pState->sLeftPadX = pState->sPrevLeftPad[0] = pStatePacket->sLeftPadX; 740 pState->sLeftPadY = pState->sPrevLeftPad[1] = pStatePacket->sLeftPadY; 741 742 if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) { 743 // The controller is interleaving both stick and pad data, both are active 744 pState->sLeftStickX = pState->sPrevLeftStick[0]; 745 pState->sLeftStickY = pState->sPrevLeftStick[1]; 746 } else { 747 // The stick is not active 748 pState->sPrevLeftStick[0] = 0; 749 pState->sPrevLeftStick[1] = 0; 750 } 751 } else { 752 // Finger-down bit not set; "left pad" is actually joystick 753 754 // XXX there's a firmware bug where sometimes padX is 0 and padY is a large number (actually the battery voltage) 755 // If that happens skip this packet and report last frames stick 756 /* 757 if ( m_eControllerType == k_eControllerType_SteamControllerV2 && pStatePacket->sLeftPadY > 900 ) { 758 pState->sLeftStickX = pState->sPrevLeftStick[0]; 759 pState->sLeftStickY = pState->sPrevLeftStick[1]; 760 } else 761 */ 762 { 763 pState->sPrevLeftStick[0] = pState->sLeftStickX = pStatePacket->sLeftPadX; 764 pState->sPrevLeftStick[1] = pState->sLeftStickY = pStatePacket->sLeftPadY; 765 } 766 /* 767 if (m_eControllerType == k_eControllerType_SteamControllerV2) { 768 UpdateV2JoystickCap(&state); 769 } 770 */ 771 772 if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) { 773 // The controller is interleaving both stick and pad data, both are active 774 pState->sLeftPadX = pState->sPrevLeftPad[0]; 775 pState->sLeftPadY = pState->sPrevLeftPad[1]; 776 } else { 777 // The trackpad is not active 778 pState->sPrevLeftPad[0] = 0; 779 pState->sPrevLeftPad[1] = 0; 780 781 // Old controllers send trackpad click for joystick button when trackpad is not active 782 if (pState->ulButtons & STEAM_BUTTON_LEFTPAD_CLICKED_MASK) { 783 pState->ulButtons &= ~STEAM_BUTTON_LEFTPAD_CLICKED_MASK; 784 pState->ulButtons |= STEAM_JOYSTICK_BUTTON_MASK; 785 } 786 } 787 } 788 789 // Fingerdown bit indicates if the packed left axis data was joystick or pad, 790 // but if we are interleaving both, the left finger is definitely on the pad. 791 if (pStatePacket->ButtonTriggerData.ulButtons & STEAM_LEFTPAD_AND_JOYSTICK_MASK) { 792 pState->ulButtons |= STEAM_LEFTPAD_FINGERDOWN_MASK; 793 } 794 795 pState->sRightPadX = pStatePacket->sRightPadX; 796 pState->sRightPadY = pStatePacket->sRightPadY; 797 798 nLeftPadX = pState->sLeftPadX; 799 nLeftPadY = pState->sLeftPadY; 800 nRightPadX = pState->sRightPadX; 801 nRightPadY = pState->sRightPadY; 802 803 RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle); 804 RotatePad(&nRightPadX, &nRightPadY, flRotationAngle); 805 806 if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) { 807 nPadOffset = 1000; 808 } else { 809 nPadOffset = 0; 810 } 811 812 pState->sLeftPadX = (short)clamp(nLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 813 pState->sLeftPadY = (short)clamp(nLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 814 815 nPadOffset = 0; 816 if (pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK) { 817 nPadOffset = 1000; 818 } else { 819 nPadOffset = 0; 820 } 821 822 pState->sRightPadX = (short)clamp(nRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 823 pState->sRightPadY = (short)clamp(nRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 824 825 pState->sTriggerL = (unsigned short)RemapValClamped((float)((pStatePacket->ButtonTriggerData.Triggers.nLeft << 7) | pStatePacket->ButtonTriggerData.Triggers.nLeft), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16); 826 pState->sTriggerR = (unsigned short)RemapValClamped((float)((pStatePacket->ButtonTriggerData.Triggers.nRight << 7) | pStatePacket->ButtonTriggerData.Triggers.nRight), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16); 827} 828 829//--------------------------------------------------------------------------- 830// Update Steam Controller state from a BLE data packet, returns true if it parsed data 831//--------------------------------------------------------------------------- 832static bool UpdateBLESteamControllerState(const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState) 833{ 834 int nLeftPadX; 835 int nLeftPadY; 836 int nRightPadX; 837 int nRightPadY; 838 int nPadOffset; 839 uint32_t ucOptionDataMask; 840 841 // 15 degrees in rad 842 const float flRotationAngle = 0.261799f; 843 844 pState->unPacketNum++; 845 ucOptionDataMask = (*pData++ & 0xF0); 846 ucOptionDataMask |= (uint32_t)(*pData++) << 8; 847 if (ucOptionDataMask & k_EBLEButtonChunk1) { 848 SDL_memcpy(&pState->ulButtons, pData, 3); 849 pData += 3; 850 } 851 if (ucOptionDataMask & k_EBLEButtonChunk2) { 852 // The middle 2 bytes of the button bits over the wire are triggers when over the wire and non-SC buttons in the internal controller state packet 853 pState->sTriggerL = (unsigned short)RemapValClamped((float)((pData[0] << 7) | pData[0]), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16); 854 pState->sTriggerR = (unsigned short)RemapValClamped((float)((pData[1] << 7) | pData[1]), 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16); 855 pData += 2; 856 } 857 if (ucOptionDataMask & k_EBLEButtonChunk3) { 858 uint8_t *pButtonByte = (uint8_t *)&pState->ulButtons; 859 pButtonByte[5] = *pData++; 860 pButtonByte[6] = *pData++; 861 pButtonByte[7] = *pData++; 862 } 863 if (ucOptionDataMask & k_EBLELeftJoystickChunk) { 864 // This doesn't handle any of the special headcrab stuff for raw joystick which is OK for now since that FW doesn't support 865 // this protocol yet either 866 int nLength = sizeof(pState->sLeftStickX) + sizeof(pState->sLeftStickY); 867 SDL_memcpy(&pState->sLeftStickX, pData, nLength); 868 pData += nLength; 869 } 870 if (ucOptionDataMask & k_EBLELeftTrackpadChunk) { 871 int nLength = sizeof(pState->sLeftPadX) + sizeof(pState->sLeftPadY); 872 SDL_memcpy(&pState->sLeftPadX, pData, nLength); 873 if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) { 874 nPadOffset = 1000; 875 } else { 876 nPadOffset = 0; 877 } 878 879 nLeftPadX = pState->sLeftPadX; 880 nLeftPadY = pState->sLeftPadY; 881 RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle); 882 pState->sLeftPadX = (short)clamp(nLeftPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 883 pState->sLeftPadY = (short)clamp(nLeftPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 884 pData += nLength; 885 } 886 if (ucOptionDataMask & k_EBLERightTrackpadChunk) { 887 int nLength = sizeof(pState->sRightPadX) + sizeof(pState->sRightPadY); 888 889 SDL_memcpy(&pState->sRightPadX, pData, nLength); 890 891 if (pState->ulButtons & STEAM_RIGHTPAD_FINGERDOWN_MASK) { 892 nPadOffset = 1000; 893 } else { 894 nPadOffset = 0; 895 } 896 897 nRightPadX = pState->sRightPadX; 898 nRightPadY = pState->sRightPadY; 899 RotatePad(&nRightPadX, &nRightPadY, flRotationAngle); 900 pState->sRightPadX = (short)clamp(nRightPadX + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 901 pState->sRightPadY = (short)clamp(nRightPadY + nPadOffset, SDL_MIN_SINT16, SDL_MAX_SINT16); 902 pData += nLength; 903 } 904 if (ucOptionDataMask & k_EBLEIMUAccelChunk) { 905 int nLength = sizeof(pState->sAccelX) + sizeof(pState->sAccelY) + sizeof(pState->sAccelZ); 906 SDL_memcpy(&pState->sAccelX, pData, nLength); 907 pData += nLength; 908 } 909 if (ucOptionDataMask & k_EBLEIMUGyroChunk) { 910 int nLength = sizeof(pState->sAccelX) + sizeof(pState->sAccelY) + sizeof(pState->sAccelZ); 911 SDL_memcpy(&pState->sGyroX, pData, nLength); 912 pData += nLength; 913 } 914 if (ucOptionDataMask & k_EBLEIMUQuatChunk) { 915 int nLength = sizeof(pState->sGyroQuatW) + sizeof(pState->sGyroQuatX) + sizeof(pState->sGyroQuatY) + sizeof(pState->sGyroQuatZ); 916 SDL_memcpy(&pState->sGyroQuatW, pData, nLength); 917 pData += nLength; 918 } 919 return true; 920} 921 922//--------------------------------------------------------------------------- 923// Update Steam Controller state from a data packet, returns true if it parsed data 924//--------------------------------------------------------------------------- 925static bool UpdateSteamControllerState(const uint8_t *pData, int nDataSize, SteamControllerStateInternal_t *pState) 926{ 927 ValveInReport_t *pInReport = (ValveInReport_t *)pData; 928 929 if (pInReport->header.unReportVersion != k_ValveInReportMsgVersion) { 930 if ((pData[0] & 0x0F) == k_EBLEReportState) { 931 return UpdateBLESteamControllerState(pData, nDataSize, pState); 932 } 933 return false; 934 } 935 936 if ((pInReport->header.ucType != ID_CONTROLLER_STATE) && 937 (pInReport->header.ucType != ID_CONTROLLER_BLE_STATE)) { 938 return false; 939 } 940 941 if (pInReport->header.ucType == ID_CONTROLLER_STATE) { 942 ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState; 943 944 // No new data to process; indicate that we received a state packet, but otherwise do nothing. 945 if (pState->unPacketNum == pStatePacket->unPacketNum) { 946 return true; 947 } 948 949 FormatStatePacketUntilGyro(pState, pStatePacket); 950 951 pState->sAccelX = pStatePacket->sAccelX; 952 pState->sAccelY = pStatePacket->sAccelY; 953 pState->sAccelZ = pStatePacket->sAccelZ; 954 955 pState->sGyroQuatW = pStatePacket->sGyroQuatW; 956 pState->sGyroQuatX = pStatePacket->sGyroQuatX; 957 pState->sGyroQuatY = pStatePacket->sGyroQuatY; 958 pState->sGyroQuatZ = pStatePacket->sGyroQuatZ; 959 960 pState->sGyroX = pStatePacket->sGyroX; 961 pState->sGyroY = pStatePacket->sGyroY; 962 pState->sGyroZ = pStatePacket->sGyroZ; 963 964 } else if (pInReport->header.ucType == ID_CONTROLLER_BLE_STATE) { 965 ValveControllerBLEStatePacket_t *pBLEStatePacket = &pInReport->payload.controllerBLEState; 966 ValveControllerStatePacket_t *pStatePacket = &pInReport->payload.controllerState; 967 968 // No new data to process; indicate that we received a state packet, but otherwise do nothing. 969 if (pState->unPacketNum == pStatePacket->unPacketNum) { 970 return true; 971 } 972 973 FormatStatePacketUntilGyro(pState, pStatePacket); 974 975 switch (pBLEStatePacket->ucGyroDataType) { 976 case 1: 977 pState->sGyroQuatW = ((float)pBLEStatePacket->sGyro[0]); 978 pState->sGyroQuatX = ((float)pBLEStatePacket->sGyro[1]); 979 pState->sGyroQuatY = ((float)pBLEStatePacket->sGyro[2]); 980 pState->sGyroQuatZ = ((float)pBLEStatePacket->sGyro[3]); 981 break; 982 983 case 2: 984 pState->sAccelX = pBLEStatePacket->sGyro[0]; 985 pState->sAccelY = pBLEStatePacket->sGyro[1]; 986 pState->sAccelZ = pBLEStatePacket->sGyro[2]; 987 break; 988 989 case 3: 990 pState->sGyroX = pBLEStatePacket->sGyro[0]; 991 pState->sGyroY = pBLEStatePacket->sGyro[1]; 992 pState->sGyroZ = pBLEStatePacket->sGyro[2]; 993 break; 994 995 default: 996 break; 997 } 998 } 999 1000 return true; 1001} 1002 1003/*****************************************************************************************************/ 1004 1005typedef struct 1006{ 1007 SDL_HIDAPI_Device *device; 1008 bool connected; 1009 bool report_sensors; 1010 uint32_t update_rate_in_us; 1011 Uint64 sensor_timestamp; 1012 Uint64 pairing_time; 1013 1014 SteamControllerPacketAssembler m_assembler; 1015 SteamControllerStateInternal_t m_state; 1016 SteamControllerStateInternal_t m_last_state; 1017} SDL_DriverSteam_Context; 1018 1019static bool IsDongle(Uint16 product_id) 1020{ 1021 return (product_id == USB_PRODUCT_VALVE_STEAM_CONTROLLER_DONGLE); 1022} 1023 1024static void HIDAPI_DriverSteam_RegisterHints(SDL_HintCallback callback, void *userdata) 1025{ 1026 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata); 1027} 1028 1029static void HIDAPI_DriverSteam_UnregisterHints(SDL_HintCallback callback, void *userdata) 1030{ 1031 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM, callback, userdata); 1032} 1033 1034static bool HIDAPI_DriverSteam_IsEnabled(void) 1035{ 1036 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_STEAM, SDL_HINT_JOYSTICK_HIDAPI_STEAM_DEFAULT); 1037} 1038 1039static bool HIDAPI_DriverSteam_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) 1040{ 1041 if (!SDL_IsJoystickSteamController(vendor_id, product_id)) { 1042 return false; 1043 } 1044 1045 if (!device) { 1046 // Might be supported by this driver, enumerate and find out 1047 return true; 1048 } 1049 1050 if (device->is_bluetooth) { 1051 return true; 1052 } 1053 1054 if (IsDongle(product_id)) { 1055 if (interface_number >= 1 && interface_number <= 4) { 1056 // This is one of the wireless controller interfaces 1057 return true; 1058 } 1059 } else { 1060 if (interface_number == 2) { 1061 // This is the controller interface (not mouse or keyboard) 1062 return true; 1063 } 1064 } 1065 return false; 1066} 1067 1068static void HIDAPI_DriverSteam_SetPairingState(SDL_DriverSteam_Context *ctx, bool enabled) 1069{ 1070 // Only have one dongle in pairing mode at a time 1071 static SDL_DriverSteam_Context *s_PairingContext = NULL; 1072 1073 if (enabled && s_PairingContext != NULL) { 1074 return; 1075 } 1076 1077 if (!enabled && s_PairingContext != ctx) { 1078 return; 1079 } 1080 1081 if (ctx->connected) { 1082 return; 1083 } 1084 1085 SetPairingState(ctx->device, enabled); 1086 1087 if (enabled) { 1088 ctx->pairing_time = SDL_GetTicks(); 1089 s_PairingContext = ctx; 1090 } else { 1091 ctx->pairing_time = 0; 1092 s_PairingContext = NULL; 1093 } 1094} 1095 1096static void HIDAPI_DriverSteam_RenewPairingState(SDL_DriverSteam_Context *ctx) 1097{ 1098 Uint64 now = SDL_GetTicks(); 1099 1100 if (now >= ctx->pairing_time + PAIRING_STATE_DURATION_SECONDS * 1000) { 1101 SetPairingState(ctx->device, true); 1102 ctx->pairing_time = now; 1103 } 1104} 1105 1106static void HIDAPI_DriverSteam_CommitPairing(SDL_DriverSteam_Context *ctx) 1107{ 1108 CommitPairing(ctx->device); 1109} 1110 1111static void SDLCALL SDL_PairingEnabledHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 1112{ 1113 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata; 1114 bool enabled = SDL_GetStringBoolean(hint, false); 1115 1116 HIDAPI_DriverSteam_SetPairingState(ctx, enabled); 1117} 1118 1119static bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) 1120{ 1121 SDL_DriverSteam_Context *ctx; 1122 1123 ctx = (SDL_DriverSteam_Context *)SDL_calloc(1, sizeof(*ctx)); 1124 if (!ctx) { 1125 return false; 1126 } 1127 ctx->device = device; 1128 device->context = ctx; 1129 1130#ifdef SDL_PLATFORM_WIN32 1131 if (device->serial) { 1132 // We get a garbage serial number on Windows 1133 SDL_free(device->serial); 1134 device->serial = NULL; 1135 } 1136#endif // SDL_PLATFORM_WIN32 1137 1138 HIDAPI_SetDeviceName(device, "Steam Controller"); 1139 1140 // If this is a wireless dongle, request a wireless state update 1141 if (IsDongle(device->product_id)) { 1142 unsigned char buf[65]; 1143 int res; 1144 1145 SDL_zero(buf); 1146 buf[0] = 0; 1147 buf[1] = ID_DONGLE_GET_WIRELESS_STATE; 1148 res = SetFeatureReport(device, buf, 2); 1149 if (res < 0) { 1150 return SDL_SetError("Failed to send ID_DONGLE_GET_WIRELESS_STATE request"); 1151 } 1152 1153 for (int attempt = 0; attempt < 5; ++attempt) { 1154 uint8_t data[128]; 1155 1156 res = ReadSteamController(device->dev, data, sizeof(data)); 1157 if (res == 0) { 1158 SDL_Delay(1); 1159 continue; 1160 } 1161 if (res < 0) { 1162 break; 1163 } 1164 1165#ifdef DEBUG_STEAM_PROTOCOL 1166 HIDAPI_DumpPacket("Initial dongle packet: size = %d", data, res); 1167#endif 1168 1169 if (D0G_IS_WIRELESS_CONNECT(data, res)) { 1170 ctx->connected = true; 1171 break; 1172 } else if (D0G_IS_WIRELESS_DISCONNECT(data, res)) { 1173 ctx->connected = false; 1174 break; 1175 } 1176 } 1177 1178 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED, 1179 SDL_PairingEnabledHintChanged, ctx); 1180 } else { 1181 // Wired and BLE controllers are always connected if HIDAPI can see them 1182 ctx->connected = true; 1183 } 1184 1185 if (ctx->connected) { 1186 return HIDAPI_JoystickConnected(device, NULL); 1187 } else { 1188 // We will enumerate any attached controllers in UpdateDevice() 1189 return true; 1190 } 1191} 1192 1193static int HIDAPI_DriverSteam_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 1194{ 1195 return -1; 1196} 1197 1198static void HIDAPI_DriverSteam_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 1199{ 1200} 1201 1202static bool SetHomeLED(SDL_HIDAPI_Device *device, Uint8 value) 1203{ 1204 unsigned char buf[65]; 1205 int nSettings = 0; 1206 1207 SDL_zero(buf); 1208 buf[1] = ID_SET_SETTINGS_VALUES; 1209 ADD_SETTING(SETTING_LED_USER_BRIGHTNESS, value); 1210 buf[2] = (unsigned char)(nSettings * 3); 1211 if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) { 1212 return SDL_SetError("Couldn't write feature report"); 1213 } 1214 return true; 1215} 1216 1217static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 1218{ 1219 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)userdata; 1220 1221 if (hint && *hint) { 1222 int value; 1223 1224 if (SDL_strchr(hint, '.') != NULL) { 1225 value = (int)(100.0f * SDL_atof(hint)); 1226 if (value > 255) { 1227 value = 255; 1228 } 1229 } else if (SDL_GetStringBoolean(hint, true)) { 1230 value = 100; 1231 } else { 1232 value = 0; 1233 } 1234 SetHomeLED(ctx->device, (Uint8)value); 1235 } 1236} 1237 1238static bool HIDAPI_DriverSteam_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1239{ 1240 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1241 float update_rate_in_hz = 0.0f; 1242 1243 SDL_AssertJoysticksLocked(); 1244 1245 ctx->report_sensors = false; 1246 SDL_zero(ctx->m_assembler); 1247 SDL_zero(ctx->m_state); 1248 SDL_zero(ctx->m_last_state); 1249 1250 if (!ResetSteamController(device, false, &ctx->update_rate_in_us)) { 1251 SDL_SetError("Couldn't reset controller"); 1252 return false; 1253 } 1254 if (ctx->update_rate_in_us > 0) { 1255 update_rate_in_hz = 1000000.0f / ctx->update_rate_in_us; 1256 } 1257 1258 InitializeSteamControllerPacketAssembler(&ctx->m_assembler, device->is_bluetooth); 1259 1260 // Initialize the joystick capabilities 1261 joystick->nbuttons = SDL_GAMEPAD_NUM_STEAM_BUTTONS; 1262 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 1263 joystick->nhats = 1; 1264 1265 if (IsDongle(device->product_id)) { 1266 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 1267 } 1268 1269 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, update_rate_in_hz); 1270 SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, update_rate_in_hz); 1271 1272 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED, 1273 SDL_HomeLEDHintChanged, ctx); 1274 1275 return true; 1276} 1277 1278static bool HIDAPI_DriverSteam_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1279{ 1280 // You should use the full Steam Input API for rumble support 1281 return SDL_Unsupported(); 1282} 1283 1284static bool HIDAPI_DriverSteam_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 1285{ 1286 return SDL_Unsupported(); 1287} 1288 1289static Uint32 HIDAPI_DriverSteam_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1290{ 1291 // You should use the full Steam Input API for extended capabilities 1292 return 0; 1293} 1294 1295static bool HIDAPI_DriverSteam_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 1296{ 1297 // You should use the full Steam Input API for LED support 1298 return SDL_Unsupported(); 1299} 1300 1301static bool HIDAPI_DriverSteam_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 1302{ 1303 if (size == 65) { 1304 if (SetFeatureReport(device, data, size) < 0) { 1305 return SDL_SetError("Couldn't write feature report"); 1306 } 1307 return true; 1308 } 1309 return SDL_Unsupported(); 1310} 1311 1312static bool HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 1313{ 1314 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1315 unsigned char buf[65]; 1316 int nSettings = 0; 1317 1318 SDL_zero(buf); 1319 buf[1] = ID_SET_SETTINGS_VALUES; 1320 if (enabled) { 1321 ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_SEND_RAW_ACCEL | SETTING_GYRO_MODE_SEND_RAW_GYRO); 1322 } else { 1323 ADD_SETTING(SETTING_IMU_MODE, SETTING_GYRO_MODE_OFF); 1324 } 1325 buf[2] = (unsigned char)(nSettings * 3); 1326 if (SetFeatureReport(device, buf, 3 + nSettings * 3) < 0) { 1327 return SDL_SetError("Couldn't write feature report"); 1328 } 1329 1330 ctx->report_sensors = enabled; 1331 1332 return true; 1333} 1334 1335static bool ControllerConnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick) 1336{ 1337 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1338 1339 if (!HIDAPI_JoystickConnected(device, NULL)) { 1340 return false; 1341 } 1342 1343 // We'll automatically accept this controller if we're in pairing mode 1344 HIDAPI_DriverSteam_CommitPairing(ctx); 1345 1346 *joystick = SDL_GetJoystickFromID(device->joysticks[0]); 1347 ctx->connected = true; 1348 return true; 1349} 1350 1351static void ControllerDisconnected(SDL_HIDAPI_Device *device, SDL_Joystick **joystick) 1352{ 1353 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1354 1355 if (device->joysticks) { 1356 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1357 } 1358 ctx->connected = false; 1359 *joystick = NULL; 1360} 1361 1362static bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) 1363{ 1364 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1365 SDL_Joystick *joystick = NULL; 1366 1367 if (device->num_joysticks > 0) { 1368 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 1369 } 1370 1371 if (ctx->pairing_time) { 1372 HIDAPI_DriverSteam_RenewPairingState(ctx); 1373 } 1374 1375 for (;;) { 1376 uint8_t data[128]; 1377 int r, nPacketLength; 1378 const Uint8 *pPacket; 1379 1380 r = ReadSteamController(device->dev, data, sizeof(data)); 1381 if (r == 0) { 1382 break; 1383 } 1384 if (r < 0) { 1385 // Failed to read from controller 1386 ControllerDisconnected(device, &joystick); 1387 return false; 1388 } 1389 1390#ifdef DEBUG_STEAM_PROTOCOL 1391 HIDAPI_DumpPacket("Steam Controller packet: size = %d", data, r); 1392#endif 1393 1394 nPacketLength = WriteSegmentToSteamControllerPacketAssembler(&ctx->m_assembler, data, r); 1395 pPacket = ctx->m_assembler.uBuffer; 1396 1397 if (nPacketLength > 0 && UpdateSteamControllerState(pPacket, nPacketLength, &ctx->m_state)) { 1398 Uint64 timestamp = SDL_GetTicksNS(); 1399 1400 if (!ctx->connected) { 1401 // Maybe we missed a wireless status packet? 1402 ControllerConnected(device, &joystick); 1403 } 1404 1405 if (!joystick) { 1406 continue; 1407 } 1408 1409 if (ctx->m_state.ulButtons != ctx->m_last_state.ulButtons) { 1410 Uint8 hat = 0; 1411 1412 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, 1413 ((ctx->m_state.ulButtons & STEAM_BUTTON_SOUTH_MASK) != 0)); 1414 1415 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, 1416 ((ctx->m_state.ulButtons & STEAM_BUTTON_EAST_MASK) != 0)); 1417 1418 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, 1419 ((ctx->m_state.ulButtons & STEAM_BUTTON_WEST_MASK) != 0)); 1420 1421 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, 1422 ((ctx->m_state.ulButtons & STEAM_BUTTON_NORTH_MASK) != 0)); 1423 1424 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, 1425 ((ctx->m_state.ulButtons & STEAM_LEFT_BUMPER_MASK) != 0)); 1426 1427 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, 1428 ((ctx->m_state.ulButtons & STEAM_RIGHT_BUMPER_MASK) != 0)); 1429 1430 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, 1431 ((ctx->m_state.ulButtons & STEAM_BUTTON_MENU_MASK) != 0)); 1432 1433 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, 1434 ((ctx->m_state.ulButtons & STEAM_BUTTON_ESCAPE_MASK) != 0)); 1435 1436 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, 1437 ((ctx->m_state.ulButtons & STEAM_BUTTON_STEAM_MASK) != 0)); 1438 1439 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, 1440 ((ctx->m_state.ulButtons & STEAM_JOYSTICK_BUTTON_MASK) != 0)); 1441 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_LEFT_PADDLE, 1442 ((ctx->m_state.ulButtons & STEAM_BUTTON_BACK_LEFT_MASK) != 0)); 1443 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_STEAM_RIGHT_PADDLE, 1444 ((ctx->m_state.ulButtons & STEAM_BUTTON_BACK_RIGHT_MASK) != 0)); 1445 1446 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, 1447 ((ctx->m_state.ulButtons & STEAM_BUTTON_RIGHTPAD_CLICKED_MASK) != 0)); 1448 1449 if (ctx->m_state.ulButtons & STEAM_DPAD_UP_MASK) { 1450 hat |= SDL_HAT_UP; 1451 } 1452 if (ctx->m_state.ulButtons & STEAM_DPAD_DOWN_MASK) { 1453 hat |= SDL_HAT_DOWN; 1454 } 1455 if (ctx->m_state.ulButtons & STEAM_DPAD_LEFT_MASK) { 1456 hat |= SDL_HAT_LEFT; 1457 } 1458 if (ctx->m_state.ulButtons & STEAM_DPAD_RIGHT_MASK) { 1459 hat |= SDL_HAT_RIGHT; 1460 } 1461 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 1462 } 1463 1464 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (int)ctx->m_state.sTriggerL * 2 - 32768); 1465 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, (int)ctx->m_state.sTriggerR * 2 - 32768); 1466 1467 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ctx->m_state.sLeftStickX); 1468 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~ctx->m_state.sLeftStickY); 1469 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, ctx->m_state.sRightPadX); 1470 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~ctx->m_state.sRightPadY); 1471 1472 if (ctx->report_sensors) { 1473 float values[3]; 1474 1475 ctx->sensor_timestamp += SDL_US_TO_NS(ctx->update_rate_in_us); 1476 1477 values[0] = (ctx->m_state.sGyroX / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); 1478 values[1] = (ctx->m_state.sGyroZ / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); 1479 values[2] = (ctx->m_state.sGyroY / 32768.0f) * (2000.0f * (SDL_PI_F / 180.0f)); 1480 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, ctx->sensor_timestamp, values, 3); 1481 1482 values[0] = (ctx->m_state.sAccelX / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; 1483 values[1] = (ctx->m_state.sAccelZ / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; 1484 values[2] = (-ctx->m_state.sAccelY / 32768.0f) * 2.0f * SDL_STANDARD_GRAVITY; 1485 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, ctx->sensor_timestamp, values, 3); 1486 } 1487 1488 ctx->m_last_state = ctx->m_state; 1489 } else if (!ctx->connected && D0G_IS_WIRELESS_CONNECT(pPacket, nPacketLength)) { 1490 ControllerConnected(device, &joystick); 1491 } else if (ctx->connected && D0G_IS_WIRELESS_DISCONNECT(pPacket, nPacketLength)) { 1492 ControllerDisconnected(device, &joystick); 1493 } 1494 } 1495 return true; 1496} 1497 1498static void HIDAPI_DriverSteam_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1499{ 1500 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1501 1502 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED, 1503 SDL_HomeLEDHintChanged, ctx); 1504 1505 CloseSteamController(device); 1506} 1507 1508static void HIDAPI_DriverSteam_FreeDevice(SDL_HIDAPI_Device *device) 1509{ 1510 SDL_DriverSteam_Context *ctx = (SDL_DriverSteam_Context *)device->context; 1511 1512 if (IsDongle(device->product_id)) { 1513 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_STEAM_PAIRING_ENABLED, 1514 SDL_PairingEnabledHintChanged, ctx); 1515 1516 HIDAPI_DriverSteam_SetPairingState(ctx, false); 1517 } 1518} 1519 1520SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = { 1521 SDL_HINT_JOYSTICK_HIDAPI_STEAM, 1522 true, 1523 HIDAPI_DriverSteam_RegisterHints, 1524 HIDAPI_DriverSteam_UnregisterHints, 1525 HIDAPI_DriverSteam_IsEnabled, 1526 HIDAPI_DriverSteam_IsSupportedDevice, 1527 HIDAPI_DriverSteam_InitDevice, 1528 HIDAPI_DriverSteam_GetDevicePlayerIndex, 1529 HIDAPI_DriverSteam_SetDevicePlayerIndex, 1530 HIDAPI_DriverSteam_UpdateDevice, 1531 HIDAPI_DriverSteam_OpenJoystick, 1532 HIDAPI_DriverSteam_RumbleJoystick, 1533 HIDAPI_DriverSteam_RumbleJoystickTriggers, 1534 HIDAPI_DriverSteam_GetJoystickCapabilities, 1535 HIDAPI_DriverSteam_SetJoystickLED, 1536 HIDAPI_DriverSteam_SendJoystickEffect, 1537 HIDAPI_DriverSteam_SetSensorsEnabled, 1538 HIDAPI_DriverSteam_CloseJoystick, 1539 HIDAPI_DriverSteam_FreeDevice, 1540}; 1541 1542#endif // SDL_JOYSTICK_HIDAPI_STEAM 1543 1544#endif // SDL_JOYSTICK_HIDAPI 1545[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.