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