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