src/joystick/hidapi/SDL_hidapi_xboxone.c
author Sam Lantinga
Mon, 16 Dec 2019 10:20:03 -0800
changeset 13348 448528dc13da
parent 13344 0532eba8e423
child 13354 8c22865bd138
permissions -rw-r--r--
Fixed bug 4898 - No rumble because of integer overflow in SDL_JoystickRumble

meyraud705

On a Dualshock 4 controller using hidapi driver, calling SDL_JoystickRumble with a duration too long (SDL_HAPTIC_INFINITY for example) causes the rumble to stop immediately.

This happens because of integer overflow on line 301 of SDL_hidapi_ps4.c
(https://hg.libsdl.org/SDL/file/99ecd178999f/src/joystick/hidapi/SDL_hidapi_ps4.c#l301), which sets expiration time in the past.
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@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@13274
    39
/* The amount of time to wait after hotplug to send controller init sequence */
slouken@13274
    40
#define CONTROLLER_INIT_DELAY_MS    100
slouken@13274
    41
slouken@13300
    42
/* This is the full init sequence for the Xbox One Elite Series 2 controller.
slouken@13300
    43
   Normally it isn't needed, but this switches the controller back to wired report mode after being in Bluetooth mode.
slouken@13300
    44
*/
slouken@13300
    45
static const Uint8 xboxone_elite_init0[] = {
slouken@13300
    46
    0x04, 0x20, 0x01, 0x00
slouken@13300
    47
};
slouken@13300
    48
static const Uint8 xboxone_elite_init1[] = {
slouken@13300
    49
    0x01, 0x20, 0x28, 0x09, 0x00, 0x04, 0x20, 0x3A,
slouken@13300
    50
    0x00, 0x00, 0x00, 0x31, 0x01
slouken@13300
    51
};
slouken@13300
    52
static const Uint8 xboxone_elite_init2[] = {
slouken@13300
    53
    0x01, 0x20, 0x28, 0x09, 0x00, 0x04, 0x20, 0x6B,
slouken@13300
    54
    0x01, 0x00, 0x00, 0x00, 0x00
slouken@13300
    55
};
slouken@13300
    56
static const Uint8 xboxone_elite_init3[] = {
slouken@13300
    57
    0x05, 0x20, 0x02, 0x0F, 0x06, 0x00, 0x00, 0x00,
slouken@13300
    58
    0x00, 0x00, 0x00, 0x55, 0x53, 0x00, 0x00, 0x00,
slouken@13300
    59
    0x00, 0x00, 0x00
slouken@13300
    60
};
slouken@13300
    61
static const Uint8 xboxone_elite_init4[] = {
slouken@13300
    62
    0x05, 0x20, 0x03, 0x01, 0x00
slouken@13300
    63
};
slouken@13300
    64
static const Uint8 xboxone_elite_init5[] = {
slouken@13300
    65
    0x0A, 0x20, 0x04, 0x03, 0x00, 0x01, 0x14
slouken@13300
    66
};
slouken@13300
    67
slouken@12088
    68
/*
slouken@12088
    69
 * This packet is required for all Xbox One pads with 2015
slouken@12088
    70
 * or later firmware installed (or present from the factory).
slouken@12088
    71
 */
slouken@12088
    72
static const Uint8 xboxone_fw2015_init[] = {
slouken@12088
    73
    0x05, 0x20, 0x00, 0x01, 0x00
slouken@12088
    74
};
slouken@12088
    75
slouken@12088
    76
/*
slouken@13344
    77
 * This packet turns on the LED on some controllers, including PowerA
slouken@13344
    78
 */
slouken@13344
    79
static const Uint8 xboxone_led_enable[] = {
slouken@13344
    80
    0x0A, 0x20, 0x04, 0x03, 0x00, 0x01, 0x14
slouken@13344
    81
};
slouken@13344
    82
slouken@13344
    83
/*
slouken@12088
    84
 * This packet is required for the Titanfall 2 Xbox One pads
slouken@12088
    85
 * (0x0e6f:0x0165) to finish initialization and for Hori pads
slouken@12088
    86
 * (0x0f0d:0x0067) to make the analog sticks work.
slouken@12088
    87
 */
slouken@12088
    88
static const Uint8 xboxone_hori_init[] = {
slouken@12088
    89
    0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
slouken@12088
    90
    0x00, 0x00, 0x00, 0x80, 0x00
slouken@12088
    91
};
slouken@12088
    92
slouken@12088
    93
/*
slouken@12088
    94
 * This packet is required for some of the PDP pads to start
slouken@12088
    95
 * sending input reports. These pads include: (0x0e6f:0x02ab),
slouken@12088
    96
 * (0x0e6f:0x02a4).
slouken@12088
    97
 */
slouken@12088
    98
static const Uint8 xboxone_pdp_init1[] = {
slouken@12088
    99
    0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
slouken@12088
   100
};
slouken@12088
   101
slouken@12088
   102
/*
slouken@12088
   103
 * This packet is required for some of the PDP pads to start
slouken@12088
   104
 * sending input reports. These pads include: (0x0e6f:0x02ab),
slouken@12088
   105
 * (0x0e6f:0x02a4).
slouken@12088
   106
 */
slouken@12088
   107
static const Uint8 xboxone_pdp_init2[] = {
slouken@12088
   108
    0x06, 0x20, 0x00, 0x02, 0x01, 0x00
slouken@12088
   109
};
slouken@12088
   110
slouken@12088
   111
/*
slouken@12088
   112
 * A specific rumble packet is required for some PowerA pads to start
slouken@12088
   113
 * sending input reports. One of those pads is (0x24c6:0x543a).
slouken@12088
   114
 */
slouken@12088
   115
static const Uint8 xboxone_rumblebegin_init[] = {
slouken@12088
   116
    0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
slouken@12088
   117
    0x1D, 0x1D, 0xFF, 0x00, 0x00
slouken@12088
   118
};
slouken@12088
   119
slouken@12088
   120
/*
slouken@12088
   121
 * A rumble packet with zero FF intensity will immediately
slouken@12088
   122
 * terminate the rumbling required to init PowerA pads.
slouken@12088
   123
 * This should happen fast enough that the motors don't
slouken@12088
   124
 * spin up to enough speed to actually vibrate the gamepad.
slouken@12088
   125
 */
slouken@12088
   126
static const Uint8 xboxone_rumbleend_init[] = {
slouken@12088
   127
    0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
slouken@12088
   128
    0x00, 0x00, 0x00, 0x00, 0x00
slouken@12088
   129
};
slouken@12088
   130
slouken@12088
   131
/*
slouken@12088
   132
 * This specifies the selection of init packets that a gamepad
slouken@12088
   133
 * will be sent on init *and* the order in which they will be
slouken@12088
   134
 * sent. The correct sequence number will be added when the
slouken@12088
   135
 * packet is going to be sent.
slouken@12088
   136
 */
slouken@12088
   137
typedef struct {
slouken@12088
   138
    Uint16 vendor_id;
slouken@12088
   139
    Uint16 product_id;
slouken@12088
   140
    const Uint8 *data;
slouken@12088
   141
    int size;
slouken@12088
   142
} SDL_DriverXboxOne_InitPacket;
slouken@12088
   143
slouken@13300
   144
slouken@12088
   145
static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
slouken@12088
   146
    { 0x0e6f, 0x0165, xboxone_hori_init, sizeof(xboxone_hori_init) },
slouken@12088
   147
    { 0x0f0d, 0x0067, xboxone_hori_init, sizeof(xboxone_hori_init) },
slouken@13300
   148
    { 0x045e, 0x0b00, xboxone_elite_init0, sizeof(xboxone_elite_init0) },
slouken@13300
   149
    { 0x045e, 0x0b00, xboxone_elite_init1, sizeof(xboxone_elite_init1) },
slouken@13300
   150
    { 0x045e, 0x0b00, xboxone_elite_init2, sizeof(xboxone_elite_init2) },
slouken@13300
   151
    { 0x045e, 0x0b00, xboxone_elite_init3, sizeof(xboxone_elite_init3) },
slouken@13300
   152
    { 0x045e, 0x0b00, xboxone_elite_init4, sizeof(xboxone_elite_init4) },
slouken@13300
   153
    { 0x045e, 0x0b00, xboxone_elite_init5, sizeof(xboxone_elite_init5) },
slouken@12088
   154
    { 0x0000, 0x0000, xboxone_fw2015_init, sizeof(xboxone_fw2015_init) },
slouken@13344
   155
    { 0x0000, 0x0000, xboxone_led_enable, sizeof(xboxone_led_enable) },
slouken@13274
   156
    { 0x0e6f, 0x0000, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
slouken@13274
   157
    { 0x0e6f, 0x0000, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
slouken@13344
   158
    { 0x24c6, 0x0000, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
slouken@13344
   159
    { 0x24c6, 0x0000, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
slouken@12088
   160
};
slouken@12088
   161
slouken@12088
   162
typedef struct {
slouken@13274
   163
    Uint16 vendor_id;
slouken@13274
   164
    Uint16 product_id;
slouken@13274
   165
    Uint32 start_time;
slouken@13274
   166
    SDL_bool initialized;
slouken@12088
   167
    Uint8 sequence;
slouken@12088
   168
    Uint8 last_state[USB_PACKET_LENGTH];
slouken@12088
   169
    Uint32 rumble_expiration;
slouken@12088
   170
} SDL_DriverXboxOne_Context;
slouken@12088
   171
slouken@12088
   172
slouken@12088
   173
static SDL_bool
slouken@13255
   174
IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id)
slouken@13255
   175
{
slouken@13255
   176
    /* Check to see if it's the Xbox One S or Xbox One Elite Series 2 in Bluetooth mode */
slouken@13255
   177
    const Uint16 USB_VENDOR_MICROSOFT = 0x045e;
slouken@13303
   178
    const Uint16 USB_PRODUCT_XBOX_ONE_S_REV1 = 0x02e0;
slouken@13303
   179
    const Uint16 USB_PRODUCT_XBOX_ONE_S_REV2 = 0x02fd;
slouken@13255
   180
    const Uint16 USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 = 0x0b05;
slouken@13255
   181
slouken@13303
   182
    if (vendor_id == USB_VENDOR_MICROSOFT) {
slouken@13303
   183
        if (product_id == USB_PRODUCT_XBOX_ONE_S_REV1 ||
slouken@13303
   184
            product_id == USB_PRODUCT_XBOX_ONE_S_REV2 ||
slouken@13303
   185
            product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2) {
slouken@13303
   186
            return SDL_TRUE;
slouken@13303
   187
        }
slouken@13255
   188
    }
slouken@13255
   189
    return SDL_FALSE;
slouken@13255
   190
}
slouken@13255
   191
slouken@13255
   192
static SDL_bool
slouken@13274
   193
SendControllerInit(hid_device *dev, SDL_DriverXboxOne_Context *ctx)
slouken@13274
   194
{
slouken@13274
   195
    Uint16 vendor_id = ctx->vendor_id;
slouken@13274
   196
    Uint16 product_id = ctx->product_id;
slouken@13274
   197
slouken@13274
   198
    if (!IsBluetoothXboxOneController(vendor_id, product_id)) {
slouken@13324
   199
        int i, j;
slouken@13274
   200
        Uint8 init_packet[USB_PACKET_LENGTH];
slouken@13274
   201
slouken@13274
   202
        for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
slouken@13274
   203
            const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
slouken@13274
   204
slouken@13274
   205
            if (packet->vendor_id && (vendor_id != packet->vendor_id)) {
slouken@13274
   206
                continue;
slouken@13274
   207
            }
slouken@13274
   208
slouken@13274
   209
            if (packet->product_id && (product_id != packet->product_id)) {
slouken@13274
   210
                continue;
slouken@13274
   211
            }
slouken@13274
   212
slouken@13274
   213
            SDL_memcpy(init_packet, packet->data, packet->size);
slouken@13274
   214
            init_packet[2] = ctx->sequence++;
slouken@13274
   215
            if (hid_write(dev, init_packet, packet->size) != packet->size) {
slouken@13274
   216
                SDL_SetError("Couldn't write Xbox One initialization packet");
slouken@13274
   217
                return SDL_FALSE;
slouken@13274
   218
            }
slouken@13324
   219
slouken@13324
   220
            /* After the init we need to sync up the rumble sequence */
slouken@13324
   221
            if (packet->data == xboxone_fw2015_init) {
slouken@13324
   222
                for (j = 0; j < 255; ++j) {
slouken@13324
   223
                    if (hid_write(dev, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init)) != sizeof(xboxone_rumbleend_init)) {
slouken@13324
   224
                        SDL_SetError("Couldn't write Xbox One initialization packet");
slouken@13324
   225
                        return SDL_FALSE;
slouken@13324
   226
                    }
slouken@13324
   227
                }
slouken@13324
   228
            }
slouken@13274
   229
        }
slouken@13274
   230
    }
slouken@13274
   231
    return SDL_TRUE;
slouken@13274
   232
}
slouken@13274
   233
slouken@13274
   234
slouken@13274
   235
static SDL_bool
slouken@13160
   236
HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
slouken@12088
   237
{
slouken@13248
   238
#ifdef __LINUX__
slouken@13255
   239
    if (IsBluetoothXboxOneController(vendor_id, product_id)) {
slouken@13248
   240
        /* We can't do rumble on this device, hid_write() fails, so don't try to open it here */
slouken@13248
   241
        return SDL_FALSE;
slouken@13248
   242
    }
slouken@13323
   243
    if (vendor_id == 0x24c6 && product_id == 0x541a) {
slouken@13324
   244
        /* The PowerA Mini controller, model 1240245-01, blocks while writing feature reports */
slouken@13323
   245
        return SDL_FALSE;
slouken@13323
   246
    }
slouken@13248
   247
#endif
slouken@13277
   248
    return (SDL_GetJoystickGameControllerType(vendor_id, product_id, name) == SDL_CONTROLLER_TYPE_XBOXONE);
slouken@12088
   249
}
slouken@12088
   250
slouken@12088
   251
static const char *
slouken@12088
   252
HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
slouken@12088
   253
{
slouken@13330
   254
    return NULL;
slouken@12088
   255
}
slouken@12088
   256
slouken@12088
   257
static SDL_bool
slouken@12896
   258
HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
slouken@12088
   259
{
slouken@12088
   260
    SDL_DriverXboxOne_Context *ctx;
slouken@12088
   261
slouken@12088
   262
    ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
slouken@12088
   263
    if (!ctx) {
slouken@12088
   264
        SDL_OutOfMemory();
slouken@12088
   265
        return SDL_FALSE;
slouken@12088
   266
    }
slouken@12896
   267
    *context = ctx;
slouken@12088
   268
slouken@13274
   269
    ctx->vendor_id = vendor_id;
slouken@13274
   270
    ctx->product_id = product_id;
slouken@13274
   271
    ctx->start_time = SDL_GetTicks();
slouken@12088
   272
slouken@12088
   273
    /* Initialize the joystick capabilities */
slouken@12088
   274
    joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
slouken@12088
   275
    joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
slouken@12088
   276
    joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
slouken@12088
   277
slouken@12088
   278
    return SDL_TRUE;
slouken@12088
   279
}
slouken@12088
   280
slouken@12088
   281
static int
slouken@12896
   282
HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
flibitijibibo@12641
   283
{
slouken@12896
   284
    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
slouken@12088
   285
    Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
slouken@12088
   286
slouken@13274
   287
    if (!ctx->initialized) {
slouken@13274
   288
        return 0;
slouken@13274
   289
    }
slouken@13274
   290
slouken@13247
   291
    /* Magnitude is 1..100 so scale the 16-bit input here */
slouken@12088
   292
    rumble_packet[2] = ctx->sequence++;
slouken@13247
   293
    rumble_packet[8] = low_frequency_rumble / 655;
slouken@13247
   294
    rumble_packet[9] = high_frequency_rumble / 655;
slouken@12088
   295
slouken@12896
   296
    if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
slouken@12088
   297
        return SDL_SetError("Couldn't send rumble packet");
slouken@12088
   298
    }
slouken@12088
   299
slouken@12088
   300
    if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
slouken@13348
   301
        ctx->rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
slouken@12088
   302
    } else {
slouken@12088
   303
        ctx->rumble_expiration = 0;
slouken@12088
   304
    }
