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