src/joystick/hidapi/SDL_hidapi_xboxone.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 15 Aug 2018 23:35:54 -0700
changeset 12119 648377d0e573
parent 12117 8cbdc9b8b055
child 12161 12a877a0ccb5
permissions -rw-r--r--
Use a single hint for both Xbox 360 and Xbox One controllers, since they are often the same driver.
slouken@12088
     1
/*
slouken@12088
     2
  Simple DirectMedia Layer
slouken@12088
     3
  Copyright (C) 1997-2018 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@12088
    21
#include "../../SDL_internal.h"
slouken@12088
    22
slouken@12088
    23
#ifdef SDL_JOYSTICK_HIDAPI
slouken@12088
    24
slouken@12088
    25
#include "SDL_hints.h"
slouken@12088
    26
#include "SDL_log.h"
slouken@12088
    27
#include "SDL_events.h"
slouken@12088
    28
#include "SDL_timer.h"
slouken@12088
    29
#include "SDL_joystick.h"
slouken@12088
    30
#include "SDL_gamecontroller.h"
slouken@12088
    31
#include "../SDL_sysjoystick.h"
slouken@12088
    32
#include "SDL_hidapijoystick_c.h"
slouken@12088
    33
slouken@12088
    34
slouken@12088
    35
#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
slouken@12088
    36
slouken@12088
    37
#define USB_PACKET_LENGTH   64
slouken@12088
    38
slouken@12088
    39
/*
slouken@12088
    40
 * This packet is required for all Xbox One pads with 2015
slouken@12088
    41
 * or later firmware installed (or present from the factory).
slouken@12088
    42
 */
slouken@12088
    43
static const Uint8 xboxone_fw2015_init[] = {
slouken@12088
    44
    0x05, 0x20, 0x00, 0x01, 0x00
slouken@12088
    45
};
slouken@12088
    46
slouken@12088
    47
/*
slouken@12088
    48
 * This packet is required for the Titanfall 2 Xbox One pads
slouken@12088
    49
 * (0x0e6f:0x0165) to finish initialization and for Hori pads
slouken@12088
    50
 * (0x0f0d:0x0067) to make the analog sticks work.
slouken@12088
    51
 */
slouken@12088
    52
static const Uint8 xboxone_hori_init[] = {
slouken@12088
    53
    0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
slouken@12088
    54
    0x00, 0x00, 0x00, 0x80, 0x00
slouken@12088
    55
};
slouken@12088
    56
slouken@12088
    57
/*
slouken@12088
    58
 * This packet is required for some of the PDP pads to start
slouken@12088
    59
 * sending input reports. These pads include: (0x0e6f:0x02ab),
slouken@12088
    60
 * (0x0e6f:0x02a4).
slouken@12088
    61
 */
slouken@12088
    62
static const Uint8 xboxone_pdp_init1[] = {
slouken@12088
    63
    0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
slouken@12088
    64
};
slouken@12088
    65
slouken@12088
    66
/*
slouken@12088
    67
 * This packet is required for some of the PDP pads to start
slouken@12088
    68
 * sending input reports. These pads include: (0x0e6f:0x02ab),
slouken@12088
    69
 * (0x0e6f:0x02a4).
slouken@12088
    70
 */
slouken@12088
    71
static const Uint8 xboxone_pdp_init2[] = {
slouken@12088
    72
    0x06, 0x20, 0x00, 0x02, 0x01, 0x00
slouken@12088
    73
};
slouken@12088
    74
slouken@12088
    75
/*
slouken@12088
    76
 * A specific rumble packet is required for some PowerA pads to start
slouken@12088
    77
 * sending input reports. One of those pads is (0x24c6:0x543a).
slouken@12088
    78
 */
slouken@12088
    79
static const Uint8 xboxone_rumblebegin_init[] = {
slouken@12088
    80
    0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
slouken@12088
    81
    0x1D, 0x1D, 0xFF, 0x00, 0x00
slouken@12088
    82
};
slouken@12088
    83
slouken@12088
    84
/*
slouken@12088
    85
 * A rumble packet with zero FF intensity will immediately
slouken@12088
    86
 * terminate the rumbling required to init PowerA pads.
slouken@12088
    87
 * This should happen fast enough that the motors don't
slouken@12088
    88
 * spin up to enough speed to actually vibrate the gamepad.
slouken@12088
    89
 */
slouken@12088
    90
static const Uint8 xboxone_rumbleend_init[] = {
slouken@12088
    91
    0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
slouken@12088
    92
    0x00, 0x00, 0x00, 0x00, 0x00
slouken@12088
    93
};
slouken@12088
    94
slouken@12088
    95
/*
slouken@12088
    96
 * This specifies the selection of init packets that a gamepad
slouken@12088
    97
 * will be sent on init *and* the order in which they will be
slouken@12088
    98
 * sent. The correct sequence number will be added when the
slouken@12088
    99
 * packet is going to be sent.
slouken@12088
   100
 */
slouken@12088
   101