slouken@12088
   305
    return 0;
slouken@12088
   306
}
slouken@12088
   307
slouken@12088
   308
static void
slouken@12088
   309
HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
slouken@12088
   310
{
slouken@12088
   311
    Sint16 axis;
slouken@12088
   312
slouken@12088
   313
    if (ctx->last_state[4] != data[4]) {
slouken@12088
   314
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   315
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   316
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   317
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   318
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   319
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   320
    }
slouken@12088
   321
slouken@12088
   322
    if (ctx->last_state[5] != data[5]) {
slouken@12088
   323
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   324
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   325
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   326
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   327
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   328
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   329
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   330
        SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   331
    }
slouken@12088
   332
slouken@12088
   333
    axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
slouken@12161
   334
    if (axis == 32704) {
slouken@12161
   335
        axis = 32767;
slouken@12161
   336
    }
slouken@12088
   337
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
slouken@12088
   338
    axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
slouken@12161
   339
    if (axis == 32704) {
slouken@12161
   340
        axis = 32767;
slouken@12161
   341
    }
slouken@12088
   342
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
slouken@12088
   343
    axis = *(Sint16*)(&data[10]);
slouken@12088
   344
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
slouken@12088
   345
    axis = *(Sint16*)(&data[12]);
slouken@12088
   346
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
slouken@12088
   347
    axis = *(Sint16*)(&data[14]);
slouken@12088
   348
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
slouken@12088
   349
    axis = *(Sint16*)(&data[16]);
slouken@12088
   350
    SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
slouken@12088
   351
slouken@12088
   352
    SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
slouken@12088
   353
}
slouken@12088
   354
