src/joystick/hidapi/SDL_hidapi_xboxone.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 09 Aug 2018 16:00:17 -0700
changeset 12088 399cc39583cc
child 12111 6c1ae9f1effb
permissions -rw-r--r--
Added HIDAPI joystick drivers for more consistent support for Xbox, PS4 and Nintendo Switch Pro controller support across platforms.
Added SDL_GameControllerRumble() and SDL_JoystickRumble() for simple force feedback outside of the SDL haptics API
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #ifdef SDL_JOYSTICK_HIDAPI
    24 
    25 #include "SDL_hints.h"
    26 #include "SDL_log.h"
    27 #include "SDL_events.h"
    28 #include "SDL_timer.h"
    29 #include "SDL_joystick.h"
    30 #include "SDL_gamecontroller.h"
    31 #include "../SDL_sysjoystick.h"
    32 #include "SDL_hidapijoystick_c.h"
    33 
    34 
    35 #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
    36 
    37 #define USB_PACKET_LENGTH   64
    38 
    39 typedef struct
    40 {
    41     Uint16 vendor_id;
    42     Uint16 product_id;
    43     const char *name;
    44 } SDL_DriverXboxOne_DeviceName;
    45 
    46 static const SDL_DriverXboxOne_DeviceName xboxone_devicenames[] = {
    47     { 0x045e, 0x02d1, "Microsoft X-Box One pad" },
    48     { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)" },
    49     { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad" },
    50     { 0x045e, 0x02ea, "Microsoft X-Box One S pad" },
    51     { 0x045e, 0x02ff, "Microsoft X-Box One pad" },
    52     { 0x0738, 0x4a01, "Mad Catz FightStick TE 2" },
    53     { 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller" },
    54     { 0x0e6f, 0x013a, "PDP Xbox One Controller" },
    55     { 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One" },
    56     { 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller" },
    57     { 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick" },
    58     { 0x0e6f, 0x0161, "PDP Xbox One Controller" },
    59     { 0x0e6f, 0x0162, "PDP Xbox One Controller" },
    60     { 0x0e6f, 0x0163, "PDP Xbox One Controller" },
    61     { 0x0e6f, 0x0164, "PDP Battlefield One" },
    62     { 0x0e6f, 0x0165, "PDP Titanfall 2" },
    63     { 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015" },
    64     { 0x0e6f, 0x02ab, "PDP Controller for Xbox One" },
    65     { 0x0e6f, 0x02a4, "PDP Wired Controller for Xbox One - Stealth Series" },
    66     { 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016" },
    67     { 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One" },
    68     { 0x0f0d, 0x0067, "HORIPAD ONE" },
    69     { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One" },
    70     { 0x1532, 0x0a00, "Razer Atrox Arcade Stick" },
    71     { 0x1532, 0x0a03, "Razer Wildcat" },
    72     { 0x24c6, 0x541a, "PowerA Xbox One Mini Wired Controller" },
    73     { 0x24c6, 0x542a, "Xbox ONE spectra" },
    74     { 0x24c6, 0x543a, "PowerA Xbox One wired controller" },
    75     { 0x24c6, 0x551a, "PowerA FUSION Pro Controller" },
    76     { 0x24c6, 0x561a, "PowerA FUSION Controller" },
    77 };
    78 
    79 /*
    80  * This packet is required for all Xbox One pads with 2015
    81  * or later firmware installed (or present from the factory).
    82  */
    83 static const Uint8 xboxone_fw2015_init[] = {
    84     0x05, 0x20, 0x00, 0x01, 0x00
    85 };
    86 
    87 /*
    88  * This packet is required for the Titanfall 2 Xbox One pads
    89  * (0x0e6f:0x0165) to finish initialization and for Hori pads
    90  * (0x0f0d:0x0067) to make the analog sticks work.
    91  */
    92 static const Uint8 xboxone_hori_init[] = {
    93     0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
    94     0x00, 0x00, 0x00, 0x80, 0x00
    95 };
    96 
    97 /*
    98  * This packet is required for some of the PDP pads to start
    99  * sending input reports. These pads include: (0x0e6f:0x02ab),
   100  * (0x0e6f:0x02a4).
   101  */
   102 static const Uint8 xboxone_pdp_init1[] = {
   103     0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
   104 };
   105 
   106 /*
   107  * This packet is required for some of the PDP pads to start
   108  * sending input reports. These pads include: (0x0e6f:0x02ab),
   109  * (0x0e6f:0x02a4).
   110  */
   111 static const Uint8 xboxone_pdp_init2[] = {
   112     0x06, 0x20, 0x00, 0x02, 0x01, 0x00
   113 };
   114 
   115 /*
   116  * A specific rumble packet is required for some PowerA pads to start
   117  * sending input reports. One of those pads is (0x24c6:0x543a).
   118  */
   119 static const Uint8 xboxone_rumblebegin_init[] = {
   120     0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
   121     0x1D, 0x1D, 0xFF, 0x00, 0x00
   122 };
   123 
   124 /*
   125  * A rumble packet with zero FF intensity will immediately
   126  * terminate the rumbling required to init PowerA pads.
   127  * This should happen fast enough that the motors don't
   128  * spin up to enough speed to actually vibrate the gamepad.
   129  */
   130 static const Uint8 xboxone_rumbleend_init[] = {
   131     0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
   132     0x00, 0x00, 0x00, 0x00, 0x00
   133 };
   134 
   135 /*
   136  * This specifies the selection of init packets that a gamepad
   137  * will be sent on init *and* the order in which they will be
   138  * sent. The correct sequence number will be added when the
   139  * packet is going to be sent.
   140  */
   141 typedef struct {
   142     Uint16 vendor_id;
   143     Uint16 product_id;
   144     const Uint8 *data;
   145     int size;
   146 } SDL_DriverXboxOne_InitPacket;
   147 
   148 static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
   149     { 0x0e6f, 0x0165, xboxone_hori_init, sizeof(xboxone_hori_init) },
   150     { 0x0f0d, 0x0067, xboxone_hori_init, sizeof(xboxone_hori_init) },
   151     { 0x0000, 0x0000, xboxone_fw2015_init, sizeof(xboxone_fw2015_init) },
   152     { 0x0e6f, 0x0246, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
   153     { 0x0e6f, 0x0246, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
   154     { 0x0e6f, 0x02ab, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
   155     { 0x0e6f, 0x02ab, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
   156     { 0x0e6f, 0x02a4, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
   157     { 0x0e6f, 0x02a4, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
   158     { 0x24c6, 0x541a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
   159     { 0x24c6, 0x542a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
   160     { 0x24c6, 0x543a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
   161     { 0x24c6, 0x541a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
   162     { 0x24c6, 0x542a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
   163     { 0x24c6, 0x543a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
   164 };
   165 
   166 typedef struct {
   167     Uint8 sequence;
   168     Uint8 last_state[USB_PACKET_LENGTH];
   169     Uint32 rumble_expiration;
   170 } SDL_DriverXboxOne_Context;
   171 
   172 
   173 static SDL_bool
   174 HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, int interface_number, Uint16 usage_page, Uint16 usage)
   175 {
   176     return SDL_IsJoystickXboxOne(vendor_id, product_id);
   177 }
   178 
   179 static const char *
   180 HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
   181 {
   182     int i;
   183 
   184     for (i = 0; i < SDL_arraysize(xboxone_devicenames); ++i) {
   185         const SDL_DriverXboxOne_DeviceName *entry = &xboxone_devicenames[i];
   186         if (vendor_id == entry->vendor_id && product_id == entry->product_id) {
   187             return entry->name;
   188         }
   189     }
   190     return NULL;
   191 }
   192 
   193 static SDL_bool
   194 HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
   195 {
   196     SDL_DriverXboxOne_Context *ctx;
   197     int i;
   198     Uint8 init_packet[USB_PACKET_LENGTH];
   199 
   200     ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
   201     if (!ctx) {
   202         SDL_OutOfMemory();
   203         return SDL_FALSE;
   204     }
   205     *context = ctx;
   206 
   207     /* Send the controller init data */
   208     for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
   209         const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
   210         if (!packet->vendor_id || (vendor_id == packet->vendor_id && product_id == packet->product_id)) {
   211             SDL_memcpy(init_packet, packet->data, packet->size);
   212             init_packet[2] = ctx->sequence++;
   213             if (hid_write(dev, init_packet, packet->size) != packet->size) {
   214                 SDL_SetError("Couldn't write Xbox One initialization packet");
   215                 SDL_free(ctx);
   216                 return SDL_FALSE;
   217             }
   218         }
   219     }
   220 
   221     /* Initialize the joystick capabilities */
   222     joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
   223     joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
   224     joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   225 
   226     return SDL_TRUE;
   227 }
   228 
   229 static int
   230 HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   231 {
   232     SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
   233     Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
   234 
   235     /* The Rock Candy Xbox One Controller limits the range of
   236          low frequency rumble strength in the range of [0 - 0x99]
   237          high frequency rumble strength in the range of [0 - 0x82]
   238 
   239        I think the valid range of rumble at the firmware level is [0 - 0x7F]
   240     */
   241     rumble_packet[2] = ctx->sequence++;
   242     rumble_packet[8] = (low_frequency_rumble >> 9);
   243     rumble_packet[9] = (high_frequency_rumble >> 9);
   244 
   245     if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
   246         return SDL_SetError("Couldn't send rumble packet");
   247     }
   248 
   249     if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
   250         ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
   251     } else {
   252         ctx->rumble_expiration = 0;
   253     }
   254     return 0;
   255 }
   256 
   257 static void
   258 HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
   259 {
   260     Sint16 axis;
   261 
   262     if (ctx->last_state[4] != data[4]) {
   263         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   264         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   265         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   266         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   267         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   268         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   269     }
   270 
   271     if (ctx->last_state[5] != data[5]) {
   272         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   273         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED);
   274         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED);
   275         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED);
   276         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED);
   277         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED);
   278         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED);
   279         SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED);
   280     }
   281 
   282     axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
   283     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis);
   284     axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
   285     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis);
   286     axis = *(Sint16*)(&data[10]);
   287     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis);
   288     axis = *(Sint16*)(&data[12]);
   289     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis);
   290     axis = *(Sint16*)(&data[14]);
   291     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis);
   292     axis = *(Sint16*)(&data[16]);
   293     SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis);
   294 
   295     SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
   296 }
   297 
   298 static void
   299 HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
   300 {
   301     if (data[1] == 0x30) {
   302         /* The Xbox One S controller needs acks for mode reports */
   303         const Uint8 seqnum = data[2];
   304         const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
   305         hid_write(dev, ack, sizeof(ack));
   306     }
   307 
   308     SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED);
   309 }
   310 
   311 static void
   312 HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
   313 {
   314     SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
   315     Uint8 data[USB_PACKET_LENGTH];
   316     int size;
   317 
   318     while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
   319         switch (data[0]) {
   320         case 0x20:
   321             HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
   322             break;
   323         case 0x07:
   324             HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
   325             break;
   326         default:
   327 #ifdef DEBUG_JOYSTICK
   328             SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
   329 #endif
   330             break;
   331         }
   332     }
   333 
   334     if (ctx->rumble_expiration) {
   335         Uint32 now = SDL_GetTicks();
   336         if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
   337             HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
   338         }
   339     }
   340 }
   341 
   342 static void
   343 HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   344 {
   345     SDL_free(context);
   346 }
   347 
   348 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
   349 {
   350     SDL_HINT_JOYSTICK_HIDAPI_XBOXONE,
   351     SDL_TRUE,
   352     HIDAPI_DriverXboxOne_IsSupportedDevice,
   353     HIDAPI_DriverXboxOne_GetDeviceName,
   354     HIDAPI_DriverXboxOne_Init,
   355     HIDAPI_DriverXboxOne_Rumble,
   356     HIDAPI_DriverXboxOne_Update,
   357     HIDAPI_DriverXboxOne_Quit
   358 };
   359 
   360 #endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
   361 
   362 #endif /* SDL_JOYSTICK_HIDAPI */
   363 
   364 /* vi: set ts=4 sw=4 expandtab: */