src/joystick/hidapi/SDL_hidapi_switch.c
author Ethan Lee <flibitijibibo@flibitijibibo.com>
Tue, 12 Mar 2019 20:27:54 -0400
changeset 12641 64597a7e8771
parent 12503 806492103856
child 12787 0411f841b035
permissions -rw-r--r--
hidapi: Add support for Wii U/Switch USB GameCube controller adapter.

Note that a single USB device is responsible for all 4 joysticks, so a large
rewrite of the DeviceDriver functions was necessary to allow a single device to
produce multiple joysticks.
     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     SDL_JoystickID joystickID;
   187     hid_device *dev;
   188     SDL_bool m_bIsUsingBluetooth;
   189     Uint8 m_nCommandNumber;
   190     SwitchCommonOutputPacket_t m_RumblePacket;
   191     Uint32 m_nRumbleExpiration;
   192     Uint8 m_rgucReadBuffer[k_unSwitchMaxOutputPacketLength];
   193     SwitchSimpleStatePacket_t m_lastSimpleState;
   194     SwitchStatePacket_t m_lastFullState;
   195 
   196     struct StickCalibrationData {
   197         struct {
   198             Sint16 sCenter;
   199             Sint16 sMin;
   200             Sint16 sMax;
   201         } axis[2];
   202     } m_StickCalData[2];
   203 
   204     struct StickExtents {
   205         struct {
   206             Sint16 sMin;
   207             Sint16 sMax;
   208         } axis[2];
   209     } m_StickExtents[2];
   210 } SDL_DriverSwitch_Context;
   211 
   212 
   213 static SDL_bool
   214 HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
   215 {
   216     return SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id);
   217 }
   218 
   219 static const char *
   220 HIDAPI_DriverSwitch_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
   221 {
   222     /* Give a user friendly name for this controller */
   223     if (SDL_IsJoystickNintendoSwitchPro(vendor_id, product_id)) {
   224         return "Nintendo Switch Pro Controller";
   225     }
   226     return NULL;
   227 }
   228 
   229 static int ReadInput(SDL_DriverSwitch_Context *ctx)
   230 {
   231     return hid_read_timeout(ctx->dev, ctx->m_rgucReadBuffer, sizeof(ctx->m_rgucReadBuffer), 0);
   232 }
   233 
   234 static int WriteOutput(SDL_DriverSwitch_Context *ctx, Uint8 *data, int size)
   235 {
   236     return hid_write(ctx->dev, data, size);
   237 }
   238 
   239 static SwitchSubcommandInputPacket_t *ReadSubcommandReply(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs expectedID)
   240 {
   241     /* Average response time for messages is ~30ms */
   242     Uint32 TimeoutMs = 100;
   243     Uint32 startTicks = SDL_GetTicks();
   244 
   245     int nRead = 0;
   246     while ((nRead = ReadInput(ctx)) != -1) {
   247         if (nRead > 0) {
   248             if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_SubcommandReply) {
   249                 SwitchSubcommandInputPacket_t *reply = (SwitchSubcommandInputPacket_t *)&ctx->m_rgucReadBuffer[ 1 ];
   250                 if (reply->ucSubcommandID == expectedID && (reply->ucSubcommandAck & 0x80)) {
   251                     return reply;
   252                 }
   253             }
   254         } else {
   255             SDL_Delay(1);
   256         }
   257 
   258         if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
   259             break;
   260         }
   261     }
   262     return NULL;
   263 }
   264 
   265 static SDL_bool ReadProprietaryReply(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs expectedID)
   266 {
   267     /* Average response time for messages is ~30ms */
   268     Uint32 TimeoutMs = 100;
   269     Uint32 startTicks = SDL_GetTicks();
   270 
   271     int nRead = 0;
   272     while ((nRead = ReadInput(ctx)) != -1) {
   273         if (nRead > 0) {
   274             if (ctx->m_rgucReadBuffer[0] == k_eSwitchInputReportIDs_CommandAck && ctx->m_rgucReadBuffer[ 1 ] == expectedID) {
   275                 return SDL_TRUE;
   276             }
   277         } else {
   278             SDL_Delay(1);
   279         }
   280 
   281         if (SDL_TICKS_PASSED(SDL_GetTicks(), startTicks + TimeoutMs)) {
   282             break;
   283         }
   284     }
   285     return SDL_FALSE;
   286 }
   287 
   288 static void ConstructSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandOutputPacket_t *outPacket)
   289 {
   290     SDL_memset(outPacket, 0, sizeof(*outPacket));
   291 
   292     outPacket->commonData.ucPacketType = k_eSwitchOutputReportIDs_RumbleAndSubcommand;
   293     outPacket->commonData.ucPacketNumber = ctx->m_nCommandNumber;
   294 
   295     SDL_memcpy(&outPacket->commonData.rumbleData, &ctx->m_RumblePacket.rumbleData, sizeof(ctx->m_RumblePacket.rumbleData));
   296 
   297     outPacket->ucSubcommandID = ucCommandID;
   298     SDL_memcpy(outPacket->rgucSubcommandData, pBuf, ucLen);
   299 
   300     ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
   301 }
   302 
   303 static SDL_bool WritePacket(SDL_DriverSwitch_Context *ctx, void *pBuf, Uint8 ucLen)
   304 {
   305     Uint8 rgucBuf[k_unSwitchMaxOutputPacketLength];
   306     const size_t unWriteSize = ctx->m_bIsUsingBluetooth ? k_unSwitchBluetoothPacketLength : k_unSwitchUSBPacketLength;
   307 
   308     if (ucLen > k_unSwitchOutputPacketDataLength) {
   309         return SDL_FALSE;
   310     }
   311 
   312     if (ucLen < unWriteSize) {
   313         SDL_memcpy(rgucBuf, pBuf, ucLen);
   314         SDL_memset(rgucBuf+ucLen, 0, unWriteSize-ucLen);
   315         pBuf = rgucBuf;
   316         ucLen = (Uint8)unWriteSize;
   317     }
   318     return (WriteOutput(ctx, (Uint8 *)pBuf, ucLen) >= 0);
   319 }
   320 
   321 static SDL_bool WriteSubcommand(SDL_DriverSwitch_Context *ctx, ESwitchSubcommandIDs ucCommandID, Uint8 *pBuf, Uint8 ucLen, SwitchSubcommandInputPacket_t **ppReply)
   322 {
   323     int nRetries = 5;
   324     SwitchSubcommandInputPacket_t *reply = NULL;
   325 
   326     while (!reply && nRetries--) {
   327         SwitchSubcommandOutputPacket_t commandPacket;
   328         ConstructSubcommand(ctx, ucCommandID, pBuf, ucLen, &commandPacket);
   329 
   330         if (!WritePacket(ctx, &commandPacket, sizeof(commandPacket))) {
   331             continue;
   332         }
   333 
   334         reply = ReadSubcommandReply(ctx, ucCommandID);
   335     }
   336 
   337     if (ppReply) {
   338         *ppReply = reply;
   339     }
   340     return reply != NULL;
   341 }
   342 
   343 static SDL_bool WriteProprietary(SDL_DriverSwitch_Context *ctx, ESwitchProprietaryCommandIDs ucCommand, Uint8 *pBuf, Uint8 ucLen, SDL_bool waitForReply)
   344 {
   345     int nRetries = 5;
   346 
   347     while (nRetries--) {
   348         SwitchProprietaryOutputPacket_t packet;
   349 
   350         if ((!pBuf && ucLen > 0) || ucLen > sizeof(packet.rgucProprietaryData)) {
   351             return SDL_FALSE;
   352         }
   353 
   354         packet.ucPacketType = k_eSwitchOutputReportIDs_Proprietary;
   355         packet.ucProprietaryID = ucCommand;
   356         SDL_memcpy(packet.rgucProprietaryData, pBuf, ucLen);
   357 
   358         if (!WritePacket(ctx, &packet, sizeof(packet))) {
   359             continue;
   360         }
   361 
   362         if (!waitForReply || ReadProprietaryReply(ctx, ucCommand)) {
   363             return SDL_TRUE;
   364         }
   365     }
   366     return SDL_FALSE;
   367 }
   368 
   369 static void SetNeutralRumble(SwitchRumbleData_t *pRumble)
   370 {
   371     pRumble->rgucData[0] = 0x00;
   372     pRumble->rgucData[1] = 0x01;
   373     pRumble->rgucData[2] = 0x40;
   374     pRumble->rgucData[3] = 0x40;
   375 }
   376 
   377 static void EncodeRumble(SwitchRumbleData_t *pRumble, Uint16 usHighFreq, Uint8 ucHighFreqAmp, Uint8 ucLowFreq, Uint16 usLowFreqAmp)
   378 {
   379     if (ucHighFreqAmp > 0 || usLowFreqAmp > 0) {
   380         // High-band frequency and low-band amplitude are actually nine-bits each so they
   381         // take a bit from the high-band amplitude and low-band frequency bytes respectively
   382         pRumble->rgucData[0] = usHighFreq & 0xFF;
   383         pRumble->rgucData[1] = ucHighFreqAmp | ((usHighFreq >> 8) & 0x01);
   384 
   385         pRumble->rgucData[2]  = ucLowFreq | ((usLowFreqAmp >> 8) & 0x80);
   386         pRumble->rgucData[3]  = usLowFreqAmp & 0xFF;
   387 
   388 #ifdef DEBUG_RUMBLE
   389         SDL_Log("Freq: %.2X %.2X  %.2X, Amp: %.2X  %.2X %.2X\n",
   390             usHighFreq & 0xFF, ((usHighFreq >> 8) & 0x01), ucLowFreq,
   391             ucHighFreqAmp, ((usLowFreqAmp >> 8) & 0x80), usLowFreqAmp & 0xFF);
   392 #endif
   393     } else {
   394         SetNeutralRumble(pRumble);
   395     }
   396 }
   397 
   398 static SDL_bool WriteRumble(SDL_DriverSwitch_Context *ctx)
   399 {
   400     /* Write into m_RumblePacket rather than a temporary buffer to allow the current rumble state
   401      * to be retained for subsequent rumble or subcommand packets sent to the controller
   402      */
   403     ctx->m_RumblePacket.ucPacketType = k_eSwitchOutputReportIDs_Rumble;
   404     ctx->m_RumblePacket.ucPacketNumber = ctx->m_nCommandNumber;
   405     ctx->m_nCommandNumber = (ctx->m_nCommandNumber + 1) & 0xF;
   406 
   407     return WritePacket(ctx, (Uint8 *)&ctx->m_RumblePacket, sizeof(ctx->m_RumblePacket));
   408 }
   409 
   410 static SDL_bool BTrySetupUSB(SDL_DriverSwitch_Context *ctx)
   411 {
   412     /* We have to send a connection handshake to the controller when communicating over USB
   413      * before we're able to send it other commands. Luckily this command is not supported
   414      * over Bluetooth, so we can use the controller's lack of response as a way to
   415      * determine if the connection is over USB or Bluetooth
   416      */
   417     if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
   418         return SDL_FALSE;
   419     }
   420     if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_HighSpeed, NULL, 0, SDL_TRUE)) {
   421         return SDL_FALSE;
   422     }
   423     if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_Handshake, NULL, 0, SDL_TRUE)) {
   424         return SDL_FALSE;
   425     }
   426     return SDL_TRUE;
   427 }
   428 
   429 static SDL_bool SetVibrationEnabled(SDL_DriverSwitch_Context *ctx, Uint8 enabled)
   430 {
   431     return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_EnableVibration, &enabled, sizeof(enabled), NULL);
   432 
   433 }
   434 static SDL_bool SetInputMode(SDL_DriverSwitch_Context *ctx, Uint8 input_mode)
   435 {
   436     return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetInputReportMode, &input_mode, 1, NULL);
   437 }
   438 
   439 static SDL_bool SetHomeLED(SDL_DriverSwitch_Context *ctx, Uint8 brightness)
   440 {
   441     Uint8 ucLedIntensity = 0;
   442     Uint8 rgucBuffer[4];
   443 
   444     if (brightness > 0) {
   445         if (brightness < 65) {
   446             ucLedIntensity = (brightness + 5) / 10;
   447         } else {
   448             ucLedIntensity = (Uint8)SDL_ceilf(0xF * SDL_powf((float)brightness / 100.f, 2.13f));
   449         }
   450     }
   451 
   452     rgucBuffer[0] = (0x0 << 4) | 0x1;  /* 0 mini cycles (besides first), cycle duration 8ms */
   453     rgucBuffer[1] = ((ucLedIntensity & 0xF) << 4) | 0x0;  /* LED start intensity (0x0-0xF), 0 cycles (LED stays on at start intensity after first cycle) */
   454     rgucBuffer[2] = ((ucLedIntensity & 0xF) << 4) | 0x0;  /* First cycle LED intensity, 0x0 intensity for second cycle */
   455     rgucBuffer[3] = (0x0 << 4) | 0x0;  /* 8ms fade transition to first cycle, 8ms first cycle LED duration */
   456 
   457     return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetHomeLight, rgucBuffer, sizeof(rgucBuffer), NULL);
   458 }
   459 
   460 static SDL_bool SetSlotLED(SDL_DriverSwitch_Context *ctx, Uint8 slot)
   461 {
   462     Uint8 led_data = (1 << slot);
   463     return WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SetPlayerLights, &led_data, sizeof(led_data), NULL);
   464 }
   465 
   466 static SDL_bool LoadStickCalibration(SDL_DriverSwitch_Context *ctx)
   467 {
   468     Uint8 *pStickCal;
   469     size_t stick, axis;
   470     SwitchSubcommandInputPacket_t *reply = NULL;
   471 
   472     /* Read Calibration Info */
   473     SwitchSPIOpData_t readParams;
   474     readParams.unAddress = k_unSPIStickCalibrationStartOffset;
   475     readParams.ucLength = k_unSPIStickCalibrationLength;
   476 
   477     if (!WriteSubcommand(ctx, k_eSwitchSubcommandIDs_SPIFlashRead, (uint8_t *)&readParams, sizeof(readParams), &reply)) {
   478         return SDL_FALSE;
   479     }
   480 
   481     /* Stick calibration values are 12-bits each and are packed by bit
   482      * For whatever reason the fields are in a different order for each stick
   483      * Left:  X-Max, Y-Max, X-Center, Y-Center, X-Min, Y-Min
   484      * Right: X-Center, Y-Center, X-Min, Y-Min, X-Max, Y-Max
   485      */
   486     pStickCal = reply->spiReadData.rgucReadData;
   487 
   488     /* Left stick */
   489     ctx->m_StickCalData[0].axis[0].sMax    = ((pStickCal[1] << 8) & 0xF00) | pStickCal[0];     /* X Axis max above center */
   490     ctx->m_StickCalData[0].axis[1].sMax    = (pStickCal[2] << 4) | (pStickCal[1] >> 4);         /* Y Axis max above center */
   491     ctx->m_StickCalData[0].axis[0].sCenter = ((pStickCal[4] << 8) & 0xF00) | pStickCal[3];     /* X Axis center */
   492     ctx->m_StickCalData[0].axis[1].sCenter = (pStickCal[5] << 4) | (pStickCal[4] >> 4);        /* Y Axis center */
   493     ctx->m_StickCalData[0].axis[0].sMin    = ((pStickCal[7] << 8) & 0xF00) | pStickCal[6];      /* X Axis min below center */
   494     ctx->m_StickCalData[0].axis[1].sMin    = (pStickCal[8] << 4) | (pStickCal[7] >> 4);        /* Y Axis min below center */
   495 
   496     /* Right stick */
   497     ctx->m_StickCalData[1].axis[0].sCenter = ((pStickCal[10] << 8) & 0xF00) | pStickCal[9];     /* X Axis center */
   498     ctx->m_StickCalData[1].axis[1].sCenter = (pStickCal[11] << 4) | (pStickCal[10] >> 4);      /* Y Axis center */
   499     ctx->m_StickCalData[1].axis[0].sMin    = ((pStickCal[13] << 8) & 0xF00) | pStickCal[12];    /* X Axis min below center */
   500     ctx->m_StickCalData[1].axis[1].sMin    = (pStickCal[14] << 4) | (pStickCal[13] >> 4);      /* Y Axis min below center */
   501     ctx->m_StickCalData[1].axis[0].sMax    = ((pStickCal[16] << 8) & 0xF00) | pStickCal[15];    /* X Axis max above center */
   502     ctx->m_StickCalData[1].axis[1].sMax    = (pStickCal[17] << 4) | (pStickCal[16] >> 4);      /* Y Axis max above center */
   503 
   504     /* Filter out any values that were uninitialized (0xFFF) in the SPI read */
   505     for (stick = 0; stick < 2; ++stick) {
   506         for (axis = 0; axis < 2; ++axis) {
   507             if (ctx->m_StickCalData[stick].axis[axis].sCenter == 0xFFF) {
   508                 ctx->m_StickCalData[stick].axis[axis].sCenter = 0;
   509             }
   510             if (ctx->m_StickCalData[stick].axis[axis].sMax == 0xFFF) {
   511                 ctx->m_StickCalData[stick].axis[axis].sMax = 0;
   512             }
   513             if (ctx->m_StickCalData[stick].axis[axis].sMin == 0xFFF) {
   514                 ctx->m_StickCalData[stick].axis[axis].sMin = 0;
   515             }
   516         }
   517     }
   518 
   519     if (ctx->m_bIsUsingBluetooth) {
   520         for (stick = 0; stick < 2; ++stick) {
   521             for(axis = 0; axis < 2; ++axis) {
   522                 ctx->m_StickExtents[stick].axis[axis].sMin = (Sint16)(SDL_MIN_SINT16 * 0.5f);
   523                 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(SDL_MAX_SINT16 * 0.5f);
   524             }
   525         }
   526     } else {
   527         for (stick = 0; stick < 2; ++stick) {
   528             for(axis = 0; axis < 2; ++axis) {
   529                 ctx->m_StickExtents[stick].axis[axis].sMin = -(Sint16)(ctx->m_StickCalData[stick].axis[axis].sMin * 0.7f);
   530                 ctx->m_StickExtents[stick].axis[axis].sMax = (Sint16)(ctx->m_StickCalData[stick].axis[axis].sMax * 0.7f);
   531             }
   532         }
   533     }
   534     return SDL_TRUE;
   535 }
   536 
   537 static float fsel(float fComparand, float fValGE, float fLT)
   538 {
   539     return fComparand >= 0 ? fValGE : fLT;
   540 }
   541 
   542 static float RemapVal(float val, float A, float B, float C, float D)
   543 {
   544     if (A == B) {
   545         return fsel(val - B , D , C);
   546     }
   547     return C + (D - C) * (val - A) / (B - A);
   548 }
   549 
   550 static Sint16 ApplyStickCalibrationCentered(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue, Sint16 sCenter)
   551 {
   552     sRawValue -= sCenter;
   553 
   554     if (sRawValue > ctx->m_StickExtents[nStick].axis[nAxis].sMax) {
   555         ctx->m_StickExtents[nStick].axis[nAxis].sMax = sRawValue;
   556     }
   557     if (sRawValue < ctx->m_StickExtents[nStick].axis[nAxis].sMin) {
   558         ctx->m_StickExtents[nStick].axis[nAxis].sMin = sRawValue;
   559     }
   560 
   561     if (sRawValue > 0) {
   562         return (Sint16)(RemapVal(sRawValue, 0, ctx->m_StickExtents[nStick].axis[nAxis].sMax, 0, SDL_MAX_SINT16));
   563     } else {
   564         return (Sint16)(RemapVal(sRawValue, ctx->m_StickExtents[nStick].axis[nAxis].sMin, 0, SDL_MIN_SINT16, 0));
   565     }
   566 }
   567 
   568 static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, int nAxis, Sint16 sRawValue)
   569 {
   570     return ApplyStickCalibrationCentered(ctx, nStick, nAxis, sRawValue, ctx->m_StickCalData[nStick].axis[nAxis].sCenter);
   571 }
   572 
   573 static SDL_bool
   574 HIDAPI_DriverSwitch_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks)
   575 {
   576     SDL_DriverSwitch_Context *ctx;
   577     Uint8 input_mode;
   578 
   579     ctx = (SDL_DriverSwitch_Context *)SDL_calloc(1, sizeof(*ctx));
   580     if (!ctx) {
   581         SDL_OutOfMemory();
   582         return SDL_FALSE;
   583     }
   584     ctx->dev = context->device;
   585 
   586     context->context = ctx;
   587 
   588     /* Initialize rumble data */
   589     SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
   590     SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
   591 
   592     /* Try setting up USB mode, and if that fails we're using Bluetooth */
   593     if (!BTrySetupUSB(ctx)) {
   594         ctx->m_bIsUsingBluetooth = SDL_TRUE;
   595     }
   596 
   597     if (!LoadStickCalibration(ctx)) {
   598         SDL_SetError("Couldn't load stick calibration");
   599         SDL_free(ctx);
   600         return SDL_FALSE;
   601     }
   602 
   603     if (!SetVibrationEnabled(ctx, 1)) {
   604         SDL_SetError("Couldn't enable vibration");
   605         SDL_free(ctx);
   606         return SDL_FALSE;
   607     }
   608 
   609     /* Set the desired input mode */
   610     if (ctx->m_bIsUsingBluetooth) {
   611         input_mode = k_eSwitchInputReportIDs_SimpleControllerState;
   612     } else {
   613         input_mode = k_eSwitchInputReportIDs_FullControllerState;
   614     }
   615     if (!SetInputMode(ctx, input_mode)) {
   616         SDL_SetError("Couldn't set input mode");
   617         SDL_free(ctx);
   618         return SDL_FALSE;
   619     }
   620 
   621     /* Start sending USB reports */
   622     if (!ctx->m_bIsUsingBluetooth) {
   623         /* ForceUSB doesn't generate an ACK, so don't wait for a reply */
   624         if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
   625             SDL_SetError("Couldn't start USB reports");
   626             SDL_free(ctx);
   627             return SDL_FALSE;
   628         }
   629     }
   630 
   631     ctx->joystickID = SDL_GetNextJoystickInstanceID();
   632     *num_joysticks += 1;
   633     SDL_PrivateJoystickAdded(ctx->joystickID);
   634 
   635     return SDL_TRUE;
   636 }
   637 
   638 static SDL_bool
   639 HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick)
   640 {
   641     SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context;
   642 
   643     /* Set the LED state */
   644     SetHomeLED(ctx, 100);
   645     SetSlotLED(ctx, (joystick->instance_id % 4));
   646 
   647     /* Initialize the joystick capabilities */
   648     joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
   649     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
   650     joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   651 
   652     return SDL_TRUE;
   653 }
   654 
   655 static int
   656 HIDAPI_DriverSwitch_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   657 {
   658     SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context;
   659 
   660     /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
   661      * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
   662      *
   663      * More information about these values can be found here:
   664      * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
   665      */
   666     const Uint16 k_usHighFreq = 0x0074;
   667     const Uint8  k_ucHighFreqAmp = 0xBE;
   668     const Uint8  k_ucLowFreq = 0x3D;
   669     const Uint16 k_usLowFreqAmp = 0x806F;
   670 
   671     if (low_frequency_rumble) {
   672         EncodeRumble(&ctx->m_RumblePacket.rumbleData[0], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
   673     } else {
   674         SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
   675     }
   676 
   677     if (high_frequency_rumble) {
   678         EncodeRumble(&ctx->m_RumblePacket.rumbleData[1], k_usHighFreq, k_ucHighFreqAmp, k_ucLowFreq, k_usLowFreqAmp);
   679     } else {
   680         SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[1]);
   681     }
   682 
   683     if (!WriteRumble(ctx)) {
   684         SDL_SetError("Couldn't send rumble packet");
   685         return -1;
   686     }
   687 
   688     if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
   689         ctx->m_nRumbleExpiration = SDL_GetTicks() + duration_ms;
   690     } else {
   691         ctx->m_nRumbleExpiration = 0;
   692     }
   693     return 0;
   694 }
   695 
   696 static void HandleSimpleControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchSimpleStatePacket_t *packet)
   697 {
   698     /* 0x8000 is the neutral value for all joystick axes */
   699     const Uint16 usJoystickCenter = 0x8000;
   700     Sint16 axis;
   701 
   702     if (packet->rgucButtons[0] != ctx->m_lastSimpleState.rgucButtons[0]) {
   703         Uint8 data = packet->rgucButtons[0];
   704         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   705         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   706         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   707         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   708         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   709         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   710 
   711         axis = (data & 0x40) ? 32767 : -32768;
   712         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   713 
   714         axis = (data & 0x80) ? 32767 : -32768;
   715         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   716     }
   717 
   718     if (packet->rgucButtons[1] != ctx->m_lastSimpleState.rgucButtons[1]) {
   719         Uint8 data = packet->rgucButtons[1];
   720         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   721         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   722         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   723         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   724         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   725     }
   726 
   727     if (packet->ucStickHat != ctx->m_lastSimpleState.ucStickHat) {
   728         SDL_bool dpad_up = SDL_FALSE;
   729         SDL_bool dpad_down = SDL_FALSE;
   730         SDL_bool dpad_left = SDL_FALSE;
   731         SDL_bool dpad_right = SDL_FALSE;
   732 
   733         switch (packet->ucStickHat) {
   734         case 0:
   735             dpad_up = SDL_TRUE;
   736             break;
   737         case 1:
   738             dpad_up = SDL_TRUE;
   739             dpad_right = SDL_TRUE;
   740             break;
   741         case 2:
   742             dpad_right = SDL_TRUE;
   743             break;
   744         case 3:
   745             dpad_right = SDL_TRUE;
   746             dpad_down = SDL_TRUE;
   747             break;
   748         case 4:
   749             dpad_down = SDL_TRUE;
   750             break;
   751         case 5:
   752             dpad_left = SDL_TRUE;
   753             dpad_down = SDL_TRUE;
   754             break;
   755         case 6:
   756             dpad_left = SDL_TRUE;
   757             break;
   758         case 7:
   759             dpad_up = SDL_TRUE;
   760             dpad_left = SDL_TRUE;
   761             break;
   762         default:
   763             break;
   764         }
   765         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down);
   766         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up);
   767         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right);
   768         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left);
   769     }
   770 
   771     axis = ApplyStickCalibrationCentered(ctx, 0, 0, packet->sJoystickLeft[0], (Sint16)usJoystickCenter);
   772     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   773 
   774     axis = ApplyStickCalibrationCentered(ctx, 0, 1, packet->sJoystickLeft[1], (Sint16)usJoystickCenter);
   775     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis);
   776 
   777     axis = ApplyStickCalibrationCentered(ctx, 1, 0, packet->sJoystickRight[0], (Sint16)usJoystickCenter);
   778     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   779 
   780     axis = ApplyStickCalibrationCentered(ctx, 1, 1, packet->sJoystickRight[1], (Sint16)usJoystickCenter);
   781     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
   782 
   783     ctx->m_lastSimpleState = *packet;
   784 }
   785 
   786 static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet)
   787 {
   788     Sint16 axis;
   789 
   790     if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) {
   791         Uint8 data = packet->controllerState.rgucButtons[0];
   792         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   793         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   794         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   795         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   796         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   797         axis = (data & 0x80) ? 32767 : -32768;
   798         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   799     }
   800 
   801     if (packet->controllerState.rgucButtons[1] != ctx->m_lastFullState.controllerState.rgucButtons[1]) {
   802         Uint8 data = packet->controllerState.rgucButtons[1];
   803         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   804         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   805         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   806         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   807 
   808         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   809     }
   810 
   811     if (packet->controllerState.rgucButtons[2] != ctx->m_lastFullState.controllerState.rgucButtons[2]) {
   812         Uint8 data = packet->controllerState.rgucButtons[2];
   813         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   814         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   815         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   816         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   817         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   818         axis = (data & 0x80) ? 32767 : -32768;
   819         SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   820     }
   821 
   822     axis = packet->controllerState.rgucJoystickLeft[0] | ((packet->controllerState.rgucJoystickLeft[1] & 0xF) << 8);
   823     axis = ApplyStickCalibration(ctx, 0, 0, axis);
   824     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   825 
   826     axis = ((packet->controllerState.rgucJoystickLeft[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickLeft[2] << 4);
   827     axis = ApplyStickCalibration(ctx, 0, 1, axis);
   828     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
   829 
   830     axis = packet->controllerState.rgucJoystickRight[0] | ((packet->controllerState.rgucJoystickRight[1] & 0xF) << 8);
   831     axis = ApplyStickCalibration(ctx, 1, 0, axis);
   832     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   833 
   834     axis = ((packet->controllerState.rgucJoystickRight[1] & 0xF0) >> 4) | (packet->controllerState.rgucJoystickRight[2] << 4);
   835     axis = ApplyStickCalibration(ctx, 1, 1, axis);
   836     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
   837 
   838     /* High nibble of battery/connection byte is battery level, low nibble is connection status
   839      * LSB of connection nibble is USB/Switch connection status
   840      */
   841     if (packet->controllerState.ucBatteryAndConnection & 0x1) {
   842         joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   843     } else {
   844         /* LSB of the battery nibble is used to report charging.
   845          * The battery level is reported from 0(empty)-8(full)
   846          */
   847         int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
   848         if (level == 0) {
   849             joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
   850         } else if (level <= 2) {
   851             joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
   852         } else if (level <= 6) {
   853             joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
   854         } else {
   855             joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
   856         }
   857     }
   858 
   859     ctx->m_lastFullState = *packet;
   860 }
   861 
   862 static SDL_bool
   863 HIDAPI_DriverSwitch_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks)
   864 {
   865     SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context;
   866     SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID);
   867     int size;
   868 
   869     if (joystick == NULL) {
   870         return SDL_TRUE; /* Nothing to do right now! */
   871     }
   872 
   873     while ((size = ReadInput(ctx)) > 0) {
   874         switch (ctx->m_rgucReadBuffer[0]) {
   875         case k_eSwitchInputReportIDs_SimpleControllerState:
   876             HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
   877             break;
   878         case k_eSwitchInputReportIDs_FullControllerState:
   879             HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]);
   880             break;
   881         default:
   882             break;
   883         }
   884     }
   885 
   886     if (ctx->m_nRumbleExpiration) {
   887         Uint32 now = SDL_GetTicks();
   888         if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) {
   889             HIDAPI_DriverSwitch_Rumble(context, joystick, 0, 0, 0);
   890         }
   891     }
   892 
   893     return (size >= 0);
   894 }
   895 
   896 static void
   897 HIDAPI_DriverSwitch_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks)
   898 {
   899     SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context;
   900 
   901     /* Restore simple input mode for other applications */
   902     SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState);
   903 
   904     *num_joysticks -= 1;
   905     if (send_event) {
   906         SDL_PrivateJoystickRemoved(ctx->joystickID);
   907     }
   908     SDL_free(context->context);
   909 }
   910 
   911 static int
   912 HIDAPI_DriverSwitch_NumJoysticks(SDL_HIDAPI_DriverData *context)
   913 {
   914     return 1;
   915 }
   916 
   917 static SDL_JoystickID
   918 HIDAPI_DriverSwitch_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index)
   919 {
   920     SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context;
   921     return ctx->joystickID;
   922 }
   923 
   924 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
   925 {
   926     SDL_HINT_JOYSTICK_HIDAPI_SWITCH,
   927     SDL_TRUE,
   928     HIDAPI_DriverSwitch_IsSupportedDevice,
   929     HIDAPI_DriverSwitch_GetDeviceName,
   930     HIDAPI_DriverSwitch_InitDriver,
   931     HIDAPI_DriverSwitch_QuitDriver,
   932     HIDAPI_DriverSwitch_UpdateDriver,
   933     HIDAPI_DriverSwitch_NumJoysticks,
   934     HIDAPI_DriverSwitch_InstanceIDForIndex,
   935     HIDAPI_DriverSwitch_OpenJoystick,
   936     HIDAPI_DriverSwitch_Rumble
   937 };
   938 
   939 #endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
   940 
   941 #endif /* SDL_JOYSTICK_HIDAPI */
   942 
   943 /* vi: set ts=4 sw=4 expandtab: */