slouken@12088
   355
static void
slouken@12088
   356
HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
slouken@12088
   357
{
slouken@12088
   358
    if (data[1] == 0x30) {
slouken@12088
   359
        /* The Xbox One S controller needs acks for mode reports */
slouken@12088
   360
        const Uint8 seqnum = data[2];
slouken@12088
   361
        const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
slouken@12088
   362
        hid_write(dev, ack, sizeof(ack));
slouken@12088
   363
    }
slouken@12088
   364
slouken@12088
   365
    SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
slouken@12088
   366
}
slouken@12088
   367
slouken@12111
   368
static SDL_bool
slouken@12896
   369
HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
slouken@12088
   370
{
slouken@12896
   371
    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
slouken@12088
   372
    Uint8 data[USB_PACKET_LENGTH];
slouken@12088
   373
    int size;
slouken@12088
   374
slouken@13274
   375
    if (!ctx->initialized) {
slouken@13274
   376
        if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->start_time + CONTROLLER_INIT_DELAY_MS)) {
slouken@13274
   377
            if (!SendControllerInit(dev, ctx)) {
slouken@13274
   378
                return SDL_FALSE;
slouken@13274
   379
            }
slouken@13274
   380
            ctx->initialized = SDL_TRUE;
slouken@13274
   381
        }
