1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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.h"
29 #include "SDL_events.h"
30 #include "SDL_timer.h"
31 #include "SDL_joystick.h"
32 #include "SDL_gamecontroller.h"
33 #include "../../SDL_hints_c.h"
34 #include "../SDL_sysjoystick.h"
35 #include "SDL_hidapijoystick_c.h"
36 #include "SDL_hidapi_rumble.h"
37
38
39 #ifdef SDL_JOYSTICK_HIDAPI_SWITCH
40
41 /* Define this to get log output for rumble logic */
42 /*#define DEBUG_RUMBLE*/
43
44 /* How often you can write rumble commands to the controller in Bluetooth mode
45 If you send commands more frequently than this, you can turn off the controller.
46 */
47 #define RUMBLE_WRITE_FREQUENCY_MS 25
48
49 /* How often you have to refresh a long duration rumble to keep the motors running */
50 #define RUMBLE_REFRESH_FREQUENCY_MS 40
51
52 typedef enum {
53 k_eSwitchInputReportIDs_SubcommandReply = 0x21,
54 k_eSwitchInputReportIDs_FullControllerState = 0x30,
55 k_eSwitchInputReportIDs_SimpleControllerState = 0x3F,
56 k_eSwitchInputReportIDs_CommandAck = 0x81,
57 } ESwitchInputReportIDs;
58
59 typedef enum {
60 k_eSwitchOutputReportIDs_RumbleAndSubcommand = 0x01,
61 k_eSwitchOutputReportIDs_Rumble = 0x10,
62 k_eSwitchOutputReportIDs_Proprietary = 0x80,
63 } ESwitchOutputReportIDs;
64
65 typedef enum {
66 k_eSwitchSubcommandIDs_BluetoothManualPair = 0x01,
67 k_eSwitchSubcommandIDs_RequestDeviceInfo = 0x02,
68 k_eSwitchSubcommandIDs_SetInputReportMode = 0x03,
69 k_eSwitchSubcommandIDs_SetHCIState = 0x06,
70 k_eSwitchSubcommandIDs_SPIFlashRead = 0x10,
71 k_eSwitchSubcommandIDs_SetPlayerLights = 0x30,
72 k_eSwitchSubcommandIDs_SetHomeLight = 0x38,
73 k_eSwitchSubcommandIDs_EnableIMU = 0x40,
74 k_eSwitchSubcommandIDs_SetIMUSensitivity = 0x41,
75 k_eSwitchSubcommandIDs_EnableVibration = 0x48,
76 } ESwitchSubcommandIDs;
77
78 typedef enum {
79 k_eSwitchProprietaryCommandIDs_Handshake = 0x02,
80 k_eSwitchProprietaryCommandIDs_HighSpeed = 0x03,
81 k_eSwitchProprietaryCommandIDs_ForceUSB = 0x04,
82 k_eSwitchProprietaryCommandIDs_ClearUSB = 0x05,
83 k_eSwitchProprietaryCommandIDs_ResetMCU = 0x06,
84 } ESwitchProprietaryCommandIDs;
85
86 typedef enum {
87 k_eSwitchDeviceInfoControllerType_JoyConLeft = 0x1,
88 k_eSwitchDeviceInfoControllerType_JoyConRight = 0x2,
89 k_eSwitchDeviceInfoControllerType_ProController = 0x3,
90 } ESwitchDeviceInfoControllerType;
91
92 #define k_unSwitchOutputPacketDataLength 49
93 #define k_unSwitchMaxOutputPacketLength 64
94 #define k_unSwitchBluetoothPacketLength k_unSwitchOutputPacketDataLength
95 #define k_unSwitchUSBPacketLength k_unSwitchMaxOutputPacketLength
96
97 #define k_unSPIStickCalibrationStartOffset 0x603D
98 #define k_unSPIStickCalibrationEndOffset 0x604E
99 #define k_unSPIStickCalibrationLength (k_unSPIStickCalibrationEndOffset - k_unSPIStickCalibrationStartOffset + 1)
100
101 #pragma pack(1)
102 typedef struct
103 {
104 Uint8 rgucButtons[2];
105 Uint8 ucStickHat;
106 Uint8 rgucJoystickLeft[2];
107 Uint8 rgucJoystickRight[2];
108 } SwitchInputOnlyControllerStatePacket_t;
109
110 typedef struct
111 {
112 Uint8 rgucButtons[2];
113 Uint8 ucStickHat;
114 Sint16 sJoystickLeft[2];
115 Sint16 sJoystickRight[2];
116 } SwitchSimpleStatePacket_t;
117
118 typedef struct
119 {
120 Uint8 ucCounter;
121 Uint8 ucBatteryAndConnection;
122 Uint8 rgucButtons[3];
123 Uint8 rgucJoystickLeft[3];
124 Uint8 rgucJoystickRight[3];
125 Uint8 ucVibrationCode;
126 } SwitchControllerStatePacket_t;
127
128 typedef struct
129 {
130 SwitchControllerStatePacket_t controllerState;
131
132 struct {
133 Sint16 sAccelX;
134 Sint16 sAccelY;
135 Sint16 sAccelZ;
136
137 Sint16 sGyroX;
138 Sint16 sGyroY;
139 Sint16 sGyroZ;
140 } imuState[3];
141 } SwitchStatePacket_t;
142
143 typedef struct
144 {
145 Uint32 unAddress;
146 Uint8 ucLength;
147 } SwitchSPIOpData_t;
148
149 typedef struct
150 {
151 SwitchControllerStatePacket_t m_controllerState;
152
153 Uint8 ucSubcommandAck;
154 Uint8 ucSubcommandID;
155
156 #define k_unSubcommandDataBytes 35
157 union {
158 Uint8 rgucSubcommandData[k_unSubcommandDataBytes];
159
160 struct {
161 SwitchSPIOpData_t opData;
162 Uint8 rgucReadData[k_unSubcommandDataBytes - sizeof(SwitchSPIOpData_t)];
163 } spiReadData;
164
165 struct {
166 Uint8 rgucFirmwareVersion[2];
167 Uint8 ucDeviceType;
168 Uint8 ucFiller1;
169 Uint8 rgucMACAddress[6];
170 Uint8 ucFiller2;
171 Uint8 ucColorLocation;
172 } deviceInfo;
173 };
174 } SwitchSubcommandInputPacket_t;
175
176 typedef struct
177 {
178 Uint8 rgucData[4];
179 } SwitchRumbleData_t;
180
181 typedef struct
182 {
183 Uint8 ucPacketType;
184 Uint8 ucPacketNumber;
185 SwitchRumbleData_t rumbleData[2];
186 } SwitchCommonOutputPacket_t;
187
188 typedef struct
189 {
190 SwitchCommonOutputPacket_t commonData;
191
192 Uint8 ucSubcommandID;
193 Uint8 rgucSubcommandData[k_unSwitchOutputPacketDataLength - sizeof(SwitchCommonOutputPacket_t) - 1];
194 } SwitchSubcommandOutputPacket_t;
195
196 typedef struct
197 {
198 Uint8 ucPacketType;
199 Uint8 ucProprietaryID;
200
201 Uint8 rgucProprietaryData[k_unSwitchOutputPacketDataLength - 1 - 1];
202 } SwitchProprietaryOutputPacket_t;
203 #pragma pack()
204
205 typedef struct {
206 SDL_HIDAPI_Device *device;
207 SDL_bool m_bInputOnly;
208 SDL_bool m_bHasHomeLED;
209 SDL_bool m_bUsingBluetooth;
210 SDL_bool m_bIsGameCube;
211 SDL_bool m_bUseButtonLabels;
212 Uint8 m_nCommandNumber;
213 SwitchCommonOutputPacket_t m_RumblePacket;
214 Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
215 SDL_bool m_bRumbleActive;
216 Uint32 m_unRumbleSent;
217 SDL_bool m_bRumblePending;
218 SDL_bool m_bRumbleZeroPending;
219 Uint32 m_unRumblePending;
220
221 SwitchInputOnlyControllerStatePacket_t m_lastInputOnlyState;
222 SwitchSimpleStatePacket_t m_lastSimpleState;
223 SwitchStatePacket_t m_lastFullState;
224
225 struct StickCalibrationData {
226 struct {
227 Sint16 sCenter;
228 Sint16 sMin;
229 Sint16 sMax;
230 } axis[2];
231 } m_StickCalData[2];
232
233 struct StickExtents {
234 struct {
235 Sint16 sMin;
236 Sint16 sMax;
237 } axis[2];
238 } m_StickExtents[2];
239 } SDL_DriverSwitch_Context;
240
241
242 static SDL_bool
HasHomeLED(int vendor_id,int product_id)243 HasHomeLED(int vendor_id, int product_id)
244 {
245 /* The Power A Nintendo Switch Pro controllers don't have a Home LED */
246 if (vendor_id == 0 && product_id == 0) {
247 return SDL_FALSE;
248 }
249
250 /* HORI Wireless Switch Pad */
251 if (vendor_id == 0x0f0d && product_id == 0x00f6) {
252 return SDL_FALSE;
253 }
254
255 return SDL_TRUE;
256 }
257
258 static SDL_bool
IsGameCubeFormFactor(int vendor_id,int product_id)259 IsGameCubeFormFactor(int vendor_id, int product_id)
260 {
261 static Uint32 gamecube_formfactor[] = {
262 MAKE_VIDPID(0x0e6f, 0x0185), /* PDP Wired Fight Pad Pro for Nintendo Switch */
263 MAKE_VIDPID(0x20d6, 0xa711), /* Core (Plus) Wired Controller */
264 };
265 Uint32 id = MAKE_VIDPID(vendor_id, product_id);
266 int i;
267
268 for (i = 0; i < SDL_arraysize(gamecube_formfactor); ++i) {
269 if (id == gamecube_formfactor[i]) {
270 return SDL_TRUE;
271 }
272 }
273 return SDL_FALSE;
274 }
275
276 static SDL_bool
HIDAPI_DriverSwitch_IsSupportedDevice(const char * name,SDL_GameControllerType type,Uint16 vendor_id,Uint16 product_id,Uint16 version,int interface_number,int interface_class,int interface_subclass,int interface_protocol)277 HIDAPI_DriverSwitch_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
278 {
279 /* The HORI Wireless Switch Pad enumerates as a HID device when connected via USB
280 with the same VID/PID as when connected over Bluetooth but doesn't actually
281 support communication over USB. The most reliable way to block this without allowing the
282 controller to continually attempt to reconnect is to filter it out by manufactuer/product string.
283 Note that the controller does have a different product string when connected over Bluetooth.
284 */
285 if (SDL_strcmp( name, "HORI Wireless Switch Pad" ) == 0) {
286 return SDL_FALSE;
287 }
288 return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO);
289 }
290
291 static const char *
HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id,Uint16 product_id)292 HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
293 {
294 /* Give a user friendly name for this controller */
295 return "Nintendo Switch Pro Controller";
296 }
297
ReadInput(SDL_DriverSwitch_Context * ctx)298 static int ReadInput(SDL_DriverSwitch_Context *ctx)
299 {
300 /* Make sure we don't try to read at the same time a write is happening */
301 if (SDL_AtomicGet(&ctx->device->rumble_pending) > 0) {
302 return 0;
303 }
304
305 return hid_read_timeout(ctx->device->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
306 }
307
WriteOutput(SDL_DriverSwitch_Context * ctx,const Uint8 * data,int size)308 static int WriteOutput(SDL_DriverSwitch_Context *ctx, const Uint8 *data, int size)
309 {
310 /* Use the rumble thread for general asynchronous writes */
311 if (SDL_HIDAPI_LockRumble() < 0) {
312 return -1;
313 }
314 return SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, size);
315 }
316
ReadSubcommandReply(SDL_DriverSwitch_Context * ctx,ESwitchSubcommandIDs expectedID)317 static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
318 {
319 /* Average response time for messages is ~30ms */
320 Uint32 TimeoutMs = 100;
321 Uint32 startTicks = SDL_GetTicks();
322
323 int nRead = 0;
324 while ((nRead = ReadInput(ctx)) != -1) {
325 if (nRead > 0) {
326 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
327 SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[1];
328 if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
329 return reply;
330 }
331 }
332 } else {
333 SDL_Delay(1);
334 }
335
336 if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
337 break;
338 }
339 }
340 return NULL;
341 }
342
ReadProprietaryReply(SDL_DriverSwitch_Context * ctx,ESwitchProprietaryCommandIDs expectedID)343 static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
344 {
345 /* Average response time for messages is ~30ms */
346 Uint32 TimeoutMs = 100;
347 Uint32 startTicks = SDL_GetTicks();
348
349 int nRead = 0;
350 while ((nRead = ReadInput(ctx)) != -1) {
351 if (nRead > 0) {
352 if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[1] == expectedID) {
353 return SDL_TRUE;
354 }
355 } else {
356 SDL_Delay(1);
357 }
358
359 if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
360 break;
361 }
362 }
363 return SDL_FALSE;
364 }
365
ConstructSubcommand(SDL_DriverSwitch_Context * ctx,ESwitchSubcommandIDs ucCommandID,Uint8 * pBuf,Uint8 ucLen,SwitchSubcommandOutputPacket_t * outPacket)366 static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
367 {
368 SDL_memset(outPacket, 0, sizeof(*outPacket));
369
370 outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
371 outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
372
373 SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
374
375 outPacket->ucSubcommandID = ucCommandID;
376 SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
377
378 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
379 }
380
WritePacket(SDL_DriverSwitch_Context * ctx,void * pBuf,Uint8 ucLen)381 static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
382 {
383 Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
384 const size_t unWriteSize = ctx->m_bUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
385
386 if (ucLen > k_unSwitchOutputPacketDataLength) {
387 return SDL_FALSE;
388 }
389
390 if (ucLen < unWriteSize) {
391 SDL_memcpy(rgucBuf, pBuf, ucLen);
392 SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
393 pBuf = rgucBuf;
394 ucLen = (Uint8)unWriteSize;
395 }
396 return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
397 }
398
WriteSubcommand(SDL_DriverSwitch_Context * ctx,ESwitchSubcommandIDs ucCommandID,Uint8 * pBuf,Uint8 ucLen,SwitchSubcommandInputPacket_t ** ppReply)399 static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
400 {
401 int nRetries = 5;
402 SwitchSubcommandInputPacket_t *reply = NULL;
403
404 while (!reply && nRetries--) {
405 SwitchSubcommandOutputPacket_t commandPacket;
406 ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
407
408 if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
409 continue;
410 }
411
412 reply = ReadSubcommandReply(ctx, ucCommandID);
413 }
414
415 if (ppReply) {
416 *ppReply = reply;
417 }
418 return reply != NULL;
419 }
420
WriteProprietary(SDL_DriverSwitch_Context * ctx,ESwitchProprietaryCommandIDs ucCommand,Uint8 * pBuf,Uint8 ucLen,SDL_bool waitForReply)421 static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
422 {
423 int nRetries = 5;
424
425 while (nRetries--) {
426 SwitchProprietaryOutputPacket_t packet;
427
428 if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
429 return SDL_FALSE;
430 }
431
432 packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
433 packet.ucProprietaryID = ucCommand;
434 if (pBuf) {
435 SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
436 }
437
438 if (!WritePacket(ctx, &packet, sizeof(packet))) {
439 continue;
440 }
441
442 if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
443 return SDL_TRUE;
444 }
445 }
446 return SDL_FALSE;
447 }
448
SetNeutralRumble(SwitchRumbleData_t * pRumble)449 static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
450 {
451 pRumble->rgucData[0] = 0x00;
452 pRumble->rgucData[1] = 0x01;
453 pRumble->rgucData[2] = 0x40;
454 pRumble->rgucData[3] = 0x40;
455 }
456
EncodeRumble(SwitchRumbleData_t * pRumble,Uint16 usHighFreq,Uint8 ucHighFreqAmp,Uint8 ucLowFreq,Uint16 usLowFreqAmp)457 static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
458 {
459 if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
460 // High-band frequency and low-band amplitude are actually nine-bits each so they
461 // take a bit from the high-band amplitude and low-band frequency bytes respectively
462 pRumble->rgucData[0] = usHighFreq & 0xFF;
463 pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
464
465 pRumble->rgucData[2] = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
466 pRumble->rgucData[3] = usLowFreqAmp & 0xFF;
467
468 #ifdef DEBUG_RUMBLE
469 SDL_Log("Freq: %.2X %.2X %.2X, Amp: %.2X %.2X %.2X\n",
470 usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
471 ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
472 #endif
473 } else {
474 SetNeutralRumble(pRumble);
475 }
476 }
477
WriteRumble(SDL_DriverSwitch_Context * ctx)478 static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
479 {
480 /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
481 * to be retained for subsequent rumble or subcommand packets sent to the controller
482 */
483 ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
484 ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
485 ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
486
487 /* Refresh the rumble state periodically */
488 ctx->m_unRumbleSent = SDL_GetTicks();
489
490 return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
491 }
492
BTrySetupUSB(SDL_DriverSwitch_Context * ctx)493 static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
494 {
495 /* We have to send a connection handshake to the controller when communicating over USB
496 * before we're able to send it other commands. Luckily this command is not supported
497 * over Bluetooth, so we can use the controller's lack of response as a way to
498 * determine if the connection is over USB or Bluetooth
499 */
500 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
501 return SDL_FALSE;
502 }
503 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) {
504 /* The 8BitDo M30 and SF30 Pro don't respond to this command, but otherwise work correctly */
505 /*return SDL_FALSE;*/
506 }
507 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
508 return SDL_FALSE;
509 }
510 return SDL_TRUE;
511 }
512
SetVibrationEnabled(SDL_DriverSwitch_Context * ctx,Uint8 enabled)513 static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
514 {
515 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
516
517 }
SetInputMode(SDL_DriverSwitch_Context * ctx,Uint8 input_mode)518 static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
519 {
520 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL);
521 }
522
SetHomeLED(SDL_DriverSwitch_Context * ctx,Uint8 brightness)523 static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
524 {
525 Uint8 ucLedIntensity = 0;
526 Uint8 rgucBuffer[4];
527
528 if (brightness > 0) {
529 if (brightness < 65) {
530 ucLedIntensity = (brightness + 5) / 10;
531 } else {
532 ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
533 }
534 }
535
536 rgucBuffer[0] = (0x0 << 4) | 0x1; /* 0 mini cycles (besides first), cycle duration 8ms */
537 rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */
538 rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0; /* First cycle LED intensity, 0x0 intensity for second cycle */
539 rgucBuffer[3] = (0x0 << 4) | 0x0; /* 8ms fade transition to first cycle, 8ms first cycle LED duration */
540
541 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
542 }
543
SetSlotLED(SDL_DriverSwitch_Context * ctx,Uint8 slot)544 static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
545 {
546 Uint8 led_data = (1 << slot);
547 return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
548 }
549
LoadStickCalibration(SDL_DriverSwitch_Context * ctx,Uint8 input_mode)550 static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
551 {
552 Uint8 *pStickCal;
553 size_t stick, axis;
554 SwitchSubcommandInputPacket_t *reply = NULL;
555
556 /* Read Calibration Info */
557 SwitchSPIOpData_t readParams;
558 readParams.unAddress = k_unSPIStickCalibrationStartOffset;
559 readParams.ucLength = k_unSPIStickCalibrationLength;
560
561 if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
562 return SDL_FALSE;
563 }
564
565 /* Stick calibration values are 12-bits each and are packed by bit
566 * For whatever reason the fields are in a different order for each stick
567 * Left: X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
568 * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
569 */
570 pStickCal = reply->spiReadData.rgucReadData;
571
572 /* Left stick */
573 ctx->m_StickCalData[0].axis[0].sMax = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0]; /* X Axis max above center */
574 ctx->m_StickCalData[0].axis[1].sMax = (pStickCal[2] << 4) | (pStickCal[1] >> 4); /* Y Axis max above center */
575 ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3]; /* X Axis center */
576 ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4); /* Y Axis center */
577 ctx->m_StickCalData[0].axis[0].sMin = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6]; /* X Axis min below center */
578 ctx->m_StickCalData[0].axis[1].sMin = (pStickCal[8] << 4) | (pStickCal[7] >> 4); /* Y Axis min below center */
579
580 /* Right stick */
581 ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9]; /* X Axis center */
582 ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4); /* Y Axis center */
583 ctx->m_StickCalData[1].axis[0].sMin = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12]; /* X Axis min below center */
584 ctx->m_StickCalData[1].axis[1].sMin = (pStickCal[14] << 4) | (pStickCal[13] >> 4); /* Y Axis min below center */
585 ctx->m_StickCalData[1].axis[0].sMax = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15]; /* X Axis max above center */
586 ctx->m_StickCalData[1].axis[1].sMax = (pStickCal[17] << 4) | (pStickCal[16] >> 4); /* Y Axis max above center */
587
588 /* Filter out any values that were uninitialized (0xFFF) in the SPI read */
589 for (stick = 0; stick < 2; ++stick) {
590 for (axis = 0; axis < 2; ++axis) {
591 if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
592 ctx->m_StickCalData[stick].axis[axis].sCenter = 0;
593 }
594 if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
595 ctx->m_StickCalData[stick].axis[axis].sMax = 0;
596 }
597 if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
598 ctx->m_StickCalData[stick].axis[axis].sMin = 0;
599 }
600 }
601 }
602
603 if (input_mode == k_eSwitchInputReportIDs_SimpleControllerState) {
604 for (stick = 0; stick < 2; ++stick) {
605 for(axis = 0; axis < 2; ++axis) {
606 ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
607 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
608 }
609 }
610 } else {
611 for (stick = 0; stick < 2; ++stick) {
612 for(axis = 0; axis < 2; ++axis) {
613 ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
614 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
615 }
616 }
617 }
618 return SDL_TRUE;
619 }
620
fsel(float fComparand,float fValGE,float fLT)621 static float fsel(float fComparand, float fValGE, float fLT)
622 {
623 return fComparand >= 0 ? fValGE : fLT;
624 }
625
RemapVal(float val,float A,float B,float C,float D)626 static float RemapVal(float val, float A, float B, float C, float D)
627 {
628 if (A == B) {
629 return fsel(val - B , D , C);
630 }
631 return C + (D - C) * (val - A) / (B - A);
632 }
633
ApplyStickCalibrationCentered(SDL_DriverSwitch_Context * ctx,int nStick,int nAxis,Sint16 sRawValue,Sint16 sCenter)634 static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter)
635 {
636 sRawValue -= sCenter;
637
638 if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
639 ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
640 }
641 if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
642 ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
643 }
644
645 if (sRawValue > 0) {
646 return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16));
647 } else {
648 return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0));
649 }
650 }
651
ApplyStickCalibration(SDL_DriverSwitch_Context * ctx,int nStick,int nAxis,Sint16 sRawValue)652 static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
653 {
654 return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
655 }
656
SDL_GameControllerButtonReportingHintChanged(void * userdata,const char * name,const char * oldValue,const char * hint)657 static void SDLCALL SDL_GameControllerButtonReportingHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
658 {
659 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)userdata;
660 ctx->m_bUseButtonLabels = SDL_GetStringBoolean(hint, SDL_TRUE);
661 }
662
RemapButton(SDL_DriverSwitch_Context * ctx,Uint8 button)663 static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button)
664 {
665 if (!ctx->m_bUseButtonLabels) {
666 /* Use button positions */
667 if (ctx->m_bIsGameCube) {
668 switch (button) {
669 case SDL_CONTROLLER_BUTTON_B:
670 return SDL_CONTROLLER_BUTTON_X;
671 case SDL_CONTROLLER_BUTTON_X:
672 return SDL_CONTROLLER_BUTTON_B;
673 default:
674 break;
675 }
676 } else {
677 switch (button) {
678 case SDL_CONTROLLER_BUTTON_A:
679 return SDL_CONTROLLER_BUTTON_B;
680 case SDL_CONTROLLER_BUTTON_B:
681 return SDL_CONTROLLER_BUTTON_A;
682 case SDL_CONTROLLER_BUTTON_X:
683 return SDL_CONTROLLER_BUTTON_Y;
684 case SDL_CONTROLLER_BUTTON_Y:
685 return SDL_CONTROLLER_BUTTON_X;
686 default:
687 break;
688 }
689 }
690 }
691 return button;
692 }
693
694 static SDL_bool
HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device * device)695 HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
696 {
697 return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE);
698 }
699
700 static int
HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id)701 HIDAPI_DriverSwitch_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
702 {
703 return -1;
704 }
705
706 static void
HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device * device,SDL_JoystickID instance_id,int player_index)707 HIDAPI_DriverSwitch_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
708 {
709 }
710
711 static SDL_bool
HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)712 HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
713 {
714 SDL_DriverSwitch_Context *ctx;
715 Uint8 input_mode;
716
717 ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
718 if (!ctx) {
719 SDL_OutOfMemory();
720 goto error;
721 }
722 ctx->device = device;
723 device->context = ctx;
724
725 device->dev = hid_open_path(device->path, 0);
726 if (!device->dev) {
727 SDL_SetError("Couldn't open %s", device->path);
728 goto error;
729 }
730
731 /* Find out whether or not we can send output reports */
732 ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
733 if (!ctx->m_bInputOnly) {
734 ctx->m_bHasHomeLED = HasHomeLED(device->vendor_id, device->product_id);
735
736 /* Initialize rumble data */
737 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
738 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
739
740 /* Try setting up USB mode, and if that fails we're using Bluetooth */
741 if (!BTrySetupUSB(ctx)) {
742 ctx->m_bUsingBluetooth = SDL_TRUE;
743 }
744
745 /* Determine the desired input mode (needed before loading stick calibration) */
746 if (ctx->m_bUsingBluetooth) {
747 input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
748 } else {
749 input_mode = k_eSwitchInputReportIDs_FullControllerState;
750 }
751
752 /* The official Nintendo Switch Pro Controller supports FullControllerState over bluetooth
753 * just fine. We really should use that, or else the epowerlevel code in
754 * HandleFullControllerState is completely pointless. We need full state if we want battery
755 * level and we only care about battery level over bluetooth anyway.
756 */
757 if (device->vendor_id == USB_VENDOR_NINTENDO &&
758 device->product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO) {
759 input_mode = k_eSwitchInputReportIDs_FullControllerState;
760 }
761
762 if (!LoadStickCalibration(ctx, input_mode)) {
763 SDL_SetError("Couldn't load stick calibration");
764 goto error;
765 }
766
767 if (!SetVibrationEnabled(ctx, 1)) {
768 SDL_SetError("Couldn't enable vibration");
769 goto error;
770 }
771
772 /* Set desired input mode */
773 if (!SetInputMode(ctx, input_mode)) {
774 SDL_SetError("Couldn't set input mode");
775 goto error;
776 }
777
778 /* Start sending USB reports */
779 if (!ctx->m_bUsingBluetooth) {
780 /* ForceUSB doesn't generate an ACK, so don't wait for a reply */
781 if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
782 SDL_SetError("Couldn't start USB reports");
783 goto error;
784 }
785 }
786
787 /* Set the LED state */
788 if (ctx->m_bHasHomeLED) {
789 SetHomeLED(ctx, 100);
790 }
791 SetSlotLED(ctx, (joystick->instance_id % 4));
792 }
793
794 if (IsGameCubeFormFactor(device->vendor_id, device->product_id)) {
795 /* This is a controller shaped like a GameCube controller, with a large central A button */
796 ctx->m_bIsGameCube = SDL_TRUE;
797 }
798
799 SDL_AddHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
800 SDL_GameControllerButtonReportingHintChanged, ctx);
801
802 /* Initialize the joystick capabilities */
803 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
804 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
805 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
806
807 return SDL_TRUE;
808
809 error:
810 if (device->dev) {
811 hid_close(device->dev);
812 device->dev = NULL;
813 }
814 if (device->context) {
815 SDL_free(device->context);
816 device->context = NULL;
817 }
818 return SDL_FALSE;
819 }
820
821 static int
HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context * ctx,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)822 HIDAPI_DriverSwitch_ActuallyRumbleJoystick(SDL_DriverSwitch_Context *ctx, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
823 {
824 /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
825 * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
826 *
827 * More information about these values can be found here:
828 * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
829 */
830 const Uint16 k_usHighFreq = 0x0074;
831 const Uint8 k_ucHighFreqAmp = 0xBE;
832 const Uint8 k_ucLowFreq = 0x3D;
833 const Uint16 k_usLowFreqAmp = 0x806F;
834
835 if (low_frequency_rumble) {
836 EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
837 } else {
838 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
839 }
840
841 if (high_frequency_rumble) {
842 EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
843 } else {
844 SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
845 }
846
847 ctx->m_bRumbleActive = (low_frequency_rumble || high_frequency_rumble) ? SDL_TRUE : SDL_FALSE;
848
849 if (!WriteRumble(ctx)) {
850 SDL_SetError("Couldn't send rumble packet");
851 return -1;
852 }
853 return 0;
854 }
855
856 static int
HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context * ctx)857 HIDAPI_DriverSwitch_SendPendingRumble(SDL_DriverSwitch_Context *ctx)
858 {
859 if ((SDL_GetTicks() - ctx->m_unRumbleSent) < RUMBLE_WRITE_FREQUENCY_MS) {
860 return 0;
861 }
862
863 if (ctx->m_bRumblePending) {
864 Uint16 low_frequency_rumble = (Uint16)(ctx->m_unRumblePending >> 16);
865 Uint16 high_frequency_rumble = (Uint16)ctx->m_unRumblePending;
866
867 #ifdef DEBUG_RUMBLE
868 SDL_Log("Sent pending rumble %d/%d\n", low_frequency_rumble, high_frequency_rumble);
869 #endif
870 ctx->m_bRumblePending = SDL_FALSE;
871 ctx->m_unRumblePending = 0;
872
873 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
874 }
875
876 if (ctx->m_bRumbleZeroPending) {
877 ctx->m_bRumbleZeroPending = SDL_FALSE;
878
879 #ifdef DEBUG_RUMBLE
880 SDL_Log("Sent pending zero rumble\n");
881 #endif
882 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, 0, 0);
883 }
884
885 return 0;
886 }
887
888 static int
HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)889 HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
890 {
891 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
892
893 if (ctx->m_bRumblePending) {
894 if (HIDAPI_DriverSwitch_SendPendingRumble(ctx) < 0) {
895 return -1;
896 }
897 }
898
899 if (ctx->m_bUsingBluetooth && (SDL_GetTicks() - ctx->m_unRumbleSent) < RUMBLE_WRITE_FREQUENCY_MS) {
900 if (low_frequency_rumble || high_frequency_rumble) {
901 Uint32 unRumblePending = ((Uint32)low_frequency_rumble << 16) | high_frequency_rumble;
902
903 /* Keep the highest rumble intensity in the given interval */
904 if (unRumblePending > ctx->m_unRumblePending) {
905 ctx->m_unRumblePending = unRumblePending;
906 }
907 ctx->m_bRumblePending = SDL_TRUE;
908 ctx->m_bRumbleZeroPending = SDL_FALSE;
909 } else {
910 /* When rumble is complete, turn it off */
911 ctx->m_bRumbleZeroPending = SDL_TRUE;
912 }
913 return 0;
914 }
915
916 #ifdef DEBUG_RUMBLE
917 SDL_Log("Sent rumble %d/%d\n", low_frequency_rumble, high_frequency_rumble);
918 #endif
919
920 return HIDAPI_DriverSwitch_ActuallyRumbleJoystick(ctx, low_frequency_rumble, high_frequency_rumble);
921 }
922
HandleInputOnlyControllerState(SDL_Joystick * joystick,SDL_DriverSwitch_Context * ctx,SwitchInputOnlyControllerStatePacket_t * packet)923 static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet)
924 {
925 Sint16 axis;
926
927 if (packet->rgucButtons[0] != ctx->m_lastInputOnlyState.rgucButtons[0]) {
928 Uint8 data = packet->rgucButtons[0];
929 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
930 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
931 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
932 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
933 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
934 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
935
936 axis = (data & 0x40) ? 32767 : -32768;
937 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
938
939 axis = (data & 0x80) ? 32767 : -32768;
940 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
941 }
942
943 if (packet->rgucButtons[1] != ctx->m_lastInputOnlyState.rgucButtons[1]) {
944 Uint8 data = packet->rgucButtons[1];
945 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
946 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
947 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
948 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
949 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
950 }
951
952 if (packet->ucStickHat != ctx->m_lastInputOnlyState.ucStickHat) {
953 SDL_bool dpad_up = SDL_FALSE;
954 SDL_bool dpad_down = SDL_FALSE;
955 SDL_bool dpad_left = SDL_FALSE;
956 SDL_bool dpad_right = SDL_FALSE;
957
958 switch (packet->ucStickHat) {
959 case 0:
960 dpad_up = SDL_TRUE;
961 break;
962 case 1:
963 dpad_up = SDL_TRUE;
964 dpad_right = SDL_TRUE;
965 break;
966 case 2:
967 dpad_right = SDL_TRUE;
968 break;
969 case 3:
970 dpad_right = SDL_TRUE;
971 dpad_down = SDL_TRUE;
972 break;
973 case 4:
974 dpad_down = SDL_TRUE;
975 break;
976 case 5:
977 dpad_left = SDL_TRUE;
978 dpad_down = SDL_TRUE;
979 break;
980 case 6:
981 dpad_left = SDL_TRUE;
982 break;
983 case 7:
984 dpad_up = SDL_TRUE;
985 dpad_left = SDL_TRUE;
986 break;
987 default:
988 break;
989 }
990 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
991 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
992 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
993 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
994 }
995
996 if (packet->rgucJoystickLeft[0] != ctx->m_lastInputOnlyState.rgucJoystickLeft[0]) {
997 axis = (Sint16)(RemapVal(packet->rgucJoystickLeft[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
998 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
999 }
1000
1001 if (packet->rgucJoystickLeft[1] != ctx->m_lastInputOnlyState.rgucJoystickLeft[1]) {
1002 axis = (Sint16)(RemapVal(packet->rgucJoystickLeft[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
1003 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
1004 }
1005
1006 if (packet->rgucJoystickRight[0] != ctx->m_lastInputOnlyState.rgucJoystickRight[0]) {
1007 axis = (Sint16)(RemapVal(packet->rgucJoystickRight[0], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
1008 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
1009 }
1010
1011 if (packet->rgucJoystickRight[1] != ctx->m_lastInputOnlyState.rgucJoystickRight[1]) {
1012 axis = (Sint16)(RemapVal(packet->rgucJoystickRight[1], SDL_MIN_UINT8, SDL_MAX_UINT8, SDL_MIN_SINT16, SDL_MAX_SINT16));
1013 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
1014 }
1015
1016 ctx->m_lastInputOnlyState = *packet;
1017 }
1018
HandleSimpleControllerState(SDL_Joystick * joystick,SDL_DriverSwitch_Context * ctx,SwitchSimpleStatePacket_t * packet)1019 static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
1020 {
1021 /* 0x8000 is the neutral value for all joystick axes */
1022 const Uint16 usJoystickCenter = 0x8000;
1023 Sint16 axis;
1024
1025 if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
1026 Uint8 data = packet->rgucButtons[0];
1027 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
1028 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
1029 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
1030 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
1031 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
1032 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
1033
1034 axis = (data & 0x40) ? 32767 : -32768;
1035 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
1036
1037 axis = (data & 0x80) ? 32767 : -32768;
1038 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
1039 }
1040
1041 if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
1042 Uint8 data = packet->rgucButtons[1];
1043 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
1044 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
1045 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
1046 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
1047 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
1048 }
1049
1050 if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
1051 SDL_bool dpad_up = SDL_FALSE;
1052 SDL_bool dpad_down = SDL_FALSE;
1053 SDL_bool dpad_left = SDL_FALSE;
1054 SDL_bool dpad_right = SDL_FALSE;
1055
1056 switch (packet->ucStickHat) {
1057 case 0:
1058 dpad_up = SDL_TRUE;
1059 break;
1060 case 1:
1061 dpad_up = SDL_TRUE;
1062 dpad_right = SDL_TRUE;
1063 break;
1064 case 2:
1065 dpad_right = SDL_TRUE;
1066 break;
1067 case 3:
1068 dpad_right = SDL_TRUE;
1069 dpad_down = SDL_TRUE;
1070 break;
1071 case 4:
1072 dpad_down = SDL_TRUE;
1073 break;
1074 case 5:
1075 dpad_left = SDL_TRUE;
1076 dpad_down = SDL_TRUE;
1077 break;
1078 case 6:
1079 dpad_left = SDL_TRUE;
1080 break;
1081 case 7:
1082 dpad_up = SDL_TRUE;
1083 dpad_left = SDL_TRUE;
1084 break;
1085 default:
1086 break;
1087 }
1088 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
1089 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
1090 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
1091 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
1092 }
1093
1094 axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter);
1095 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
1096
1097 axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter);
1098 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
1099
1100 axis = ApplyStickCalibrationCentered(ctx, 1, 0, packet->sJoystickRight[0], (Sint16)usJoystickCenter);
1101 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
1102
1103 axis = ApplyStickCalibrationCentered(ctx, 1, 1, packet->sJoystickRight[1], (Sint16)usJoystickCenter);
1104 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
1105
1106 ctx->m_lastSimpleState = *packet;
1107 }
1108
HandleFullControllerState(SDL_Joystick * joystick,SDL_DriverSwitch_Context * ctx,SwitchStatePacket_t * packet)1109 static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
1110 {
1111 Sint16 axis;
1112
1113 if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
1114 Uint8 data = packet->controllerState.rgucButtons[0];
1115 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_A), (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
1116 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_B), (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
1117 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_X), (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
1118 SDL_PrivateJoystickButton(joystick, RemapButton(ctx, SDL_CONTROLLER_BUTTON_Y), (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
1119 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
1120 axis = (data & 0x80) ? 32767 : -32768;
1121 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
1122 }
1123
1124 if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
1125 Uint8 data = packet->controllerState.rgucButtons[1];
1126 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
1127 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
1128 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
1129 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
1130
1131 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
1132 }
1133
1134 if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
1135 Uint8 data = packet->controllerState.rgucButtons[2];
1136 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
1137 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
1138 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
1139 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
1140 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
1141 axis = (data & 0x80) ? 32767 : -32768;
1142 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
1143 }
1144
1145 axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
1146 axis = ApplyStickCalibration(ctx, 0, 0, axis);
1147 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
1148
1149 axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
1150 axis = ApplyStickCalibration(ctx, 0, 1, axis);
1151 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
1152
1153 axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
1154 axis = ApplyStickCalibration(ctx, 1, 0, axis);
1155 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
1156
1157 axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
1158 axis = ApplyStickCalibration(ctx, 1, 1, axis);
1159 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
1160
1161 /* High nibble of battery/connection byte is battery level, low nibble is connection status
1162 * LSB of connection nibble is USB/Switch connection status
1163 */
1164 if (packet->controllerState.ucBatteryAndConnection & 0x1) {
1165 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
1166 } else {
1167 /* LSB of the battery nibble is used to report charging.
1168 * The battery level is reported from 0(empty)-8(full)
1169 */
1170 int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
1171 if (level == 0) {
1172 joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
1173 } else if (level <= 2) {
1174 joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
1175 } else if (level <= 6) {
1176 joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
1177 } else {
1178 joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
1179 }
1180 }
1181
1182 ctx->m_lastFullState = *packet;
1183 }
1184
1185 static SDL_bool
HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device * device)1186 HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
1187 {
1188 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1189 SDL_Joystick *joystick = NULL;
1190 int size;
1191
1192 if (device->num_joysticks > 0) {
1193 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
1194 }
1195 if (!joystick) {
1196 return SDL_FALSE;
1197 }
1198
1199 while ((size = ReadInput(ctx)) > 0) {
1200 if (ctx->m_bInputOnly) {
1201 HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);
1202 } else {
1203 switch (ctx->m_rgucReadBuffer[0]) {
1204 case k_eSwitchInputReportIDs_SimpleControllerState:
1205 HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
1206 break;
1207 case k_eSwitchInputReportIDs_FullControllerState:
1208 HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
1209 break;
1210 default:
1211 break;
1212 }
1213 }
1214 }
1215
1216 if (ctx->m_bRumblePending || ctx->m_bRumbleZeroPending) {
1217 HIDAPI_DriverSwitch_SendPendingRumble(ctx);
1218 } else if (ctx->m_bRumbleActive &&
1219 SDL_TICKS_PASSED(SDL_GetTicks(), ctx->m_unRumbleSent + RUMBLE_REFRESH_FREQUENCY_MS)) {
1220 #ifdef DEBUG_RUMBLE
1221 SDL_Log("Sent continuing rumble\n");
1222 #endif
1223 WriteRumble(ctx);
1224 }
1225
1226 if (size < 0) {
1227 /* Read error, device is disconnected */
1228 HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE);
1229 }
1230 return (size >= 0);
1231 }
1232
1233 static void
HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device * device,SDL_Joystick * joystick)1234 HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
1235 {
1236 SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
1237
1238 if (!ctx->m_bInputOnly) {
1239 /* Restore simple input mode for other applications */
1240 SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
1241 }
1242
1243 SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
1244 SDL_GameControllerButtonReportingHintChanged, ctx);
1245
1246 hid_close(device->dev);
1247 device->dev = NULL;
1248
1249 SDL_free(device->context);
1250 device->context = NULL;
1251 }
1252
1253 static void
HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device * device)1254 HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device)
1255 {
1256 }
1257
1258 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
1259 {
1260 SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
1261 SDL_TRUE,
1262 HIDAPI_DriverSwitch_IsSupportedDevice,
1263 HIDAPI_DriverSwitch_GetDeviceName,
1264 HIDAPI_DriverSwitch_InitDevice,
1265 HIDAPI_DriverSwitch_GetDevicePlayerIndex,
1266 HIDAPI_DriverSwitch_SetDevicePlayerIndex,
1267 HIDAPI_DriverSwitch_UpdateDevice,
1268 HIDAPI_DriverSwitch_OpenJoystick,
1269 HIDAPI_DriverSwitch_RumbleJoystick,
1270 HIDAPI_DriverSwitch_CloseJoystick,
1271 HIDAPI_DriverSwitch_FreeDevice,
1272 NULL
1273 };
1274
1275 #endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
1276
1277 #endif /* SDL_JOYSTICK_HIDAPI */
1278
1279 /* vi: set ts=4 sw=4 expandtab: */
1280