1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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_c.h"
29#include "../SDL_sysjoystick.h"
30#include "SDL_hidapijoystick_c.h"
31#include "SDL_hidapi_rumble.h"
32#include "SDL_hidapi_nintendo.h"
33
34#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
35
36// Define this if you want to log all packets from the controller
37// #define DEBUG_SWITCH_PROTOCOL
38
39// Define this to get log output for rumble logic
40// #define DEBUG_RUMBLE
41
42/* The initialization sequence doesn't appear to work correctly on Windows unless
43 the reads and writes are on the same thread.
44
45 ... and now I can't reproduce this, so I'm leaving it in, but disabled for now.
46 */
47// #define SWITCH_SYNCHRONOUS_WRITES
48
49/* How often you can write rumble commands to the controller.
50 If you send commands more frequently than this, you can turn off the controller
51 in Bluetooth mode, or the motors can miss the command in USB mode.
52 */
53#define RUMBLE_WRITE_FREQUENCY_MS 30
54
55// How often you have to refresh a long duration rumble to keep the motors running
56#define RUMBLE_REFRESH_FREQUENCY_MS 50
57
58#define SWITCH_GYRO_SCALE 14.2842f
59#define SWITCH_ACCEL_SCALE 4096.f
60
61#define SWITCH_GYRO_SCALE_OFFSET 13371.0f
62#define SWITCH_GYRO_SCALE_MULT 936.0f
63#define SWITCH_ACCEL_SCALE_OFFSET 16384.0f
64#define SWITCH_ACCEL_SCALE_MULT 4.0f
65
66enum
67{
68 SDL_GAMEPAD_BUTTON_SWITCH_SHARE = 11,
69 SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1,
70 SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1,
71 SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2,
72 SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2,
73 SDL_GAMEPAD_NUM_SWITCH_BUTTONS,
74};
75
76typedef enum
77{
78 k_eSwitchInputReportIDs_SubcommandReply = 0x21,
79 k_eSwitchInputReportIDs_FullControllerState = 0x30,
80 k_eSwitchInputReportIDs_FullControllerAndMcuState = 0x31,
81 k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
82 k_eSwitchInputReportIDs_CommandAck = 0x81,
83} ESwitchInputReportIDs;
84
85typedef enum
86{
87 k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
88 k_eSwitchOutputReportIDs_Rumble = 0x10,
89 k_eSwitchOutputReportIDs_Proprietary = 0x80,
90} ESwitchOutputReportIDs;
91
92typedef enum
93{
94 k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
95 k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,
96 k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,
97 k_eSwitchSubcommandIDs_SetHCIState = 0x06,
98 k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,
99 k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,
100 k_eSwitchSubcommandIDs_SetHomeLight = 0x38,
101 k_eSwitchSubcommandIDs_EnableIMU = 0x40,
102 k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,
103 k_eSwitchSubcommandIDs_EnableVibration = 0x48,
104} ESwitchSubcommandIDs;
105
106typedef enum
107{
108 k_eSwitchProprietaryCommandIDs_Status = 0x01,
109 k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
110 k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
111 k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,
112 k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,
113 k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,
114} ESwitchProprietaryCommandIDs;
115
116#define k_unSwitchOutputPacketDataLength 49
117#define k_unSwitchMaxOutputPacketLength 64
118#define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength
119#define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength
120
121#define k_unSPIStickFactoryCalibrationStartOffset 0x603D
122#define k_unSPIStickFactoryCalibrationEndOffset 0x604E
123#define k_unSPIStickFactoryCalibrationLength (k_unSPIStickFactoryCalibrationEndOffset - k_unSPIStickFactoryCalibrationStartOffset + 1)
124
125#define k_unSPIStickUserCalibrationStartOffset 0x8010
126#define k_unSPIStickUserCalibrationEndOffset 0x8025
127#define k_unSPIStickUserCalibrationLength (k_unSPIStickUserCalibrationEndOffset - k_unSPIStickUserCalibrationStartOffset + 1)
128
129#define k_unSPIIMUScaleStartOffset 0x6020
130#define k_unSPIIMUScaleEndOffset 0x6037
131#define k_unSPIIMUScaleLength (k_unSPIIMUScaleEndOffset - k_unSPIIMUScaleStartOffset + 1)
132
133#define k_unSPIIMUUserScaleStartOffset 0x8026
134#define k_unSPIIMUUserScaleEndOffset 0x8039
135#define k_unSPIIMUUserScaleLength (k_unSPIIMUUserScaleEndOffset - k_unSPIIMUUserScaleStartOffset + 1)
136
137#pragma pack(1)
138typedef struct
139{
140 Uint8 rgucButtons[2];
141 Uint8 ucStickHat;
142 Uint8 rgucJoystickLeft[2];
143 Uint8 rgucJoystickRight[2];
144} SwitchInputOnlyControllerStatePacket_t;
145
146typedef struct
147{
148 Uint8 rgucButtons[2];
149 Uint8 ucStickHat;
150 Sint16 sJoystickLeft[2];
151 Sint16 sJoystickRight[2];
152} SwitchSimpleStatePacket_t;
153
154typedef struct
155{
156 Uint8 ucCounter;
157 Uint8 ucBatteryAndConnection;
158 Uint8 rgucButtons[3];
159 Uint8 rgucJoystickLeft[3];
160 Uint8 rgucJoystickRight[3];
161 Uint8 ucVibrationCode;
162} SwitchControllerStatePacket_t;
163
164typedef struct
165{
166 SwitchControllerStatePacket_t controllerState;
167
168 struct
169 {
170 Sint16 sAccelX;
171 Sint16 sAccelY;
172 Sint16 sAccelZ;
173
174 Sint16 sGyroX;
175 Sint16 sGyroY;
176 Sint16 sGyroZ;
177 } imuState[3];
178} SwitchStatePacket_t;
179
180typedef struct
181{
182 Uint32 unAddress;
183 Uint8 ucLength;
184} SwitchSPIOpData_t;
185
186typedef struct
187{
188 SwitchControllerStatePacket_t m_controllerState;
189
190 Uint8 ucSubcommandAck;
191 Uint8 ucSubcommandID;
192
193#define k_unSubcommandDataBytes 35
194 union
195 {
196 Uint8 rgucSubcommandData[k_unSubcommandDataBytes];
197
198 struct
199 {
200 SwitchSPIOpData_t opData;
201 Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)];
202 } spiReadData;
203
204 struct
205 {
206 Uint8 rgucFirmwareVersion[2];
207 Uint8 ucDeviceType;
208 Uint8 ucFiller1;
209 Uint8 rgucMACAddress[6];
210 Uint8 ucFiller2;
211 Uint8 ucColorLocation;
212 } deviceInfo;
213
214 struct
215 {
216 SwitchSPIOpData_t opData;
217 Uint8 rgucLeftCalibration[9];
218 Uint8 rgucRightCalibration[9];
219 } stickFactoryCalibration;
220
221 struct
222 {
223 SwitchSPIOpData_t opData;
224 Uint8 rgucLeftMagic[2];
225 Uint8 rgucLeftCalibration[9];
226 Uint8 rgucRightMagic[2];
227 Uint8 rgucRightCalibration[9];
228 } stickUserCalibration;
229 };
230} SwitchSubcommandInputPacket_t;
231
232typedef struct
233{
234 Uint8 ucPacketType;
235 Uint8 ucCommandID;
236 Uint8 ucFiller;
237
238 Uint8 ucDeviceType;
239 Uint8 rgucMACAddress[6];
240} SwitchProprietaryStatusPacket_t;
241
242typedef struct
243{
244 Uint8 rgucData[4];
245} SwitchRumbleData_t;
246
247typedef struct
248{
249 Uint8 ucPacketType;
250 Uint8 ucPacketNumber;
251 SwitchRumbleData_t rumbleData[2];
252} SwitchCommonOutputPacket_t;
253
254typedef struct
255{
256 SwitchCommonOutputPacket_t commonData;
257
258 Uint8 ucSubcommandID;
259 Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1];
260} SwitchSubcommandOutputPacket_t;
261
262typedef struct
263{
264 Uint8 ucPacketType;
265 Uint8 ucProprietaryID;
266
267 Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];
268} SwitchProprietaryOutputPacket_t;
269#pragma pack()
270
271/* Enhanced report hint mode:
272 * "0": enhanced features are never used
273 * "1": enhanced features are always used
274 * "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it.
275 */
276typedef enum
277{
278 SWITCH_ENHANCED_REPORT_HINT_OFF,
279 SWITCH_ENHANCED_REPORT_HINT_ON,
280 SWITCH_ENHANCED_REPORT_HINT_AUTO
281} HIDAPI_Switch_EnhancedReportHint;
282
283typedef struct
284{
285 SDL_HIDAPI_Device *device;
286 SDL_Joystick *joystick;
287 bool m_bInputOnly;
288 bool m_bUseButtonLabels;
289 bool m_bPlayerLights;
290 int m_nPlayerIndex;
291 bool m_bSyncWrite;
292 int m_nMaxWriteAttempts;
293 ESwitchDeviceInfoControllerType m_eControllerType;
294 Uint8 m_nInitialInputMode;
295 Uint8 m_nCurrentInputMode;
296 Uint8 m_rgucMACAddress[6];
297 Uint8 m_nCommandNumber;
298 HIDAPI_Switch_EnhancedReportHint m_eEnhancedReportHint;
299 bool m_bEnhancedMode;
300 bool m_bEnhancedModeAvailable;
301 SwitchCommonOutputPacket_t m_RumblePacket;
302 Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
303 bool m_bRumbleActive;
304 Uint64 m_ulRumbleSent;
305 bool m_bRumblePending;
306 bool m_bRumbleZeroPending;
307 Uint32 m_unRumblePending;
308 bool m_bSensorsSupported;
309 bool m_bReportSensors;
310 bool m_bHasSensorData;
311 Uint64 m_ulLastInput;
312 Uint64 m_ulLastIMUReset;
313 Uint64 m_ulIMUSampleTimestampNS;
314 Uint32 m_unIMUSamples;
315 Uint64 m_ulIMUUpdateIntervalNS;
316 Uint64 m_ulTimestampNS;
317 bool m_bVerticalMode;
318
319 SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
320 SwitchSimpleStatePacket_t m_lastSimpleState;
321 SwitchStatePacket_t m_lastFullState;
322
323 struct StickCalibrationData
324 {
325 struct
326 {
327 Sint16 sCenter;
328 Sint16 sMin;
329 Sint16 sMax;
330 } axis[2];
331 } m_StickCalData[2];
332
333 struct StickExtents
334 {
335 struct
336 {
337 Sint16 sMin;
338 Sint16 sMax;
339 } axis[2];
340 } m_StickExtents[2], m_SimpleStickExtents[2];
341
342 struct IMUScaleData
343 {
344 float fAccelScaleX;
345 float fAccelScaleY;
346 float fAccelScaleZ;
347
348 float fGyroScaleX;
349 float fGyroScaleY;
350 float fGyroScaleZ;
351 } m_IMUScaleData;
352} SDL_DriverSwitch_Context;
353
354static int ReadInput(SDL_DriverSwitch_Context *ctx)
355{
356 int result;
357
358 // Make sure we don't try to read at the same time a write is happening
359 if (SDL_GetAtomicInt(&ctx->device->rumble_pending) > 0) {
360 return 0;
361 }
362
363 result = SDL_hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
364
365 // See if we can guess the initial input mode
366 if (result > 0 && !ctx->m_bInputOnly && !ctx->m_nInitialInputMode) {
367 switch (ctx->m_rgucReadBuffer[0]) {
368 case k_eSwitchInputReportIDs_FullControllerState:
369 case k_eSwitchInputReportIDs_FullControllerAndMcuState:
370 case k_eSwitchInputReportIDs_SimpleControllerState:
371 ctx->m_nInitialInputMode = ctx->m_rgucReadBuffer[0];
372 break;
373 default:
374 break;
375 }
376 }
377 return result;
378}
379
380static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int size)
381{
382#ifdef SWITCH_SYNCHRONOUS_WRITES
383 return SDL_hid_write(ctx->device->dev, data, size);
384#else
385 // Use the rumble thread for general asynchronous writes
386 if (!SDL_HIDAPI_LockRumble()) {
387 return -1;
388 }
389 return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size);
390#endif // SWITCH_SYNCHRONOUS_WRITES
391}
392
393static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
394{
395 // Average response time for messages is ~30ms
396 Uint64 endTicks = SDL_GetTicks() + 100;
397
398 int nRead = 0;
399 while ((nRead = ReadInput(ctx)) != -1) {
400 if (nRead > 0) {
401 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
402 SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1];
403 if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
404 return reply;
405 }
406 }
407 } else {
408 SDL_Delay(1);
409 }
410
411 if (SDL_GetTicks() >= endTicks) {
412 break;
413 }
414 }
415 return NULL;
416}
417
418static bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
419{
420 // Average response time for messages is ~30ms
421 Uint64 endTicks = SDL_GetTicks() + 100;
422
423 int nRead = 0;
424 while ((nRead = ReadInput(ctx)) != -1) {
425 if (nRead > 0) {
426 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) {
427 return true;
428 }
429 } else {
430 SDL_Delay(1);
431 }
432
433 if (SDL_GetTicks() >= endTicks) {
434 break;
435 }
436 }
437 return false;
438}
439
440static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
441{
442 SDL_memset(outPacket, 0, sizeof(*outPacket));
443
444 outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
445 outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
446
447 SDL_memcpy(outPacket->commonData.rumbleData, ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
448
449 outPacket->ucSubcommandID = ucCommandID;
450 if (pBuf) {
451 SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
452 }
453
454 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
455}
456
457static bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
458{
459 Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
460 const size_t unWriteSize = ctx->device->is_bluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
461
462 if (ucLen > k_unSwitchOutputPacketDataLength) {
463 return false;
464 }
465
466 if (ucLen < unWriteSize) {
467 SDL_memcpy(rgucBuf, pBuf, ucLen);
468 SDL_memset(rgucBuf + ucLen, 0, unWriteSize - ucLen);
469 pBuf = rgucBuf;
470 ucLen = (Uint8)unWriteSize;
471 }
472 if (ctx->m_bSyncWrite) {
473 return SDL_hid_write(ctx->device->dev, (Uint8 *)pBuf, ucLen) >= 0;
474 } else {
475 return WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0;
476 }
477}
478
479static bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, const Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
480{
481 SwitchSubcommandInputPacket_t *reply = NULL;
482 int nTries;
483
484 for (nTries = 1; !reply && nTries <= ctx->m_nMaxWriteAttempts; ++nTries) {
485 SwitchSubcommandOutputPacket_t commandPacket;
486 ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
487
488 if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
489 continue;
490 }
491
492 reply = ReadSubcommandReply(ctx, ucCommandID);
493 }
494
495 if (ppReply) {
496 *ppReply = reply;
497 }
498 return reply != NULL;
499}
500
501static bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, bool waitForReply)
502{
503 int nTries;
504
505 for (nTries = 1; nTries <= ctx->m_nMaxWriteAttempts; ++nTries) {
506 SwitchProprietaryOutputPacket_t packet;
507
508 if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
509 return false;
510 }
511
512 SDL_zero(packet);
513 packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
514 packet.ucProprietaryID = ucCommand;
515 if (pBuf) {
516 SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
517 }
518
519 if (!WritePacket(ctx, &packet, sizeof(packet))) {
520 continue;
521 }
522
523 if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
524 // SDL_Log("Succeeded%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries);
525 return true;
526 }
527 }
528 // SDL_Log("Failed%s after %d tries", ctx->m_bSyncWrite ? " (sync)" : "", nTries);
529 return false;
530}
531
532static Uint8 EncodeRumbleHighAmplitude(Uint16 amplitude)
533{
534 /* More information about these values can be found here:
535 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
536 */
537 Uint16 hfa[101][2] = { { 0, 0x0 }, { 514, 0x2 }, { 775, 0x4 }, { 921, 0x6 }, { 1096, 0x8 }, { 1303, 0x0a }, { 1550, 0x0c }, { 1843, 0x0e }, { 2192, 0x10 }, { 2606, 0x12 }, { 3100, 0x14 }, { 3686, 0x16 }, { 4383, 0x18 }, { 5213, 0x1a }, { 6199, 0x1c }, { 7372, 0x1e }, { 7698, 0x20 }, { 8039, 0x22 }, { 8395, 0x24 }, { 8767, 0x26 }, { 9155, 0x28 }, { 9560, 0x2a }, { 9984, 0x2c }, { 10426, 0x2e }, { 10887, 0x30 }, { 11369, 0x32 }, { 11873, 0x34 }, { 12398, 0x36 }, { 12947, 0x38 }, { 13520, 0x3a }, { 14119, 0x3c }, { 14744, 0x3e }, { 15067, 0x40 }, { 15397, 0x42 }, { 15734, 0x44 }, { 16079, 0x46 }, { 16431, 0x48 }, { 16790, 0x4a }, { 17158, 0x4c }, { 17534, 0x4e }, { 17918, 0x50 }, { 18310, 0x52 }, { 18711, 0x54 }, { 19121, 0x56 }, { 19540, 0x58 }, { 19967, 0x5a }, { 20405, 0x5c }, { 20851, 0x5e }, { 21308, 0x60 }, { 21775, 0x62 }, { 22251, 0x64 }, { 22739, 0x66 }, { 23236, 0x68 }, { 23745, 0x6a }, { 24265, 0x6c }, { 24797, 0x6e }, { 25340, 0x70 }, { 25894, 0x72 }, { 26462, 0x74 }, { 27041, 0x76 }, { 27633, 0x78 }, { 28238, 0x7a }, { 28856, 0x7c }, { 29488, 0x7e }, { 30134, 0x80 }, { 30794, 0x82 }, { 31468, 0x84 }, { 32157, 0x86 }, { 32861, 0x88 }, { 33581, 0x8a }, { 34316, 0x8c }, { 35068, 0x8e }, { 35836, 0x90 }, { 36620, 0x92 }, { 37422, 0x94 }, { 38242, 0x96 }, { 39079, 0x98 }, { 39935, 0x9a }, { 40809, 0x9c }, { 41703, 0x9e }, { 42616, 0xa0 }, { 43549, 0xa2 }, { 44503, 0xa4 }, { 45477, 0xa6 }, { 46473, 0xa8 }, { 47491, 0xaa }, { 48531, 0xac }, { 49593, 0xae }, { 50679, 0xb0 }, { 51789, 0xb2 }, { 52923, 0xb4 }, { 54082, 0xb6 }, { 55266, 0xb8 }, { 56476, 0xba }, { 57713, 0xbc }, { 58977, 0xbe }, { 60268, 0xc0 }, { 61588, 0xc2 }, { 62936, 0xc4 }, { 64315, 0xc6 }, { 65535, 0xc8 } };
538 int index = 0;
539 for (; index < 101; index++) {
540 if (amplitude <= hfa[index][0]) {
541 return (Uint8)hfa[index][1];
542 }
543 }
544 return (Uint8)hfa[100][1];
545}
546
547static Uint16 EncodeRumbleLowAmplitude(Uint16 amplitude)
548{
549 /* More information about these values can be found here:
550 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
551 */
552 Uint16 lfa[101][2] = { { 0, 0x0040 }, { 514, 0x8040 }, { 775, 0x0041 }, { 921, 0x8041 }, { 1096, 0x0042 }, { 1303, 0x8042 }, { 1550, 0x0043 }, { 1843, 0x8043 }, { 2192, 0x0044 }, { 2606, 0x8044 }, { 3100, 0x0045 }, { 3686, 0x8045 }, { 4383, 0x0046 }, { 5213, 0x8046 }, { 6199, 0x0047 }, { 7372, 0x8047 }, { 7698, 0x0048 }, { 8039, 0x8048 }, { 8395, 0x0049 }, { 8767, 0x8049 }, { 9155, 0x004a }, { 9560, 0x804a }, { 9984, 0x004b }, { 10426, 0x804b }, { 10887, 0x004c }, { 11369, 0x804c }, { 11873, 0x004d }, { 12398, 0x804d }, { 12947, 0x004e }, { 13520, 0x804e }, { 14119, 0x004f }, { 14744, 0x804f }, { 15067, 0x0050 }, { 15397, 0x8050 }, { 15734, 0x0051 }, { 16079, 0x8051 }, { 16431, 0x0052 }, { 16790, 0x8052 }, { 17158, 0x0053 }, { 17534, 0x8053 }, { 17918, 0x0054 }, { 18310, 0x8054 }, { 18711, 0x0055 }, { 19121, 0x8055 }, { 19540, 0x0056 }, { 19967, 0x8056 }, { 20405, 0x0057 }, { 20851, 0x8057 }, { 21308, 0x0058 }, { 21775, 0x8058 }, { 22251, 0x0059 }, { 22739, 0x8059 }, { 23236, 0x005a }, { 23745, 0x805a }, { 24265, 0x005b }, { 24797, 0x805b }, { 25340, 0x005c }, { 25894, 0x805c }, { 26462, 0x005d }, { 27041, 0x805d }, { 27633, 0x005e }, { 28238, 0x805e }, { 28856, 0x005f }, { 29488, 0x805f }, { 30134, 0x0060 }, { 30794, 0x8060 }, { 31468, 0x0061 }, { 32157, 0x8061 }, { 32861, 0x0062 }, { 33581, 0x8062 }, { 34316, 0x0063 }, { 35068, 0x8063 }, { 35836, 0x0064 }, { 36620, 0x8064 }, { 37422, 0x0065 }, { 38242, 0x8065 }, { 39079, 0x0066 }, { 39935, 0x8066 }, { 40809, 0x0067 }, { 41703, 0x8067 }, { 42616, 0x0068 }, { 43549, 0x8068 }, { 44503, 0x0069 }, { 45477, 0x8069 }, { 46473, 0x006a }, { 47491, 0x806a }, { 48531, 0x006b }, { 49593, 0x806b }, { 50679, 0x006c }, { 51789, 0x806c }, { 52923, 0x006d }, { 54082, 0x806d }, { 55266, 0x006e }, { 56476, 0x806e }, { 57713, 0x006f }, { 58977, 0x806f }, { 60268, 0x0070 }, { 61588, 0x8070 }, { 62936, 0x0071 }, { 64315, 0x8071 }, { 65535, 0x0072 } };
553 int index = 0;
554 for (; index < 101; index++) {
555 if (amplitude <= lfa[index][0]) {
556 return lfa[index][1];
557 }
558 }
559 return lfa[100][1];
560}
561
562static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
563{
564 pRumble->rgucData[0] = 0x00;
565 pRumble->rgucData[1] = 0x01;
566 pRumble->rgucData[2] = 0x40;
567 pRumble->rgucData[3] = 0x40;
568}
569
570static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
571{
572 if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
573 // High-band frequency and low-band amplitude are actually nine-bits each so they
574 // take a bit from the high-band amplitude and low-band frequency bytes respectively
575 pRumble->rgucData[0] = usHighFreq & 0xFF;
576 pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
577
578 pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
579 pRumble->rgucData[3] = usLowFreqAmp & 0xFF;
580
581#ifdef DEBUG_RUMBLE
582 SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X",
583 usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
584 ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
585#endif
586 } else {
587 SetNeutralRumble(pRumble);
588 }
589}
590
591static bool WriteRumble(SDL_DriverSwitch_Context *ctx)
592{
593 /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
594 * to be retained for subsequent rumble or subcommand packets sent to the controller
595 */
596 ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
597 ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
598 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
599
600 // Refresh the rumble state periodically
601 ctx->m_ulRumbleSent = SDL_GetTicks();
602
603 return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
604}
605
606static ESwitchDeviceInfoControllerType CalculateControllerType(SDL_DriverSwitch_Context *ctx, ESwitchDeviceInfoControllerType eControllerType)
607{
608 SDL_HIDAPI_Device *device = ctx->device;
609
610 // The N64 controller reports as a Pro controller over USB
611 if (eControllerType == k_eSwitchDeviceInfoControllerType_ProController &&
612 device->product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) {
613 eControllerType = k_eSwitchDeviceInfoControllerType_N64;
614 }
615
616 if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown) {
617 // This might be a Joy-Con that's missing from a charging grip slot
618 if (device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
619 if (device->interface_number == 1) {
620 eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft;
621 } else {
622 eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight;
623 }
624 }
625 }
626 return eControllerType;
627}
628
629static bool BReadDeviceInfo(SDL_DriverSwitch_Context *ctx)
630{
631 SwitchSubcommandInputPacket_t *reply = NULL;
632
633 if (ctx->device->is_bluetooth) {
634 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {
635 // Byte 2: Controller ID (1=LJC, 2=RJC, 3=Pro)
636 ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType);
637
638 // Bytes 4-9: MAC address (big-endian)
639 SDL_memcpy(ctx->m_rgucMACAddress, reply->deviceInfo.rgucMACAddress, sizeof(ctx->m_rgucMACAddress));
640
641 return true;
642 }
643 } else {
644 if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) {
645 SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0];
646 size_t i;
647
648 ctx->m_eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType);
649
650 for (i = 0; i < sizeof(ctx->m_rgucMACAddress); ++i) {
651 ctx->m_rgucMACAddress[i] = status->rgucMACAddress[sizeof(ctx->m_rgucMACAddress) - i - 1];
652 }
653
654 return true;
655 }
656 }
657 return false;
658}
659
660static bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
661{
662 /* We have to send a connection handshake to the controller when communicating over USB
663 * before we're able to send it other commands. Luckily this command is not supported
664 * over Bluetooth, so we can use the controller's lack of response as a way to
665 * determine if the connection is over USB or Bluetooth
666 */
667 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) {
668 return false;
669 }
670 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, true)) {
671 // The 8BitDo M30 and SF30 Pro don't respond to this command, but otherwise work correctly
672 // return false;
673 }
674 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, true)) {
675 // This fails on the right Joy-Con when plugged into the charging grip
676 // return false;
677 }
678 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) {
679 return false;
680 }
681 return true;
682}
683
684static bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
685{
686 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
687}
688static bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
689{
690#ifdef FORCE_SIMPLE_REPORTS
691 input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
692#endif
693#ifdef FORCE_FULL_REPORTS
694 input_mode = k_eSwitchInputReportIDs_FullControllerState;
695#endif
696
697 if (input_mode == ctx->m_nCurrentInputMode) {
698 return true;
699 } else {
700 ctx->m_nCurrentInputMode = input_mode;
701
702 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, sizeof(input_mode), NULL);
703 }
704}
705
706static bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
707{
708 Uint8 ucLedIntensity = 0;
709 Uint8 rgucBuffer[4];
710
711 if (brightness > 0) {
712 if (brightness < 65) {
713 ucLedIntensity = (brightness + 5) / 10;
714 } else {
715 ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
716 }
717 }
718
719 rgucBuffer[0] = (0x0 << 4) | 0x1; // 0 mini cycles (besides first), cycle duration 8ms
720 rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; // LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle)
721 rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; // First cycle LED intensity, 0x0 intensity for second cycle
722 rgucBuffer[3] = (0x0 << 4) | 0x0; // 8ms fade transition to first cycle, 8ms first cycle LED duration
723
724 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
725}
726
727static void SDLCALL SDL_HomeLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
728{
729 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
730
731 if (hint && *hint) {
732 int value;
733
734 if (SDL_strchr(hint, '.') != NULL) {
735 value = (int)(100.0f * SDL_atof(hint));
736 if (value > 255) {
737 value = 255;
738 }
739 } else if (SDL_GetStringBoolean(hint, true)) {
740 value = 100;
741 } else {
742 value = 0;
743 }
744 SetHomeLED(ctx, (Uint8)value);
745 }
746}
747
748static void UpdateSlotLED(SDL_DriverSwitch_Context *ctx)
749{
750 if (!ctx->m_bInputOnly) {
751 Uint8 led_data = 0;
752
753 if (ctx->m_bPlayerLights && ctx->m_nPlayerIndex >= 0) {
754 led_data = (1 << (ctx->m_nPlayerIndex % 4));
755 }
756 WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
757 }
758}
759
760static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
761{
762 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
763 bool bPlayerLights = SDL_GetStringBoolean(hint, true);
764
765 if (bPlayerLights != ctx->m_bPlayerLights) {
766 ctx->m_bPlayerLights = bPlayerLights;
767
768 UpdateSlotLED(ctx);
769 HIDAPI_UpdateDeviceProperties(ctx->device);
770 }
771}
772
773static void GetInitialInputMode(SDL_DriverSwitch_Context *ctx)
774{
775 if (!ctx->m_nInitialInputMode) {
776 // This will set the initial input mode if it can
777 ReadInput(ctx);
778 }
779}
780
781static Uint8 GetDefaultInputMode(SDL_DriverSwitch_Context *ctx)
782{
783 Uint8 input_mode;
784
785 // Determine the desired input mode
786 if (ctx->m_nInitialInputMode) {
787 input_mode = ctx->m_nInitialInputMode;
788 } else {
789 if (ctx->device->is_bluetooth) {
790 input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
791 } else {
792 input_mode = k_eSwitchInputReportIDs_FullControllerState;
793 }
794 }
795
796 switch (ctx->m_eEnhancedReportHint) {
797 case SWITCH_ENHANCED_REPORT_HINT_OFF:
798 input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
799 break;
800 case SWITCH_ENHANCED_REPORT_HINT_ON:
801 if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) {
802 input_mode = k_eSwitchInputReportIDs_FullControllerState;
803 }
804 break;
805 case SWITCH_ENHANCED_REPORT_HINT_AUTO:
806 /* Joy-Con controllers switch their thumbsticks into D-pad mode in simple mode,
807 * so let's enable full controller state for them.
808 */
809 if (ctx->device->vendor_id == USB_VENDOR_NINTENDO &&
810 (ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
811 ctx->device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT)) {
812 input_mode = k_eSwitchInputReportIDs_FullControllerState;
813 }
814 break;
815 }
816
817 // Wired controllers break if they are put into simple controller state
818 if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState &&
819 !ctx->device->is_bluetooth) {
820 input_mode = k_eSwitchInputReportIDs_FullControllerState;
821 }
822 return input_mode;
823}
824
825static Uint8 GetSensorInputMode(SDL_DriverSwitch_Context *ctx)
826{
827 Uint8 input_mode;
828
829 // Determine the desired input mode
830 if (!ctx->m_nInitialInputMode ||
831 ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) {
832 input_mode = k_eSwitchInputReportIDs_FullControllerState;
833 } else {
834 input_mode = ctx->m_nInitialInputMode;
835 }
836 return input_mode;
837}
838
839static void UpdateInputMode(SDL_DriverSwitch_Context *ctx)
840{
841 Uint8 input_mode;
842
843 if (ctx->m_bReportSensors) {
844 input_mode = GetSensorInputMode(ctx);
845 } else {
846 input_mode = GetDefaultInputMode(ctx);
847 }
848 SetInputMode(ctx, input_mode);
849}
850
851static void SetEnhancedModeAvailable(SDL_DriverSwitch_Context *ctx)
852{
853 if (ctx->m_bEnhancedModeAvailable) {
854 return;
855 }
856 ctx->m_bEnhancedModeAvailable = true;
857
858 if (ctx->m_bSensorsSupported) {
859 // Use the right sensor in the combined Joy-Con pair
860 if (!ctx->device->parent ||
861 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
862 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 200.0f);
863 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 200.0f);
864 }
865 if (ctx->device->parent &&
866 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
867 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_L, 200.0f);
868 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_L, 200.0f);
869 }
870 if (ctx->device->parent &&
871 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
872 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO_R, 200.0f);
873 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL_R, 200.0f);
874 }
875 }
876}
877
878static void SetEnhancedReportHint(SDL_DriverSwitch_Context *ctx, HIDAPI_Switch_EnhancedReportHint eEnhancedReportHint)
879{
880 ctx->m_eEnhancedReportHint = eEnhancedReportHint;
881
882 switch (eEnhancedReportHint) {
883 case SWITCH_ENHANCED_REPORT_HINT_OFF:
884 ctx->m_bEnhancedMode = false;
885 break;
886 case SWITCH_ENHANCED_REPORT_HINT_ON:
887 SetEnhancedModeAvailable(ctx);
888 ctx->m_bEnhancedMode = true;
889 break;
890 case SWITCH_ENHANCED_REPORT_HINT_AUTO:
891 SetEnhancedModeAvailable(ctx);
892 break;
893 }
894
895 UpdateInputMode(ctx);
896}
897
898static void UpdateEnhancedModeOnEnhancedReport(SDL_DriverSwitch_Context *ctx)
899{
900 if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) {
901 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);
902 }
903}
904
905static void UpdateEnhancedModeOnApplicationUsage(SDL_DriverSwitch_Context *ctx)
906{
907 if (ctx->m_eEnhancedReportHint == SWITCH_ENHANCED_REPORT_HINT_AUTO) {
908 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);
909 }
910}
911
912static void SDLCALL SDL_EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
913{
914 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
915
916 if (hint && SDL_strcasecmp(hint, "auto") == 0) {
917 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_AUTO);
918 } else if (SDL_GetStringBoolean(hint, true)) {
919 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_ON);
920 } else {
921 SetEnhancedReportHint(ctx, SWITCH_ENHANCED_REPORT_HINT_OFF);
922 }
923}
924
925static bool SetIMUEnabled(SDL_DriverSwitch_Context *ctx, bool enabled)
926{
927 Uint8 imu_data = enabled ? 1 : 0;
928 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableIMU, &imu_data, sizeof(imu_data), NULL);
929}
930
931static bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
932{
933 Uint8 *pLeftStickCal;
934 Uint8 *pRightStickCal;
935 size_t stick, axis;
936 SwitchSubcommandInputPacket_t *user_reply = NULL;
937 SwitchSubcommandInputPacket_t *factory_reply = NULL;
938 SwitchSPIOpData_t readUserParams;
939 SwitchSPIOpData_t readFactoryParams;
940
941 // Read User Calibration Info
942 readUserParams.unAddress = k_unSPIStickUserCalibrationStartOffset;
943 readUserParams.ucLength = k_unSPIStickUserCalibrationLength;
944
945 // This isn't readable on all controllers, so ignore failure
946 WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readUserParams, sizeof(readUserParams), &user_reply);
947
948 // Read Factory Calibration Info
949 readFactoryParams.unAddress = k_unSPIStickFactoryCalibrationStartOffset;
950 readFactoryParams.ucLength = k_unSPIStickFactoryCalibrationLength;
951
952 const int MAX_ATTEMPTS = 3;
953 for (int attempt = 0; ; ++attempt) {
954 if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readFactoryParams, sizeof(readFactoryParams), &factory_reply)) {
955 return false;
956 }
957
958 if (factory_reply->stickFactoryCalibration.opData.unAddress == k_unSPIStickFactoryCalibrationStartOffset) {
959 // We successfully read the calibration data
960 break;
961 }
962
963 if (attempt == MAX_ATTEMPTS) {
964 return false;
965 }
966 }
967
968 // Automatically select the user calibration if magic bytes are set
969 if (user_reply && user_reply->stickUserCalibration.rgucLeftMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucLeftMagic[1] == 0xA1) {
970 pLeftStickCal = user_reply->stickUserCalibration.rgucLeftCalibration;
971 } else {
972 pLeftStickCal = factory_reply->stickFactoryCalibration.rgucLeftCalibration;
973 }
974
975 if (user_reply && user_reply->stickUserCalibration.rgucRightMagic[0] == 0xB2 && user_reply->stickUserCalibration.rgucRightMagic[1] == 0xA1) {
976 pRightStickCal = user_reply->stickUserCalibration.rgucRightCalibration;
977 } else {
978 pRightStickCal = factory_reply->stickFactoryCalibration.rgucRightCalibration;
979 }
980
981 /* Stick calibration values are 12-bits each and are packed by bit
982 * For whatever reason the fields are in a different order for each stick
983 * Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
984 * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
985 */
986
987 // Left stick
988 ctx->m_StickCalData[0].axis[0].sMax = ((pLeftStickCal[1] << 8) & 0xF00) | pLeftStickCal[0]; // X Axis max above center
989 ctx->m_StickCalData[0].axis[1].sMax = (pLeftStickCal[2] << 4) | (pLeftStickCal[1] >> 4); // Y Axis max above center
990 ctx->m_StickCalData[0].axis[0].sCenter = ((pLeftStickCal[4] << 8) & 0xF00) | pLeftStickCal[3]; // X Axis center
991 ctx->m_StickCalData[0].axis[1].sCenter = (pLeftStickCal[5] << 4) | (pLeftStickCal[4] >> 4); // Y Axis center
992 ctx->m_StickCalData[0].axis[0].sMin = ((pLeftStickCal[7] << 8) & 0xF00) | pLeftStickCal[6]; // X Axis min below center
993 ctx->m_StickCalData[0].axis[1].sMin = (pLeftStickCal[8] << 4) | (pLeftStickCal[7] >> 4); // Y Axis min below center
994
995 // Right stick
996 ctx->m_StickCalData[1].axis[0].sCenter = ((pRightStickCal[1] << 8) & 0xF00) | pRightStickCal[0]; // X Axis center
997 ctx->m_StickCalData[1].axis[1].sCenter = (pRightStickCal[2] << 4) | (pRightStickCal[1] >> 4); // Y Axis center
998 ctx->m_StickCalData[1].axis[0].sMin = ((pRightStickCal[4] << 8) & 0xF00) | pRightStickCal[3]; // X Axis min below center
999 ctx->m_StickCalData[1].axis[1].sMin = (pRightStickCal[5] << 4) | (pRightStickCal[4] >> 4); // Y Axis min below center
1000 ctx->m_StickCalData[1].axis[0].sMax = ((pRightStickCal[7] << 8) & 0xF00) | pRightStickCal[6]; // X Axis max above center
1001 ctx->m_StickCalData[1].axis[1].sMax = (pRightStickCal[8] << 4) | (pRightStickCal[7] >> 4); // Y Axis max above center
1002
1003 // Filter out any values that were uninitialized (0xFFF) in the SPI read
1004 for (stick = 0; stick < 2; ++stick) {
1005 for (axis = 0; axis < 2; ++axis) {
1006 if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
1007 ctx->m_StickCalData[stick].axis[axis].sCenter = 2048;
1008 }
1009 if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
1010 ctx->m_StickCalData[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f);
1011 }
1012 if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
1013 ctx->m_StickCalData[stick].axis[axis].sMin = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sCenter * 0.7f);
1014 }
1015 }
1016 }
1017
1018 for (stick = 0; stick < 2; ++stick) {
1019 for (axis = 0; axis < 2; ++axis) {
1020 ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
1021 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
1022 }
1023 }
1024
1025 for (stick = 0; stick < 2; ++stick) {
1026 for (axis = 0; axis < 2; ++axis) {
1027 ctx->m_SimpleStickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
1028 ctx->m_SimpleStickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
1029 }
1030 }
1031
1032 return true;
1033}
1034
1035static bool LoadIMUCalibration(SDL_DriverSwitch_Context *ctx)
1036{
1037 SwitchSubcommandInputPacket_t *reply = NULL;
1038
1039 // Read Calibration Info
1040 SwitchSPIOpData_t readParams;
1041 readParams.unAddress = k_unSPIIMUScaleStartOffset;
1042 readParams.ucLength = k_unSPIIMUScaleLength;
1043
1044 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
1045 Uint8 *pIMUScale;
1046 Sint16 sAccelRawX, sAccelRawY, sAccelRawZ, sGyroRawX, sGyroRawY, sGyroRawZ;
1047
1048 // IMU scale gives us multipliers for converting raw values to real world values
1049 pIMUScale = reply->spiReadData.rgucReadData;
1050
1051 sAccelRawX = (pIMUScale[1] << 8) | pIMUScale[0];
1052 sAccelRawY = (pIMUScale[3] << 8) | pIMUScale[2];
1053 sAccelRawZ = (pIMUScale[5] << 8) | pIMUScale[4];
1054
1055 sGyroRawX = (pIMUScale[13] << 8) | pIMUScale[12];
1056 sGyroRawY = (pIMUScale[15] << 8) | pIMUScale[14];
1057 sGyroRawZ = (pIMUScale[17] << 8) | pIMUScale[16];
1058
1059 // Check for user calibration data. If it's present and set, it'll override the factory settings
1060 readParams.unAddress = k_unSPIIMUUserScaleStartOffset;
1061 readParams.ucLength = k_unSPIIMUUserScaleLength;
1062 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply) && (pIMUScale[0] | pIMUScale[1] << 8) == 0xA1B2) {
1063 pIMUScale = reply->spiReadData.rgucReadData;
1064
1065 sAccelRawX = (pIMUScale[3] << 8) | pIMUScale[2];
1066 sAccelRawY = (pIMUScale[5] << 8) | pIMUScale[4];
1067 sAccelRawZ = (pIMUScale[7] << 8) | pIMUScale[6];
1068
1069 sGyroRawX = (pIMUScale[15] << 8) | pIMUScale[14];
1070 sGyroRawY = (pIMUScale[17] << 8) | pIMUScale[16];
1071 sGyroRawZ = (pIMUScale[19] << 8) | pIMUScale[18];
1072 }
1073
1074 // Accelerometer scale
1075 ctx->m_IMUScaleData.fAccelScaleX = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawX) * SDL_STANDARD_GRAVITY;
1076 ctx->m_IMUScaleData.fAccelScaleY = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawY) * SDL_STANDARD_GRAVITY;
1077 ctx->m_IMUScaleData.fAccelScaleZ = SWITCH_ACCEL_SCALE_MULT / (SWITCH_ACCEL_SCALE_OFFSET - (float)sAccelRawZ) * SDL_STANDARD_GRAVITY;
1078
1079 // Gyro scale
1080 ctx->m_IMUScaleData.fGyroScaleX = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawX) * SDL_PI_F / 180.0f;
1081 ctx->m_IMUScaleData.fGyroScaleY = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawY) * SDL_PI_F / 180.0f;
1082 ctx->m_IMUScaleData.fGyroScaleZ = SWITCH_GYRO_SCALE_MULT / (SWITCH_GYRO_SCALE_OFFSET - (float)sGyroRawZ) * SDL_PI_F / 180.0f;
1083
1084 } else {
1085 // Use default values
1086 const float accelScale = SDL_STANDARD_GRAVITY / SWITCH_ACCEL_SCALE;
1087 const float gyroScale = SDL_PI_F / 180.0f / SWITCH_GYRO_SCALE;
1088
1089 ctx->m_IMUScaleData.fAccelScaleX = accelScale;
1090 ctx->m_IMUScaleData.fAccelScaleY = accelScale;
1091 ctx->m_IMUScaleData.fAccelScaleZ = accelScale;
1092
1093 ctx->m_IMUScaleData.fGyroScaleX = gyroScale;
1094 ctx->m_IMUScaleData.fGyroScaleY = gyroScale;
1095 ctx->m_IMUScaleData.fGyroScaleZ = gyroScale;
1096 }
1097 return true;
1098}
1099
1100static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
1101{
1102 sRawValue -= ctx->m_StickCalData[nStick].axis[nAxis].sCenter;
1103
1104 if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
1105 ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
1106 }
1107 if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
1108 ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
1109 }
1110
1111 return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, ctx->m_StickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);
1112}
1113
1114static Sint16 ApplySimpleStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
1115{
1116 // 0x8000 is the neutral value for all joystick axes
1117 const Uint16 usJoystickCenter = 0x8000;
1118
1119 sRawValue -= usJoystickCenter;
1120
1121 if (sRawValue > ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax) {
1122 ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax = sRawValue;
1123 }
1124 if (sRawValue < ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin) {
1125 ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin = sRawValue;
1126 }
1127
1128 return (Sint16)HIDAPI_RemapVal(sRawValue, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMin, ctx->m_SimpleStickExtents[nStick].axis[nAxis].sMax, SDL_MIN_SINT16, SDL_MAX_SINT16);
1129}
1130
1131static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
1132{
1133 if (ctx->m_bUseButtonLabels) {
1134 // Use button labels instead of positions, e.g. Nintendo Online Classic controllers
1135 switch (button) {
1136 case SDL_GAMEPAD_BUTTON_SOUTH:
1137 return SDL_GAMEPAD_BUTTON_EAST;
1138 case SDL_GAMEPAD_BUTTON_EAST:
1139 return SDL_GAMEPAD_BUTTON_SOUTH;
1140 case SDL_GAMEPAD_BUTTON_WEST:
1141 return SDL_GAMEPAD_BUTTON_NORTH;
1142 case SDL_GAMEPAD_BUTTON_NORTH:
1143 return SDL_GAMEPAD_BUTTON_WEST;
1144 default:
1145 break;
1146 }
1147 }
1148 return button;
1149}
1150
1151static int GetMaxWriteAttempts(SDL_HIDAPI_Device *device)
1152{
1153 if (device->vendor_id == USB_VENDOR_NINTENDO &&
1154 device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
1155 // This device is a little slow and we know we're always on USB
1156 return 20;
1157 } else {
1158 return 5;
1159 }
1160}
1161
1162static ESwitchDeviceInfoControllerType ReadJoyConControllerType(SDL_HIDAPI_Device *device)
1163{
1164 ESwitchDeviceInfoControllerType eControllerType = k_eSwitchDeviceInfoControllerType_Unknown;
1165 const int MAX_ATTEMPTS = 1; // Don't try too long, in case this is a zombie Bluetooth controller
1166 int attempts = 0;
1167
1168 // Create enough of a context to read the controller type from the device
1169 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
1170 if (ctx) {
1171 ctx->device = device;
1172 ctx->m_bSyncWrite = true;
1173 ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);
1174
1175 for ( ; ; ) {
1176 ++attempts;
1177 if (device->is_bluetooth) {
1178 SwitchSubcommandInputPacket_t *reply = NULL;
1179
1180 if (WriteSubcommand(ctx, k_eSwitchSubcommandIDs_RequestDeviceInfo, NULL, 0, &reply)) {
1181 eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)reply->deviceInfo.ucDeviceType);
1182 }
1183 } else {
1184 if (WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Status, NULL, 0, true)) {
1185 SwitchProprietaryStatusPacket_t *status = (SwitchProprietaryStatusPacket_t *)&ctx->m_rgucReadBuffer[0];
1186
1187 eControllerType = CalculateControllerType(ctx, (ESwitchDeviceInfoControllerType)status->ucDeviceType);
1188 }
1189 }
1190 if (eControllerType == k_eSwitchDeviceInfoControllerType_Unknown && attempts < MAX_ATTEMPTS) {
1191 // Wait a bit and try again
1192 SDL_Delay(100);
1193 continue;
1194 }
1195 break;
1196 }
1197 SDL_free(ctx);
1198 }
1199 return eControllerType;
1200}
1201
1202static bool HasHomeLED(SDL_DriverSwitch_Context *ctx)
1203{
1204 Uint16 vendor_id = ctx->device->vendor_id;
1205 Uint16 product_id = ctx->device->product_id;
1206
1207 // The Power A Nintendo Switch Pro controllers don't have a Home LED
1208 if (vendor_id == 0 && product_id == 0) {
1209 return false;
1210 }
1211
1212 // HORI Wireless Switch Pad
1213 if (vendor_id == 0x0f0d && product_id == 0x00f6) {
1214 return false;
1215 }
1216
1217 // Third party controllers don't have a home LED and will shut off if we try to set it
1218 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_Unknown ||
1219 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_LicProController) {
1220 return false;
1221 }
1222
1223 // The Nintendo Online classic controllers don't have a Home LED
1224 if (vendor_id == USB_VENDOR_NINTENDO &&
1225 ctx->m_eControllerType > k_eSwitchDeviceInfoControllerType_ProController) {
1226 return false;
1227 }
1228
1229 return true;
1230}
1231
1232static bool AlwaysUsesLabels(Uint16 vendor_id, Uint16 product_id, ESwitchDeviceInfoControllerType eControllerType)
1233{
1234 // Some controllers don't have a diamond button configuration, so should always use labels
1235 if (SDL_IsJoystickGameCube(vendor_id, product_id)) {
1236 return true;
1237 }
1238 switch (eControllerType) {
1239 case k_eSwitchDeviceInfoControllerType_HVCLeft:
1240 case k_eSwitchDeviceInfoControllerType_HVCRight:
1241 case k_eSwitchDeviceInfoControllerType_NESLeft:
1242 case k_eSwitchDeviceInfoControllerType_NESRight:
1243 case k_eSwitchDeviceInfoControllerType_N64:
1244 case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
1245 return true;
1246 default:
1247 return false;
1248 }
1249}
1250
1251static void HIDAPI_DriverNintendoClassic_RegisterHints(SDL_HintCallback callback, void *userdata)
1252{
1253 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);
1254}
1255
1256static void HIDAPI_DriverNintendoClassic_UnregisterHints(SDL_HintCallback callback, void *userdata)
1257{
1258 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, callback, userdata);
1259}
1260
1261static bool HIDAPI_DriverNintendoClassic_IsEnabled(void)
1262{
1263 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
1264}
1265
1266static bool HIDAPI_DriverNintendoClassic_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)
1267{
1268 if (vendor_id == USB_VENDOR_NINTENDO) {
1269 if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
1270 if (SDL_strncmp(name, "NES Controller", 14) == 0 ||
1271 SDL_strncmp(name, "HVC Controller", 14) == 0) {
1272 return true;
1273 }
1274 }
1275
1276 if (product_id == USB_PRODUCT_NINTENDO_N64_CONTROLLER) {
1277 return true;
1278 }
1279
1280 if (product_id == USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER) {
1281 return true;
1282 }
1283
1284 if (product_id == USB_PRODUCT_NINTENDO_SNES_CONTROLLER) {
1285 return true;
1286 }
1287 }
1288
1289 return false;
1290}
1291
1292static void HIDAPI_DriverJoyCons_RegisterHints(SDL_HintCallback callback, void *userdata)
1293{
1294 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata);
1295}
1296
1297static void HIDAPI_DriverJoyCons_UnregisterHints(SDL_HintCallback callback, void *userdata)
1298{
1299 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, callback, userdata);
1300}
1301
1302static bool HIDAPI_DriverJoyCons_IsEnabled(void)
1303{
1304 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
1305}
1306
1307static bool HIDAPI_DriverJoyCons_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)
1308{
1309 if (vendor_id == USB_VENDOR_NINTENDO) {
1310 if (product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO && device && device->dev) {
1311 // This might be a Kinvoca Joy-Con that reports VID/PID as a Switch Pro controller
1312 ESwitchDeviceInfoControllerType eControllerType = ReadJoyConControllerType(device);
1313 if (eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
1314 eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1315 return true;
1316 }
1317 }
1318
1319 if (product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT ||
1320 product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT ||
1321 product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
1322 return true;
1323 }
1324 }
1325 return false;
1326}
1327
1328static void HIDAPI_DriverSwitch_RegisterHints(SDL_HintCallback callback, void *userdata)
1329{
1330 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata);
1331}
1332
1333static void HIDAPI_DriverSwitch_UnregisterHints(SDL_HintCallback callback, void *userdata)
1334{
1335 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, callback, userdata);
1336}
1337
1338static bool HIDAPI_DriverSwitch_IsEnabled(void)
1339{
1340 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT));
1341}
1342
1343static bool HIDAPI_DriverSwitch_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)
1344{
1345 /* The HORI Wireless Switch Pad enumerates as a HID device when connected via USB
1346 with the same VID/PID as when connected over Bluetooth but doesn't actually
1347 support communication over USB. The most reliable way to block this without allowing the
1348 controller to continually attempt to reconnect is to filter it out by manufacturer/product string.
1349 Note that the controller does have a different product string when connected over Bluetooth.
1350 */
1351 if (SDL_strcmp(name, "HORI Wireless Switch Pad") == 0) {
1352 return false;
1353 }
1354
1355 // If it's handled by another driver, it's not handled here
1356 if (HIDAPI_DriverNintendoClassic_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol) ||
1357 HIDAPI_DriverJoyCons_IsSupportedDevice(device, name, type, vendor_id, product_id, version, interface_number, interface_class, interface_subclass, interface_protocol)) {
1358 return false;
1359 }
1360
1361 return (type == SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO);
1362}
1363
1364static void UpdateDeviceIdentity(SDL_HIDAPI_Device *device)
1365{
1366 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1367
1368 if (ctx->m_bInputOnly) {
1369 if (SDL_IsJoystickGameCube(device->vendor_id, device->product_id)) {
1370 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1371 }
1372 } else {
1373 char serial[18];
1374
1375 switch (ctx->m_eControllerType) {
1376 case k_eSwitchDeviceInfoControllerType_JoyConLeft:
1377 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
1378 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT);
1379 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
1380 break;
1381 case k_eSwitchDeviceInfoControllerType_JoyConRight:
1382 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
1383 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT);
1384 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
1385 break;
1386 case k_eSwitchDeviceInfoControllerType_ProController:
1387 case k_eSwitchDeviceInfoControllerType_LicProController:
1388 HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
1389 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_PRO);
1390 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
1391 break;
1392 case k_eSwitchDeviceInfoControllerType_HVCLeft:
1393 HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (1)");
1394 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1395 break;
1396 case k_eSwitchDeviceInfoControllerType_HVCRight:
1397 HIDAPI_SetDeviceName(device, "Nintendo HVC Controller (2)");
1398 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1399 break;
1400 case k_eSwitchDeviceInfoControllerType_NESLeft:
1401 HIDAPI_SetDeviceName(device, "Nintendo NES Controller (L)");
1402 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1403 break;
1404 case k_eSwitchDeviceInfoControllerType_NESRight:
1405 HIDAPI_SetDeviceName(device, "Nintendo NES Controller (R)");
1406 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1407 break;
1408 case k_eSwitchDeviceInfoControllerType_SNES:
1409 HIDAPI_SetDeviceName(device, "Nintendo SNES Controller");
1410 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SNES_CONTROLLER);
1411 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1412 break;
1413 case k_eSwitchDeviceInfoControllerType_N64:
1414 HIDAPI_SetDeviceName(device, "Nintendo N64 Controller");
1415 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_N64_CONTROLLER);
1416 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1417 break;
1418 case k_eSwitchDeviceInfoControllerType_SEGA_Genesis:
1419 HIDAPI_SetDeviceName(device, "Nintendo SEGA Genesis Controller");
1420 HIDAPI_SetDeviceProduct(device, USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SEGA_GENESIS_CONTROLLER);
1421 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1422 break;
1423 case k_eSwitchDeviceInfoControllerType_Unknown:
1424 // We couldn't read the device info for this controller, might not be fully compliant
1425 if (device->vendor_id == USB_VENDOR_NINTENDO) {
1426 switch (device->product_id) {
1427 case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT:
1428 ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConLeft;
1429 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (L)");
1430 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
1431 break;
1432 case USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT:
1433 ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_JoyConRight;
1434 HIDAPI_SetDeviceName(device, "Nintendo Switch Joy-Con (R)");
1435 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
1436 break;
1437 case USB_PRODUCT_NINTENDO_SWITCH_PRO:
1438 ctx->m_eControllerType = k_eSwitchDeviceInfoControllerType_ProController;
1439 HIDAPI_SetDeviceName(device, "Nintendo Switch Pro Controller");
1440 device->type = SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO;
1441 break;
1442 default:
1443 break;
1444 }
1445 }
1446 return;
1447 default:
1448 device->type = SDL_GAMEPAD_TYPE_STANDARD;
1449 break;
1450 }
1451 device->guid.data[15] = ctx->m_eControllerType;
1452
1453 (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
1454 ctx->m_rgucMACAddress[0],
1455 ctx->m_rgucMACAddress[1],
1456 ctx->m_rgucMACAddress[2],
1457 ctx->m_rgucMACAddress[3],
1458 ctx->m_rgucMACAddress[4],
1459 ctx->m_rgucMACAddress[5]);
1460 HIDAPI_SetDeviceSerial(device, serial);
1461 }
1462}
1463
1464static bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
1465{
1466 SDL_DriverSwitch_Context *ctx;
1467
1468 ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
1469 if (!ctx) {
1470 return false;
1471 }
1472 ctx->device = device;
1473 device->context = ctx;
1474
1475 ctx->m_nMaxWriteAttempts = GetMaxWriteAttempts(device);
1476 ctx->m_bSyncWrite = true;
1477
1478 // Find out whether or not we can send output reports
1479 ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
1480 if (!ctx->m_bInputOnly) {
1481 // Initialize rumble data, important for reading device info on the MOBAPAD M073
1482 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
1483 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
1484
1485 BReadDeviceInfo(ctx);
1486 }
1487 UpdateDeviceIdentity(device);
1488
1489 // Prefer the USB device over the Bluetooth device
1490 if (device->is_bluetooth) {
1491 if (HIDAPI_HasConnectedUSBDevice(device->serial)) {
1492 return true;
1493 }
1494 } else {
1495 HIDAPI_DisconnectBluetoothDevice(device->serial);
1496 }
1497 return HIDAPI_JoystickConnected(device, NULL);
1498}
1499
1500static int HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
1501{
1502 return -1;
1503}
1504
1505static void HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
1506{
1507 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1508
1509 if (!ctx->joystick) {
1510 return;
1511 }
1512
1513 ctx->m_nPlayerIndex = player_index;
1514
1515 UpdateSlotLED(ctx);
1516}
1517
1518static bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1519{
1520 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1521
1522 SDL_AssertJoysticksLocked();
1523
1524 ctx->joystick = joystick;
1525
1526 ctx->m_bSyncWrite = true;
1527
1528 if (!ctx->m_bInputOnly) {
1529#ifdef SDL_PLATFORM_MACOS
1530 // Wait for the OS to finish its handshake with the controller
1531 SDL_Delay(250);
1532#endif
1533 GetInitialInputMode(ctx);
1534 ctx->m_nCurrentInputMode = ctx->m_nInitialInputMode;
1535
1536 // Initialize rumble data
1537 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
1538 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
1539
1540 if (!device->is_bluetooth) {
1541 if (!BTrySetupUSB(ctx)) {
1542 SDL_SetError("Couldn't setup USB mode");
1543 return false;
1544 }
1545 }
1546
1547 if (!LoadStickCalibration(ctx)) {
1548 SDL_SetError("Couldn't load stick calibration");
1549 return false;
1550 }
1551
1552 if (ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCLeft &&
1553 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_HVCRight &&
1554 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESLeft &&
1555 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_NESRight &&
1556 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SNES &&
1557 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_N64 &&
1558 ctx->m_eControllerType != k_eSwitchDeviceInfoControllerType_SEGA_Genesis) {
1559 if (LoadIMUCalibration(ctx)) {
1560 ctx->m_bSensorsSupported = true;
1561 }
1562 }
1563
1564 // Enable vibration
1565 SetVibrationEnabled(ctx, 1);
1566
1567 // Set desired input mode
1568 SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,
1569 SDL_EnhancedReportsChanged, ctx);
1570
1571 // Start sending USB reports
1572 if (!device->is_bluetooth) {
1573 // ForceUSB doesn't generate an ACK, so don't wait for a reply
1574 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false)) {
1575 SDL_SetError("Couldn't start USB reports");
1576 return false;
1577 }
1578 }
1579
1580 // Set the LED state
1581 if (HasHomeLED(ctx)) {
1582 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
1583 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1584 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED,
1585 SDL_HomeLEDHintChanged, ctx);
1586 } else {
1587 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED,
1588 SDL_HomeLEDHintChanged, ctx);
1589 }
1590 }
1591 }
1592
1593 if (AlwaysUsesLabels(device->vendor_id, device->product_id, ctx->m_eControllerType)) {
1594 ctx->m_bUseButtonLabels = true;
1595 }
1596
1597 // Initialize player index (needed for setting LEDs)
1598 ctx->m_nPlayerIndex = SDL_GetJoystickPlayerIndex(joystick);
1599 ctx->m_bPlayerLights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, true);
1600 UpdateSlotLED(ctx);
1601
1602 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
1603 SDL_PlayerLEDHintChanged, ctx);
1604
1605 // Initialize the joystick capabilities
1606 joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS;
1607 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
1608 joystick->nhats = 1;
1609
1610 // Set up for input
1611 ctx->m_bSyncWrite = false;
1612 ctx->m_ulLastIMUReset = ctx->m_ulLastInput = SDL_GetTicks();
1613 ctx->m_ulIMUUpdateIntervalNS = SDL_MS_TO_NS(5); // Start off at 5 ms update rate
1614
1615 // Set up for vertical mode
1616 ctx->m_bVerticalMode = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, false);
1617
1618 return true;
1619}
1620
1621static bool HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *ctx, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1622{
1623 /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
1624 * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
1625 *
1626 * More information about these values can be found here:
1627 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
1628 */
1629 const Uint16 k_usHighFreq = 0x0074;
1630 const Uint8 k_ucHighFreqAmp = EncodeRumbleHighAmplitude(high_frequency_rumble);
1631 const Uint8 k_ucLowFreq = 0x3D;
1632 const Uint16 k_usLowFreqAmp = EncodeRumbleLowAmplitude(low_frequency_rumble);
1633
1634 if (low_frequency_rumble || high_frequency_rumble) {
1635 EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
1636 EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
1637 } else {
1638 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
1639 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
1640 }
1641
1642 ctx->m_bRumbleActive = (low_frequency_rumble || high_frequency_rumble);
1643
1644 if (!WriteRumble(ctx)) {
1645 return SDL_SetError("Couldn't send rumble packet");
1646 }
1647 return true;
1648}
1649
1650static bool HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *ctx)
1651{
1652 if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) {
1653 return true;
1654 }
1655
1656 if (ctx->m_bRumblePending) {
1657 Uint16 low_frequency_rumble = (Uint16)(ctx->m_unRumblePending >> 16);
1658 Uint16 high_frequency_rumble = (Uint16)ctx->m_unRumblePending;
1659
1660#ifdef DEBUG_RUMBLE
1661 SDL_Log("Sent pending rumble %d/%d, %d ms after previous rumble", low_frequency_rumble, high_frequency_rumble, SDL_GetTicks() - ctx->m_ulRumbleSent);
1662#endif
1663 ctx->m_bRumblePending = false;
1664 ctx->m_unRumblePending = 0;
1665
1666 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
1667 }
1668
1669 if (ctx->m_bRumbleZeroPending) {
1670 ctx->m_bRumbleZeroPending = false;
1671
1672#ifdef DEBUG_RUMBLE
1673 SDL_Log("Sent pending zero rumble, %d ms after previous rumble", SDL_GetTicks() - ctx->m_ulRumbleSent);
1674#endif
1675 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, 0, 0);
1676 }
1677
1678 return true;
1679}
1680
1681static bool HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1682{
1683 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1684
1685 if (ctx->m_bInputOnly) {
1686 return SDL_Unsupported();
1687 }
1688
1689 if (device->parent) {
1690 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
1691 // Just handle low frequency rumble
1692 high_frequency_rumble = 0;
1693 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1694 // Just handle high frequency rumble
1695 low_frequency_rumble = 0;
1696 }
1697 }
1698
1699 if (ctx->m_bRumblePending) {
1700 if (!HIDAPI_DriverSwitch_SendPendingRumble(ctx)) {
1701 return false;
1702 }
1703 }
1704
1705 if (SDL_GetTicks() < (ctx->m_ulRumbleSent + RUMBLE_WRITE_FREQUENCY_MS)) {
1706 if (low_frequency_rumble || high_frequency_rumble) {
1707 Uint32 unRumblePending = ((Uint32)low_frequency_rumble << 16) | high_frequency_rumble;
1708
1709 // Keep the highest rumble intensity in the given interval
1710 if (unRumblePending > ctx->m_unRumblePending) {
1711 ctx->m_unRumblePending = unRumblePending;
1712 }
1713 ctx->m_bRumblePending = true;
1714 ctx->m_bRumbleZeroPending = false;
1715 } else {
1716 // When rumble is complete, turn it off
1717 ctx->m_bRumbleZeroPending = true;
1718 }
1719 return true;
1720 }
1721
1722#ifdef DEBUG_RUMBLE
1723 SDL_Log("Sent rumble %d/%d", low_frequency_rumble, high_frequency_rumble);
1724#endif
1725
1726 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
1727}
1728
1729static bool HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1730{
1731 return SDL_Unsupported();
1732}
1733
1734static Uint32 HIDAPI_DriverSwitch_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1735{
1736 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1737 Uint32 result = 0;
1738
1739 if (ctx->m_bPlayerLights && !ctx->m_bInputOnly) {
1740 result |= SDL_JOYSTICK_CAP_PLAYER_LED;
1741 }
1742
1743 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_ProController && !ctx->m_bInputOnly) {
1744 // Doesn't have an RGB LED, so don't return SDL_JOYSTICK_CAP_RGB_LED here
1745 result |= SDL_JOYSTICK_CAP_RUMBLE;
1746 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
1747 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
1748 result |= SDL_JOYSTICK_CAP_RUMBLE;
1749 }
1750 return result;
1751}
1752
1753static bool HIDAPI_DriverSwitch_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
1754{
1755 return SDL_Unsupported();
1756}
1757
1758static bool HIDAPI_DriverSwitch_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
1759{
1760 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1761
1762 if (size == sizeof(SwitchCommonOutputPacket_t)) {
1763 const SwitchCommonOutputPacket_t *packet = (SwitchCommonOutputPacket_t *)data;
1764
1765 if (packet->ucPacketType != k_eSwitchOutputReportIDs_Rumble) {
1766 return SDL_SetError("Unknown Nintendo Switch Pro effect type");
1767 }
1768
1769 SDL_copyp(&ctx->m_RumblePacket.rumbleData[0], &packet->rumbleData[0]);
1770 SDL_copyp(&ctx->m_RumblePacket.rumbleData[1], &packet->rumbleData[1]);
1771 if (!WriteRumble(ctx)) {
1772 return false;
1773 }
1774
1775 // This overwrites any internal rumble
1776 ctx->m_bRumblePending = false;
1777 ctx->m_bRumbleZeroPending = false;
1778 return true;
1779 } else if (size >= 2 && size <= 256) {
1780 const Uint8 *payload = (const Uint8 *)data;
1781 ESwitchSubcommandIDs cmd = (ESwitchSubcommandIDs)payload[0];
1782
1783 if (cmd == k_eSwitchSubcommandIDs_SetInputReportMode && !device->is_bluetooth) {
1784 // Going into simple mode over USB disables input reports, so don't do that
1785 return true;
1786 }
1787 if (cmd == k_eSwitchSubcommandIDs_SetHomeLight && !HasHomeLED(ctx)) {
1788 // Setting the home LED when it's not supported can cause the controller to reset
1789 return true;
1790 }
1791
1792 if (!WriteSubcommand(ctx, cmd, &payload[1], (Uint8)(size - 1), NULL)) {
1793 return false;
1794 }
1795 return true;
1796 }
1797 return SDL_Unsupported();
1798}
1799
1800static bool HIDAPI_DriverSwitch_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
1801{
1802 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1803
1804 UpdateEnhancedModeOnApplicationUsage(ctx);
1805
1806 if (!ctx->m_bSensorsSupported || (enabled && !ctx->m_bEnhancedMode)) {
1807 return SDL_Unsupported();
1808 }
1809
1810 ctx->m_bReportSensors = enabled;
1811 ctx->m_unIMUSamples = 0;
1812 ctx->m_ulIMUSampleTimestampNS = SDL_GetTicksNS();
1813
1814 UpdateInputMode(ctx);
1815 SetIMUEnabled(ctx, enabled);
1816
1817 return true;
1818}
1819
1820static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
1821{
1822 Sint16 axis;
1823 Uint64 timestamp = SDL_GetTicksNS();
1824
1825 if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) {
1826 Uint8 data = packet->rgucButtons[0];
1827 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x02) != 0));
1828 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x04) != 0));
1829 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));
1830 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
1831 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
1832 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
1833 }
1834
1835 if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) {
1836 Uint8 data = packet->rgucButtons[1];
1837 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
1838 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
1839 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
1840 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));
1841 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
1842 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
1843 }
1844
1845 if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
1846 Uint8 hat;
1847
1848 switch (packet->ucStickHat) {
1849 case 0:
1850 hat = SDL_HAT_UP;
1851 break;
1852 case 1:
1853 hat = SDL_HAT_RIGHTUP;
1854 break;
1855 case 2:
1856 hat = SDL_HAT_RIGHT;
1857 break;
1858 case 3:
1859 hat = SDL_HAT_RIGHTDOWN;
1860 break;
1861 case 4:
1862 hat = SDL_HAT_DOWN;
1863 break;
1864 case 5:
1865 hat = SDL_HAT_LEFTDOWN;
1866 break;
1867 case 6:
1868 hat = SDL_HAT_LEFT;
1869 break;
1870 case 7:
1871 hat = SDL_HAT_LEFTUP;
1872 break;
1873 default:
1874 hat = SDL_HAT_CENTERED;
1875 break;
1876 }
1877 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1878 }
1879
1880 axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768;
1881 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
1882
1883 axis = (packet->rgucButtons[0] & 0x80) ? 32767 : -32768;
1884 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
1885
1886 if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {
1887 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1888 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
1889 }
1890
1891 if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {
1892 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1893 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
1894 }
1895
1896 if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {
1897 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1898 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
1899 }
1900
1901 if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {
1902 axis = (Sint16)HIDAPI_RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16);
1903 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
1904 }
1905
1906 ctx->m_lastInputOnlyState = *packet;
1907}
1908
1909static void HandleCombinedSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
1910{
1911 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
1912 Uint8 data = packet->rgucButtons[0];
1913 Uint8 hat = 0;
1914
1915 if (data & 0x01) {
1916 hat |= SDL_HAT_LEFT;
1917 }
1918 if (data & 0x02) {
1919 hat |= SDL_HAT_DOWN;
1920 }
1921 if (data & 0x04) {
1922 hat |= SDL_HAT_UP;
1923 }
1924 if (data & 0x08) {
1925 hat |= SDL_HAT_RIGHT;
1926 }
1927 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
1928
1929 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x10) != 0));
1930 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x20) != 0));
1931 }
1932
1933 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
1934 Uint8 data = packet->rgucButtons[1];
1935 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
1936 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
1937 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
1938 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));
1939 }
1940
1941 Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768;
1942 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
1943
1944 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
1945 switch (packet->ucStickHat) {
1946 case 0:
1947 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
1948 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
1949 break;
1950 case 1:
1951 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
1952 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
1953 break;
1954 case 2:
1955 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
1956 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
1957 break;
1958 case 3:
1959 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
1960 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
1961 break;
1962 case 4:
1963 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
1964 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
1965 break;
1966 case 5:
1967 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
1968 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
1969 break;
1970 case 6:
1971 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
1972 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
1973 break;
1974 case 7:
1975 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
1976 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
1977 break;
1978 default:
1979 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
1980 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
1981 break;
1982 }
1983 }
1984}
1985
1986static void HandleCombinedSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
1987{
1988 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
1989 Uint8 data = packet->rgucButtons[0];
1990 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0));
1991 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));
1992 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));
1993 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x08) != 0));
1994 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x10) != 0));
1995 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x20) != 0));
1996 }
1997
1998 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
1999 Uint8 data = packet->rgucButtons[1];
2000 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2001 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));
2002 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2003 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));
2004 }
2005
2006 Sint16 axis = (packet->rgucButtons[1] & 0x80) ? 32767 : -32768;
2007 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2008
2009 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2010 switch (packet->ucStickHat) {
2011 case 0:
2012 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);
2013 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
2014 break;
2015 case 1:
2016 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);
2017 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);
2018 break;
2019 case 2:
2020 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
2021 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);
2022 break;
2023 case 3:
2024 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);
2025 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MIN);
2026 break;
2027 case 4:
2028 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);
2029 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
2030 break;
2031 case 5:
2032 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MAX);
2033 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);
2034 break;
2035 case 6:
2036 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
2037 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);
2038 break;
2039 case 7:
2040 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, SDL_JOYSTICK_AXIS_MIN);
2041 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, SDL_JOYSTICK_AXIS_MAX);
2042 break;
2043 default:
2044 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, 0);
2045 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, 0);
2046 break;
2047 }
2048 }
2049}
2050
2051static void HandleMiniSimpleControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2052{
2053 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2054 Uint8 data = packet->rgucButtons[0];
2055 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));
2056 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2057 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2058 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
2059 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
2060 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
2061 }
2062
2063 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2064 Uint8 data = packet->rgucButtons[1];
2065 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0));
2066 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
2067 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0));
2068 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0));
2069 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0));
2070 }
2071
2072 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2073 switch (packet->ucStickHat) {
2074 case 0:
2075 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2076 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2077 break;
2078 case 1:
2079 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2080 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2081 break;
2082 case 2:
2083 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2084 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2085 break;
2086 case 3:
2087 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2088 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2089 break;
2090 case 4:
2091 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2092 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2093 break;
2094 case 5:
2095 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2096 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2097 break;
2098 case 6:
2099 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2100 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2101 break;
2102 case 7:
2103 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2104 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2105 break;
2106 default:
2107 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2108 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2109 break;
2110 }
2111 }
2112}
2113
2114static void HandleMiniSimpleControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2115{
2116 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2117 Uint8 data = packet->rgucButtons[0];
2118 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));
2119 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2120 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2121 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
2122 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
2123 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
2124 }
2125
2126 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2127 Uint8 data = packet->rgucButtons[1];
2128 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2129 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2130 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2131 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2132 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0));
2133 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0));
2134 }
2135
2136 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2137 switch (packet->ucStickHat) {
2138 case 0:
2139 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2140 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2141 break;
2142 case 1:
2143 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2144 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2145 break;
2146 case 2:
2147 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2148 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2149 break;
2150 case 3:
2151 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MAX);
2152 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2153 break;
2154 case 4:
2155 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2156 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2157 break;
2158 case 5:
2159 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2160 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MAX);
2161 break;
2162 case 6:
2163 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2164 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2165 break;
2166 case 7:
2167 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, SDL_JOYSTICK_AXIS_MIN);
2168 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, SDL_JOYSTICK_AXIS_MIN);
2169 break;
2170 default:
2171 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, 0);
2172 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, 0);
2173 break;
2174 }
2175 }
2176}
2177
2178static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
2179{
2180 Uint64 timestamp = SDL_GetTicksNS();
2181
2182 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
2183 if (ctx->device->parent || ctx->m_bVerticalMode) {
2184 HandleCombinedSimpleControllerStateL(timestamp, joystick, ctx, packet);
2185 } else {
2186 HandleMiniSimpleControllerStateL(timestamp, joystick, ctx, packet);
2187 }
2188 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2189 if (ctx->device->parent || ctx->m_bVerticalMode) {
2190 HandleCombinedSimpleControllerStateR(timestamp, joystick, ctx, packet);
2191 } else {
2192 HandleMiniSimpleControllerStateR(timestamp, joystick, ctx, packet);
2193 }
2194 } else {
2195 Sint16 axis;
2196
2197 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
2198 Uint8 data = packet->rgucButtons[0];
2199 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x01) != 0));
2200 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2201 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2202 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x08) != 0));
2203 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x10) != 0));
2204 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x20) != 0));
2205 }
2206
2207 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
2208 Uint8 data = packet->rgucButtons[1];
2209 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
2210 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2211 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
2212 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x08) != 0));
2213 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2214 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2215 }
2216
2217 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
2218 Uint8 hat;
2219
2220 switch (packet->ucStickHat) {
2221 case 0:
2222 hat = SDL_HAT_UP;
2223 break;
2224 case 1:
2225 hat = SDL_HAT_RIGHTUP;
2226 break;
2227 case 2:
2228 hat = SDL_HAT_RIGHT;
2229 break;
2230 case 3:
2231 hat = SDL_HAT_RIGHTDOWN;
2232 break;
2233 case 4:
2234 hat = SDL_HAT_DOWN;
2235 break;
2236 case 5:
2237 hat = SDL_HAT_LEFTDOWN;
2238 break;
2239 case 6:
2240 hat = SDL_HAT_LEFT;
2241 break;
2242 case 7:
2243 hat = SDL_HAT_LEFTUP;
2244 break;
2245 default:
2246 hat = SDL_HAT_CENTERED;
2247 break;
2248 }
2249 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
2250 }
2251
2252 axis = (packet->rgucButtons[0] & 0x40) ? 32767 : -32768;
2253 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
2254
2255 axis = ((packet->rgucButtons[0] & 0x80) || (packet->rgucButtons[1] & 0x80)) ? 32767 : -32768;
2256 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2257
2258 axis = ApplySimpleStickCalibration(ctx, 0, 0, packet->sJoystickLeft[0]);
2259 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2260
2261 axis = ApplySimpleStickCalibration(ctx, 0, 1, packet->sJoystickLeft[1]);
2262 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
2263
2264 axis = ApplySimpleStickCalibration(ctx, 1, 0, packet->sJoystickRight[0]);
2265 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
2266
2267 axis = ApplySimpleStickCalibration(ctx, 1, 1, packet->sJoystickRight[1]);
2268 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
2269 }
2270
2271 ctx->m_lastSimpleState = *packet;
2272}
2273
2274static void SendSensorUpdate(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SDL_SensorType type, Uint64 sensor_timestamp, const Sint16 *values)
2275{
2276 float data[3];
2277
2278 /* Note the order of components has been shuffled to match PlayStation controllers,
2279 * since that's our de facto standard from already supporting those controllers, and
2280 * users will want consistent axis mappings across devices.
2281 */
2282 if (type == SDL_SENSOR_GYRO || type == SDL_SENSOR_GYRO_L || type == SDL_SENSOR_GYRO_R) {
2283 data[0] = -(ctx->m_IMUScaleData.fGyroScaleY * (float)values[1]);
2284 data[1] = ctx->m_IMUScaleData.fGyroScaleZ * (float)values[2];
2285 data[2] = -(ctx->m_IMUScaleData.fGyroScaleX * (float)values[0]);
2286 } else {
2287 data[0] = -(ctx->m_IMUScaleData.fAccelScaleY * (float)values[1]);
2288 data[1] = ctx->m_IMUScaleData.fAccelScaleZ * (float)values[2];
2289 data[2] = -(ctx->m_IMUScaleData.fAccelScaleX * (float)values[0]);
2290 }
2291
2292 // Right Joy-Con flips some axes, so let's flip them back for consistency
2293 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2294 data[0] = -data[0];
2295 data[1] = -data[1];
2296 }
2297
2298 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft &&
2299 !ctx->device->parent && !ctx->m_bVerticalMode) {
2300 // Mini-gamepad mode, swap some axes around
2301 float tmp = data[2];
2302 data[2] = -data[0];
2303 data[0] = tmp;
2304 }
2305
2306 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight &&
2307 !ctx->device->parent && !ctx->m_bVerticalMode) {
2308 // Mini-gamepad mode, swap some axes around
2309 float tmp = data[2];
2310 data[2] = data[0];
2311 data[0] = -tmp;
2312 }
2313
2314 SDL_SendJoystickSensor(timestamp, joystick, type, sensor_timestamp, data, 3);
2315}
2316
2317static void HandleCombinedControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2318{
2319 Sint16 axis;
2320
2321 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2322 Uint8 data = packet->controllerState.rgucButtons[1];
2323 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
2324 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2325 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2326 }
2327
2328 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
2329 Uint8 data = packet->controllerState.rgucButtons[2];
2330 Uint8 hat = 0;
2331
2332 if (data & 0x01) {
2333 hat |= SDL_HAT_DOWN;
2334 }
2335 if (data & 0x02) {
2336 hat |= SDL_HAT_UP;
2337 }
2338 if (data & 0x04) {
2339 hat |= SDL_HAT_RIGHT;
2340 }
2341 if (data & 0x08) {
2342 hat |= SDL_HAT_LEFT;
2343 }
2344 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
2345
2346 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x10) != 0));
2347 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x20) != 0));
2348 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));
2349 axis = (data & 0x80) ? 32767 : -32768;
2350 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
2351 }
2352
2353 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
2354 axis = ApplyStickCalibration(ctx, 0, 0, axis);
2355 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2356
2357 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
2358 axis = ApplyStickCalibration(ctx, 0, 1, axis);
2359 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
2360}
2361
2362static void HandleCombinedControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2363{
2364 Sint16 axis;
2365
2366 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
2367 Uint8 data = packet->controllerState.rgucButtons[0];
2368 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));
2369 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0));
2370 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));
2371 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));
2372 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x10) != 0));
2373 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x20) != 0));
2374 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));
2375 axis = (data & 0x80) ? 32767 : -32768;
2376 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2377 }
2378
2379 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2380 Uint8 data = packet->controllerState.rgucButtons[1];
2381 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2382 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0));
2383 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2384 }
2385
2386 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
2387 axis = ApplyStickCalibration(ctx, 1, 0, axis);
2388 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
2389
2390 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
2391 axis = ApplyStickCalibration(ctx, 1, 1, axis);
2392 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
2393}
2394
2395static void HandleMiniControllerStateL(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2396{
2397 Sint16 axis;
2398
2399 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2400 Uint8 data = packet->controllerState.rgucButtons[1];
2401 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x01) != 0));
2402 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2403 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x20) != 0));
2404 }
2405
2406 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
2407 Uint8 data = packet->controllerState.rgucButtons[2];
2408 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0));
2409 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x01) != 0));
2410 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x02) != 0));
2411 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x04) != 0));
2412 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0));
2413 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0));
2414 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE1, ((data & 0x40) != 0));
2415 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_LEFT_PADDLE2, ((data & 0x80) != 0));
2416 }
2417
2418 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
2419 axis = ApplyStickCalibration(ctx, 0, 0, axis);
2420 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
2421
2422 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
2423 axis = ApplyStickCalibration(ctx, 0, 1, axis);
2424 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, ~axis);
2425}
2426
2427static void HandleMiniControllerStateR(Uint64 timestamp, SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
2428{
2429 Sint16 axis;
2430
2431 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
2432 Uint8 data = packet->controllerState.rgucButtons[0];
2433 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x08) != 0));
2434 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x02) != 0));
2435 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x04) != 0));
2436 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x01) != 0));
2437 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x10) != 0));
2438 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x20) != 0));
2439 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE1, ((data & 0x40) != 0));
2440 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_RIGHT_PADDLE2, ((data & 0x80) != 0));
2441 }
2442
2443 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2444 Uint8 data = packet->controllerState.rgucButtons[1];
2445 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2446 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x04) != 0));
2447 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2448 }
2449
2450 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
2451 axis = ApplyStickCalibration(ctx, 1, 0, axis);
2452 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
2453
2454 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
2455 axis = ApplyStickCalibration(ctx, 1, 1, axis);
2456 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2457}
2458
2459static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock and lock the device lock to be able to change IMU state
2460{
2461 Uint64 timestamp = SDL_GetTicksNS();
2462
2463 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
2464 if (ctx->device->parent || ctx->m_bVerticalMode) {
2465 HandleCombinedControllerStateL(timestamp, joystick, ctx, packet);
2466 } else {
2467 HandleMiniControllerStateL(timestamp, joystick, ctx, packet);
2468 }
2469 } else if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2470 if (ctx->device->parent || ctx->m_bVerticalMode) {
2471 HandleCombinedControllerStateR(timestamp, joystick, ctx, packet);
2472 } else {
2473 HandleMiniControllerStateR(timestamp, joystick, ctx, packet);
2474 }
2475 } else {
2476 Sint16 axis;
2477
2478 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
2479 Uint8 data = packet->controllerState.rgucButtons[0];
2480 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_SOUTH), ((data & 0x04) != 0));
2481 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_EAST), ((data & 0x08) != 0));
2482 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_WEST), ((data & 0x01) != 0));
2483 SDL_SendJoystickButton(timestamp, joystick, RemapButton(ctx, SDL_GAMEPAD_BUTTON_NORTH), ((data & 0x02) != 0));
2484 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x40) != 0));
2485 }
2486
2487 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
2488 Uint8 data = packet->controllerState.rgucButtons[1];
2489 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x01) != 0));
2490 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x02) != 0));
2491 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x04) != 0));
2492 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x08) != 0));
2493
2494 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x10) != 0));
2495 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SWITCH_SHARE, ((data & 0x20) != 0));
2496 }
2497
2498 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
2499 Uint8 data = packet->controllerState.rgucButtons[2];
2500 Uint8 hat = 0;
2501
2502 if (data & 0x01) {
2503 hat |= SDL_HAT_DOWN;
2504 }
2505 if (data & 0x02) {
2506 hat |= SDL_HAT_UP;
2507 }
2508 if (data & 0x04) {
2509 hat |= SDL_HAT_RIGHT;
2510 }
2511 if (data & 0x08) {
2512 hat |= SDL_HAT_LEFT;
2513 }
2514 SDL_SendJoystickHat(timestamp, joystick, 0, hat);
2515
2516 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x40) != 0));
2517 }
2518
2519 axis = (packet->controllerState.rgucButtons[0] & 0x80) ? 32767 : -32768;
2520 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
2521
2522 axis = (packet->controllerState.rgucButtons[2] & 0x80) ? 32767 : -32768;
2523 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
2524
2525 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
2526 axis = ApplyStickCalibration(ctx, 0, 0, axis);
2527 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
2528
2529 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
2530 axis = ApplyStickCalibration(ctx, 0, 1, axis);
2531 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, ~axis);
2532
2533 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
2534 axis = ApplyStickCalibration(ctx, 1, 0, axis);
2535 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
2536
2537 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
2538 axis = ApplyStickCalibration(ctx, 1, 1, axis);
2539 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
2540 }
2541
2542 /* High nibble of battery/connection byte is battery level, low nibble is connection status (always 0 on 8BitDo Pro 2)
2543 * LSB of connection nibble is USB/Switch connection status
2544 * LSB of the battery nibble is used to report charging.
2545 * The battery level is reported from 0(empty)-8(full)
2546 */
2547 SDL_PowerState state;
2548 int charging = (packet->controllerState.ucBatteryAndConnection & 0x10);
2549 int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
2550 int percent = (int)SDL_roundf((level / 8.0f) * 100.0f);
2551
2552 if (charging) {
2553 if (level == 8) {
2554 state = SDL_POWERSTATE_CHARGED;
2555 } else {
2556 state = SDL_POWERSTATE_CHARGING;
2557 }
2558 } else {
2559 state = SDL_POWERSTATE_ON_BATTERY;
2560 }
2561 SDL_SendJoystickPowerInfo(joystick, state, percent);
2562
2563 if (ctx->m_bReportSensors) {
2564 bool bHasSensorData = (packet->imuState[0].sAccelZ != 0 ||
2565 packet->imuState[0].sAccelY != 0 ||
2566 packet->imuState[0].sAccelX != 0);
2567 if (bHasSensorData) {
2568 const Uint32 IMU_UPDATE_RATE_SAMPLE_FREQUENCY = 1000;
2569 Uint64 sensor_timestamp[3];
2570
2571 ctx->m_bHasSensorData = true;
2572
2573 // We got three IMU samples, calculate the IMU update rate and timestamps
2574 ctx->m_unIMUSamples += 3;
2575 if (ctx->m_unIMUSamples >= IMU_UPDATE_RATE_SAMPLE_FREQUENCY) {
2576 Uint64 now = SDL_GetTicksNS();
2577 Uint64 elapsed = (now - ctx->m_ulIMUSampleTimestampNS);
2578
2579 if (elapsed > 0) {
2580 ctx->m_ulIMUUpdateIntervalNS = elapsed / ctx->m_unIMUSamples;
2581 }
2582 ctx->m_unIMUSamples = 0;
2583 ctx->m_ulIMUSampleTimestampNS = now;
2584 }
2585
2586 ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;
2587 sensor_timestamp[0] = ctx->m_ulTimestampNS;
2588 ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;
2589 sensor_timestamp[1] = ctx->m_ulTimestampNS;
2590 ctx->m_ulTimestampNS += ctx->m_ulIMUUpdateIntervalNS;
2591 sensor_timestamp[2] = ctx->m_ulTimestampNS;
2592
2593 if (!ctx->device->parent ||
2594 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2595 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[0], &packet->imuState[2].sGyroX);
2596 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[0], &packet->imuState[2].sAccelX);
2597
2598 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[1], &packet->imuState[1].sGyroX);
2599 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[1], &packet->imuState[1].sAccelX);
2600
2601 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO, sensor_timestamp[2], &packet->imuState[0].sGyroX);
2602 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL, sensor_timestamp[2], &packet->imuState[0].sAccelX);
2603 }
2604
2605 if (ctx->device->parent &&
2606 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft) {
2607 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[0], &packet->imuState[2].sGyroX);
2608 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[0], &packet->imuState[2].sAccelX);
2609
2610 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[1], &packet->imuState[1].sGyroX);
2611 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[1], &packet->imuState[1].sAccelX);
2612
2613 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_L, sensor_timestamp[2], &packet->imuState[0].sGyroX);
2614 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_L, sensor_timestamp[2], &packet->imuState[0].sAccelX);
2615 }
2616 if (ctx->device->parent &&
2617 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2618 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[0], &packet->imuState[2].sGyroX);
2619 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[0], &packet->imuState[2].sAccelX);
2620
2621 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[1], &packet->imuState[1].sGyroX);
2622 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[1], &packet->imuState[1].sAccelX);
2623
2624 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_GYRO_R, sensor_timestamp[2], &packet->imuState[0].sGyroX);
2625 SendSensorUpdate(timestamp, joystick, ctx, SDL_SENSOR_ACCEL_R, sensor_timestamp[2], &packet->imuState[0].sAccelX);
2626 }
2627
2628 } else if (ctx->m_bHasSensorData) {
2629 // Uh oh, someone turned off the IMU?
2630 const int IMU_RESET_DELAY_MS = 3000;
2631 Uint64 now = SDL_GetTicks();
2632
2633 if (now >= (ctx->m_ulLastIMUReset + IMU_RESET_DELAY_MS)) {
2634 SDL_HIDAPI_Device *device = ctx->device;
2635
2636 if (device->updating) {
2637 SDL_UnlockMutex(device->dev_lock);
2638 }
2639
2640 SetIMUEnabled(ctx, true);
2641
2642 if (device->updating) {
2643 SDL_LockMutex(device->dev_lock);
2644 }
2645 ctx->m_ulLastIMUReset = now;
2646 }
2647
2648 } else {
2649 // We have never gotten IMU data, probably not supported on this device
2650 }
2651 }
2652
2653 ctx->m_lastFullState = *packet;
2654}
2655
2656static bool HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
2657{
2658 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
2659 SDL_Joystick *joystick = NULL;
2660 int size;
2661 int packet_count = 0;
2662 Uint64 now = SDL_GetTicks();
2663
2664 if (device->num_joysticks > 0) {
2665 joystick = SDL_GetJoystickFromID(device->joysticks[0]);
2666 }
2667
2668 while ((size = ReadInput(ctx)) > 0) {
2669#ifdef DEBUG_SWITCH_PROTOCOL
2670 HIDAPI_DumpPacket("Nintendo Switch packet: size = %d", ctx->m_rgucReadBuffer, size);
2671#endif
2672 ++packet_count;
2673 ctx->m_ulLastInput = now;
2674
2675 if (!joystick) {
2676 continue;
2677 }
2678
2679 if (ctx->m_bInputOnly) {
2680 HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);
2681 } else {
2682 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
2683 continue;
2684 }
2685
2686 ctx->m_nCurrentInputMode = ctx->m_rgucReadBuffer[0];
2687
2688 switch (ctx->m_rgucReadBuffer[0]) {
2689 case k_eSwitchInputReportIDs_SimpleControllerState:
2690 HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
2691 break;
2692 case k_eSwitchInputReportIDs_FullControllerState:
2693 case k_eSwitchInputReportIDs_FullControllerAndMcuState:
2694 // This is the extended report, we can enable sensors now in auto mode
2695 UpdateEnhancedModeOnEnhancedReport(ctx);
2696
2697 HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
2698 break;
2699 default:
2700 break;
2701 }
2702 }
2703 }
2704
2705 if (joystick) {
2706 if (packet_count == 0) {
2707 if (!ctx->m_bInputOnly && !device->is_bluetooth &&
2708 ctx->device->product_id != USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
2709 const int INPUT_WAIT_TIMEOUT_MS = 100;
2710 if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) {
2711 // Steam may have put the controller back into non-reporting mode
2712 bool wasSyncWrite = ctx->m_bSyncWrite;
2713
2714 ctx->m_bSyncWrite = true;
2715 WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, false);
2716 ctx->m_bSyncWrite = wasSyncWrite;
2717 }
2718 } else if (device->is_bluetooth &&
2719 ctx->m_nCurrentInputMode != k_eSwitchInputReportIDs_SimpleControllerState) {
2720 const int INPUT_WAIT_TIMEOUT_MS = 3000;
2721 if (now >= (ctx->m_ulLastInput + INPUT_WAIT_TIMEOUT_MS)) {
2722 // Bluetooth may have disconnected, try reopening the controller
2723 size = -1;
2724 }
2725 }
2726 }
2727
2728 if (ctx->m_bRumblePending || ctx->m_bRumbleZeroPending) {
2729 HIDAPI_DriverSwitch_SendPendingRumble(ctx);
2730 } else if (ctx->m_bRumbleActive &&
2731 now >= (ctx->m_ulRumbleSent + RUMBLE_REFRESH_FREQUENCY_MS)) {
2732#ifdef DEBUG_RUMBLE
2733 SDL_Log("Sent continuing rumble, %d ms after previous rumble", now - ctx->m_ulRumbleSent);
2734#endif
2735 WriteRumble(ctx);
2736 }
2737 }
2738
2739 // Reconnect the Bluetooth device once the USB device is gone
2740 if (device->num_joysticks == 0 && device->is_bluetooth && packet_count > 0 &&
2741 !device->parent &&
2742 !HIDAPI_HasConnectedUSBDevice(device->serial)) {
2743 HIDAPI_JoystickConnected(device, NULL);
2744 }
2745
2746 if (size < 0 && device->num_joysticks > 0) {
2747 // Read error, device is disconnected
2748 HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
2749 }
2750 return (size >= 0);
2751}
2752
2753static void HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
2754{
2755 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
2756
2757 if (!ctx->m_bInputOnly) {
2758 // Restore simple input mode for other applications
2759 if (!ctx->m_nInitialInputMode ||
2760 ctx->m_nInitialInputMode == k_eSwitchInputReportIDs_SimpleControllerState) {
2761 SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
2762 }
2763 }
2764
2765 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS,
2766 SDL_EnhancedReportsChanged, ctx);
2767
2768 if (ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConLeft ||
2769 ctx->m_eControllerType == k_eSwitchDeviceInfoControllerType_JoyConRight) {
2770 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED,
2771 SDL_HomeLEDHintChanged, ctx);
2772 } else {
2773 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED,
2774 SDL_HomeLEDHintChanged, ctx);
2775 }
2776
2777 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED,
2778 SDL_PlayerLEDHintChanged, ctx);
2779
2780 ctx->joystick = NULL;
2781
2782 ctx->m_bReportSensors = false;
2783 ctx->m_bEnhancedMode = false;
2784 ctx->m_bEnhancedModeAvailable = false;
2785}
2786
2787static void HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device)
2788{
2789}
2790
2791SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverNintendoClassic = {
2792 SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC,
2793 true,
2794 HIDAPI_DriverNintendoClassic_RegisterHints,
2795 HIDAPI_DriverNintendoClassic_UnregisterHints,
2796 HIDAPI_DriverNintendoClassic_IsEnabled,
2797 HIDAPI_DriverNintendoClassic_IsSupportedDevice,
2798 HIDAPI_DriverSwitch_InitDevice,
2799 HIDAPI_DriverSwitch_GetDevicePlayerIndex,
2800 HIDAPI_DriverSwitch_SetDevicePlayerIndex,
2801 HIDAPI_DriverSwitch_UpdateDevice,
2802 HIDAPI_DriverSwitch_OpenJoystick,
2803 HIDAPI_DriverSwitch_RumbleJoystick,
2804 HIDAPI_DriverSwitch_RumbleJoystickTriggers,
2805 HIDAPI_DriverSwitch_GetJoystickCapabilities,
2806 HIDAPI_DriverSwitch_SetJoystickLED,
2807 HIDAPI_DriverSwitch_SendJoystickEffect,
2808 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
2809 HIDAPI_DriverSwitch_CloseJoystick,
2810 HIDAPI_DriverSwitch_FreeDevice,
2811};
2812
2813SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverJoyCons = {
2814 SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS,
2815 true,
2816 HIDAPI_DriverJoyCons_RegisterHints,
2817 HIDAPI_DriverJoyCons_UnregisterHints,
2818 HIDAPI_DriverJoyCons_IsEnabled,
2819 HIDAPI_DriverJoyCons_IsSupportedDevice,
2820 HIDAPI_DriverSwitch_InitDevice,
2821 HIDAPI_DriverSwitch_GetDevicePlayerIndex,
2822 HIDAPI_DriverSwitch_SetDevicePlayerIndex,
2823 HIDAPI_DriverSwitch_UpdateDevice,
2824 HIDAPI_DriverSwitch_OpenJoystick,
2825 HIDAPI_DriverSwitch_RumbleJoystick,
2826 HIDAPI_DriverSwitch_RumbleJoystickTriggers,
2827 HIDAPI_DriverSwitch_GetJoystickCapabilities,
2828 HIDAPI_DriverSwitch_SetJoystickLED,
2829 HIDAPI_DriverSwitch_SendJoystickEffect,
2830 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
2831 HIDAPI_DriverSwitch_CloseJoystick,
2832 HIDAPI_DriverSwitch_FreeDevice,
2833};
2834
2835SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = {
2836 SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
2837 true,
2838 HIDAPI_DriverSwitch_RegisterHints,
2839 HIDAPI_DriverSwitch_UnregisterHints,
2840 HIDAPI_DriverSwitch_IsEnabled,
2841 HIDAPI_DriverSwitch_IsSupportedDevice,
2842 HIDAPI_DriverSwitch_InitDevice,
2843 HIDAPI_DriverSwitch_GetDevicePlayerIndex,
2844 HIDAPI_DriverSwitch_SetDevicePlayerIndex,
2845 HIDAPI_DriverSwitch_UpdateDevice,
2846 HIDAPI_DriverSwitch_OpenJoystick,
2847 HIDAPI_DriverSwitch_RumbleJoystick,
2848 HIDAPI_DriverSwitch_RumbleJoystickTriggers,
2849 HIDAPI_DriverSwitch_GetJoystickCapabilities,
2850 HIDAPI_DriverSwitch_SetJoystickLED,
2851 HIDAPI_DriverSwitch_SendJoystickEffect,
2852 HIDAPI_DriverSwitch_SetJoystickSensorsEnabled,
2853 HIDAPI_DriverSwitch_CloseJoystick,
2854 HIDAPI_DriverSwitch_FreeDevice,
2855};
2856
2857#endif // SDL_JOYSTICK_HIDAPI_SWITCH
2858
2859#endif // SDL_JOYSTICK_HIDAPI
2860