slouken@13274
   382
    }
slouken@13274
   383
slouken@12896
   384
    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
slouken@13247
   385
#ifdef DEBUG_XBOX_PROTOCOL
slouken@13247
   386
        SDL_Log("Xbox One packet: size = %d\n"
slouken@13247
   387
                "                 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n"
slouken@13247
   388
                "                 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n"
slouken@13247
   389
                "                 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
slouken@13247
   390
                    size,
slouken@13247
   391
                    data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
slouken@13247
   392
                    data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
slouken@13247
   393
                    data[16], data[17], data[18], data[19]);
slouken@13247
   394
#endif
slouken@12088
   395
        switch (data[0]) {
slouken@12088
   396
        case 0x20:
slouken@12896
   397
            HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
slouken@12088
   398
            break;
slouken@12088
   399
        case 0x07:
slouken@12896
   400
            HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
slouken@12088
   401
            break;
slouken@12088
   402
        default:
slouken@12088
   403
#ifdef DEBUG_JOYSTICK
slouken@12088
   404
            SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
slouken@12088
   405
#endif
slouken@12088
   406
            break;
slouken@12088
   407
        }
slouken@12088
   408
    }
slouken@12088
   409
slouken@12088
   410
    if (ctx->rumble_expiration) {
slouken@12088
   411
        Uint32 now = SDL_GetTicks();
slouken@12088
   412
        if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
slouken@12896
   413
            HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
slouken@12088
   414
        }
