SDL_hidapi_xbox360w.c (12342B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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_events.h" 27 #include "SDL_timer.h" 28 #include "SDL_joystick.h" 29 #include "SDL_gamecontroller.h" 30 #include "../SDL_sysjoystick.h" 31 #include "SDL_hidapijoystick_c.h" 32 #include "SDL_hidapi_rumble.h" 33 34 35 #ifdef SDL_JOYSTICK_HIDAPI_XBOX360 36 37 /* Define this if you want to log all packets from the controller */ 38 /*#define DEBUG_XBOX_PROTOCOL*/ 39 40 41 typedef struct { 42 SDL_bool connected; 43 Uint8 last_state[USB_PACKET_LENGTH]; 44 } SDL_DriverXbox360W_Context; 45 46 47 static SDL_bool 48 HIDAPI_DriverXbox360W_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) 49 { 50 const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */ 51 52 if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == 0x0291 || product_id == 0x02a9 || product_id == 0x0719)) || 53 (type == SDL_CONTROLLER_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) { 54 return SDL_TRUE; 55 } 56 return SDL_FALSE; 57 } 58 59 static const char * 60 HIDAPI_DriverXbox360W_GetDeviceName(Uint16 vendor_id, Uint16 product_id) 61 { 62 return "Xbox 360 Wireless Controller"; 63 } 64 65 static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot) 66 { 67 Uint8 mode = 0x02 + slot; 68 const Uint8 led_packet[] = { 0x00, 0x00, 0x08, (0x40 + (mode % 0x0e)), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 69 70 if (hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) { 71 return SDL_FALSE; 72 } 73 return SDL_TRUE; 74 } 75 76 static void 77 UpdatePowerLevel(SDL_Joystick *joystick, Uint8 level) 78 { 79 float normalized_level = (float)level / 255.0f; 80 81 if (normalized_level <= 0.05f) { 82 joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY; 83 } else if (normalized_level <= 0.20f) { 84 joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW; 85 } else if (normalized_level <= 0.70f) { 86 joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM; 87 } else { 88 joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; 89 } 90 } 91 92 static SDL_bool 93 HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device) 94 { 95 SDL_DriverXbox360W_Context *ctx; 96 97 /* Requests controller presence information from the wireless dongle */ 98 const Uint8 init_packet[] = { 0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 99 100 ctx = (SDL_DriverXbox360W_Context *)SDL_calloc(1, sizeof(*ctx)); 101 if (!ctx) { 102 SDL_OutOfMemory(); 103 return SDL_FALSE; 104 } 105 106 device->dev = hid_open_path(device->path, 0); 107 if (!device->dev) { 108 SDL_free(ctx); 109 SDL_SetError("Couldn't open %s", device->path); 110 return SDL_FALSE; 111 } 112 device->context = ctx; 113 114 if (hid_write(device->dev, init_packet, sizeof(init_packet)) != sizeof(init_packet)) { 115 SDL_SetError("Couldn't write init packet"); 116 return SDL_FALSE; 117 } 118 119 return SDL_TRUE; 120 } 121 122 static int 123 HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 124 { 125 return -1; 126 } 127 128 static void 129 HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 130 { 131 if (!device->dev) { 132 return; 133 } 134 SetSlotLED(device->dev, (player_index % 4)); 135 } 136 137 static SDL_bool 138 HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 139 { 140 SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context; 141 142 SDL_zeroa(ctx->last_state); 143 144 /* Initialize the joystick capabilities */ 145 joystick->nbuttons = 15; 146 joystick->naxes = SDL_CONTROLLER_AXIS_MAX; 147 joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN; 148 149 return SDL_TRUE; 150 } 151 152 static int 153 HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 154 { 155 Uint8 rumble_packet[] = { 0x00, 0x01, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 156 157 rumble_packet[5] = (low_frequency_rumble >> 8); 158 rumble_packet[6] = (high_frequency_rumble >> 8); 159 160 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 161 return SDL_SetError("Couldn't send rumble packet"); 162 } 163 return 0; 164 } 165 166 static int 167 HIDAPI_DriverXbox360W_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 168 { 169 return SDL_Unsupported(); 170 } 171 172 static SDL_bool 173 HIDAPI_DriverXbox360W_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 174 { 175 /* Doesn't have an RGB LED, so don't return true here */ 176 return SDL_FALSE; 177 } 178 179 static int 180 HIDAPI_DriverXbox360W_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 181 { 182 return SDL_Unsupported(); 183 } 184 185 static int 186 HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) 187 { 188 return SDL_Unsupported(); 189 } 190 191 static void 192 HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size) 193 { 194 Sint16 axis; 195 const SDL_bool invert_y_axes = SDL_TRUE; 196 197 if (ctx->last_state[2] != data[2]) { 198 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, (data[2] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 199 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, (data[2] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 200 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, (data[2] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 201 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, (data[2] & 0x08) ? SDL_PRESSED : SDL_RELEASED); 202 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[2] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 203 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[2] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 204 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[2] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 205 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[2] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 206 } 207 208 if (ctx->last_state[3] != data[3]) { 209 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED); 210 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED); 211 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED); 212 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED); 213 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED); 214 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED); 215 SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED); 216 } 217 218 axis = ((int)data[4] * 257) - 32768; 219 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); 220 axis = ((int)data[5] * 257) - 32768; 221 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); 222 axis = *(Sint16*)(&data[6]); 223 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); 224 axis = *(Sint16*)(&data[8]); 225 if (invert_y_axes) { 226 axis = ~axis; 227 } 228 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); 229 axis = *(Sint16*)(&data[10]); 230 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); 231 axis = *(Sint16*)(&data[12]); 232 if (invert_y_axes) { 233 axis = ~axis; 234 } 235 SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); 236 237 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 238 } 239 240 static SDL_bool 241 HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device) 242 { 243 SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context; 244 SDL_Joystick *joystick = NULL; 245 Uint8 data[USB_PACKET_LENGTH]; 246 int size; 247 248 if (device->num_joysticks > 0) { 249 joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); 250 } 251 252 while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 253 #ifdef DEBUG_XBOX_PROTOCOL 254 HIDAPI_DumpPacket("Xbox 360 wireless packet: size = %d", data, size); 255 #endif 256 if (size == 2 && data[0] == 0x08) { 257 SDL_bool connected = (data[1] & 0x80) ? SDL_TRUE : SDL_FALSE; 258 #ifdef DEBUG_JOYSTICK 259 SDL_Log("Connected = %s\n", connected ? "TRUE" : "FALSE"); 260 #endif 261 if (connected != ctx->connected) { 262 ctx->connected = connected; 263 264 if (connected) { 265 SDL_JoystickID joystickID; 266 267 HIDAPI_JoystickConnected(device, &joystickID); 268 269 } else if (device->num_joysticks > 0) { 270 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 271 } 272 } 273 } else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) { 274 /* Serial number is data[7-13] */ 275 #ifdef DEBUG_JOYSTICK 276 SDL_Log("Battery status (initial): %d\n", data[17]); 277 #endif 278 if (joystick) { 279 UpdatePowerLevel(joystick, data[17]); 280 } 281 } else if (size == 29 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x13) { 282 #ifdef DEBUG_JOYSTICK 283 SDL_Log("Battery status: %d\n", data[4]); 284 #endif 285 if (joystick) { 286 UpdatePowerLevel(joystick, data[4]); 287 } 288 } else if (size == 29 && data[0] == 0x00 && (data[1] & 0x01) == 0x01) { 289 if (joystick) { 290 HIDAPI_DriverXbox360W_HandleStatePacket(joystick, device->dev, ctx, data+4, size-4); 291 } 292 } 293 } 294 295 if (joystick) { 296 if (size < 0) { 297 /* Read error, device is disconnected */ 298 HIDAPI_JoystickDisconnected(device, joystick->instance_id); 299 } 300 } 301 return (size >= 0); 302 } 303 304 static void 305 HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 306 { 307 } 308 309 static void 310 HIDAPI_DriverXbox360W_FreeDevice(SDL_HIDAPI_Device *device) 311 { 312 hid_close(device->dev); 313 device->dev = NULL; 314 315 SDL_free(device->context); 316 device->context = NULL; 317 } 318 319 SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W = 320 { 321 SDL_HINT_JOYSTICK_HIDAPI_XBOX, 322 SDL_TRUE, 323 HIDAPI_DriverXbox360W_IsSupportedDevice, 324 HIDAPI_DriverXbox360W_GetDeviceName, 325 HIDAPI_DriverXbox360W_InitDevice, 326 HIDAPI_DriverXbox360W_GetDevicePlayerIndex, 327 HIDAPI_DriverXbox360W_SetDevicePlayerIndex, 328 HIDAPI_DriverXbox360W_UpdateDevice, 329 HIDAPI_DriverXbox360W_OpenJoystick, 330 HIDAPI_DriverXbox360W_RumbleJoystick, 331 HIDAPI_DriverXbox360W_RumbleJoystickTriggers, 332 HIDAPI_DriverXbox360W_HasJoystickLED, 333 HIDAPI_DriverXbox360W_SetJoystickLED, 334 HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled, 335 HIDAPI_DriverXbox360W_CloseJoystick, 336 HIDAPI_DriverXbox360W_FreeDevice, 337 }; 338 339 #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ 340 341 #endif /* SDL_JOYSTICK_HIDAPI */ 342 343 /* vi: set ts=4 sw=4 expandtab: */