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