Atlas - SDL_hidapi_xboxone.c

Home / ext / SDL2 / src / joystick / hidapi Lines: 2 | Size: 12271 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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/* 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, Uint16 usage_page, Uint16 usage) 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) { 154 SDL_OutOfMemory(); 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]) { 215 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 216 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[4] & 0x08) ? SDL_PRESSED : SDL_RELEASED); 217 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[4] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 218 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[4] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 219 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[4] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 220 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[4] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 221 } 222 223 if (ctx->last_state[5] != data[5]) { 224 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[5] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 225 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[5] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 226 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[5] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 227 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[5] & 0x08) ? SDL_PRESSED : SDL_RELEASED); 228 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[5] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 229 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[5] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 230 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[5] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 231 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[5] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 232 } 233 234 axis = ((int)*(Sint16*)(&data[6]) * 64) - 32768; 235 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 236 axis = ((int)*(Sint16*)(&data[8]) * 64) - 32768; 237 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 238 axis = *(Sint16*)(&data[10]); 239 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 240 axis = *(Sint16*)(&data[12]); 241 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, ~axis); 242 axis = *(Sint16*)(&data[14]); 243 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 244 axis = *(Sint16*)(&data[16]); 245 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~axis); 246 247 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 248} 249 250static void 251HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size) 252{ 253 if (data[1] == 0x30) { 254 /* The Xbox One S controller needs acks for mode reports */ 255 const Uint8 seqnum = data[2]; 256 const Uint8 ack[] = { 0x01, 0x20, seqnum, 0x09, 0x00, 0x07, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; 257 hid_write(dev, ack, sizeof(ack)); 258 } 259 260 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 261} 262 263static SDL_bool 264HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context) 265{ 266 SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context; 267 Uint8 data[USB_PACKET_LENGTH]; 268 int size; 269 270 while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { 271 switch (data[0]) { 272 case 0x20: 273 HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size); 274 break; 275 case 0x07: 276 HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size); 277 break; 278 default: 279#ifdef DEBUG_JOYSTICK 280 SDL_Log("Unknown Xbox One packet: 0x%.2x\n", data[0]); 281#endif 282 break; 283 } 284 } 285 286 if (ctx->rumble_expiration) { 287 Uint32 now = SDL_GetTicks(); 288 if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) { 289 HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0); 290 } 291 } 292 293 return (size >= 0); 294} 295 296static void 297HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) 298{ 299 SDL_free(context); 300} 301 302SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = 303{ 304 SDL_HINT_JOYSTICK_HIDAPI_XBOX, 305 SDL_TRUE, 306 HIDAPI_DriverXboxOne_IsSupportedDevice, 307 HIDAPI_DriverXboxOne_GetDeviceName, 308 HIDAPI_DriverXboxOne_Init, 309 HIDAPI_DriverXboxOne_Rumble, 310 HIDAPI_DriverXboxOne_Update, 311 HIDAPI_DriverXboxOne_Quit 312}; 313 314#endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */ 315 316#endif /* SDL_JOYSTICK_HIDAPI */ 317 318/* vi: set ts=4 sw=4 expandtab: */ 319
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.