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