Atlas - SDL_hidapi_switch.c
Home / ext / SDL2 / src / joystick / hidapi Lines: 2 | Size: 34426 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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/* This driver supports the Nintendo Switch Pro controller. 22 Code and logic contributed by Valve Corporation under the SDL zlib license. 23*/ 24#include "../../SDL_internal.h" 25 26#ifdef SDL_JOYSTICK_HIDAPI 27 28#include "SDL_hints.h" 29#include "SDL_log.h" 30#include "SDL_events.h" 31#include "SDL_timer.h" 32#include "SDL_joystick.h" 33#include "SDL_gamecontroller.h" 34#include "../SDL_sysjoystick.h" 35#include "SDL_hidapijoystick_c.h" 36 37 38#ifdef SDL_JOYSTICK_HIDAPI_SWITCH 39 40typedef enum { 41 k_eSwitchInputReportIDs_SubcommandReply = 0x21, 42 k_eSwitchInputReportIDs_FullControllerState = 0x30, 43 k_eSwitchInputReportIDs_SimpleControllerState = 0x3F, 44 k_eSwitchInputReportIDs_CommandAck = 0x81, 45} ESwitchInputReportIDs; 46 47typedef enum { 48 k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01, 49 k_eSwitchOutputReportIDs_Rumble = 0x10, 50 k_eSwitchOutputReportIDs_Proprietary = 0x80, 51} ESwitchOutputReportIDs; 52 53typedef enum { 54 k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01, 55 k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02, 56 k_eSwitchSubcommandIDs_SetInputReportMode = 0x03, 57 k_eSwitchSubcommandIDs_SetHCIState = 0x06, 58 k_eSwitchSubcommandIDs_SPIFlashRead = 0x10, 59 k_eSwitchSubcommandIDs_SetPlayerLights = 0x30, 60 k_eSwitchSubcommandIDs_SetHomeLight = 0x38, 61 k_eSwitchSubcommandIDs_EnableIMU = 0x40, 62 k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41, 63 k_eSwitchSubcommandIDs_EnableVibration = 0x48, 64} ESwitchSubcommandIDs; 65 66typedef enum { 67 k_eSwitchProprietaryCommandIDs_Handshake = 0x02, 68 k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03, 69 k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04, 70 k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05, 71 k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06, 72} ESwitchProprietaryCommandIDs; 73 74typedef enum { 75 k_eSwitchDeviceInfoControllerType_JoyConLeft = 0x1, 76 k_eSwitchDeviceInfoControllerType_JoyConRight = 0x2, 77 k_eSwitchDeviceInfoControllerType_ProController = 0x3, 78} ESwitchDeviceInfoControllerType; 79 80#define k_unSwitchOutputPacketDataLength 49 81#define k_unSwitchMaxOutputPacketLength 64 82#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength 83#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength 84 85#define k_unSPIStickCalibrationStartOffset 0x603D 86#define k_unSPIStickCalibrationEndOffset 0x604E 87#define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1) 88 89#pragma pack(1) 90typedef struct 91{ 92 Uint8 rgucButtons[2]; 93 Uint8 ucStickHat; 94 Sint16 sJoystickLeft[2]; 95 Sint16 sJoystickRight[2]; 96} SwitchSimpleStatePacket_t; 97 98typedef struct 99{ 100 Uint8 ucCounter; 101 Uint8 ucBatteryAndConnection; 102 Uint8 rgucButtons[3]; 103 Uint8 rgucJoystickLeft[3]; 104 Uint8 rgucJoystickRight[3]; 105 Uint8 ucVibrationCode; 106} SwitchControllerStatePacket_t; 107 108typedef struct 109{ 110 SwitchControllerStatePacket_t controllerState; 111 112 struct { 113 Sint16 sAccelX; 114 Sint16 sAccelY; 115 Sint16 sAccelZ; 116 117 Sint16 sGyroX; 118 Sint16 sGyroY; 119 Sint16 sGyroZ; 120 } imuState[3]; 121} SwitchStatePacket_t; 122 123typedef struct 124{ 125 Uint32 unAddress; 126 Uint8 ucLength; 127} SwitchSPIOpData_t; 128 129typedef struct 130{ 131 SwitchControllerStatePacket_t m_controllerState; 132 133 Uint8 ucSubcommandAck; 134 Uint8 ucSubcommandID; 135 136 #define k_unSubcommandDataBytes 35 137 union { 138 Uint8 rgucSubcommandData[ k_unSubcommandDataBytes ]; 139 140 struct { 141 SwitchSPIOpData_t opData; 142 Uint8 rgucReadData[ k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t) ]; 143 } spiReadData; 144 145 struct { 146 Uint8 rgucFirmwareVersion[2]; 147 Uint8 ucDeviceType; 148 Uint8 ucFiller1; 149 Uint8 rgucMACAddress[6]; 150 Uint8 ucFiller2; 151 Uint8 ucColorLocation; 152 } deviceInfo; 153 }; 154} SwitchSubcommandInputPacket_t; 155 156typedef struct 157{ 158 Uint8 rgucData[4]; 159} SwitchRumbleData_t; 160 161typedef struct 162{ 163 Uint8 ucPacketType; 164 Uint8 ucPacketNumber; 165 SwitchRumbleData_t rumbleData[2]; 166} SwitchCommonOutputPacket_t; 167 168typedef struct 169{ 170 SwitchCommonOutputPacket_t commonData; 171 172 Uint8 ucSubcommandID; 173 Uint8 rgucSubcommandData[ k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1 ]; 174} SwitchSubcommandOutputPacket_t; 175 176typedef struct 177{ 178 Uint8 ucPacketType; 179 Uint8 ucProprietaryID; 180 181 Uint8 rgucProprietaryData[ k_unSwitchOutputPacketDataLength - 1 - 1 ]; 182} SwitchProprietaryOutputPacket_t; 183#pragma pack() 184 185typedef struct { 186 hid_device *dev; 187 SDL_bool m_bIsUsingBluetooth; 188 Uint8 m_nCommandNumber; 189 SwitchCommonOutputPacket_t m_RumblePacket; 190 Uint32 m_nRumbleExpiration; 191 Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength]; 192 SwitchSimpleStatePacket_t m_lastSimpleState; 193 SwitchStatePacket_t m_lastFullState; 194 195 struct StickCalibrationData { 196 struct { 197 Sint16 sCenter; 198 Sint16 sMin; 199 Sint16 sMax; 200 } axis[2]; 201 } m_StickCalData[2]; 202 203 struct StickExtents { 204 struct { 205 Sint16 sMin; 206 Sint16 sMax; 207 } axis[2]; 208 } m_StickExtents[2]; 209} SDL_DriverSwitch_Context; 210 211 212static SDL_bool 213HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, Uint16 usage_page, Uint16 usage) 214{ 215 return SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id); 216} 217 218static const char * 219HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id) 220{ 221 /* Give a user friendly name for this controller */ 222 if (SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id)) { 223 return "Nintendo Switch Pro Controller"; 224 } 225 return NULL; 226} 227 228static int ReadInput(SDL_DriverSwitch_Context *ctx) 229{ 230 return hid_read_timeout(ctx->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0); 231} 232 233static int WriteOutput(SDL_DriverSwitch_Context *ctx, Uint8 *data, int size) 234{ 235 return hid_write(ctx->dev, data, size); 236} 237 238static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID) 239{ 240 /* Average response time for messages is ~30ms */ 241 Uint32 TimeoutMs = 100; 242 Uint32 startTicks = SDL_GetTicks(); 243 244 int nRead = 0; 245 while ((nRead = ReadInput(ctx)) != -1) { 246 if (nRead > 0) { 247 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) { 248 SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[ 1 ]; 249 if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) { 250 return reply; 251 } 252 } 253 } else { 254 SDL_Delay(1); 255 } 256 257 if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) { 258 break; 259 } 260 } 261 return NULL; 262} 263 264static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID) 265{ 266 /* Average response time for messages is ~30ms */ 267 Uint32 TimeoutMs = 100; 268 Uint32 startTicks = SDL_GetTicks(); 269 270 int nRead = 0; 271 while ((nRead = ReadInput(ctx)) != -1) { 272 if (nRead > 0) { 273 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[ 1 ] == expectedID) { 274 return SDL_TRUE; 275 } 276 } else { 277 SDL_Delay(1); 278 } 279 280 if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) { 281 break; 282 } 283 } 284 return SDL_FALSE; 285} 286 287static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket) 288{ 289 SDL_memset(outPacket, 0, sizeof(*outPacket)); 290 291 outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand; 292 outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber; 293 294 SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData)); 295 296 outPacket->ucSubcommandID = ucCommandID; 297 SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen); 298 299 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF; 300} 301 302static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen) 303{ 304 Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength]; 305 const size_t unWriteSize = ctx->m_bIsUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength; 306 307 if (ucLen > k_unSwitchOutputPacketDataLength) { 308 return SDL_FALSE; 309 } 310 311 if (ucLen < unWriteSize) { 312 SDL_memcpy(rgucBuf, pBuf, ucLen); 313 SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen); 314 pBuf = rgucBuf; 315 ucLen = (Uint8)unWriteSize; 316 } 317 return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0); 318} 319 320static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply) 321{ 322 int nRetries = 5; 323 SwitchSubcommandInputPacket_t *reply = NULL; 324 325 while (!reply && nRetries--) { 326 SwitchSubcommandOutputPacket_t commandPacket; 327 ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket); 328 329 if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) { 330 continue; 331 } 332 333 reply = ReadSubcommandReply(ctx, ucCommandID); 334 } 335 336 if (ppReply) { 337 *ppReply = reply; 338 } 339 return reply != NULL; 340} 341 342static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply) 343{ 344 int nRetries = 5; 345 346 while (nRetries--) { 347 SwitchProprietaryOutputPacket_t packet; 348 349 if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) { 350 return SDL_FALSE; 351 } 352 353 packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary; 354 packet.ucProprietaryID = ucCommand; 355 SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen); 356 357 if (!WritePacket(ctx, &packet, sizeof(packet))) { 358 continue; 359 } 360 361 if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) { 362 return SDL_TRUE; 363 } 364 } 365 return SDL_FALSE; 366} 367 368static void SetNeutralRumble(SwitchRumbleData_t *pRumble) 369{ 370 pRumble->rgucData[0] = 0x00; 371 pRumble->rgucData[1] = 0x01; 372 pRumble->rgucData[2] = 0x40; 373 pRumble->rgucData[3] = 0x40; 374} 375 376static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp) 377{ 378 if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) { 379 // High-band frequency and low-band amplitude are actually nine-bits each so they 380 // take a bit from the high-band amplitude and low-band frequency bytes respectively 381 pRumble->rgucData[0] = usHighFreq & 0xFF; 382 pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01); 383 384 pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80); 385 pRumble->rgucData[3] = usLowFreqAmp & 0xFF; 386 387#ifdef DEBUG_RUMBLE 388 SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X\n", 389 usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq, 390 ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF); 391#endif 392 } else { 393 SetNeutralRumble(pRumble); 394 } 395} 396 397static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx) 398{ 399 /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state 400 * to be retained for subsequent rumble or subcommand packets sent to the controller 401 */ 402 ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble; 403 ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber; 404 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF; 405 406 return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket)); 407} 408 409static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx) 410{ 411 /* We have to send a connection handshake to the controller when communicating over USB 412 * before we're able to send it other commands. Luckily this command is not supported 413 * over Bluetooth, so we can use the controller's lack of response as a way to 414 * determine if the connection is over USB or Bluetooth 415 */ 416 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) { 417 return SDL_FALSE; 418 } 419 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) { 420 return SDL_FALSE; 421 } 422 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) { 423 return SDL_FALSE; 424 } 425 return SDL_TRUE; 426} 427 428static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled) 429{ 430 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL); 431 432} 433static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode) 434{ 435 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL); 436} 437 438static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness) 439{ 440 Uint8 ucLedIntensity = 0; 441 Uint8 rgucBuffer[4]; 442 443 if (brightness > 0) { 444 if (brightness < 65) { 445 ucLedIntensity = (brightness + 5) / 10; 446 } else { 447 ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f)); 448 } 449 } 450 451 rgucBuffer[0] = (0x0 << 4) | 0x1; /* 0 mini cycles (besides first), cycle duration 8ms */ 452 rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */ 453 rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* First cycle LED intensity, 0x0 intensity for second cycle */ 454 rgucBuffer[3] = (0x0 << 4) | 0x0; /* 8ms fade transition to first cycle, 8ms first cycle LED duration */ 455 456 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL); 457} 458 459static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot) 460{ 461 Uint8 led_data = (1 << slot); 462 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL); 463} 464 465static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx) 466{ 467 Uint8 *pStickCal; 468 size_t stick, axis; 469 SwitchSubcommandInputPacket_t *reply = NULL; 470 471 /* Read Calibration Info */ 472 SwitchSPIOpData_t readParams; 473 readParams.unAddress = k_unSPIStickCalibrationStartOffset; 474 readParams.ucLength = k_unSPIStickCalibrationLength; 475 476 if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) { 477 return SDL_FALSE; 478 } 479 480 /* Stick calibration values are 12-bits each and are packed by bit 481 * For whatever reason the fields are in a different order for each stick 482 * Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min 483 * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max 484 */ 485 pStickCal = reply->spiReadData.rgucReadData; 486 487 /* Left stick */ 488 ctx->m_StickCalData[0].axis[0].sMax = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0]; /* X Axis max above center */ 489 ctx->m_StickCalData[0].axis[1].sMax = (pStickCal[2] << 4) | (pStickCal[1] >> 4); /* Y Axis max above center */ 490 ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3]; /* X Axis center */ 491 ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4); /* Y Axis center */ 492 ctx->m_StickCalData[0].axis[0].sMin = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6]; /* X Axis min below center */ 493 ctx->m_StickCalData[0].axis[1].sMin = (pStickCal[8] << 4) | (pStickCal[7] >> 4); /* Y Axis min below center */ 494 495 /* Right stick */ 496 ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9]; /* X Axis center */ 497 ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4); /* Y Axis center */ 498 ctx->m_StickCalData[1].axis[0].sMin = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12]; /* X Axis min below center */ 499 ctx->m_StickCalData[1].axis[1].sMin = (pStickCal[14] << 4) | (pStickCal[13] >> 4); /* Y Axis min below center */ 500 ctx->m_StickCalData[1].axis[0].sMax = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15]; /* X Axis max above center */ 501 ctx->m_StickCalData[1].axis[1].sMax = (pStickCal[17] << 4) | (pStickCal[16] >> 4); /* Y Axis max above center */ 502 503 /* Filter out any values that were uninitialized (0xFFF) in the SPI read */ 504 for (stick = 0; stick < 2; ++stick) { 505 for (axis = 0; axis < 2; ++axis) { 506 if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) { 507 ctx->m_StickCalData[stick].axis[axis].sCenter = 0; 508 } 509 if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) { 510 ctx->m_StickCalData[stick].axis[axis].sMax = 0; 511 } 512 if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) { 513 ctx->m_StickCalData[stick].axis[axis].sMin = 0; 514 } 515 } 516 } 517 518 if (ctx->m_bIsUsingBluetooth) { 519 for (stick = 0; stick < 2; ++stick) { 520 for(axis = 0; axis < 2; ++axis) { 521 ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f); 522 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f); 523 } 524 } 525 } else { 526 for (stick = 0; stick < 2; ++stick) { 527 for(axis = 0; axis < 2; ++axis) { 528 ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f); 529 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f); 530 } 531 } 532 } 533 return SDL_TRUE; 534} 535 536static float fsel(float fComparand, float fValGE, float fLT) 537{ 538 return fComparand >= 0 ? fValGE : fLT; 539} 540 541static float RemapVal(float val, float A, float B, float C, float D) 542{ 543 if (A == B) { 544 return fsel(val - B , D , C); 545 } 546 return C + (D - C) * (val - A) / (B - A); 547} 548 549static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter) 550{ 551 sRawValue -= sCenter; 552 553 if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) { 554 ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue; 555 } 556 if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) { 557 ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue; 558 } 559 560 if (sRawValue > 0) { 561 return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16)); 562 } else { 563 return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0)); 564 } 565} 566 567static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue) 568{ 569 return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter); 570} 571 572static SDL_bool 573HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) 574{ 575 SDL_DriverSwitch_Context *ctx; 576 Uint8 input_mode; 577 578 ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx)); 579 if (!ctx) { 580 SDL_OutOfMemory(); 581 return SDL_FALSE; 582 } 583 ctx->dev = dev; 584 585 *context = ctx; 586 587 /* Initialize rumble data */ 588 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]); 589 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]); 590 591 /* Try setting up USB mode, and if that fails we're using Bluetooth */ 592 if (!BTrySetupUSB(ctx)) { 593 ctx->m_bIsUsingBluetooth = SDL_TRUE; 594 } 595 596 if (!LoadStickCalibration(ctx)) { 597 SDL_SetError("Couldn't load stick calibration"); 598 SDL_free(ctx); 599 return SDL_FALSE; 600 } 601 602 if (!SetVibrationEnabled(ctx, 1)) { 603 SDL_SetError("Couldn't enable vibration"); 604 SDL_free(ctx); 605 return SDL_FALSE; 606 } 607 608 /* Set the desired input mode */ 609 if (ctx->m_bIsUsingBluetooth) { 610 input_mode = k_eSwitchInputReportIDs_SimpleControllerState; 611 } else { 612 input_mode = k_eSwitchInputReportIDs_FullControllerState; 613 } 614 if (!SetInputMode(ctx, input_mode)) { 615 SDL_SetError("Couldn't set input mode"); 616 SDL_free(ctx); 617 return SDL_FALSE; 618 } 619 620 /* Start sending USB reports */ 621 if (!ctx->m_bIsUsingBluetooth) { 622 /* ForceUSB doesn't generate an ACK, so don't wait for a reply */ 623 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) { 624 SDL_SetError("Couldn't start USB reports"); 625 SDL_free(ctx); 626 return SDL_FALSE; 627 } 628 } 629 630 /* Set the LED state */ 631 SetHomeLED(ctx, 100); 632 SetSlotLED(ctx, (joystick->instance_id % 4)); 633 634 /* Initialize the joystick capabilities */ 635 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; 636 joystick->naxes = SDL_CONTROLLER_AXIS_MAX; 637 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; 638 639 return SDL_TRUE; 640} 641 642static int 643HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) 644{ 645 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; 646 647 /* Experimentally determined rumble values. These will only matter on some controllers as tested ones 648 * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble 649 * 650 * More information about these values can be found here: 651 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md 652 */ 653 const Uint16 k_usHighFreq = 0x0074; 654 const Uint8 k_ucHighFreqAmp = 0xBE; 655 const Uint8 k_ucLowFreq = 0x3D; 656 const Uint16 k_usLowFreqAmp = 0x806F; 657 658 if (low_frequency_rumble) { 659 EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp); 660 } else { 661 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]); 662 } 663 664 if (high_frequency_rumble) { 665 EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp); 666 } else { 667 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]); 668 } 669 670 if (!WriteRumble(ctx)) { 671 SDL_SetError("Couldn't send rumble packet"); 672 return -1; 673 } 674 675 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) { 676 ctx->m_nRumbleExpiration = SDL_GetTicks() + duration_ms; 677 } else { 678 ctx->m_nRumbleExpiration = 0; 679 } 680 return 0; 681} 682 683static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet) 684{ 685 /* 0x8000 is the neutral value for all joystick axes */ 686 const Uint16 usJoystickCenter = 0x8000; 687 Sint16 axis; 688 689 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) { 690 Uint8 data = packet->rgucButtons[0]; 691 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 692 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 693 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED); 694 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED); 695 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED); 696 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED); 697 698 axis = (data & 0x40) ? 32767 : -32768; 699 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 700 701 axis = (data & 0x80) ? 32767 : -32768; 702 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 703 } 704 705 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) { 706 Uint8 data = packet->rgucButtons[1]; 707 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 708 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 709 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED); 710 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED); 711 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED); 712 } 713 714 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) { 715 SDL_bool dpad_up = SDL_FALSE; 716 SDL_bool dpad_down = SDL_FALSE; 717 SDL_bool dpad_left = SDL_FALSE; 718 SDL_bool dpad_right = SDL_FALSE; 719 720 switch (packet->ucStickHat) { 721 case 0: 722 dpad_up = SDL_TRUE; 723 break; 724 case 1: 725 dpad_up = SDL_TRUE; 726 dpad_right = SDL_TRUE; 727 break; 728 case 2: 729 dpad_right = SDL_TRUE; 730 break; 731 case 3: 732 dpad_right = SDL_TRUE; 733 dpad_down = SDL_TRUE; 734 break; 735 case 4: 736 dpad_down = SDL_TRUE; 737 break; 738 case 5: 739 dpad_left = SDL_TRUE; 740 dpad_down = SDL_TRUE; 741 break; 742 case 6: 743 dpad_left = SDL_TRUE; 744 break; 745 case 7: 746 dpad_up = SDL_TRUE; 747 dpad_left = SDL_TRUE; 748 break; 749 default: 750 break; 751 } 752 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down); 753 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up); 754 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right); 755 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left); 756 } 757 758 axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter); 759 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 760 761 axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter); 762 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); 763 764 axis = ApplyStickCalibrationCentered(ctx, 1, 0, packet->sJoystickRight[0], (Sint16)usJoystickCenter); 765 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 766 767 axis = ApplyStickCalibrationCentered(ctx, 1, 1, packet->sJoystickRight[1], (Sint16)usJoystickCenter); 768 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); 769 770 ctx->m_lastSimpleState = *packet; 771} 772 773static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) 774{ 775 Sint16 axis; 776 777 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) { 778 Uint8 data = packet->controllerState.rgucButtons[0]; 779 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 780 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 781 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED); 782 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED); 783 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED); 784 axis = (data & 0x80) ? 32767 : -32768; 785 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 786 } 787 788 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) { 789 Uint8 data = packet->controllerState.rgucButtons[1]; 790 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 791 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 792 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED); 793 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED); 794 795 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED); 796 } 797 798 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) { 799 Uint8 data = packet->controllerState.rgucButtons[2]; 800 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); 801 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); 802 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED); 803 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED); 804 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED); 805 axis = (data & 0x80) ? 32767 : -32768; 806 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 807 } 808 809 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8); 810 axis = ApplyStickCalibration(ctx, 0, 0, axis); 811 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 812 813 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4); 814 axis = ApplyStickCalibration(ctx, 0, 1, axis); 815 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis); 816 817 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8); 818 axis = ApplyStickCalibration(ctx, 1, 0, axis); 819 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 820 821 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4); 822 axis = ApplyStickCalibration(ctx, 1, 1, axis); 823 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis); 824 825 /* High nibble of battery/connection byte is battery level, low nibble is connection status 826 * LSB of connection nibble is USB/Switch connection status 827 */ 828 if (packet->controllerState.ucBatteryAndConnection & 0x1) { 829 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; 830 } else { 831 /* LSB of the battery nibble is used to report charging. 832 * The battery level is reported from 0(empty)-8(full) 833 */ 834 int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4; 835 if (level == 0) { 836 joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY; 837 } else if (level <= 2) { 838 joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW; 839 } else if (level <= 6) { 840 joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM; 841 } else { 842 joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; 843 } 844 } 845 846 ctx->m_lastFullState = *packet; 847} 848 849static SDL_bool 850HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *context) 851{ 852 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; 853 int size; 854 855 while ((size = ReadInput(ctx)) > 0) { 856 switch (ctx->m_rgucReadBuffer[0]) { 857 case k_eSwitchInputReportIDs_SimpleControllerState: 858 HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]); 859 break; 860 case k_eSwitchInputReportIDs_FullControllerState: 861 HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]); 862 break; 863 default: 864 break; 865 } 866 } 867 868 if (ctx->m_nRumbleExpiration) { 869 Uint32 now = SDL_GetTicks(); 870 if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) { 871 HIDAPI_DriverSwitch_Rumble(joystick, dev, context, 0, 0, 0); 872 } 873 } 874 875 return (size >= 0); 876} 877 878static void 879HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) 880{ 881 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; 882 883 /* Restore simple input mode for other applications */ 884 SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState); 885 886 SDL_free(context); 887} 888 889SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = 890{ 891 SDL_HINT_JOYSTICK_HIDAPI_SWITCH, 892 SDL_TRUE, 893 HIDAPI_DriverSwitch_IsSupportedDevice, 894 HIDAPI_DriverSwitch_GetDeviceName, 895 HIDAPI_DriverSwitch_Init, 896 HIDAPI_DriverSwitch_Rumble, 897 HIDAPI_DriverSwitch_Update, 898 HIDAPI_DriverSwitch_Quit 899}; 900 901#endif /* SDL_JOYSTICK_HIDAPI_SWITCH */ 902 903#endif /* SDL_JOYSTICK_HIDAPI */ 904 905/* vi: set ts=4 sw=4 expandtab: */ 906[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.