slouken@12088
   415
    }
slouken@12111
   416
slouken@12162
   417
    return (size >= 0);
slouken@12088
   418
}
slouken@12088
   419
slouken@12896
   420
static void
slouken@12896
   421
HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
slouken@12896
   422
{
slouken@12896
   423
    SDL_free(context);
slouken@12896
   424
}
slouken@12896
   425
slouken@12088
   426
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
slouken@12088
   427
{
slouken@12119
   428
    SDL_HINT_JOYSTICK_HIDAPI_XBOX,
slouken@12088
   429
    SDL_TRUE,
slouken@12088
   430
    HIDAPI_DriverXboxOne_IsSupportedDevice,
slouken@12088
   431
    HIDAPI_DriverXboxOne_GetDeviceName,
slouken@12896
   432
    HIDAPI_DriverXboxOne_Init,
slouken@12896
   433
    HIDAPI_DriverXboxOne_Rumble,
slouken@12896
   434
    HIDAPI_DriverXboxOne_Update,
slouken@12896
   435
    HIDAPI_DriverXboxOne_Quit
slouken@12088
   436
};
slouken@12088
   437
slouken@12088
   438
#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
slouken@12088
   439
slouken@12088
   440
#endif /* SDL_JOYSTICK_HIDAPI */
slouken@12088
   441
slouken@12088
   442
/* vi: set ts=4 sw=4 expandtab: */