Atlas - SDL_hidapi_shield.c
Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 21798 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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_sysjoystick.h" 26#include "SDL_hidapijoystick_c.h" 27#include "SDL_hidapi_rumble.h" 28 29#ifdef SDL_JOYSTICK_HIDAPI_SHIELD 30 31// Define this if you want to log all packets from the controller 32#if 0 33#define DEBUG_SHIELD_PROTOCOL 34#endif 35 36#define CMD_BATTERY_STATE 0x07 37#define CMD_RUMBLE 0x39 38#define CMD_CHARGE_STATE 0x3A 39 40// Milliseconds between polls of battery state 41#define BATTERY_POLL_INTERVAL_MS 60000 42 43// Milliseconds between retransmission of rumble to keep motors running 44#define RUMBLE_REFRESH_INTERVAL_MS 500 45 46// Reports that are too small are dropped over Bluetooth 47#define HID_REPORT_SIZE 33 48 49enum 50{ 51 SDL_GAMEPAD_BUTTON_SHIELD_SHARE = 11, 52 SDL_GAMEPAD_BUTTON_SHIELD_V103_TOUCHPAD, 53 SDL_GAMEPAD_BUTTON_SHIELD_V103_MINUS, 54 SDL_GAMEPAD_BUTTON_SHIELD_V103_PLUS, 55 SDL_GAMEPAD_NUM_SHIELD_V103_BUTTONS, 56 57 SDL_GAMEPAD_NUM_SHIELD_V104_BUTTONS = SDL_GAMEPAD_BUTTON_SHIELD_SHARE + 1, 58}; 59 60typedef enum 61{ 62 k_ShieldReportIdControllerState = 0x01, 63 k_ShieldReportIdControllerTouch = 0x02, 64 k_ShieldReportIdCommandResponse = 0x03, 65 k_ShieldReportIdCommandRequest = 0x04, 66} EShieldReportId; 67 68// This same report structure is used for both requests and responses 69typedef struct 70{ 71 Uint8 report_id; 72 Uint8 cmd; 73 Uint8 seq_num; 74 Uint8 payload[HID_REPORT_SIZE - 3]; 75} ShieldCommandReport_t; 76SDL_COMPILE_TIME_ASSERT(ShieldCommandReport_t, sizeof(ShieldCommandReport_t) == HID_REPORT_SIZE); 77 78typedef struct 79{ 80 Uint8 seq_num; 81 82 bool has_charging; 83 Uint8 charging; 84 bool has_battery_level; 85 Uint8 battery_level; 86 Uint64 last_battery_query_time; 87 88 bool rumble_report_pending; 89 bool rumble_update_pending; 90 Uint8 left_motor_amplitude; 91 Uint8 right_motor_amplitude; 92 Uint64 last_rumble_time; 93 94 Uint8 last_state[USB_PACKET_LENGTH]; 95} SDL_DriverShield_Context; 96 97static void HIDAPI_DriverShield_RegisterHints(SDL_HintCallback callback, void *userdata) 98{ 99 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, callback, userdata); 100} 101 102static void HIDAPI_DriverShield_UnregisterHints(SDL_HintCallback callback, void *userdata) 103{ 104 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, callback, userdata); 105} 106 107static bool HIDAPI_DriverShield_IsEnabled(void) 108{ 109 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_SHIELD, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 110} 111 112static bool HIDAPI_DriverShield_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) 113{ 114 return SDL_IsJoystickNVIDIASHIELDController(vendor_id, product_id); 115} 116 117static bool HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device) 118{ 119 SDL_DriverShield_Context *ctx; 120 121 ctx = (SDL_DriverShield_Context *)SDL_calloc(1, sizeof(*ctx)); 122 if (!ctx) { 123 return false; 124 } 125 device->context = ctx; 126 127 HIDAPI_SetDeviceName(device, "NVIDIA SHIELD Controller"); 128 129 return HIDAPI_JoystickConnected(device, NULL); 130} 131 132static int HIDAPI_DriverShield_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 133{ 134 return -1; 135} 136 137static void HIDAPI_DriverShield_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 138{ 139} 140 141static bool HIDAPI_DriverShield_SendCommand(SDL_HIDAPI_Device *device, Uint8 cmd, const void *data, int size) 142{ 143#ifdef SDL_PLATFORM_MACOS 144 // We hang for several seconds when trying to send output reports on macOS 145 return SDL_Unsupported(); 146 147#else 148 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context; 149 ShieldCommandReport_t cmd_pkt; 150 151 if (size > sizeof(cmd_pkt.payload)) { 152 return SDL_SetError("Command data exceeds HID report size"); 153 } 154 155 if (!SDL_HIDAPI_LockRumble()) { 156 return false; 157 } 158 159 cmd_pkt.report_id = k_ShieldReportIdCommandRequest; 160 cmd_pkt.cmd = cmd; 161 cmd_pkt.seq_num = ctx->seq_num++; 162 if (data) { 163 SDL_memcpy(cmd_pkt.payload, data, size); 164 } 165 166 // Zero unused data in the payload 167 if (size != sizeof(cmd_pkt.payload)) { 168 SDL_memset(&cmd_pkt.payload[size], 0, sizeof(cmd_pkt.payload) - size); 169 } 170 171 if (SDL_HIDAPI_SendRumbleAndUnlock(device, (Uint8 *)&cmd_pkt, sizeof(cmd_pkt)) != sizeof(cmd_pkt)) { 172 return SDL_SetError("Couldn't send command packet"); 173 } 174 175 return true; 176 177#endif // MACOS 178} 179 180static bool HIDAPI_DriverShield_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 181{ 182 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context; 183 184 SDL_AssertJoysticksLocked(); 185 186 ctx->rumble_report_pending = false; 187 ctx->rumble_update_pending = false; 188 ctx->left_motor_amplitude = 0; 189 ctx->right_motor_amplitude = 0; 190 ctx->last_rumble_time = 0; 191 SDL_zeroa(ctx->last_state); 192 193 // Initialize the joystick capabilities 194 if (device->product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) { 195 joystick->nbuttons = SDL_GAMEPAD_NUM_SHIELD_V103_BUTTONS; 196 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 197 joystick->nhats = 1; 198 199 SDL_PrivateJoystickAddTouchpad(joystick, 1); 200 } else { 201 joystick->nbuttons = SDL_GAMEPAD_NUM_SHIELD_V104_BUTTONS; 202 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 203 joystick->nhats = 1; 204 } 205 206 // Request battery and charging info 207 ctx->last_battery_query_time = SDL_GetTicks(); 208 HIDAPI_DriverShield_SendCommand(device, CMD_CHARGE_STATE, NULL, 0); 209 HIDAPI_DriverShield_SendCommand(device, CMD_BATTERY_STATE, NULL, 0); 210 211 return true; 212} 213 214static bool HIDAPI_DriverShield_SendNextRumble(SDL_HIDAPI_Device *device) 215{ 216 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context; 217 Uint8 rumble_data[3]; 218 219 if (!ctx->rumble_update_pending) { 220 return true; 221 } 222 223 rumble_data[0] = 0x01; // enable 224 rumble_data[1] = ctx->left_motor_amplitude; 225 rumble_data[2] = ctx->right_motor_amplitude; 226 227 ctx->rumble_update_pending = false; 228 ctx->last_rumble_time = SDL_GetTicks(); 229 230 return HIDAPI_DriverShield_SendCommand(device, CMD_RUMBLE, rumble_data, sizeof(rumble_data)); 231} 232 233static bool HIDAPI_DriverShield_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 234{ 235 if (device->product_id == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103) { 236 Uint8 rumble_packet[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 237 238 rumble_packet[2] = (low_frequency_rumble >> 8); 239 rumble_packet[4] = (high_frequency_rumble >> 8); 240 241 if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { 242 return SDL_SetError("Couldn't send rumble packet"); 243 } 244 return true; 245 246 } else { 247 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context; 248 249 // The rumble motors are quite intense, so tone down the intensity like the official driver does 250 ctx->left_motor_amplitude = low_frequency_rumble >> 11; 251 ctx->right_motor_amplitude = high_frequency_rumble >> 11; 252 ctx->rumble_update_pending = true; 253 254 if (ctx->rumble_report_pending) { 255 // We will service this after the hardware acknowledges the previous request 256 return true; 257 } 258 259 return HIDAPI_DriverShield_SendNextRumble(device); 260 } 261} 262 263static bool HIDAPI_DriverShield_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 264{ 265 return SDL_Unsupported(); 266} 267 268static Uint32 HIDAPI_DriverShield_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 269{ 270 return SDL_JOYSTICK_CAP_RUMBLE; 271} 272 273static bool HIDAPI_DriverShield_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 274{ 275 return SDL_Unsupported(); 276} 277 278static bool HIDAPI_DriverShield_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) 279{ 280 const Uint8 *data_bytes = (const Uint8 *)data; 281 282 if (size > 1) { 283 // Single command byte followed by a variable length payload 284 return HIDAPI_DriverShield_SendCommand(device, data_bytes[0], &data_bytes[1], size - 1); 285 } else if (size == 1) { 286 // Single command byte with no payload 287 return HIDAPI_DriverShield_SendCommand(device, data_bytes[0], NULL, 0); 288 } else { 289 return SDL_SetError("Effect data must at least contain a command byte"); 290 } 291} 292 293static bool HIDAPI_DriverShield_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 294{ 295 return SDL_Unsupported(); 296} 297 298static void HIDAPI_DriverShield_HandleStatePacketV103(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, Uint8 *data, int size) 299{ 300 Uint64 timestamp = SDL_GetTicksNS(); 301 Sint16 axis; 302 303 if (ctx->last_state[3] != data[3]) { 304 Uint8 hat; 305 306 switch (data[3]) { 307 case 0: 308 hat = SDL_HAT_UP; 309 break; 310 case 1: 311 hat = SDL_HAT_RIGHTUP; 312 break; 313 case 2: 314 hat = SDL_HAT_RIGHT; 315 break; 316 case 3: 317 hat = SDL_HAT_RIGHTDOWN; 318 break; 319 case 4: 320 hat = SDL_HAT_DOWN; 321 break; 322 case 5: 323 hat = SDL_HAT_LEFTDOWN; 324 break; 325 case 6: 326 hat = SDL_HAT_LEFT; 327 break; 328 case 7: 329 hat = SDL_HAT_LEFTUP; 330 break; 331 default: 332 hat = SDL_HAT_CENTERED; 333 break; 334 } 335 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 336 } 337 338 if (ctx->last_state[1] != data[1]) { 339 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[1] & 0x01) != 0)); 340 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[1] & 0x02) != 0)); 341 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[1] & 0x04) != 0)); 342 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[1] & 0x08) != 0)); 343 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[1] & 0x10) != 0)); 344 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[1] & 0x20) != 0)); 345 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[1] & 0x40) != 0)); 346 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[1] & 0x80) != 0)); 347 } 348 349 if (ctx->last_state[2] != data[2]) { 350 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[2] & 0x02) != 0)); 351 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_V103_PLUS, ((data[2] & 0x08) != 0)); 352 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_V103_MINUS, ((data[2] & 0x10) != 0)); 353 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[2] & 0x20) != 0)); 354 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[2] & 0x40) != 0)); 355 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_SHARE, ((data[2] & 0x80) != 0)); 356 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[2] & 0x80) != 0)); 357 } 358 359 axis = LOAD16(data[4], data[5]) - 0x8000; 360 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 361 axis = LOAD16(data[6], data[7]) - 0x8000; 362 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 363 364 axis = LOAD16(data[8], data[9]) - 0x8000; 365 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 366 axis = LOAD16(data[10], data[11]) - 0x8000; 367 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 368 369 axis = LOAD16(data[12], data[13]) - 0x8000; 370 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 371 axis = LOAD16(data[14], data[15]) - 0x8000; 372 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 373 374 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 375} 376 377#undef clamp 378#define clamp(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val))) 379 380static void HIDAPI_DriverShield_HandleTouchPacketV103(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, const Uint8 *data, int size) 381{ 382 bool touchpad_down; 383 float touchpad_x, touchpad_y; 384 Uint64 timestamp = SDL_GetTicksNS(); 385 386 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_V103_TOUCHPAD, ((data[1] & 0x01) != 0)); 387 388 // It's a triangular pad, but just use the center as the usable touch area 389 touchpad_down = ((data[1] & 0x80) == 0); 390 touchpad_x = clamp((float)(data[2] - 0x70) / 0x50, 0.0f, 1.0f); 391 touchpad_y = clamp((float)(data[4] - 0x40) / 0x15, 0.0f, 1.0f); 392 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, touchpad_down, touchpad_x, touchpad_y, touchpad_down ? 1.0f : 0.0f); 393} 394 395static void HIDAPI_DriverShield_HandleStatePacketV104(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, Uint8 *data, int size) 396{ 397 Uint64 timestamp = SDL_GetTicksNS(); 398 Sint16 axis; 399 400 if (size < 23) { 401 return; 402 } 403 404 if (ctx->last_state[2] != data[2]) { 405 Uint8 hat; 406 407 switch (data[2]) { 408 case 0: 409 hat = SDL_HAT_UP; 410 break; 411 case 1: 412 hat = SDL_HAT_RIGHTUP; 413 break; 414 case 2: 415 hat = SDL_HAT_RIGHT; 416 break; 417 case 3: 418 hat = SDL_HAT_RIGHTDOWN; 419 break; 420 case 4: 421 hat = SDL_HAT_DOWN; 422 break; 423 case 5: 424 hat = SDL_HAT_LEFTDOWN; 425 break; 426 case 6: 427 hat = SDL_HAT_LEFT; 428 break; 429 case 7: 430 hat = SDL_HAT_LEFTUP; 431 break; 432 default: 433 hat = SDL_HAT_CENTERED; 434 break; 435 } 436 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 437 } 438 439 if (ctx->last_state[3] != data[3]) { 440 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[3] & 0x01) != 0)); 441 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[3] & 0x02) != 0)); 442 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[3] & 0x04) != 0)); 443 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[3] & 0x08) != 0)); 444 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[3] & 0x10) != 0)); 445 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[3] & 0x20) != 0)); 446 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[3] & 0x40) != 0)); 447 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[3] & 0x80) != 0)); 448 } 449 450 if (ctx->last_state[4] != data[4]) { 451 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[4] & 0x01) != 0)); 452 } 453 454 axis = LOAD16(data[9], data[10]) - 0x8000; 455 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 456 axis = LOAD16(data[11], data[12]) - 0x8000; 457 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 458 459 axis = LOAD16(data[13], data[14]) - 0x8000; 460 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 461 axis = LOAD16(data[15], data[16]) - 0x8000; 462 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 463 464 axis = LOAD16(data[19], data[20]) - 0x8000; 465 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 466 axis = LOAD16(data[21], data[22]) - 0x8000; 467 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 468 469 if (ctx->last_state[17] != data[17]) { 470 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SHIELD_SHARE, ((data[17] & 0x01) != 0)); 471 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[17] & 0x02) != 0)); 472 //SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[17] & 0x04) != 0)); 473 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[17] & 0x01) != 0)); 474 } 475 476 SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); 477} 478 479static void HIDAPI_DriverShield_UpdatePowerInfo(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx) 480{ 481 if (!ctx->has_charging || !ctx->has_battery_level) { 482 return; 483 } 484 485 SDL_PowerState state = ctx->charging ? SDL_POWERSTATE_CHARGING : SDL_POWERSTATE_ON_BATTERY; 486 int percent = ctx->battery_level * 20; 487 SDL_SendJoystickPowerInfo(joystick, state, percent); 488} 489 490static bool HIDAPI_DriverShield_UpdateDevice(SDL_HIDAPI_Device *device) 491{ 492 SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context; 493 SDL_Joystick *joystick = NULL; 494 Uint8 data[USB_PACKET_LENGTH]; 495 int size = 0; 496 ShieldCommandReport_t *cmd_resp_report; 497 498 if (device->num_joysticks > 0) { 499 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 500 } else { 501 return false; 502 } 503 504 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 505#ifdef DEBUG_SHIELD_PROTOCOL 506 HIDAPI_DumpPacket("NVIDIA SHIELD packet: size = %d", data, size); 507#endif 508 if (!joystick) { 509 continue; 510 } 511 512 // Byte 0 is HID report ID 513 switch (data[0]) { 514 case k_ShieldReportIdControllerState: 515 if (size == 16) { 516 HIDAPI_DriverShield_HandleStatePacketV103(joystick, ctx, data, size); 517 } else { 518 HIDAPI_DriverShield_HandleStatePacketV104(joystick, ctx, data, size); 519 } 520 break; 521 case k_ShieldReportIdControllerTouch: 522 HIDAPI_DriverShield_HandleTouchPacketV103(joystick, ctx, data, size); 523 break; 524 case k_ShieldReportIdCommandResponse: 525 cmd_resp_report = (ShieldCommandReport_t *)data; 526 switch (cmd_resp_report->cmd) { 527 case CMD_RUMBLE: 528 ctx->rumble_report_pending = false; 529 HIDAPI_DriverShield_SendNextRumble(device); 530 break; 531 case CMD_CHARGE_STATE: 532 ctx->has_charging = true; 533 ctx->charging = cmd_resp_report->payload[0]; 534 HIDAPI_DriverShield_UpdatePowerInfo(joystick, ctx); 535 break; 536 case CMD_BATTERY_STATE: 537 ctx->has_battery_level = true; 538 ctx->battery_level = cmd_resp_report->payload[2]; 539 HIDAPI_DriverShield_UpdatePowerInfo(joystick, ctx); 540 break; 541 } 542 break; 543 } 544 } 545 546 // Ask for battery state again if we're due for an update 547 if (joystick && SDL_GetTicks() >= (ctx->last_battery_query_time + BATTERY_POLL_INTERVAL_MS)) { 548 ctx->last_battery_query_time = SDL_GetTicks(); 549 HIDAPI_DriverShield_SendCommand(device, CMD_BATTERY_STATE, NULL, 0); 550 } 551 552 // Retransmit rumble packets if they've lasted longer than the hardware supports 553 if ((ctx->left_motor_amplitude != 0 || ctx->right_motor_amplitude != 0) && 554 SDL_GetTicks() >= (ctx->last_rumble_time + RUMBLE_REFRESH_INTERVAL_MS)) { 555 ctx->rumble_update_pending = true; 556 HIDAPI_DriverShield_SendNextRumble(device); 557 } 558 559 if (size < 0) { 560 // Read error, device is disconnected 561 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 562 } 563 return (size >= 0); 564} 565 566static void HIDAPI_DriverShield_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 567{ 568} 569 570static void HIDAPI_DriverShield_FreeDevice(SDL_HIDAPI_Device *device) 571{ 572} 573 574SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverShield = { 575 SDL_HINT_JOYSTICK_HIDAPI_SHIELD, 576 true, 577 HIDAPI_DriverShield_RegisterHints, 578 HIDAPI_DriverShield_UnregisterHints, 579 HIDAPI_DriverShield_IsEnabled, 580 HIDAPI_DriverShield_IsSupportedDevice, 581 HIDAPI_DriverShield_InitDevice, 582 HIDAPI_DriverShield_GetDevicePlayerIndex, 583 HIDAPI_DriverShield_SetDevicePlayerIndex, 584 HIDAPI_DriverShield_UpdateDevice, 585 HIDAPI_DriverShield_OpenJoystick, 586 HIDAPI_DriverShield_RumbleJoystick, 587 HIDAPI_DriverShield_RumbleJoystickTriggers, 588 HIDAPI_DriverShield_GetJoystickCapabilities, 589 HIDAPI_DriverShield_SetJoystickLED, 590 HIDAPI_DriverShield_SendJoystickEffect, 591 HIDAPI_DriverShield_SetJoystickSensorsEnabled, 592 HIDAPI_DriverShield_CloseJoystick, 593 HIDAPI_DriverShield_FreeDevice, 594}; 595 596#endif // SDL_JOYSTICK_HIDAPI_SHIELD 597 598#endif // SDL_JOYSTICK_HIDAPI 599[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.