SDL 2.0
SDL_hidapi_xboxone.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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"
33
34
35#ifdef SDL_JOYSTICK_HIDAPI_XBOXONE
36
37#define USB_PACKET_LENGTH 64
38
39/*
40 * This packet is required for all Xbox One pads with 2015
41 * or later firmware installed (or present from the factory).
42 */
43static const Uint8 xboxone_fw2015_init[] = {
44 0x05, 0x20, 0x00, 0x01, 0x00
45};
46
47/*
48 * This packet is required for the Titanfall 2 Xbox One pads
49 * (0x0e6f:0x0165) to finish initialization and for Hori pads
50 * (0x0f0d:0x0067) to make the analog sticks work.
51 */
52static const Uint8 xboxone_hori_init[] = {
53 0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
54 0x00, 0x00, 0x00, 0x80, 0x00
55};
56
57/*
58 * This packet is required for some of the PDP pads to start
59 * sending input reports. These pads include: (0x0e6f:0x02ab),
60 * (0x0e6f:0x02a4).
61 */
62static const Uint8 xboxone_pdp_init1[] = {
63 0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
64};
65
66/*
67 * This packet is required for some of the PDP pads to start
68 * sending input reports. These pads include: (0x0e6f:0x02ab),
69 * (0x0e6f:0x02a4).
70 */
71static const Uint8 xboxone_pdp_init2[] = {
72 0x06, 0x20, 0x00, 0x02, 0x01, 0x00
73};
74
75/*
76 * A specific rumble packet is required for some PowerA pads to start
77 * sending input reports. One of those pads is (0x24c6:0x543a).
78 */
79static const Uint8 xboxone_rumblebegin_init[] = {
80 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
81 0x1D, 0x1D, 0xFF, 0x00, 0x00
82};
83
84/*
85 * A rumble packet with zero FF intensity will immediately
86 * terminate the rumbling required to init PowerA pads.
87 * This should happen fast enough that the motors don't
88 * spin up to enough speed to actually vibrate the gamepad.
89 */
90static const Uint8 xboxone_rumbleend_init[] = {
91 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00
93};
94
95/*
96 * This specifies the selection of init packets that a gamepad
97 * will be sent on init *and* the order in which they will be
98 * sent. The correct sequence number will be added when the
99 * packet is going to be sent.
100 */
101typedef struct {
102 Uint16 vendor_id;
103 Uint16 product_id;
104 const Uint8 *data;
105 int size;
106} SDL_DriverXboxOne_InitPacket;
107
108static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = {
109 { 0x0e6f, 0x0165, xboxone_hori_init, sizeof(xboxone_hori_init) },
110 { 0x0f0d, 0x0067, xboxone_hori_init, sizeof(xboxone_hori_init) },
111 { 0x0000, 0x0000, xboxone_fw2015_init, sizeof(xboxone_fw2015_init) },
112 { 0x0e6f, 0x0246, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
113 { 0x0e6f, 0x0246, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
114 { 0x0e6f, 0x02ab, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
115 { 0x0e6f, 0x02ab, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
116 { 0x0e6f, 0x02a4, xboxone_pdp_init1, sizeof(xboxone_pdp_init1) },
117 { 0x0e6f, 0x02a4, xboxone_pdp_init2, sizeof(xboxone_pdp_init2) },
118 { 0x24c6, 0x541a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
119 { 0x24c6, 0x542a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
120 { 0x24c6, 0x543a, xboxone_rumblebegin_init, sizeof(xboxone_rumblebegin_init) },
121 { 0x24c6, 0x541a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
122 { 0x24c6, 0x542a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
123 { 0x24c6, 0x543a, xboxone_rumbleend_init, sizeof(xboxone_rumbleend_init) },
124};
125
126typedef struct {
127 Uint8 sequence;
128 Uint8 last_state[USB_PACKET_LENGTH];
129 Uint32 rumble_expiration;
130} SDL_DriverXboxOne_Context;
131
132
133static SDL_bool
134HIDAPI_DriverXboxOne_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number)
135{
136 return SDL_IsJoystickXboxOne(vendor_id, product_id);
137}
138
139static const char *
140HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
141{
142 return HIDAPI_XboxControllerName(vendor_id, product_id);
143}
144
145static SDL_bool
146HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
147{
148 SDL_DriverXboxOne_Context *ctx;
149 int i;
150 Uint8 init_packet[USB_PACKET_LENGTH];
151
152 ctx = (SDL_DriverXboxOne_Context *)SDL_calloc(1, sizeof(*ctx));
153 if (!ctx) {
155 return SDL_FALSE;
156 }
157 *context = ctx;
158
159 /* Send the controller init data */
160 for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) {
161 const SDL_DriverXboxOne_InitPacket *packet = &xboxone_init_packets[i];
162 if (!packet->vendor_id || (vendor_id == packet->vendor_id && product_id == packet->product_id)) {
163 SDL_memcpy(init_packet, packet->data, packet->size);
164 init_packet[2] = ctx->sequence++;
165 if (hid_write(dev, init_packet, packet->size) != packet->size) {
166 SDL_SetError("Couldn't write Xbox One initialization packet");
167 SDL_free(ctx);
168 return SDL_FALSE;
169 }
170 }
171 }
172
173 /* Initialize the joystick capabilities */
174 joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
175 joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
176 joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
177
178 return SDL_TRUE;
179}
180
181static int
182HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
183{
184 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
185 Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
186
187 /* The Rock Candy Xbox One Controller limits the range of
188 low frequency rumble strength in the range of [0 - 0x99]
189 high frequency rumble strength in the range of [0 - 0x82]
190
191 I think the valid range of rumble at the firmware level is [0 - 0x7F]
192 */
193 rumble_packet[2] = ctx->sequence++;
194 rumble_packet[8] = (low_frequency_rumble >> 9);
195 rumble_packet[9] = (high_frequency_rumble >> 9);
196
197 if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
198 return SDL_SetError("Couldn't send rumble packet");
199 }
200
201 if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
202 ctx->rumble_expiration = SDL_GetTicks() + duration_ms;
203 } else {
204 ctx->rumble_expiration = 0;
205 }
206 return 0;
207}
208
209static void
210HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
211{
212 Sint16 axis;
213
214 if (ctx->last_state[4] != data[4]) {
221 }
222
223 if (ctx->last_state[5] != data[5]) {
232 }
233
234 axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768;
235 if (axis == 32704) {
236 axis = 32767;
237 }
239 axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768;
240 if (axis == 32704) {
241 axis = 32767;
242 }
244 axis = *(Sint16*)(&data[10]);
246 axis = *(Sint16*)(&data[12]);
248 axis = *(Sint16*)(&data[14]);
250 axis = *(Sint16*)(&data[16]);
252
253 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
254}
255
256static void
257HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size)
258{
259 if (data[1] == 0x30) {
260 /* The Xbox One S controller needs acks for mode reports */
261 const Uint8 seqnum = data[2];
262 const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
263 hid_write(dev, ack, sizeof(ack));
264 }
265
267}
268
269static SDL_bool
270HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
271{
272 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
273 Uint8 data[USB_PACKET_LENGTH];
274 int size;
275
276 while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
277 switch (data[0]) {
278 case 0x20:
279 HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
280 break;
281 case 0x07:
282 HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
283 break;
284 default:
285#ifdef DEBUG_JOYSTICK
286 SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]);
287#endif
288 break;
289 }
290 }
291
292 if (ctx->rumble_expiration) {
293 Uint32 now = SDL_GetTicks();
294 if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
295 HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
296 }
297 }
298
299 return (size >= 0);
300}
301
302static void
303HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
304{
306}
307
309{
311 SDL_TRUE,
312 HIDAPI_DriverXboxOne_IsSupportedDevice,
313 HIDAPI_DriverXboxOne_GetDeviceName,
314 HIDAPI_DriverXboxOne_Init,
315 HIDAPI_DriverXboxOne_Rumble,
316 HIDAPI_DriverXboxOne_Update,
317 HIDAPI_DriverXboxOne_Quit
318};
319
320#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
321
322#endif /* SDL_JOYSTICK_HIDAPI */
323
324/* vi: set ts=4 sw=4 expandtab: */
#define SDL_SetError
#define SDL_free
#define SDL_memcpy
#define SDL_Log
#define SDL_calloc
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_RELEASED
Definition: SDL_events.h:49
#define SDL_PRESSED
Definition: SDL_events.h:50
@ SDL_CONTROLLER_AXIS_LEFTX
@ SDL_CONTROLLER_AXIS_TRIGGERRIGHT
@ SDL_CONTROLLER_AXIS_RIGHTY
@ SDL_CONTROLLER_AXIS_RIGHTX
@ SDL_CONTROLLER_AXIS_MAX
@ SDL_CONTROLLER_AXIS_TRIGGERLEFT
@ SDL_CONTROLLER_AXIS_LEFTY
@ SDL_CONTROLLER_BUTTON_B
@ SDL_CONTROLLER_BUTTON_BACK
@ SDL_CONTROLLER_BUTTON_LEFTSTICK
@ SDL_CONTROLLER_BUTTON_START
@ SDL_CONTROLLER_BUTTON_DPAD_LEFT
@ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ SDL_CONTROLLER_BUTTON_DPAD_DOWN
@ SDL_CONTROLLER_BUTTON_DPAD_UP
@ SDL_CONTROLLER_BUTTON_MAX
@ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
@ SDL_CONTROLLER_BUTTON_GUIDE
@ SDL_CONTROLLER_BUTTON_DPAD_RIGHT
@ SDL_CONTROLLER_BUTTON_X
@ SDL_CONTROLLER_BUTTON_RIGHTSTICK
@ SDL_CONTROLLER_BUTTON_Y
@ SDL_CONTROLLER_BUTTON_A
const char * HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id)
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX
A variable controlling whether the HIDAPI driver for XBox controllers should be used.
Definition: SDL_hints.h:566
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:833
SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor, Uint16 product)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:966
@ SDL_JOYSTICK_POWER_WIRED
Definition: SDL_joystick.h:104
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLsizeiptr size
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
uint32_t Uint32
Definition: SDL_stdinc.h:203
int16_t Sint16
Definition: SDL_stdinc.h:185
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
uint16_t Uint16
Definition: SDL_stdinc.h:191
uint8_t Uint8
Definition: SDL_stdinc.h:179
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
EGLContext ctx
Definition: eglext.h:208
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
struct hid_device_ hid_device
Definition: hidapi.h:50
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length)
Write an Output report to a HID device.
SDL_Texture * axis
static screen_context_t context
Definition: video.c:25