Atlas - SDL_hidapi_ps5.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 64555 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_hints_c.h" 26#include "../SDL_sysjoystick.h" 27#include "SDL_hidapijoystick_c.h" 28#include "SDL_hidapi_rumble.h" 29 30#ifdef SDL_JOYSTICK_HIDAPI_PS5 31 32// Define this if you want to log all packets from the controller 33#if 0 34#define DEBUG_PS5_PROTOCOL 35#endif 36 37// Define this if you want to log calibration data 38#if 0 39#define DEBUG_PS5_CALIBRATION 40#endif 41 42#define GYRO_RES_PER_DEGREE 1024.0f 43#define ACCEL_RES_PER_G 8192.0f 44#define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500 45 46enum 47{ 48 SDL_GAMEPAD_BUTTON_PS5_TOUCHPAD = 11, 49 SDL_GAMEPAD_BUTTON_PS5_MICROPHONE, 50 SDL_GAMEPAD_BUTTON_PS5_LEFT_FUNCTION, 51 SDL_GAMEPAD_BUTTON_PS5_RIGHT_FUNCTION, 52 SDL_GAMEPAD_BUTTON_PS5_LEFT_PADDLE, 53 SDL_GAMEPAD_BUTTON_PS5_RIGHT_PADDLE 54}; 55 56typedef enum 57{ 58 k_EPS5ReportIdState = 0x01, 59 k_EPS5ReportIdUsbEffects = 0x02, 60 k_EPS5ReportIdBluetoothEffects = 0x31, 61 k_EPS5ReportIdBluetoothState = 0x31, 62} EPS5ReportId; 63 64typedef enum 65{ 66 k_EPS5FeatureReportIdCapabilities = 0x03, 67 k_EPS5FeatureReportIdCalibration = 0x05, 68 k_EPS5FeatureReportIdSerialNumber = 0x09, 69 k_EPS5FeatureReportIdFirmwareInfo = 0x20, 70} EPS5FeatureReportId; 71 72typedef struct 73{ 74 Uint8 ucLeftJoystickX; 75 Uint8 ucLeftJoystickY; 76 Uint8 ucRightJoystickX; 77 Uint8 ucRightJoystickY; 78 Uint8 rgucButtonsHatAndCounter[3]; 79 Uint8 ucTriggerLeft; 80 Uint8 ucTriggerRight; 81} PS5SimpleStatePacket_t; 82 83typedef struct 84{ 85 Uint8 ucLeftJoystickX; // 0 86 Uint8 ucLeftJoystickY; // 1 87 Uint8 ucRightJoystickX; // 2 88 Uint8 ucRightJoystickY; // 3 89 Uint8 ucTriggerLeft; // 4 90 Uint8 ucTriggerRight; // 5 91 Uint8 ucCounter; // 6 92 Uint8 rgucButtonsAndHat[4]; // 7 93 Uint8 rgucPacketSequence[4]; // 11 - 32 bit little endian 94 Uint8 rgucGyroX[2]; // 15 95 Uint8 rgucGyroY[2]; // 17 96 Uint8 rgucGyroZ[2]; // 19 97 Uint8 rgucAccelX[2]; // 21 98 Uint8 rgucAccelY[2]; // 23 99 Uint8 rgucAccelZ[2]; // 25 100 Uint8 rgucSensorTimestamp[4]; // 27 - 16/32 bit little endian 101 102} PS5StatePacketCommon_t; 103 104typedef struct 105{ 106 Uint8 ucLeftJoystickX; // 0 107 Uint8 ucLeftJoystickY; // 1 108 Uint8 ucRightJoystickX; // 2 109 Uint8 ucRightJoystickY; // 3 110 Uint8 ucTriggerLeft; // 4 111 Uint8 ucTriggerRight; // 5 112 Uint8 ucCounter; // 6 113 Uint8 rgucButtonsAndHat[4]; // 7 114 Uint8 rgucPacketSequence[4]; // 11 - 32 bit little endian 115 Uint8 rgucGyroX[2]; // 15 116 Uint8 rgucGyroY[2]; // 17 117 Uint8 rgucGyroZ[2]; // 19 118 Uint8 rgucAccelX[2]; // 21 119 Uint8 rgucAccelY[2]; // 23 120 Uint8 rgucAccelZ[2]; // 25 121 Uint8 rgucSensorTimestamp[4]; // 27 - 32 bit little endian 122 Uint8 ucSensorTemp; // 31 123 Uint8 ucTouchpadCounter1; // 32 - high bit clear + counter 124 Uint8 rgucTouchpadData1[3]; // 33 - X/Y, 12 bits per axis 125 Uint8 ucTouchpadCounter2; // 36 - high bit clear + counter 126 Uint8 rgucTouchpadData2[3]; // 37 - X/Y, 12 bits per axis 127 Uint8 rgucUnknown1[8]; // 40 128 Uint8 rgucTimer2[4]; // 48 - 32 bit little endian 129 Uint8 ucBatteryLevel; // 52 130 Uint8 ucConnectState; // 53 - 0x08 = USB, 0x01 = headphone 131 132 // There's more unknown data at the end, and a 32-bit CRC on Bluetooth 133} PS5StatePacket_t; 134 135typedef struct 136{ 137 Uint8 ucLeftJoystickX; // 0 138 Uint8 ucLeftJoystickY; // 1 139 Uint8 ucRightJoystickX; // 2 140 Uint8 ucRightJoystickY; // 3 141 Uint8 ucTriggerLeft; // 4 142 Uint8 ucTriggerRight; // 5 143 Uint8 ucCounter; // 6 144 Uint8 rgucButtonsAndHat[4]; // 7 145 Uint8 rgucPacketSequence[4]; // 11 - 32 bit little endian 146 Uint8 rgucGyroX[2]; // 15 147 Uint8 rgucGyroY[2]; // 17 148 Uint8 rgucGyroZ[2]; // 19 149 Uint8 rgucAccelX[2]; // 21 150 Uint8 rgucAccelY[2]; // 23 151 Uint8 rgucAccelZ[2]; // 25 152 Uint8 rgucSensorTimestamp[2]; // 27 - 16 bit little endian 153 Uint8 ucBatteryLevel; // 29 154 Uint8 ucUnknown; // 30 155 Uint8 ucTouchpadCounter1; // 31 - high bit clear + counter 156 Uint8 rgucTouchpadData1[3]; // 32 - X/Y, 12 bits per axis 157 Uint8 ucTouchpadCounter2; // 35 - high bit clear + counter 158 Uint8 rgucTouchpadData2[3]; // 36 - X/Y, 12 bits per axis 159 Uint8 rgucDeviceSpecific[8]; // 40 160 161 // There's more unknown data at the end, and a 32-bit CRC on Bluetooth 162} PS5StatePacketAlt_t; 163 164typedef struct 165{ 166 Uint8 ucEnableBits1; // 0 167 Uint8 ucEnableBits2; // 1 168 Uint8 ucRumbleRight; // 2 169 Uint8 ucRumbleLeft; // 3 170 Uint8 ucHeadphoneVolume; // 4 171 Uint8 ucSpeakerVolume; // 5 172 Uint8 ucMicrophoneVolume; // 6 173 Uint8 ucAudioEnableBits; // 7 174 Uint8 ucMicLightMode; // 8 175 Uint8 ucAudioMuteBits; // 9 176 Uint8 rgucRightTriggerEffect[11]; // 10 177 Uint8 rgucLeftTriggerEffect[11]; // 21 178 Uint8 rgucUnknown1[6]; // 32 179 Uint8 ucEnableBits3; // 38 180 Uint8 rgucUnknown2[2]; // 39 181 Uint8 ucLedAnim; // 41 182 Uint8 ucLedBrightness; // 42 183 Uint8 ucPadLights; // 43 184 Uint8 ucLedRed; // 44 185 Uint8 ucLedGreen; // 45 186 Uint8 ucLedBlue; // 46 187} DS5EffectsState_t; 188 189typedef enum 190{ 191 k_EDS5EffectRumbleStart = (1 << 0), 192 k_EDS5EffectRumble = (1 << 1), 193 k_EDS5EffectLEDReset = (1 << 2), 194 k_EDS5EffectLED = (1 << 3), 195 k_EDS5EffectPadLights = (1 << 4), 196 k_EDS5EffectMicLight = (1 << 5) 197} EDS5Effect; 198 199typedef enum 200{ 201 k_EDS5LEDResetStateNone, 202 k_EDS5LEDResetStatePending, 203 k_EDS5LEDResetStateComplete, 204} EDS5LEDResetState; 205 206typedef struct 207{ 208 Sint16 bias; 209 float sensitivity; 210} IMUCalibrationData; 211 212/* Rumble hint mode: 213 * "0": enhanced features are never used 214 * "1": enhanced features are always used 215 * "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it. 216 */ 217typedef enum 218{ 219 PS5_ENHANCED_REPORT_HINT_OFF, 220 PS5_ENHANCED_REPORT_HINT_ON, 221 PS5_ENHANCED_REPORT_HINT_AUTO 222} HIDAPI_PS5_EnhancedReportHint; 223 224typedef struct 225{ 226 SDL_HIDAPI_Device *device; 227 SDL_Joystick *joystick; 228 bool is_dongle; 229 bool use_alternate_report; 230 bool sensors_supported; 231 bool lightbar_supported; 232 bool vibration_supported; 233 bool playerled_supported; 234 bool touchpad_supported; 235 bool effects_supported; 236 bool guitar_whammy_supported; 237 bool guitar_tilt_supported; 238 bool guitar_effects_selector_supported; 239 HIDAPI_PS5_EnhancedReportHint enhanced_report_hint; 240 bool enhanced_reports; 241 bool enhanced_mode; 242 bool enhanced_mode_available; 243 bool report_sensors; 244 bool report_touchpad; 245 bool report_battery; 246 bool hardware_calibration; 247 IMUCalibrationData calibration[6]; 248 Uint16 firmware_version; 249 Uint64 last_packet; 250 int player_index; 251 bool player_lights; 252 bool enhanced_rumble; 253 Uint8 rumble_left; 254 Uint8 rumble_right; 255 bool color_set; 256 Uint8 led_red; 257 Uint8 led_green; 258 Uint8 led_blue; 259 EDS5LEDResetState led_reset_state; 260 Uint64 sensor_ticks; 261 Uint32 last_tick; 262 union 263 { 264 PS5SimpleStatePacket_t simple; 265 PS5StatePacketCommon_t state; 266 PS5StatePacketAlt_t alt_state; 267 PS5StatePacket_t full_state; 268 Uint8 data[64]; 269 } last_state; 270} SDL_DriverPS5_Context; 271 272static bool HIDAPI_DriverPS5_InternalSendJoystickEffect(SDL_DriverPS5_Context *ctx, const void *effect, int size, bool application_usage); 273 274static void HIDAPI_DriverPS5_RegisterHints(SDL_HintCallback callback, void *userdata) 275{ 276 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5, callback, userdata); 277} 278 279static void HIDAPI_DriverPS5_UnregisterHints(SDL_HintCallback callback, void *userdata) 280{ 281 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5, callback, userdata); 282} 283 284static bool HIDAPI_DriverPS5_IsEnabled(void) 285{ 286 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS5, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 287} 288 289static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length) 290{ 291 SDL_memset(report, 0, length); 292 report[0] = report_id; 293 return SDL_hid_get_feature_report(dev, report, length); 294} 295 296static bool HIDAPI_DriverPS5_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) 297{ 298 Uint8 data[USB_PACKET_LENGTH]; 299 int size; 300 301 if (type == SDL_GAMEPAD_TYPE_PS5) { 302 return true; 303 } 304 305 if (HIDAPI_SupportsPlaystationDetection(vendor_id, product_id)) { 306 if (device && device->dev) { 307 size = ReadFeatureReport(device->dev, k_EPS5FeatureReportIdCapabilities, data, sizeof(data)); 308 if (size == 48 && data[2] == 0x28) { 309 // Supported third party controller 310 return true; 311 } else { 312 return false; 313 } 314 } else { 315 // Might be supported by this driver, enumerate and find out 316 return true; 317 } 318 } 319 return false; 320} 321 322static void SetLedsForPlayerIndex(DS5EffectsState_t *effects, int player_index) 323{ 324 /* This list is the same as what hid-sony.c uses in the Linux kernel. 325 The first 4 values correspond to what the PS4 assigns. 326 */ 327 static const Uint8 colors[7][3] = { 328 { 0x00, 0x00, 0x40 }, // Blue 329 { 0x40, 0x00, 0x00 }, // Red 330 { 0x00, 0x40, 0x00 }, // Green 331 { 0x20, 0x00, 0x20 }, // Pink 332 { 0x20, 0x10, 0x00 }, // Orange 333 { 0x00, 0x10, 0x10 }, // Teal 334 { 0x10, 0x10, 0x10 } // White 335 }; 336 337 if (player_index >= 0) { 338 player_index %= SDL_arraysize(colors); 339 } else { 340 player_index = 0; 341 } 342 343 effects->ucLedRed = colors[player_index][0]; 344 effects->ucLedGreen = colors[player_index][1]; 345 effects->ucLedBlue = colors[player_index][2]; 346} 347 348static void SetLightsForPlayerIndex(DS5EffectsState_t *effects, int player_index) 349{ 350 static const Uint8 lights[] = { 351 0x04, 352 0x0A, 353 0x15, 354 0x1B, 355 0x1F 356 }; 357 358 if (player_index >= 0) { 359 // Bitmask, 0x1F enables all lights, 0x20 changes instantly instead of fade 360 player_index %= SDL_arraysize(lights); 361 effects->ucPadLights = lights[player_index] | 0x20; 362 } else { 363 effects->ucPadLights = 0x00; 364 } 365} 366 367static bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device) 368{ 369 SDL_DriverPS5_Context *ctx; 370 Uint8 data[USB_PACKET_LENGTH * 2]; 371 int size; 372 char serial[18]; 373 SDL_JoystickType joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD; 374 375 ctx = (SDL_DriverPS5_Context *)SDL_calloc(1, sizeof(*ctx)); 376 if (!ctx) { 377 return false; 378 } 379 ctx->device = device; 380 381 device->context = ctx; 382 383 if (device->serial && SDL_strlen(device->serial) == 12) { 384 int i, j; 385 386 j = -1; 387 for (i = 0; i < 12; i += 2) { 388 j += 1; 389 SDL_memmove(&serial[j], &device->serial[i], 2); 390 j += 2; 391 serial[j] = '-'; 392 } 393 serial[j] = '\0'; 394 } else { 395 serial[0] = '\0'; 396 } 397 398 // Read a report to see what mode we're in 399 size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 16); 400#ifdef DEBUG_PS5_PROTOCOL 401 if (size > 0) { 402 HIDAPI_DumpPacket("PS5 first packet: size = %d", data, size); 403 } else { 404 SDL_Log("PS5 first packet: size = %d", size); 405 } 406#endif 407 if (size == 64) { 408 // Connected over USB 409 ctx->enhanced_reports = true; 410 } else if (size > 0 && data[0] == k_EPS5ReportIdBluetoothEffects) { 411 // Connected over Bluetooth, using enhanced reports 412 ctx->enhanced_reports = true; 413 } else { 414 // Connected over Bluetooth, using simple reports (DirectInput enabled) 415 } 416 417 if (device->vendor_id == USB_VENDOR_SONY && ctx->enhanced_reports) { 418 /* Read the serial number (Bluetooth address in reverse byte order) 419 This will also enable enhanced reports over Bluetooth 420 */ 421 if (ReadFeatureReport(device->dev, k_EPS5FeatureReportIdSerialNumber, data, sizeof(data)) >= 7) { 422 (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", 423 data[6], data[5], data[4], data[3], data[2], data[1]); 424 } 425 426 /* Read the firmware version 427 This will also enable enhanced reports over Bluetooth 428 */ 429 if (ReadFeatureReport(device->dev, k_EPS5FeatureReportIdFirmwareInfo, data, USB_PACKET_LENGTH) >= 46) { 430 ctx->firmware_version = (Uint16)data[44] | ((Uint16)data[45] << 8); 431 } 432 } 433 434 if (device->vendor_id == USB_VENDOR_SONY) { 435 if (device->product_id == USB_PRODUCT_SONY_DS5_EDGE || 436 ctx->firmware_version == 0 || // Assume that it's updated firmware over Bluetooth 437 ctx->firmware_version >= 0x0224) { 438 ctx->enhanced_rumble = true; 439 } 440 } 441 442 // Get the device capabilities 443 if (device->vendor_id == USB_VENDOR_SONY) { 444 ctx->sensors_supported = true; 445 ctx->lightbar_supported = true; 446 ctx->vibration_supported = true; 447 ctx->playerled_supported = true; 448 ctx->touchpad_supported = true; 449 } else { 450 // Third party controller capability request 451 size = ReadFeatureReport(device->dev, k_EPS5FeatureReportIdCapabilities, data, sizeof(data)); 452 if (size == 48 && data[2] == 0x28) { 453 Uint8 capabilities = data[4]; 454 Uint8 capabilities2 = data[20]; 455 Uint8 device_specific_capabilities = data[24]; 456 Uint8 device_type = data[5]; 457 458#ifdef DEBUG_PS5_PROTOCOL 459 HIDAPI_DumpPacket("PS5 capabilities: size = %d", data, size); 460#endif 461 if (capabilities & 0x02) { 462 ctx->sensors_supported = true; 463 } 464 if (capabilities & 0x04) { 465 ctx->lightbar_supported = true; 466 } 467 if (capabilities & 0x08) { 468 ctx->vibration_supported = true; 469 } 470 if (capabilities & 0x40) { 471 ctx->touchpad_supported = true; 472 } 473 if (capabilities2 & 0x80) { 474 ctx->playerled_supported = true; 475 } 476 477 if (capabilities2 & 0x01) { 478 ctx->report_battery = true; 479 } 480 481 switch (device_type) { 482 case 0x00: 483 joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD; 484 break; 485 case 0x01: 486 joystick_type = SDL_JOYSTICK_TYPE_GUITAR; 487 if (device_specific_capabilities & 0x01) { 488 ctx->guitar_effects_selector_supported = true; 489 } 490 if (device_specific_capabilities & 0x02) { 491 ctx->guitar_tilt_supported = true; 492 } 493 if (device_specific_capabilities & 0x04) { 494 ctx->guitar_whammy_supported = true; 495 } 496 break; 497 case 0x02: 498 joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT; 499 break; 500 case 0x06: 501 joystick_type = SDL_JOYSTICK_TYPE_WHEEL; 502 break; 503 case 0x07: 504 joystick_type = SDL_JOYSTICK_TYPE_ARCADE_STICK; 505 break; 506 case 0x08: 507 joystick_type = SDL_JOYSTICK_TYPE_FLIGHT_STICK; 508 break; 509 default: 510 joystick_type = SDL_JOYSTICK_TYPE_UNKNOWN; 511 break; 512 } 513 514 ctx->use_alternate_report = true; 515 ctx->report_battery = true; 516 517 if (device->vendor_id == USB_VENDOR_NACON_ALT && 518 (device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRED || 519 device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS)) { 520 // This doesn't report vibration capability, but it can do rumble 521 ctx->vibration_supported = true; 522 } 523 } else if (device->vendor_id == USB_VENDOR_RAZER && 524 (device->product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_PS5_WIRED || 525 device->product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_PS5_WIRELESS)) { 526 // The Razer Wolverine V2 Pro doesn't respond to the detection protocol, but has a touchpad and sensors and no vibration 527 ctx->sensors_supported = true; 528 ctx->touchpad_supported = true; 529 ctx->use_alternate_report = true; 530 } else if (device->vendor_id == USB_VENDOR_RAZER && 531 (device->product_id == USB_PRODUCT_RAZER_KITSUNE || 532 device->product_id == USB_PRODUCT_RAZER_RAIJU_V3_PRO_PS5_WIRED || 533 device->product_id == USB_PRODUCT_RAZER_RAIJU_V3_PRO_PS5_WIRELESS)) { 534 // The Razer Kitsune and Raiju don't respond to the detection protocol, but have a touchpad 535 joystick_type = SDL_JOYSTICK_TYPE_ARCADE_STICK; 536 ctx->touchpad_supported = true; 537 ctx->use_alternate_report = true; 538 } 539 } 540 ctx->effects_supported = (ctx->lightbar_supported || ctx->vibration_supported || ctx->playerled_supported); 541 542 if ((device->vendor_id == USB_VENDOR_NACON_ALT && 543 device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS) || 544 (device->vendor_id == USB_VENDOR_RAZER && 545 (device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS5_WIRELESS || 546 device->product_id == USB_PRODUCT_RAZER_WOLVERINE_V2_PRO_PS5_WIRELESS || 547 device->product_id == USB_PRODUCT_RAZER_RAIJU_V3_PRO_PS5_WIRELESS))) { 548 ctx->is_dongle = true; 549 } 550 551 device->joystick_type = joystick_type; 552 device->type = SDL_GAMEPAD_TYPE_PS5; 553 if (device->vendor_id == USB_VENDOR_SONY) { 554 if (SDL_IsJoystickDualSenseEdge(device->vendor_id, device->product_id)) { 555 HIDAPI_SetDeviceName(device, "DualSense Edge Wireless Controller"); 556 } else { 557 HIDAPI_SetDeviceName(device, "DualSense Wireless Controller"); 558 } 559 } 560 HIDAPI_SetDeviceSerial(device, serial); 561 562 if (ctx->is_dongle) { 563 // We don't know if this is connected yet, wait for reports 564 return true; 565 } 566 567 // Prefer the USB device over the Bluetooth device 568 if (device->is_bluetooth) { 569 if (HIDAPI_HasConnectedUSBDevice(device->serial)) { 570 return true; 571 } 572 } else { 573 HIDAPI_DisconnectBluetoothDevice(device->serial); 574 } 575 return HIDAPI_JoystickConnected(device, NULL); 576} 577 578static int HIDAPI_DriverPS5_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 579{ 580 return -1; 581} 582 583static void HIDAPI_DriverPS5_LoadCalibrationData(SDL_HIDAPI_Device *device) 584{ 585 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 586 int i, size; 587 Uint8 data[USB_PACKET_LENGTH]; 588 589 size = ReadFeatureReport(device->dev, k_EPS5FeatureReportIdCalibration, data, sizeof(data)); 590 if (size < 35) { 591#ifdef DEBUG_PS5_CALIBRATION 592 SDL_Log("Short read of calibration data: %d, ignoring calibration", size); 593#endif 594 return; 595 } 596 597 { 598 Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias; 599 Sint16 sGyroPitchPlus, sGyroPitchMinus; 600 Sint16 sGyroYawPlus, sGyroYawMinus; 601 Sint16 sGyroRollPlus, sGyroRollMinus; 602 Sint16 sGyroSpeedPlus, sGyroSpeedMinus; 603 604 Sint16 sAccXPlus, sAccXMinus; 605 Sint16 sAccYPlus, sAccYMinus; 606 Sint16 sAccZPlus, sAccZMinus; 607 608 float flNumerator; 609 Sint16 sRange2g; 610 611#ifdef DEBUG_PS5_CALIBRATION 612 HIDAPI_DumpPacket("PS5 calibration packet: size = %d", data, size); 613#endif 614 615 sGyroPitchBias = LOAD16(data[1], data[2]); 616 sGyroYawBias = LOAD16(data[3], data[4]); 617 sGyroRollBias = LOAD16(data[5], data[6]); 618 619 sGyroPitchPlus = LOAD16(data[7], data[8]); 620 sGyroPitchMinus = LOAD16(data[9], data[10]); 621 sGyroYawPlus = LOAD16(data[11], data[12]); 622 sGyroYawMinus = LOAD16(data[13], data[14]); 623 sGyroRollPlus = LOAD16(data[15], data[16]); 624 sGyroRollMinus = LOAD16(data[17], data[18]); 625 626 sGyroSpeedPlus = LOAD16(data[19], data[20]); 627 sGyroSpeedMinus = LOAD16(data[21], data[22]); 628 629 sAccXPlus = LOAD16(data[23], data[24]); 630 sAccXMinus = LOAD16(data[25], data[26]); 631 sAccYPlus = LOAD16(data[27], data[28]); 632 sAccYMinus = LOAD16(data[29], data[30]); 633 sAccZPlus = LOAD16(data[31], data[32]); 634 sAccZMinus = LOAD16(data[33], data[34]); 635 636 flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE; 637 ctx->calibration[0].bias = sGyroPitchBias; 638 ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus); 639 640 ctx->calibration[1].bias = sGyroYawBias; 641 ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus); 642 643 ctx->calibration[2].bias = sGyroRollBias; 644 ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus); 645 646 sRange2g = sAccXPlus - sAccXMinus; 647 ctx->calibration[3].bias = sAccXPlus - sRange2g / 2; 648 ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g; 649 650 sRange2g = sAccYPlus - sAccYMinus; 651 ctx->calibration[4].bias = sAccYPlus - sRange2g / 2; 652 ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g; 653 654 sRange2g = sAccZPlus - sAccZMinus; 655 ctx->calibration[5].bias = sAccZPlus - sRange2g / 2; 656 ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g; 657 658 ctx->hardware_calibration = true; 659 for (i = 0; i < 6; ++i) { 660 float divisor = (i < 3 ? 64.0f : 1.0f); 661#ifdef DEBUG_PS5_CALIBRATION 662 SDL_Log("calibration[%d] bias = %d, sensitivity = %f", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity); 663#endif 664 // Some controllers have a bad calibration 665 if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabsf(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) { 666#ifdef DEBUG_PS5_CALIBRATION 667 SDL_Log("invalid calibration, ignoring"); 668#endif 669 ctx->hardware_calibration = false; 670 } 671 } 672 } 673} 674 675static float HIDAPI_DriverPS5_ApplyCalibrationData(SDL_DriverPS5_Context *ctx, int index, Sint16 value) 676{ 677 float result; 678 679 if (ctx->hardware_calibration) { 680 IMUCalibrationData *calibration = &ctx->calibration[index]; 681 682 result = (value - calibration->bias) * calibration->sensitivity; 683 } else if (index < 3) { 684 result = value * 64.f; 685 } else { 686 result = value; 687 } 688 689 // Convert the raw data to the units expected by SDL 690 if (index < 3) { 691 result = (result / GYRO_RES_PER_DEGREE) * SDL_PI_F / 180.0f; 692 } else { 693 result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; 694 } 695 return result; 696} 697 698static bool HIDAPI_DriverPS5_UpdateEffects(SDL_DriverPS5_Context *ctx, int effect_mask, bool application_usage) 699{ 700 DS5EffectsState_t effects; 701 702 // Make sure the Bluetooth connection sequence has completed before sending LED color change 703 if (ctx->device->is_bluetooth && ctx->enhanced_reports && 704 (effect_mask & (k_EDS5EffectLED | k_EDS5EffectPadLights)) != 0) { 705 if (ctx->led_reset_state != k_EDS5LEDResetStateComplete) { 706 ctx->led_reset_state = k_EDS5LEDResetStatePending; 707 return true; 708 } 709 } 710 711 SDL_zero(effects); 712 713 if (ctx->vibration_supported) { 714 if (ctx->rumble_left || ctx->rumble_right) { 715 if (ctx->enhanced_rumble) { 716 effects.ucEnableBits3 |= 0x04; // Enable improved rumble emulation on 2.24 firmware and newer 717 718 effects.ucRumbleLeft = ctx->rumble_left; 719 effects.ucRumbleRight = ctx->rumble_right; 720 } else { 721 effects.ucEnableBits1 |= 0x01; // Enable rumble emulation 722 723 // Shift to reduce effective rumble strength to match Xbox controllers 724 effects.ucRumbleLeft = ctx->rumble_left >> 1; 725 effects.ucRumbleRight = ctx->rumble_right >> 1; 726 } 727 effects.ucEnableBits1 |= 0x02; // Disable audio haptics 728 } else { 729 // Leaving emulated rumble bits off will restore audio haptics 730 } 731 732 if ((effect_mask & k_EDS5EffectRumbleStart) != 0) { 733 effects.ucEnableBits1 |= 0x02; // Disable audio haptics 734 } 735 if ((effect_mask & k_EDS5EffectRumble) != 0) { 736 // Already handled above 737 } 738 } 739 if (ctx->lightbar_supported) { 740 if ((effect_mask & k_EDS5EffectLEDReset) != 0) { 741 effects.ucEnableBits2 |= 0x08; // Reset LED state 742 } 743 if ((effect_mask & k_EDS5EffectLED) != 0) { 744 effects.ucEnableBits2 |= 0x04; // Enable LED color 745 746 // Populate the LED state with the appropriate color from our lookup table 747 if (ctx->color_set) { 748 effects.ucLedRed = ctx->led_red; 749 effects.ucLedGreen = ctx->led_green; 750 effects.ucLedBlue = ctx->led_blue; 751 } else { 752 SetLedsForPlayerIndex(&effects, ctx->player_index); 753 } 754 } 755 } 756 if (ctx->playerled_supported) { 757 if ((effect_mask & k_EDS5EffectPadLights) != 0) { 758 effects.ucEnableBits2 |= 0x10; // Enable touchpad lights 759 760 if (ctx->player_lights) { 761 SetLightsForPlayerIndex(&effects, ctx->player_index); 762 } else { 763 effects.ucPadLights = 0x00; 764 } 765 } 766 } 767 if ((effect_mask & k_EDS5EffectMicLight) != 0) { 768 effects.ucEnableBits2 |= 0x01; // Enable microphone light 769 770 effects.ucMicLightMode = 0; // Bitmask, 0x00 = off, 0x01 = solid, 0x02 = pulse 771 } 772 773 return HIDAPI_DriverPS5_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage); 774} 775 776static void HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_DriverPS5_Context *ctx) 777{ 778 bool led_reset_complete = false; 779 780 if (ctx->enhanced_reports && ctx->sensors_supported && !ctx->use_alternate_report) { 781 const PS5StatePacketCommon_t *packet = &ctx->last_state.state; 782 783 // Check the timer to make sure the Bluetooth connection LED animation is complete 784 const Uint32 connection_complete = 10200000; 785 Uint32 timestamp = LOAD32(packet->rgucSensorTimestamp[0], 786 packet->rgucSensorTimestamp[1], 787 packet->rgucSensorTimestamp[2], 788 packet->rgucSensorTimestamp[3]); 789 if (timestamp >= connection_complete) { 790 led_reset_complete = true; 791 } 792 } else { 793 // We don't know how to check the timer, just assume it's complete for now 794 led_reset_complete = true; 795 } 796 797 if (led_reset_complete) { 798 HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectLEDReset, false); 799 800 ctx->led_reset_state = k_EDS5LEDResetStateComplete; 801 802 HIDAPI_DriverPS5_UpdateEffects(ctx, (k_EDS5EffectLED | k_EDS5EffectPadLights), false); 803 } 804} 805 806static void HIDAPI_DriverPS5_TickleBluetooth(SDL_HIDAPI_Device *device) 807{ 808 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 809 810 if (ctx->enhanced_reports) { 811 // This is just a dummy packet that should have no effect, since we don't set the CRC 812 Uint8 data[78]; 813 814 SDL_zeroa(data); 815 816 data[0] = k_EPS5ReportIdBluetoothEffects; 817 data[1] = 0x02; // Magic value 818 819 if (SDL_HIDAPI_LockRumble()) { 820 SDL_HIDAPI_SendRumbleAndUnlock(device, data, sizeof(data)); 821 } 822 } else { 823 // We can't even send an invalid effects packet, or it will put the controller in enhanced mode 824 if (device->num_joysticks > 0) { 825 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 826 } 827 } 828} 829 830static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx) 831{ 832 if (ctx->enhanced_mode_available) { 833 return; 834 } 835 ctx->enhanced_mode_available = true; 836 837 if (ctx->touchpad_supported) { 838 SDL_PrivateJoystickAddTouchpad(ctx->joystick, 2); 839 ctx->report_touchpad = true; 840 } 841 842 if (ctx->sensors_supported) { 843 // Standard DualSense sensor update rate is 250 Hz over USB 844 float update_rate = 250.0f; 845 846 if (ctx->device->is_bluetooth) { 847 // Bluetooth sensor update rate appears to be 1000 Hz 848 update_rate = 1000.0f; 849 } else if (SDL_IsJoystickDualSenseEdge(ctx->device->vendor_id, ctx->device->product_id)) { 850 // DualSense Edge sensor update rate is 1000 Hz over USB 851 update_rate = 1000.0f; 852 } 853 854 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, update_rate); 855 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, update_rate); 856 } 857 858 if (ctx->guitar_tilt_supported) { 859 float update_rate = 250.0f; 860 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, update_rate); 861 } 862 863 ctx->report_battery = true; 864 865 HIDAPI_UpdateDeviceProperties(ctx->device); 866} 867 868static void HIDAPI_DriverPS5_SetEnhancedMode(SDL_DriverPS5_Context *ctx) 869{ 870 HIDAPI_DriverPS5_SetEnhancedModeAvailable(ctx); 871 872 if (!ctx->enhanced_mode) { 873 ctx->enhanced_mode = true; 874 875 // Switch into enhanced report mode 876 HIDAPI_DriverPS5_UpdateEffects(ctx, 0, false); 877 878 // Update the light effects 879 HIDAPI_DriverPS5_UpdateEffects(ctx, (k_EDS5EffectLED | k_EDS5EffectPadLights), false); 880 } 881} 882 883static void HIDAPI_DriverPS5_SetEnhancedReportHint(SDL_DriverPS5_Context *ctx, HIDAPI_PS5_EnhancedReportHint enhanced_report_hint) 884{ 885 switch (enhanced_report_hint) { 886 case PS5_ENHANCED_REPORT_HINT_OFF: 887 // Nothing to do, enhanced mode is a one-way ticket 888 break; 889 case PS5_ENHANCED_REPORT_HINT_ON: 890 HIDAPI_DriverPS5_SetEnhancedMode(ctx); 891 break; 892 case PS5_ENHANCED_REPORT_HINT_AUTO: 893 HIDAPI_DriverPS5_SetEnhancedModeAvailable(ctx); 894 break; 895 } 896 ctx->enhanced_report_hint = enhanced_report_hint; 897} 898 899static void HIDAPI_DriverPS5_UpdateEnhancedModeOnEnhancedReport(SDL_DriverPS5_Context *ctx) 900{ 901 ctx->enhanced_reports = true; 902 903 if (ctx->enhanced_report_hint == PS5_ENHANCED_REPORT_HINT_AUTO) { 904 HIDAPI_DriverPS5_SetEnhancedReportHint(ctx, PS5_ENHANCED_REPORT_HINT_ON); 905 } 906} 907 908static void HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(SDL_DriverPS5_Context *ctx) 909{ 910 if (ctx->enhanced_report_hint == PS5_ENHANCED_REPORT_HINT_AUTO) { 911 HIDAPI_DriverPS5_SetEnhancedReportHint(ctx, PS5_ENHANCED_REPORT_HINT_ON); 912 } 913} 914 915static void SDLCALL SDL_PS5EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 916{ 917 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)userdata; 918 919 if (ctx->device->is_bluetooth) { 920 if (hint && SDL_strcasecmp(hint, "auto") == 0) { 921 HIDAPI_DriverPS5_SetEnhancedReportHint(ctx, PS5_ENHANCED_REPORT_HINT_AUTO); 922 } else if (SDL_GetStringBoolean(hint, true)) { 923 HIDAPI_DriverPS5_SetEnhancedReportHint(ctx, PS5_ENHANCED_REPORT_HINT_ON); 924 } else { 925 HIDAPI_DriverPS5_SetEnhancedReportHint(ctx, PS5_ENHANCED_REPORT_HINT_OFF); 926 } 927 } else { 928 HIDAPI_DriverPS5_SetEnhancedReportHint(ctx, PS5_ENHANCED_REPORT_HINT_ON); 929 } 930} 931 932static void SDLCALL SDL_PS5PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 933{ 934 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)userdata; 935 bool player_lights = SDL_GetStringBoolean(hint, true); 936 937 if (player_lights != ctx->player_lights) { 938 ctx->player_lights = player_lights; 939 940 HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectPadLights, false); 941 } 942} 943 944static void HIDAPI_DriverPS5_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 945{ 946 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 947 948 if (!ctx->joystick) { 949 return; 950 } 951 952 ctx->player_index = player_index; 953 954 // This will set the new LED state based on the new player index 955 // SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode 956 HIDAPI_DriverPS5_UpdateEffects(ctx, (k_EDS5EffectLED | k_EDS5EffectPadLights), false); 957} 958 959static bool HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 960{ 961 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 962 963 SDL_AssertJoysticksLocked(); 964 965 ctx->joystick = joystick; 966 ctx->last_packet = SDL_GetTicks(); 967 ctx->report_sensors = false; 968 ctx->report_touchpad = false; 969 ctx->rumble_left = 0; 970 ctx->rumble_right = 0; 971 ctx->color_set = false; 972 ctx->led_reset_state = k_EDS5LEDResetStateNone; 973 SDL_zero(ctx->last_state); 974 975 // Initialize player index (needed for setting LEDs) 976 ctx->player_index = SDL_GetJoystickPlayerIndex(joystick); 977 ctx->player_lights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, true); 978 979 // Initialize the joystick capabilities 980 if (SDL_IsJoystickDualSenseEdge(device->vendor_id, device->product_id)) { 981 joystick->nbuttons = 17; // paddles and touchpad and microphone 982 } else if (ctx->touchpad_supported) { 983 joystick->nbuttons = 13; // touchpad and microphone 984 } else { 985 joystick->nbuttons = 11; 986 } 987 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 988 joystick->nhats = 1; 989 joystick->firmware_version = ctx->firmware_version; 990 991 if (ctx->is_dongle) { 992 joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS; 993 } 994 995 SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, 996 SDL_PS5EnhancedReportsChanged, ctx); 997 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, 998 SDL_PS5PlayerLEDHintChanged, ctx); 999 1000 return true; 1001} 1002 1003static bool HIDAPI_DriverPS5_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 1004{ 1005 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1006 1007 if (!ctx->vibration_supported) { 1008 return SDL_Unsupported(); 1009 } 1010 1011 if (!ctx->rumble_left && !ctx->rumble_right) { 1012 HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectRumbleStart, true); 1013 } 1014 1015 ctx->rumble_left = (low_frequency_rumble >> 8); 1016 ctx->rumble_right = (high_frequency_rumble >> 8); 1017 1018 return HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectRumble, true); 1019} 1020 1021static bool HIDAPI_DriverPS5_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 1022{ 1023 return SDL_Unsupported(); 1024} 1025 1026static Uint32 HIDAPI_DriverPS5_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1027{ 1028 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1029 Uint32 result = 0; 1030 1031 if (ctx->enhanced_mode_available) { 1032 if (ctx->lightbar_supported) { 1033 result |= SDL_JOYSTICK_CAP_RGB_LED; 1034 } 1035 if (ctx->playerled_supported) { 1036 result |= SDL_JOYSTICK_CAP_PLAYER_LED; 1037 } 1038 if (ctx->vibration_supported) { 1039 result |= SDL_JOYSTICK_CAP_RUMBLE; 1040 } 1041 } 1042 1043 return result; 1044} 1045 1046static bool HIDAPI_DriverPS5_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 1047{ 1048 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1049 1050 if (!ctx->lightbar_supported) { 1051 return SDL_Unsupported(); 1052 } 1053 1054 ctx->color_set = true; 1055 ctx->led_red = red; 1056 ctx->led_green = green; 1057 ctx->led_blue = blue; 1058 1059 return HIDAPI_DriverPS5_UpdateEffects(ctx, k_EDS5EffectLED, true); 1060} 1061 1062static bool HIDAPI_DriverPS5_InternalSendJoystickEffect(SDL_DriverPS5_Context *ctx, const void *effect, int size, bool application_usage) 1063{ 1064 Uint8 data[78]; 1065 int report_size, offset; 1066 Uint8 *pending_data; 1067 int *pending_size; 1068 int maximum_size; 1069 1070 if (!ctx->effects_supported) { 1071 // We shouldn't be sending packets to this controller 1072 return SDL_Unsupported(); 1073 } 1074 1075 if (!ctx->enhanced_mode) { 1076 if (application_usage) { 1077 HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx); 1078 1079 // Wait briefly before sending additional effects 1080 SDL_Delay(10); 1081 } 1082 1083 if (!ctx->enhanced_mode) { 1084 // We're not in enhanced mode, effects aren't allowed 1085 return SDL_Unsupported(); 1086 } 1087 } 1088 1089 SDL_zeroa(data); 1090 1091 if (ctx->device->is_bluetooth) { 1092 data[0] = k_EPS5ReportIdBluetoothEffects; 1093 data[1] = 0x00; // Tag and sequence 1094 data[2] = 0x10; // Magic value 1095 1096 report_size = 78; 1097 offset = 3; 1098 } else { 1099 data[0] = k_EPS5ReportIdUsbEffects; 1100 1101 report_size = 48; 1102 offset = 1; 1103 } 1104 1105 SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size)); 1106 1107 if (ctx->device->is_bluetooth) { 1108 // Bluetooth reports need a CRC at the end of the packet (at least on Linux) 1109 Uint8 ubHdr = 0xA2; // hidp header is part of the CRC calculation 1110 Uint32 unCRC; 1111 unCRC = SDL_crc32(0, &ubHdr, 1); 1112 unCRC = SDL_crc32(unCRC, data, (size_t)(report_size - sizeof(unCRC))); 1113 SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC)); 1114 } 1115 1116 if (!SDL_HIDAPI_LockRumble()) { 1117 return false; 1118 } 1119 1120 // See if we can update an existing pending request 1121 if (SDL_HIDAPI_GetPendingRumbleLocked(ctx->device, &pending_data, &pending_size, &maximum_size)) { 1122 DS5EffectsState_t *effects = (DS5EffectsState_t *)&data[offset]; 1123 DS5EffectsState_t *pending_effects = (DS5EffectsState_t *)&pending_data[offset]; 1124 if (report_size == *pending_size && 1125 effects->ucEnableBits1 == pending_effects->ucEnableBits1 && 1126 effects->ucEnableBits2 == pending_effects->ucEnableBits2) { 1127 // We're simply updating the data for this request 1128 SDL_memcpy(pending_data, data, report_size); 1129 SDL_HIDAPI_UnlockRumble(); 1130 return true; 1131 } 1132 } 1133 1134 if (SDL_HIDAPI_SendRumbleAndUnlock(ctx->device, data, report_size) != report_size) { 1135 return false; 1136 } 1137 1138 return true; 1139} 1140 1141static bool HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size) 1142{ 1143 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1144 1145 return HIDAPI_DriverPS5_InternalSendJoystickEffect(ctx, effect, size, true); 1146} 1147 1148static bool HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 1149{ 1150 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1151 1152 HIDAPI_DriverPS5_UpdateEnhancedModeOnApplicationUsage(ctx); 1153 1154 if ((!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) && !ctx->guitar_tilt_supported) { 1155 return SDL_Unsupported(); 1156 } 1157 1158 if (enabled) { 1159 HIDAPI_DriverPS5_LoadCalibrationData(device); 1160 } 1161 ctx->report_sensors = enabled; 1162 1163 return true; 1164} 1165 1166static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet, Uint64 timestamp) 1167{ 1168 Sint16 axis; 1169 1170 if (ctx->last_state.simple.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) { 1171 { 1172 Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4); 1173 1174 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data & 0x01) != 0)); 1175 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data & 0x02) != 0)); 1176 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data & 0x04) != 0)); 1177 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data & 0x08) != 0)); 1178 } 1179 { 1180 Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F); 1181 Uint8 hat; 1182 1183 switch (data) { 1184 case 0: 1185 hat = SDL_HAT_UP; 1186 break; 1187 case 1: 1188 hat = SDL_HAT_RIGHTUP; 1189 break; 1190 case 2: 1191 hat = SDL_HAT_RIGHT; 1192 break; 1193 case 3: 1194 hat = SDL_HAT_RIGHTDOWN; 1195 break; 1196 case 4: 1197 hat = SDL_HAT_DOWN; 1198 break; 1199 case 5: 1200 hat = SDL_HAT_LEFTDOWN; 1201 break; 1202 case 6: 1203 hat = SDL_HAT_LEFT; 1204 break; 1205 case 7: 1206 hat = SDL_HAT_LEFTUP; 1207 break; 1208 default: 1209 hat = SDL_HAT_CENTERED; 1210 break; 1211 } 1212 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 1213 } 1214 } 1215 1216 if (ctx->last_state.simple.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) { 1217 Uint8 data = packet->rgucButtonsHatAndCounter[1]; 1218 1219 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x01) != 0)); 1220 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x02) != 0)); 1221 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x10) != 0)); 1222 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x20) != 0)); 1223 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x40) != 0)); 1224 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x80) != 0)); 1225 } 1226 1227 if (ctx->last_state.simple.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) { 1228 Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03); 1229 1230 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x01) != 0)); 1231 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_TOUCHPAD, ((data & 0x02) != 0)); 1232 } 1233 1234 if (packet->ucTriggerLeft == 0 && (packet->rgucButtonsHatAndCounter[1] & 0x04)) { 1235 axis = SDL_JOYSTICK_AXIS_MAX; 1236 } else { 1237 axis = ((int)packet->ucTriggerLeft * 257) - 32768; 1238 } 1239 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 1240 if (packet->ucTriggerRight == 0 && (packet->rgucButtonsHatAndCounter[1] & 0x08)) { 1241 axis = SDL_JOYSTICK_AXIS_MAX; 1242 } else { 1243 axis = ((int)packet->ucTriggerRight * 257) - 32768; 1244 } 1245 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 1246 axis = ((int)packet->ucLeftJoystickX * 257) - 32768; 1247 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 1248 axis = ((int)packet->ucLeftJoystickY * 257) - 32768; 1249 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 1250 axis = ((int)packet->ucRightJoystickX * 257) - 32768; 1251 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 1252 axis = ((int)packet->ucRightJoystickY * 257) - 32768; 1253 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 1254 1255 SDL_memcpy(&ctx->last_state.simple, packet, sizeof(ctx->last_state.simple)); 1256} 1257 1258static void HIDAPI_DriverPS5_HandleStatePacketCommon(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketCommon_t *packet, Uint64 timestamp) 1259{ 1260 Sint16 axis; 1261 1262 if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) { 1263 { 1264 Uint8 data = (packet->rgucButtonsAndHat[0] >> 4); 1265 1266 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data & 0x01) != 0)); 1267 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data & 0x02) != 0)); 1268 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data & 0x04) != 0)); 1269 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data & 0x08) != 0)); 1270 } 1271 { 1272 Uint8 data = (packet->rgucButtonsAndHat[0] & 0x0F); 1273 Uint8 hat; 1274 1275 switch (data) { 1276 case 0: 1277 hat = SDL_HAT_UP; 1278 break; 1279 case 1: 1280 hat = SDL_HAT_RIGHTUP; 1281 break; 1282 case 2: 1283 hat = SDL_HAT_RIGHT; 1284 break; 1285 case 3: 1286 hat = SDL_HAT_RIGHTDOWN; 1287 break; 1288 case 4: 1289 hat = SDL_HAT_DOWN; 1290 break; 1291 case 5: 1292 hat = SDL_HAT_LEFTDOWN; 1293 break; 1294 case 6: 1295 hat = SDL_HAT_LEFT; 1296 break; 1297 case 7: 1298 hat = SDL_HAT_LEFTUP; 1299 break; 1300 default: 1301 hat = SDL_HAT_CENTERED; 1302 break; 1303 } 1304 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 1305 } 1306 } 1307 1308 if (ctx->last_state.state.rgucButtonsAndHat[1] != packet->rgucButtonsAndHat[1]) { 1309 Uint8 data = packet->rgucButtonsAndHat[1]; 1310 1311 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x01) != 0)); 1312 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x02) != 0)); 1313 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x10) != 0)); 1314 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x20) != 0)); 1315 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x40) != 0)); 1316 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x80) != 0)); 1317 } 1318 1319 if (ctx->last_state.state.rgucButtonsAndHat[2] != packet->rgucButtonsAndHat[2]) { 1320 Uint8 data = packet->rgucButtonsAndHat[2]; 1321 1322 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x01) != 0)); 1323 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_TOUCHPAD, ((data & 0x02) != 0)); 1324 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_MICROPHONE, ((data & 0x04) != 0)); 1325 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_LEFT_FUNCTION, ((data & 0x10) != 0)); 1326 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_RIGHT_FUNCTION, ((data & 0x20) != 0)); 1327 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_LEFT_PADDLE, ((data & 0x40) != 0)); 1328 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS5_RIGHT_PADDLE, ((data & 0x80) != 0)); 1329 } 1330 1331 if (packet->ucTriggerLeft == 0 && (packet->rgucButtonsAndHat[1] & 0x04)) { 1332 axis = SDL_JOYSTICK_AXIS_MAX; 1333 } else { 1334 axis = ((int)packet->ucTriggerLeft * 257) - 32768; 1335 } 1336 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 1337 if (packet->ucTriggerRight == 0 && (packet->rgucButtonsAndHat[1] & 0x08)) { 1338 axis = SDL_JOYSTICK_AXIS_MAX; 1339 } else { 1340 axis = ((int)packet->ucTriggerRight * 257) - 32768; 1341 } 1342 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 1343 axis = ((int)packet->ucLeftJoystickX * 257) - 32768; 1344 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 1345 axis = ((int)packet->ucLeftJoystickY * 257) - 32768; 1346 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 1347 axis = ((int)packet->ucRightJoystickX * 257) - 32768; 1348 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 1349 axis = ((int)packet->ucRightJoystickY * 257) - 32768; 1350 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 1351 1352 if (ctx->report_sensors) { 1353 Uint64 sensor_timestamp; 1354 float data[3]; 1355 1356 if (ctx->use_alternate_report) { 1357 // 16-bit timestamp 1358 Uint32 delta; 1359 Uint16 tick = LOAD16(packet->rgucSensorTimestamp[0], 1360 packet->rgucSensorTimestamp[1]); 1361 if (ctx->last_tick < tick) { 1362 delta = (tick - ctx->last_tick); 1363 } else { 1364 delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1); 1365 } 1366 ctx->last_tick = tick; 1367 ctx->sensor_ticks += delta; 1368 1369 // Sensor timestamp is in 1us units 1370 sensor_timestamp = SDL_US_TO_NS(ctx->sensor_ticks); 1371 } else { 1372 // 32-bit timestamp 1373 Uint32 delta; 1374 Uint32 tick = LOAD32(packet->rgucSensorTimestamp[0], 1375 packet->rgucSensorTimestamp[1], 1376 packet->rgucSensorTimestamp[2], 1377 packet->rgucSensorTimestamp[3]); 1378 if (ctx->last_tick < tick) { 1379 delta = (tick - ctx->last_tick); 1380 } else { 1381 delta = (SDL_MAX_UINT32 - ctx->last_tick + tick + 1); 1382 } 1383 ctx->last_tick = tick; 1384 ctx->sensor_ticks += delta; 1385 1386 // Sensor timestamp is in 0.33us units 1387 sensor_timestamp = (ctx->sensor_ticks * SDL_NS_PER_US) / 3; 1388 } 1389 1390 data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1])); 1391 data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1])); 1392 data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1])); 1393 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, data, 3); 1394 1395 data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1])); 1396 data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1])); 1397 data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1])); 1398 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, data, 3); 1399 } 1400} 1401 1402static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet, Uint64 timestamp) 1403{ 1404 static const float TOUCHPAD_SCALEX = 5.20833333e-4f; // 1.0f / 1920 1405 static const float TOUCHPAD_SCALEY = 9.34579439e-4f; // 1.0f / 1070 1406 bool touchpad_down; 1407 int touchpad_x, touchpad_y; 1408 1409 if (ctx->report_touchpad) { 1410 touchpad_down = ((packet->ucTouchpadCounter1 & 0x80) == 0); 1411 touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8); 1412 touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4); 1413 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, touchpad_down, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_down ? 1.0f : 0.0f); 1414 1415 touchpad_down = ((packet->ucTouchpadCounter2 & 0x80) == 0); 1416 touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8); 1417 touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4); 1418 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 1, touchpad_down, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_down ? 1.0f : 0.0f); 1419 } 1420 1421 if (ctx->report_battery) { 1422 SDL_PowerState state; 1423 int percent; 1424 Uint8 status = (packet->ucBatteryLevel >> 4) & 0x0F; 1425 Uint8 level = (packet->ucBatteryLevel & 0x0F); 1426 1427 switch (status) { 1428 case 0: 1429 state = SDL_POWERSTATE_ON_BATTERY; 1430 percent = SDL_min(level * 10 + 5, 100); 1431 break; 1432 case 1: 1433 state = SDL_POWERSTATE_CHARGING; 1434 percent = SDL_min(level * 10 + 5, 100); 1435 break; 1436 case 2: 1437 state = SDL_POWERSTATE_CHARGED; 1438 percent = 100; 1439 break; 1440 default: 1441 state = SDL_POWERSTATE_UNKNOWN; 1442 percent = 0; 1443 break; 1444 } 1445 SDL_SendJoystickPowerInfo(joystick, state, percent); 1446 } 1447 1448 HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, dev, ctx, (PS5StatePacketCommon_t *)packet, timestamp); 1449 1450 SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); 1451} 1452 1453static void HIDAPI_DriverPS5_HandleStatePacketAlt(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacketAlt_t *packet, Uint64 timestamp) 1454{ 1455 static const float TOUCHPAD_SCALEX = 5.20833333e-4f; // 1.0f / 1920 1456 static const float TOUCHPAD_SCALEY = 9.34579439e-4f; // 1.0f / 1070 1457 bool touchpad_down; 1458 int touchpad_x, touchpad_y; 1459 Sint16 axis; 1460 1461 if (ctx->report_touchpad) { 1462 touchpad_down = ((packet->ucTouchpadCounter1 & 0x80) == 0); 1463 touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8); 1464 touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4); 1465 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, touchpad_down, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_down ? 1.0f : 0.0f); 1466 1467 touchpad_down = ((packet->ucTouchpadCounter2 & 0x80) == 0); 1468 touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8); 1469 touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4); 1470 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 1, touchpad_down, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_down ? 1.0f : 0.0f); 1471 } 1472 1473 if (ctx->report_battery) { 1474 SDL_PowerState state; 1475 int percent; 1476 Uint8 status = (packet->ucBatteryLevel >> 4) & 0x0F; 1477 Uint8 level = (packet->ucBatteryLevel & 0x0F); 1478 1479 // 0x0C means a controller isn't reporting battery levels 1480 if (level != 0x0C) { 1481 switch (status) { 1482 case 0: 1483 state = SDL_POWERSTATE_ON_BATTERY; 1484 percent = SDL_min(level * 10 + 5, 100); 1485 break; 1486 case 1: 1487 state = SDL_POWERSTATE_CHARGING; 1488 percent = SDL_min(level * 10 + 5, 100); 1489 break; 1490 case 2: 1491 state = SDL_POWERSTATE_CHARGED; 1492 percent = 100; 1493 break; 1494 default: 1495 state = SDL_POWERSTATE_UNKNOWN; 1496 percent = 0; 1497 break; 1498 } 1499 SDL_SendJoystickPowerInfo(joystick, state, percent); 1500 } 1501 } 1502 1503 HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, dev, ctx, (PS5StatePacketCommon_t *)packet, timestamp); 1504 1505 if (ctx->guitar_whammy_supported) { 1506 axis = ((int)packet->rgucDeviceSpecific[1] * 257) - 32768; 1507 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 1508 } 1509 1510 if (ctx->guitar_effects_selector_supported) { 1511 // Align pickup selector mappings with PS3 instruments 1512 static const Sint16 effects_mappings[] = {24576, 11008, -1792, -13568, -26880}; 1513 if (packet->rgucDeviceSpecific[0] < SDL_arraysize(effects_mappings)) { 1514 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[packet->rgucDeviceSpecific[0]]); 1515 } 1516 } 1517 1518 if (ctx->guitar_tilt_supported) { 1519 float sensor_data[3]; 1520 sensor_data[0] = ((float)packet->rgucDeviceSpecific[2] / 255) * SDL_STANDARD_GRAVITY; 1521 sensor_data[1] = 0; 1522 sensor_data[2] = 0; 1523 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, sensor_data, SDL_arraysize(sensor_data)); 1524 1525 // Align tilt mappings with PS3 instruments 1526 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, packet->rgucDeviceSpecific[2] > 0xF0); 1527 } 1528 1529 SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); 1530} 1531 1532static bool VerifyCRC(Uint8 *data, int size) 1533{ 1534 Uint8 ubHdr = 0xA1; // hidp header is part of the CRC calculation 1535 Uint32 unCRC, unPacketCRC; 1536 Uint8 *packetCRC = data + size - sizeof(unPacketCRC); 1537 unCRC = SDL_crc32(0, &ubHdr, 1); 1538 unCRC = SDL_crc32(unCRC, data, (size_t)(size - sizeof(unCRC))); 1539 1540 unPacketCRC = LOAD32(packetCRC[0], 1541 packetCRC[1], 1542 packetCRC[2], 1543 packetCRC[3]); 1544 return (unCRC == unPacketCRC); 1545} 1546 1547static bool HIDAPI_DriverPS5_IsPacketValid(SDL_DriverPS5_Context *ctx, Uint8 *data, int size) 1548{ 1549 switch (data[0]) { 1550 case k_EPS5ReportIdState: 1551 if (ctx->is_dongle && size >= (1 + sizeof(PS5StatePacketAlt_t))) { 1552 // The report timestamp doesn't change when the controller isn't connected 1553 PS5StatePacketAlt_t *packet = (PS5StatePacketAlt_t *)&data[1]; 1554 if (SDL_memcmp(packet->rgucPacketSequence, ctx->last_state.state.rgucPacketSequence, sizeof(packet->rgucPacketSequence)) == 0) { 1555 return false; 1556 } 1557 if (ctx->last_state.state.rgucPacketSequence[0] == 0 && 1558 ctx->last_state.state.rgucPacketSequence[1] == 0 && 1559 ctx->last_state.state.rgucPacketSequence[2] == 0 && 1560 ctx->last_state.state.rgucPacketSequence[3] == 0) { 1561 // We don't have any state to compare yet, go ahead and copy it 1562 SDL_memcpy(&ctx->last_state, &data[1], sizeof(PS5StatePacketAlt_t)); 1563 return false; 1564 } 1565 } 1566 return true; 1567 1568 case k_EPS5ReportIdBluetoothState: 1569 if (VerifyCRC(data, size)) { 1570 return true; 1571 } 1572 break; 1573 default: 1574 break; 1575 } 1576 return false; 1577} 1578 1579static bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) 1580{ 1581 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1582 SDL_Joystick *joystick = NULL; 1583 Uint8 data[USB_PACKET_LENGTH * 2]; 1584 int size; 1585 int packet_count = 0; 1586 Uint64 now = SDL_GetTicks(); 1587 1588 if (device->num_joysticks > 0) { 1589 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 1590 } 1591 1592 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 1593 Uint64 timestamp = SDL_GetTicksNS(); 1594 1595#ifdef DEBUG_PS5_PROTOCOL 1596 HIDAPI_DumpPacket("PS5 packet: size = %d", data, size); 1597#endif 1598 if (!HIDAPI_DriverPS5_IsPacketValid(ctx, data, size)) { 1599 continue; 1600 } 1601 1602 ++packet_count; 1603 ctx->last_packet = now; 1604 1605 if (!joystick) { 1606 continue; 1607 } 1608 1609 switch (data[0]) { 1610 case k_EPS5ReportIdState: 1611 if (size == 10 || size == 78) { 1612 HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1], timestamp); 1613 } else { 1614 if (ctx->use_alternate_report) { 1615 HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[1], timestamp); 1616 } else { 1617 HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1], timestamp); 1618 } 1619 } 1620 break; 1621 case k_EPS5ReportIdBluetoothState: 1622 // This is the extended report, we can enable effects now in auto mode 1623 HIDAPI_DriverPS5_UpdateEnhancedModeOnEnhancedReport(ctx); 1624 1625 if (ctx->use_alternate_report) { 1626 HIDAPI_DriverPS5_HandleStatePacketAlt(joystick, device->dev, ctx, (PS5StatePacketAlt_t *)&data[2], timestamp); 1627 } else { 1628 HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2], timestamp); 1629 } 1630 if (ctx->led_reset_state == k_EDS5LEDResetStatePending) { 1631 HIDAPI_DriverPS5_CheckPendingLEDReset(ctx); 1632 } 1633 break; 1634 default: 1635#ifdef DEBUG_JOYSTICK 1636 SDL_Log("Unknown PS5 packet: 0x%.2x", data[0]); 1637#endif 1638 break; 1639 } 1640 } 1641 1642 if (device->is_bluetooth) { 1643 if (packet_count == 0) { 1644 // Check to see if it looks like the device disconnected 1645 if (now >= (ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) { 1646 // Send an empty output report to tickle the Bluetooth stack 1647 HIDAPI_DriverPS5_TickleBluetooth(device); 1648 ctx->last_packet = now; 1649 } 1650 } else { 1651 // Reconnect the Bluetooth device once the USB device is gone 1652 if (device->num_joysticks == 0 && 1653 !HIDAPI_HasConnectedUSBDevice(device->serial)) { 1654 HIDAPI_JoystickConnected(device, NULL); 1655 } 1656 } 1657 } 1658 1659 if (ctx->is_dongle) { 1660 if (packet_count == 0) { 1661 if (device->num_joysticks > 0) { 1662 // Check to see if it looks like the device disconnected 1663 if (now >= (ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) { 1664 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1665 } 1666 } 1667 } else { 1668 if (device->num_joysticks == 0) { 1669 HIDAPI_JoystickConnected(device, NULL); 1670 } 1671 } 1672 } 1673 1674 if (packet_count == 0 && size < 0 && device->num_joysticks > 0) { 1675 // Read error, device is disconnected 1676 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1677 } 1678 return (size >= 0); 1679} 1680 1681static void HIDAPI_DriverPS5_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1682{ 1683 SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; 1684 1685 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, 1686 SDL_PS5EnhancedReportsChanged, ctx); 1687 1688 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, 1689 SDL_PS5PlayerLEDHintChanged, ctx); 1690 1691 ctx->joystick = NULL; 1692 1693 ctx->report_sensors = false; 1694 ctx->enhanced_mode = false; 1695 ctx->enhanced_mode_available = false; 1696} 1697 1698static void HIDAPI_DriverPS5_FreeDevice(SDL_HIDAPI_Device *device) 1699{ 1700} 1701 1702SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5 = { 1703 SDL_HINT_JOYSTICK_HIDAPI_PS5, 1704 true, 1705 HIDAPI_DriverPS5_RegisterHints, 1706 HIDAPI_DriverPS5_UnregisterHints, 1707 HIDAPI_DriverPS5_IsEnabled, 1708 HIDAPI_DriverPS5_IsSupportedDevice, 1709 HIDAPI_DriverPS5_InitDevice, 1710 HIDAPI_DriverPS5_GetDevicePlayerIndex, 1711 HIDAPI_DriverPS5_SetDevicePlayerIndex, 1712 HIDAPI_DriverPS5_UpdateDevice, 1713 HIDAPI_DriverPS5_OpenJoystick, 1714 HIDAPI_DriverPS5_RumbleJoystick, 1715 HIDAPI_DriverPS5_RumbleJoystickTriggers, 1716 HIDAPI_DriverPS5_GetJoystickCapabilities, 1717 HIDAPI_DriverPS5_SetJoystickLED, 1718 HIDAPI_DriverPS5_SendJoystickEffect, 1719 HIDAPI_DriverPS5_SetJoystickSensorsEnabled, 1720 HIDAPI_DriverPS5_CloseJoystick, 1721 HIDAPI_DriverPS5_FreeDevice, 1722}; 1723 1724#endif // SDL_JOYSTICK_HIDAPI_PS5 1725 1726#endif // SDL_JOYSTICK_HIDAPI 1727
[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.