typedef struct {
slouken@12088
   102
    Uint16 vendor_id;
slouken@12088
   103
    Uint16 product_id;
slouken@12088
   104
    const Uint8 *data;
slouken@12088
   105
    int size;
slouken@12088
   106
} SDL_DriverXboxOne_InitPacket;
slouken@12088
   107
slouken@12088
   108
static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
slouken@12088
   109
    { 0x0e6f, 0x0165, xboxone_hori_init, sizeof(xboxone_hori_init) },
slouken@12088
   110
    { 0x0f0d, 0x0067, xboxone_hori_init, sizeof(xboxone_hori_init) },
slouken@12088
   111
    { 0x0000, 0x0000, xboxone_fw2015_init, sizeof(xboxone_fw2015_init) },
slouken@12088
   112
    { 0x0e6f, 0x0246, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
slouken@12088
   113
    { 0x0e6f, 0x0246, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
slouken@12088
   114
    { 0x0e6f, 0x02ab, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
slouken@12088
   115
    { 0x0e6f, 0x02ab, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
slouken@12088
   116
    { 0x0e6f, 0x02a4, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
slouken@12088
   117
    { 0x0e6f, 0x02a4, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
slouken@12088
   118
    { 0x24c6, 0x541a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
slouken@12088
   119
    { 0x24c6, 0x542a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
slouken@12088
   120
    { 0x24c6, 0x543a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
slouken@12088
   121
    { 0x24c6, 0x541a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
slouken@12088
   122
    { 0x24c6, 0x542a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
slouken@12088
   123
    { 0x24c6, 0x543a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
slouken@12088
   124
};
slouken@12088
   125
slouken@12088
   126
typedef struct {
slouken@12088
   127
    Uint8 sequence;
slouken@12088
   128
    Uint8 last_state[USB_PACKET_LENGTH];
slouken@12088
   129
    Uint32 rumble_expiration;
slouken@12088
   130
} SDL_DriverXboxOne_Context;
slouken@12088
   131
slouken@12088
   132
slouken@12088
   133
static SDL_bool
slouken@12115
   134
HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, Uint16 usage_page, Uint16 usage)
slouken@12088
   135
{
slouken@12088
   136
    return SDL_IsJoystickXboxOne(vendor_id, product_id);
slouken@12088
   137
}
slouken@12088
   138
slouken@12088
   139
static const char *
slouken@12088
   140
HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
slouken@12088
   141
{
slouken@12117
   142
    return HIDAPI_XboxControllerName(vendor_id, product_id);
slouken@12088
   143
}
slouken@12088
   144
slouken@12088
   145
static SDL_bool
slouken@12088
   146
HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
slouken@12088
   147
{
slouken@12088
   148
    SDL_DriverXboxOne_Context *ctx;
slouken@12088
   149
    int i;
slouken@12088
   150
    Uint8 init_packet[USB_PACKET_LENGTH];
slouken@12088
   151
slouken@12088
   152
    ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
slouken@12088
   153
    if (!ctx) {
slouken@12088
   154
        SDL_OutOfMemory();
slouken@12088
   155
        return SDL_FALSE;
slouken@12088
   156
    }
slouken@12088
   157
    *context = ctx;
slouken@12088
   158
slouken@12088
   159
    /* Send the controller init data */
slouken@12088
   160
    for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
slouken@12088
   161
        const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
slouken@12088
   162
        if (!packet->vendor_id || (vendor_id == packet->vendor_id && product_id == packet->product_id)) {
slouken@12088
   163
            SDL_memcpy(init_packet, packet->data, packet->size);
slouken@12088
   164
            init_packet[2] = ctx->sequence++;
slouken@12088
   165
            if (hid_write(dev, init_packet, packet->size) != packet->size) {
slouken@12088
   166
                SDL_SetError("Couldn't write Xbox One initialization packet");
slouken@12088
   167
                SDL_free(ctx);
slouken@12088
   168
                return SDL_FALSE;
slouken@12088
   169
            }
slouken@12088
   170
        }
slouken@12088
   171
    }
slouken@12088
   172
slouken@12088
   173
    /* Initialize the joystick capabilities */
slouken@12088
   174
    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
slouken@12088
   175
    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
slouken@12088
   176
    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
slouken@12088
   177
slouken@12088
   178
    return SDL_TRUE;
slouken@12088
   179
}
slouken@12088
   180
slouken@12088
   181
static int
slouken@12088
   182
HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
slouken@12088
   183
{
slouken@12088
   184
    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
slouken@12088
   185
    Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
slouken@12088
   186
slouken@12088
   187
    /* The Rock Candy Xbox One Controller limits the range of
slouken@12088
   188
         low frequency rumble strength in the range of [0 - 0x99]
slouken@12088
   189
         high frequency rumble strength in the range of [0 - 0x82]
slouken@12088
   190
slouken@12088
   191
       I think the valid range of rumble at the firmware level is [0 - 0x7F]
slouken@12088
   192
    */
slouken@12088
   193
    rumble_packet[2] = ctx->sequence++;
slouken@12088
   194
    rumble_packet[8] = (low_frequency_rumble >> 9);
slouken@12088
   195
    rumble_packet[9] = (high_frequency_rumble >> 9);
slouken@12088
   196
slouken@12088
   197
    if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
slouken@12088
   198
        return SDL_SetError("Couldn't send rumble packet");
slouken@12088
   199
    }
slouken@12088
   200
slouken@12088
   201
    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
slouken@12088
   202
        ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
slouken@12088
   203
    } else {
slouken@12088
   204
        ctx->rumble_expiration = 0;
slouken@12088
   205
    }
slouken@12088
   206
    return 0;
slouken@12088
   207
}
slouken@12088
   208
slouken@12088
   209
static void
slouken@12088
   210
HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
slouken@12088
   211
{
slouken@12088
   212
    Sint16 axis;
slouken@12088
   213
slouken@12088
   214
    if (ctx->last_state[4] != data[4]) {
slouken@12088
   215
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   216
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   217
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   218
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   219
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   220
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   221
    }
slouken@12088
   222
slouken@12088
   223
    if (ctx->last_state[5] != data[5]) {
slouken@12088
   224
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   225
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   226
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   227
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   228
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   229
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   230
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   231
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   232
    }
slouken@12088
   233
slouken@12088
   234
    axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
slouken@12088
   235
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
slouken@12088
   236
    axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
slouken@12088
   237
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
slouken@12088
   238
    axis = *(Sint16*)(&data[10]);
slouken@12088
   239
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
slouken@12088
   240
    axis = *(Sint16*)(&data[12]);
slouken@12088
   241
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
slouken@12088
   242
    axis = *(Sint16*)(&data[14]);
slouken@12088
   243
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
slouken@12088
   244
    axis = *(Sint16*)(&data[16]);
slouken@12088
   245
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
slouken@12088
   246
slouken@12088
   247
    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
slouken@12088
   248
}
slouken@12088
   249
slouken@12088
   250
static void
slouken@12088
   251
HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
slouken@12088
   252
{
slouken@12088
   253
    if (data[1] == 0x30) {
slouken@12088
   254
        /* The Xbox One S controller needs acks for mode reports */
slouken@12088
   255
        const Uint8 seqnum = data[2];
slouken@12088
   256
        const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
slouken@12088
   257
        hid_write(dev, ack, sizeof(ack));
slouken@12088
   258
    }
slouken@12088
   259
slouken@12088
   260
    SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   261
}
slouken@12088
   262
slouken@12111
   263
static SDL_bool
slouken@12088
   264
HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
slouken@12088
   265
{
slouken@12088
   266
    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
slouken@12088
   267
    Uint8 data[USB_PACKET_LENGTH];
slouken@12088
   268
    int size;
slouken@12088
   269
slouken@12088
   270
    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
slouken@12088
   271
        switch (data[0]) {
slouken@12088
   272
        case 0x20:
slouken@12088
   273
            HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
slouken@12088
   274
            break;
slouken@12088
   275
        case 0x07:
slouken@12088
   276
            HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
slouken@12088
   277
            break;
slouken@12088
   278
        default:
slouken@12088
   279
#ifdef DEBUG_JOYSTICK
slouken@12088
   280
            SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
slouken@12088
   281
#endif
slouken@12088
   282
            break;
slouken@12088
   283
        }
slouken@12088
   284
    }
slouken@12088
   285
slouken@12088
   286
    if (ctx->rumble_expiration) {
slouken@12088
   287
        Uint32 now = SDL_GetTicks();
slouken@12088
   288
        if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
slouken@12088
   289
            HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
slouken@12088
   290
        }
slouken@12088
   291
    }
slouken@12111
   292
slouken@12111
   293
	return (size >= 0);
slouken@12088
   294
}
slouken@12088
   295
slouken@12088
   296
static void
slouken@12088
   297
HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
slouken@12088
   298
{
slouken@12088
   299
    SDL_free(context);
slouken@12088
   300
}
slouken@12088
   301
slouken@12088
   302
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
slouken@12088
   303
{
slouken@12119
   304
    SDL_HINT_JOYSTICK_HIDAPI_XBOX,
slouken@12088
   305
    SDL_TRUE,
slouken@12088
   306
    HIDAPI_DriverXboxOne_IsSupportedDevice,
slouken@12088
   307
    HIDAPI_DriverXboxOne_GetDeviceName,
slouken@12088
   308
    HIDAPI_DriverXboxOne_Init,
slouken@12088
   309
    HIDAPI_DriverXboxOne_Rumble,
slouken@12088
   310
    HIDAPI_DriverXboxOne_Update,
slouken@12088
   311
    HIDAPI_DriverXboxOne_Quit
slouken@12088
   312
};
slouken@12088
   313
slouken@12088
   314
#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
slouken@12088
   315
slouken@12088
   316
#endif /* SDL_JOYSTICK_HIDAPI */
slouken@12088
   317
slouken@12088
   318
/* vi: set ts=4 sw=4 expandtab: */