Atlas - SDL_hidapi_ps4.c

Home / ext / SDL / src / joystick / hidapi Lines: 1 | Size: 50214 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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/* This driver supports both simplified reports and the extended input reports enabled by Steam. 22 Code and logic contributed by Valve Corporation under the SDL zlib license. 23*/ 24#include "SDL_internal.h" 25 26#ifdef SDL_JOYSTICK_HIDAPI 27 28#include "../../SDL_hints_c.h" 29#include "../SDL_sysjoystick.h" 30#include "SDL_hidapijoystick_c.h" 31#include "SDL_hidapi_rumble.h" 32 33#ifdef SDL_JOYSTICK_HIDAPI_PS4 34 35// Define this if you want to log all packets from the controller 36#if 0 37#define DEBUG_PS4_PROTOCOL 38#endif 39 40// Define this if you want to log calibration data 41#if 0 42#define DEBUG_PS4_CALIBRATION 43#endif 44 45#define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500 46 47#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8)) 48#define LOAD32(A, B, C, D) ((((Uint32)(A)) << 0) | \ 49 (((Uint32)(B)) << 8) | \ 50 (((Uint32)(C)) << 16) | \ 51 (((Uint32)(D)) << 24)) 52 53enum 54{ 55 SDL_GAMEPAD_BUTTON_PS4_TOUCHPAD = 11 56}; 57 58typedef enum 59{ 60 k_EPS4ReportIdUsbState = 1, 61 k_EPS4ReportIdUsbEffects = 5, 62 k_EPS4ReportIdBluetoothState1 = 17, 63 k_EPS4ReportIdBluetoothState2 = 18, 64 k_EPS4ReportIdBluetoothState3 = 19, 65 k_EPS4ReportIdBluetoothState4 = 20, 66 k_EPS4ReportIdBluetoothState5 = 21, 67 k_EPS4ReportIdBluetoothState6 = 22, 68 k_EPS4ReportIdBluetoothState7 = 23, 69 k_EPS4ReportIdBluetoothState8 = 24, 70 k_EPS4ReportIdBluetoothState9 = 25, 71 k_EPS4ReportIdBluetoothEffects = 17, 72 k_EPS4ReportIdDisconnectMessage = 226, 73} EPS4ReportId; 74 75typedef enum 76{ 77 k_ePS4FeatureReportIdGyroCalibration_USB = 0x02, 78 k_ePS4FeatureReportIdCapabilities = 0x03, 79 k_ePS4FeatureReportIdGyroCalibration_BT = 0x05, 80 k_ePS4FeatureReportIdSerialNumber = 0x12, 81} EPS4FeatureReportID; 82 83typedef struct 84{ 85 Uint8 ucLeftJoystickX; 86 Uint8 ucLeftJoystickY; 87 Uint8 ucRightJoystickX; 88 Uint8 ucRightJoystickY; 89 Uint8 rgucButtonsHatAndCounter[3]; 90 Uint8 ucTriggerLeft; 91 Uint8 ucTriggerRight; 92 Uint8 rgucTimestamp[2]; 93 Uint8 _rgucPad0[1]; 94 Uint8 rgucGyroX[2]; 95 Uint8 rgucGyroY[2]; 96 Uint8 rgucGyroZ[2]; 97 Uint8 rgucAccelX[2]; 98 Uint8 rgucAccelY[2]; 99 Uint8 rgucAccelZ[2]; 100 Uint8 _rgucPad1[5]; 101 Uint8 ucBatteryLevel; 102 Uint8 _rgucPad2[4]; 103 Uint8 ucTouchpadCounter1; 104 Uint8 rgucTouchpadData1[3]; 105 Uint8 ucTouchpadCounter2; 106 Uint8 rgucTouchpadData2[3]; 107} PS4StatePacket_t; 108 109typedef struct 110{ 111 Uint8 ucRumbleRight; 112 Uint8 ucRumbleLeft; 113 Uint8 ucLedRed; 114 Uint8 ucLedGreen; 115 Uint8 ucLedBlue; 116 Uint8 ucLedDelayOn; 117 Uint8 ucLedDelayOff; 118 Uint8 _rgucPad0[8]; 119 Uint8 ucVolumeLeft; 120 Uint8 ucVolumeRight; 121 Uint8 ucVolumeMic; 122 Uint8 ucVolumeSpeaker; 123} DS4EffectsState_t; 124 125typedef struct 126{ 127 Sint16 bias; 128 float scale; 129} IMUCalibrationData; 130 131/* Rumble hint mode: 132 * "0": enhanced features are never used 133 * "1": enhanced features are always used 134 * "auto": enhanced features are advertised to the application, but SDL doesn't touch the controller state unless the application explicitly requests it. 135 */ 136typedef enum 137{ 138 PS4_ENHANCED_REPORT_HINT_OFF, 139 PS4_ENHANCED_REPORT_HINT_ON, 140 PS4_ENHANCED_REPORT_HINT_AUTO 141} HIDAPI_PS4_EnhancedReportHint; 142 143typedef struct 144{ 145 SDL_HIDAPI_Device *device; 146 SDL_Joystick *joystick; 147 bool is_dongle; 148 bool is_nacon_dongle; 149 bool official_controller; 150 bool sensors_supported; 151 bool lightbar_supported; 152 bool vibration_supported; 153 bool touchpad_supported; 154 bool effects_supported; 155 HIDAPI_PS4_EnhancedReportHint enhanced_report_hint; 156 bool enhanced_reports; 157 bool enhanced_mode; 158 bool enhanced_mode_available; 159 Uint8 report_interval; 160 bool report_sensors; 161 bool report_touchpad; 162 bool report_battery; 163 bool hardware_calibration; 164 IMUCalibrationData calibration[6]; 165 Uint64 last_packet; 166 int player_index; 167 Uint8 rumble_left; 168 Uint8 rumble_right; 169 bool color_set; 170 Uint8 led_red; 171 Uint8 led_green; 172 Uint8 led_blue; 173 Uint16 gyro_numerator; 174 Uint16 gyro_denominator; 175 Uint16 accel_numerator; 176 Uint16 accel_denominator; 177 Uint64 sensor_ticks; 178 Uint16 last_tick; 179 Uint16 valid_crc_packets; // wrapping counter 180 PS4StatePacket_t last_state; 181} SDL_DriverPS4_Context; 182 183static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage); 184 185static void HIDAPI_DriverPS4_RegisterHints(SDL_HintCallback callback, void *userdata) 186{ 187 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4, callback, userdata); 188} 189 190static void HIDAPI_DriverPS4_UnregisterHints(SDL_HintCallback callback, void *userdata) 191{ 192 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4, callback, userdata); 193} 194 195static bool HIDAPI_DriverPS4_IsEnabled(void) 196{ 197 return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT)); 198} 199 200static int ReadFeatureReport(SDL_hid_device *dev, Uint8 report_id, Uint8 *report, size_t length) 201{ 202 SDL_memset(report, 0, length); 203 report[0] = report_id; 204 return SDL_hid_get_feature_report(dev, report, length); 205} 206 207static bool HIDAPI_DriverPS4_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) 208{ 209 Uint8 data[USB_PACKET_LENGTH]; 210 int size; 211 212 if (type == SDL_GAMEPAD_TYPE_PS4) { 213 return true; 214 } 215 216 if (HIDAPI_SupportsPlaystationDetection(vendor_id, product_id)) { 217 if (device && device->dev) { 218 size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdCapabilities, data, sizeof(data)); 219 if (size == 48 && data[2] == 0x27) { 220 // Supported third party controller 221 return true; 222 } else { 223 return false; 224 } 225 } else { 226 // Might be supported by this driver, enumerate and find out 227 return true; 228 } 229 } 230 231 return false; 232} 233 234static void SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index) 235{ 236 /* This list is the same as what hid-sony.c uses in the Linux kernel. 237 The first 4 values correspond to what the PS4 assigns. 238 */ 239 static const Uint8 colors[7][3] = { 240 { 0x00, 0x00, 0x40 }, // Blue 241 { 0x40, 0x00, 0x00 }, // Red 242 { 0x00, 0x40, 0x00 }, // Green 243 { 0x20, 0x00, 0x20 }, // Pink 244 { 0x02, 0x01, 0x00 }, // Orange 245 { 0x00, 0x01, 0x01 }, // Teal 246 { 0x01, 0x01, 0x01 } // White 247 }; 248 249 if (player_index >= 0) { 250 player_index %= SDL_arraysize(colors); 251 } else { 252 player_index = 0; 253 } 254 255 effects->ucLedRed = colors[player_index][0]; 256 effects->ucLedGreen = colors[player_index][1]; 257 effects->ucLedBlue = colors[player_index][2]; 258} 259 260static bool ReadWiredSerial(SDL_HIDAPI_Device *device, char *serial, size_t serial_size) 261{ 262 Uint8 data[USB_PACKET_LENGTH]; 263 int size; 264 265 size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data)); 266 if (size >= 7 && (data[1] || data[2] || data[3] || data[4] || data[5] || data[6])) { 267 (void)SDL_snprintf(serial, serial_size, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", 268 data[6], data[5], data[4], data[3], data[2], data[1]); 269 return true; 270 } 271 return false; 272} 273 274static bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device) 275{ 276 SDL_DriverPS4_Context *ctx; 277 Uint8 data[USB_PACKET_LENGTH]; 278 int size; 279 char serial[18]; 280 SDL_JoystickType joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD; 281 282 ctx = (SDL_DriverPS4_Context *)SDL_calloc(1, sizeof(*ctx)); 283 if (!ctx) { 284 return false; 285 } 286 ctx->device = device; 287 288 ctx->gyro_numerator = 1; 289 ctx->gyro_denominator = 16; 290 ctx->accel_numerator = 1; 291 ctx->accel_denominator = 8192; 292 293 device->context = ctx; 294 295 if (device->serial && SDL_strlen(device->serial) == 12) { 296 int i, j; 297 298 j = -1; 299 for (i = 0; i < 12; i += 2) { 300 j += 1; 301 SDL_memmove(&serial[j], &device->serial[i], 2); 302 j += 2; 303 serial[j] = '-'; 304 } 305 serial[j] = '\0'; 306 } else { 307 serial[0] = '\0'; 308 } 309 310 // Check for type of connection 311 ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE); 312 if (ctx->is_dongle) { 313 ReadWiredSerial(device, serial, sizeof(serial)); 314 ctx->enhanced_reports = true; 315 } else if (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_STRIKEPAD) { 316 ctx->enhanced_reports = true; 317 318 } else if (device->vendor_id == USB_VENDOR_SONY) { 319 if (device->is_bluetooth) { 320 // Read a report to see if we're in enhanced mode 321 size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 16); 322#ifdef DEBUG_PS4_PROTOCOL 323 if (size > 0) { 324 HIDAPI_DumpPacket("PS4 first packet: size = %d", data, size); 325 } else { 326 SDL_Log("PS4 first packet: size = %d", size); 327 } 328#endif 329 if (size > 0 && 330 data[0] >= k_EPS4ReportIdBluetoothState1 && 331 data[0] <= k_EPS4ReportIdBluetoothState9) { 332 ctx->enhanced_reports = true; 333 } 334 } else { 335 ReadWiredSerial(device, serial, sizeof(serial)); 336 ctx->enhanced_reports = true; 337 } 338 } else { 339 // Third party controllers appear to all be wired 340 ctx->enhanced_reports = true; 341 } 342 343 if (device->vendor_id == USB_VENDOR_SONY) { 344 ctx->official_controller = true; 345 ctx->sensors_supported = true; 346 ctx->lightbar_supported = true; 347 ctx->vibration_supported = true; 348 ctx->touchpad_supported = true; 349 } else { 350 // Third party controller capability request 351 size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdCapabilities, data, sizeof(data)); 352 // Get the device capabilities 353 if (size == 48 && data[2] == 0x27) { 354 Uint8 capabilities = data[4]; 355 Uint8 device_type = data[5]; 356 Uint16 gyro_numerator = LOAD16(data[10], data[11]); 357 Uint16 gyro_denominator = LOAD16(data[12], data[13]); 358 Uint16 accel_numerator = LOAD16(data[14], data[15]); 359 Uint16 accel_denominator = LOAD16(data[16], data[17]); 360 361#ifdef DEBUG_PS4_PROTOCOL 362 HIDAPI_DumpPacket("PS4 capabilities: size = %d", data, size); 363#endif 364 if (capabilities & 0x02) { 365 ctx->sensors_supported = true; 366 } 367 if (capabilities & 0x04) { 368 ctx->lightbar_supported = true; 369 } 370 if (capabilities & 0x08) { 371 ctx->vibration_supported = true; 372 } 373 if (capabilities & 0x40) { 374 ctx->touchpad_supported = true; 375 } 376 377 switch (device_type) { 378 case 0x00: 379 joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD; 380 break; 381 case 0x01: 382 joystick_type = SDL_JOYSTICK_TYPE_GUITAR; 383 break; 384 case 0x02: 385 joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT; 386 break; 387 case 0x04: 388 joystick_type = SDL_JOYSTICK_TYPE_DANCE_PAD; 389 break; 390 case 0x06: 391 joystick_type = SDL_JOYSTICK_TYPE_WHEEL; 392 break; 393 case 0x07: 394 joystick_type = SDL_JOYSTICK_TYPE_ARCADE_STICK; 395 break; 396 case 0x08: 397 joystick_type = SDL_JOYSTICK_TYPE_FLIGHT_STICK; 398 break; 399 default: 400 joystick_type = SDL_JOYSTICK_TYPE_UNKNOWN; 401 break; 402 } 403 404 if (gyro_numerator && gyro_denominator) { 405 ctx->gyro_numerator = gyro_numerator; 406 ctx->gyro_denominator = gyro_denominator; 407 } 408 if (accel_numerator && accel_denominator) { 409 ctx->accel_numerator = accel_numerator; 410 ctx->accel_denominator = accel_denominator; 411 } 412 } else if (device->vendor_id == USB_VENDOR_RAZER) { 413 // The Razer Raiju doesn't respond to the detection protocol, but has a touchpad and vibration 414 ctx->vibration_supported = true; 415 ctx->touchpad_supported = true; 416 } 417 } 418 ctx->effects_supported = (ctx->lightbar_supported || ctx->vibration_supported); 419 420 if (device->vendor_id == USB_VENDOR_NACON_ALT && 421 device->product_id == USB_PRODUCT_NACON_REVOLUTION_5_PRO_PS4_WIRELESS) { 422 ctx->is_nacon_dongle = true; 423 } 424 425 if (device->vendor_id == USB_VENDOR_PDP && 426 (device->product_id == USB_PRODUCT_VICTRIX_FS_PRO || 427 device->product_id == USB_PRODUCT_VICTRIX_FS_PRO_V2)) { 428 /* The Victrix FS Pro V2 reports that it has lightbar support, 429 * but it doesn't respond to the effects packet, and will hang 430 * on reboot if we send it. 431 */ 432 ctx->effects_supported = false; 433 } 434 435 device->joystick_type = joystick_type; 436 device->type = SDL_GAMEPAD_TYPE_PS4; 437 if (ctx->official_controller) { 438 HIDAPI_SetDeviceName(device, "PS4 Controller"); 439 } 440 HIDAPI_SetDeviceSerial(device, serial); 441 442 // Prefer the USB device over the Bluetooth device 443 if (device->is_bluetooth) { 444 if (HIDAPI_HasConnectedUSBDevice(device->serial)) { 445 return true; 446 } 447 } else { 448 HIDAPI_DisconnectBluetoothDevice(device->serial); 449 } 450 if ((ctx->is_dongle || ctx->is_nacon_dongle) && serial[0] == '\0') { 451 // Not yet connected 452 return true; 453 } 454 return HIDAPI_JoystickConnected(device, NULL); 455} 456 457static int HIDAPI_DriverPS4_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) 458{ 459 return -1; 460} 461 462static bool HIDAPI_DriverPS4_LoadOfficialCalibrationData(SDL_HIDAPI_Device *device) 463{ 464 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 465 int i, tries, size; 466 bool have_data = false; 467 Uint8 data[USB_PACKET_LENGTH]; 468 469 if (!ctx->official_controller) { 470#ifdef DEBUG_PS4_CALIBRATION 471 SDL_Log("Not an official controller, ignoring calibration"); 472#endif 473 return false; 474 } 475 476 for (tries = 0; tries < 5; ++tries) { 477 // For Bluetooth controllers, this report switches them into advanced report mode 478 size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_USB, data, sizeof(data)); 479 if (size < 35) { 480#ifdef DEBUG_PS4_CALIBRATION 481 SDL_Log("Short read of calibration data: %d, ignoring calibration", size); 482#endif 483 return false; 484 } 485 486 if (device->is_bluetooth) { 487 size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_BT, data, sizeof(data)); 488 if (size < 35) { 489#ifdef DEBUG_PS4_CALIBRATION 490 SDL_Log("Short read of calibration data: %d, ignoring calibration", size); 491#endif 492 return false; 493 } 494 } 495 496 // In some cases this report returns all zeros. Usually immediately after connection with the PS4 Dongle 497 for (i = 0; i < size; ++i) { 498 if (data[i]) { 499 have_data = true; 500 break; 501 } 502 } 503 if (have_data) { 504 break; 505 } 506 507 SDL_Delay(2); 508 } 509 510 if (have_data) { 511 Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias; 512 Sint16 sGyroPitchPlus, sGyroPitchMinus; 513 Sint16 sGyroYawPlus, sGyroYawMinus; 514 Sint16 sGyroRollPlus, sGyroRollMinus; 515 Sint16 sGyroSpeedPlus, sGyroSpeedMinus; 516 517 Sint16 sAccXPlus, sAccXMinus; 518 Sint16 sAccYPlus, sAccYMinus; 519 Sint16 sAccZPlus, sAccZMinus; 520 521 float flNumerator; 522 float flDenominator; 523 Sint16 sRange2g; 524 525#ifdef DEBUG_PS4_CALIBRATION 526 HIDAPI_DumpPacket("PS4 calibration packet: size = %d", data, size); 527#endif 528 529 sGyroPitchBias = LOAD16(data[1], data[2]); 530 sGyroYawBias = LOAD16(data[3], data[4]); 531 sGyroRollBias = LOAD16(data[5], data[6]); 532 533 if (device->is_bluetooth || ctx->is_dongle) { 534 sGyroPitchPlus = LOAD16(data[7], data[8]); 535 sGyroYawPlus = LOAD16(data[9], data[10]); 536 sGyroRollPlus = LOAD16(data[11], data[12]); 537 sGyroPitchMinus = LOAD16(data[13], data[14]); 538 sGyroYawMinus = LOAD16(data[15], data[16]); 539 sGyroRollMinus = LOAD16(data[17], data[18]); 540 } else { 541 sGyroPitchPlus = LOAD16(data[7], data[8]); 542 sGyroPitchMinus = LOAD16(data[9], data[10]); 543 sGyroYawPlus = LOAD16(data[11], data[12]); 544 sGyroYawMinus = LOAD16(data[13], data[14]); 545 sGyroRollPlus = LOAD16(data[15], data[16]); 546 sGyroRollMinus = LOAD16(data[17], data[18]); 547 } 548 549 sGyroSpeedPlus = LOAD16(data[19], data[20]); 550 sGyroSpeedMinus = LOAD16(data[21], data[22]); 551 552 sAccXPlus = LOAD16(data[23], data[24]); 553 sAccXMinus = LOAD16(data[25], data[26]); 554 sAccYPlus = LOAD16(data[27], data[28]); 555 sAccYMinus = LOAD16(data[29], data[30]); 556 sAccZPlus = LOAD16(data[31], data[32]); 557 sAccZMinus = LOAD16(data[33], data[34]); 558 559 flNumerator = (float)(sGyroSpeedPlus + sGyroSpeedMinus) * ctx->gyro_denominator / ctx->gyro_numerator; 560 flDenominator = (float)(SDL_abs(sGyroPitchPlus - sGyroPitchBias) + SDL_abs(sGyroPitchMinus - sGyroPitchBias)); 561 if (flDenominator != 0.0f) { 562 ctx->calibration[0].bias = sGyroPitchBias; 563 ctx->calibration[0].scale = flNumerator / flDenominator; 564 } 565 566 flDenominator = (float)(SDL_abs(sGyroYawPlus - sGyroYawBias) + SDL_abs(sGyroYawMinus - sGyroYawBias)); 567 if (flDenominator != 0.0f) { 568 ctx->calibration[1].bias = sGyroYawBias; 569 ctx->calibration[1].scale = flNumerator / flDenominator; 570 } 571 572 flDenominator = (float)(SDL_abs(sGyroRollPlus - sGyroRollBias) + SDL_abs(sGyroRollMinus - sGyroRollBias)); 573 if (flDenominator != 0.0f) { 574 ctx->calibration[2].bias = sGyroRollBias; 575 ctx->calibration[2].scale = flNumerator / flDenominator; 576 } 577 578 sRange2g = sAccXPlus - sAccXMinus; 579 ctx->calibration[3].bias = sAccXPlus - sRange2g / 2; 580 ctx->calibration[3].scale = (2.0f * ctx->accel_denominator / ctx->accel_numerator) / sRange2g; 581 582 sRange2g = sAccYPlus - sAccYMinus; 583 ctx->calibration[4].bias = sAccYPlus - sRange2g / 2; 584 ctx->calibration[4].scale = (2.0f * ctx->accel_denominator / ctx->accel_numerator) / sRange2g; 585 586 sRange2g = sAccZPlus - sAccZMinus; 587 ctx->calibration[5].bias = sAccZPlus - sRange2g / 2; 588 ctx->calibration[5].scale = (2.0f * ctx->accel_denominator / ctx->accel_numerator) / sRange2g; 589 590 ctx->hardware_calibration = true; 591 for (i = 0; i < 6; ++i) { 592#ifdef DEBUG_PS4_CALIBRATION 593 SDL_Log("calibration[%d] bias = %d, sensitivity = %f", i, ctx->calibration[i].bias, ctx->calibration[i].scale); 594#endif 595 // Some controllers have a bad calibration 596 if (SDL_abs(ctx->calibration[i].bias) > 1024 || SDL_fabsf(1.0f - ctx->calibration[i].scale) > 0.5f) { 597#ifdef DEBUG_PS4_CALIBRATION 598 SDL_Log("invalid calibration, ignoring"); 599#endif 600 ctx->hardware_calibration = false; 601 } 602 } 603 } else { 604#ifdef DEBUG_PS4_CALIBRATION 605 SDL_Log("Calibration data not available"); 606#endif 607 } 608 return ctx->hardware_calibration; 609} 610 611static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device) 612{ 613 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 614 int i; 615 616 if (!HIDAPI_DriverPS4_LoadOfficialCalibrationData(device)) { 617 for (i = 0; i < SDL_arraysize(ctx->calibration); ++i) { 618 ctx->calibration[i].bias = 0; 619 ctx->calibration[i].scale = 1.0f; 620 } 621 } 622 623 // Scale the raw data to the units expected by SDL 624 for (i = 0; i < SDL_arraysize(ctx->calibration); ++i) { 625 double scale = ctx->calibration[i].scale; 626 627 if (i < 3) { 628 scale *= ((double)ctx->gyro_numerator / ctx->gyro_denominator) * SDL_PI_D / 180.0; 629 630 if (device->vendor_id == USB_VENDOR_SONY && 631 device->product_id == USB_PRODUCT_SONY_DS4_STRIKEPAD) { 632 // The Armor-X Pro seems to only deliver half the rotation it should 633 scale *= 2.0; 634 } 635 } else { 636 scale *= ((double)ctx->accel_numerator / ctx->accel_denominator) * SDL_STANDARD_GRAVITY; 637 638 if (device->vendor_id == USB_VENDOR_SONY && 639 device->product_id == USB_PRODUCT_SONY_DS4_STRIKEPAD) { 640 /* The Armor-X Pro seems to only deliver half the acceleration it should, 641 * and in the opposite direction on all axes */ 642 scale *= -2.0; 643 } 644 } 645 ctx->calibration[i].scale = (float)scale; 646 } 647} 648 649static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, int index, Sint16 value) 650{ 651 IMUCalibrationData *calibration = &ctx->calibration[index]; 652 653 return ((float)value - calibration->bias) * calibration->scale; 654} 655 656static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool application_usage) 657{ 658 DS4EffectsState_t effects; 659 660 SDL_zero(effects); 661 662 if (ctx->vibration_supported) { 663 effects.ucRumbleLeft = ctx->rumble_left; 664 effects.ucRumbleRight = ctx->rumble_right; 665 } 666 667 if (ctx->lightbar_supported) { 668 // Populate the LED state with the appropriate color from our lookup table 669 if (ctx->color_set) { 670 effects.ucLedRed = ctx->led_red; 671 effects.ucLedGreen = ctx->led_green; 672 effects.ucLedBlue = ctx->led_blue; 673 } else { 674 SetLedsForPlayerIndex(&effects, ctx->player_index); 675 } 676 } 677 return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage); 678} 679 680static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device) 681{ 682 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 683 684 if (ctx->enhanced_reports) { 685 // This is just a dummy packet that should have no effect, since we don't set the CRC 686 Uint8 data[78]; 687 688 SDL_zeroa(data); 689 690 data[0] = k_EPS4ReportIdBluetoothEffects; 691 data[1] = 0xC0; // Magic value HID + CRC 692 693 if (SDL_HIDAPI_LockRumble()) { 694 SDL_HIDAPI_SendRumbleAndUnlock(device, data, sizeof(data)); 695 } 696 } else { 697#if 0 /* The 8BitDo Zero 2 has perfect emulation of a PS4 controller, except it 698 * only sends reports when the state changes, so we can't disconnect here. 699 */ 700 // We can't even send an invalid effects packet, or it will put the controller in enhanced mode 701 if (device->num_joysticks > 0) { 702 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 703 } 704#endif 705 } 706} 707 708static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx) 709{ 710 if (ctx->enhanced_mode_available) { 711 return; 712 } 713 ctx->enhanced_mode_available = true; 714 715 if (ctx->touchpad_supported) { 716 SDL_PrivateJoystickAddTouchpad(ctx->joystick, 2); 717 ctx->report_touchpad = true; 718 } 719 720 if (ctx->sensors_supported) { 721 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, (float)(1000 / ctx->report_interval)); 722 SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval)); 723 } 724 725 if (ctx->official_controller) { 726 ctx->report_battery = true; 727 } 728 729 HIDAPI_UpdateDeviceProperties(ctx->device); 730} 731 732static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx) 733{ 734 HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx); 735 736 if (!ctx->enhanced_mode) { 737 ctx->enhanced_mode = true; 738 739 // Switch into enhanced report mode 740 HIDAPI_DriverPS4_UpdateEffects(ctx, false); 741 } 742} 743 744static void HIDAPI_DriverPS4_SetEnhancedReportHint(SDL_DriverPS4_Context *ctx, HIDAPI_PS4_EnhancedReportHint enhanced_report_hint) 745{ 746 switch (enhanced_report_hint) { 747 case PS4_ENHANCED_REPORT_HINT_OFF: 748 // Nothing to do, enhanced mode is a one-way ticket 749 break; 750 case PS4_ENHANCED_REPORT_HINT_ON: 751 HIDAPI_DriverPS4_SetEnhancedMode(ctx); 752 break; 753 case PS4_ENHANCED_REPORT_HINT_AUTO: 754 HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx); 755 break; 756 } 757 ctx->enhanced_report_hint = enhanced_report_hint; 758} 759 760static void HIDAPI_DriverPS4_UpdateEnhancedModeOnEnhancedReport(SDL_DriverPS4_Context *ctx) 761{ 762 ctx->enhanced_reports = true; 763 764 if (ctx->enhanced_report_hint == PS4_ENHANCED_REPORT_HINT_AUTO) { 765 HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON); 766 } 767} 768 769static void HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(SDL_DriverPS4_Context *ctx) 770{ 771 if (ctx->enhanced_report_hint == PS4_ENHANCED_REPORT_HINT_AUTO) { 772 HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON); 773 } 774} 775 776static void SDLCALL SDL_PS4EnhancedReportsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 777{ 778 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata; 779 780 if (ctx->device->is_bluetooth) { 781 if (hint && SDL_strcasecmp(hint, "auto") == 0) { 782 HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_AUTO); 783 } else if (SDL_GetStringBoolean(hint, true)) { 784 HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON); 785 } else { 786 HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_OFF); 787 } 788 } else { 789 HIDAPI_DriverPS4_SetEnhancedReportHint(ctx, PS4_ENHANCED_REPORT_HINT_ON); 790 } 791} 792 793static void SDLCALL SDL_PS4ReportIntervalHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 794{ 795 const int DEFAULT_REPORT_INTERVAL = 4; 796 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata; 797 int new_report_interval = DEFAULT_REPORT_INTERVAL; 798 799 if (hint) { 800 int report_interval = SDL_atoi(hint); 801 switch (report_interval) { 802 case 1: 803 case 2: 804 case 4: 805 // Valid values 806 new_report_interval = report_interval; 807 break; 808 default: 809 break; 810 } 811 } 812 813 if (new_report_interval != ctx->report_interval) { 814 ctx->report_interval = (Uint8)new_report_interval; 815 816 HIDAPI_DriverPS4_UpdateEffects(ctx, false); 817 SDL_LockJoysticks(); 818 SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_GYRO, (float)(1000 / ctx->report_interval)); 819 SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval)); 820 SDL_UnlockJoysticks(); 821 } 822} 823 824static void HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) 825{ 826 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 827 828 if (!ctx->joystick) { 829 return; 830 } 831 832 ctx->player_index = player_index; 833 834 // This will set the new LED state based on the new player index 835 // SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode 836 HIDAPI_DriverPS4_UpdateEffects(ctx, false); 837} 838 839static bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 840{ 841 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 842 843 SDL_AssertJoysticksLocked(); 844 845 ctx->joystick = joystick; 846 ctx->last_packet = SDL_GetTicks(); 847 ctx->report_sensors = false; 848 ctx->report_touchpad = false; 849 ctx->rumble_left = 0; 850 ctx->rumble_right = 0; 851 ctx->color_set = false; 852 SDL_zero(ctx->last_state); 853 854 // Initialize player index (needed for setting LEDs) 855 ctx->player_index = SDL_GetJoystickPlayerIndex(joystick); 856 857 // Initialize the joystick capabilities 858 joystick->nbuttons = 11; 859 if (ctx->touchpad_supported) { 860 joystick->nbuttons += 1; 861 } 862 joystick->naxes = SDL_GAMEPAD_AXIS_COUNT; 863 joystick->nhats = 1; 864 865 SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL, 866 SDL_PS4ReportIntervalHintChanged, ctx); 867 SDL_AddHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, 868 SDL_PS4EnhancedReportsChanged, ctx); 869 return true; 870} 871 872static bool HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) 873{ 874 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 875 876 if (!ctx->vibration_supported) { 877 return SDL_Unsupported(); 878 } 879 880 ctx->rumble_left = (low_frequency_rumble >> 8); 881 ctx->rumble_right = (high_frequency_rumble >> 8); 882 883 return HIDAPI_DriverPS4_UpdateEffects(ctx, true); 884} 885 886static bool HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) 887{ 888 return SDL_Unsupported(); 889} 890 891static Uint32 HIDAPI_DriverPS4_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 892{ 893 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 894 Uint32 result = 0; 895 896 if (ctx->enhanced_mode_available) { 897 if (ctx->lightbar_supported) { 898 result |= SDL_JOYSTICK_CAP_RGB_LED; 899 } 900 if (ctx->vibration_supported) { 901 result |= SDL_JOYSTICK_CAP_RUMBLE; 902 } 903 } 904 905 return result; 906} 907 908static bool HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) 909{ 910 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 911 912 if (!ctx->lightbar_supported) { 913 return SDL_Unsupported(); 914 } 915 916 ctx->color_set = true; 917 ctx->led_red = red; 918 ctx->led_green = green; 919 ctx->led_blue = blue; 920 921 return HIDAPI_DriverPS4_UpdateEffects(ctx, true); 922} 923 924static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage) 925{ 926 Uint8 data[78]; 927 int report_size, offset; 928 929 if (!ctx->effects_supported) { 930 // We shouldn't be sending packets to this controller 931 return SDL_Unsupported(); 932 } 933 934 if (!ctx->enhanced_mode) { 935 if (application_usage) { 936 HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx); 937 } 938 939 if (!ctx->enhanced_mode) { 940 // We're not in enhanced mode, effects aren't allowed 941 return SDL_Unsupported(); 942 } 943 } 944 945 SDL_zeroa(data); 946 947 if (ctx->device->is_bluetooth && ctx->official_controller) { 948 data[0] = k_EPS4ReportIdBluetoothEffects; 949 data[1] = 0xC0 | ctx->report_interval; // Magic value HID + CRC, also sets update interval 950 data[3] = 0x03; // 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval 951 952 report_size = 78; 953 offset = 6; 954 } else { 955 data[0] = k_EPS4ReportIdUsbEffects; 956 data[1] = 0x07; // Magic value 957 958 report_size = 32; 959 offset = 4; 960 } 961 962 SDL_memcpy(&data[offset], effect, SDL_min((sizeof(data) - offset), (size_t)size)); 963 964 if (ctx->device->is_bluetooth) { 965 // Bluetooth reports need a CRC at the end of the packet (at least on Linux) 966 Uint8 ubHdr = 0xA2; // hidp header is part of the CRC calculation 967 Uint32 unCRC; 968 unCRC = SDL_crc32(0, &ubHdr, 1); 969 unCRC = SDL_crc32(unCRC, data, (size_t)(report_size - sizeof(unCRC))); 970 SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC)); 971 } 972 973 if (SDL_HIDAPI_SendRumble(ctx->device, data, report_size) != report_size) { 974 return SDL_SetError("Couldn't send rumble packet"); 975 } 976 return true; 977} 978 979static bool HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size) 980{ 981 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 982 983 return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, true); 984} 985 986static bool HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled) 987{ 988 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 989 990 HIDAPI_DriverPS4_UpdateEnhancedModeOnApplicationUsage(ctx); 991 992 if (!ctx->sensors_supported || (enabled && !ctx->enhanced_mode)) { 993 return SDL_Unsupported(); 994 } 995 996 if (enabled) { 997 HIDAPI_DriverPS4_LoadCalibrationData(device); 998 } 999 ctx->report_sensors = enabled; 1000 1001 return true; 1002} 1003 1004static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet, int size) 1005{ 1006 static const float TOUCHPAD_SCALEX = 5.20833333e-4f; // 1.0f / 1920 1007 static const float TOUCHPAD_SCALEY = 1.08695652e-3f; // 1.0f / 920 // This is noted as being 944 resolution, but 920 feels better 1008 Sint16 axis; 1009 bool touchpad_down; 1010 int touchpad_x, touchpad_y; 1011 Uint64 timestamp = SDL_GetTicksNS(); 1012 1013 if (size > 9 && ctx->report_touchpad && ctx->enhanced_reports) { 1014 touchpad_down = ((packet->ucTouchpadCounter1 & 0x80) == 0); 1015 touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8); 1016 touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4); 1017 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, touchpad_down, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_down ? 1.0f : 0.0f); 1018 1019 touchpad_down = ((packet->ucTouchpadCounter2 & 0x80) == 0); 1020 touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8); 1021 touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4); 1022 SDL_SendJoystickTouchpad(timestamp, joystick, 0, 1, touchpad_down, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_down ? 1.0f : 0.0f); 1023 } 1024 1025 if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) { 1026 { 1027 Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4); 1028 1029 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data & 0x01) != 0)); 1030 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data & 0x02) != 0)); 1031 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data & 0x04) != 0)); 1032 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data & 0x08) != 0)); 1033 } 1034 { 1035 Uint8 hat; 1036 Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F); 1037 1038 switch (data) { 1039 case 0: 1040 hat = SDL_HAT_UP; 1041 break; 1042 case 1: 1043 hat = SDL_HAT_RIGHTUP; 1044 break; 1045 case 2: 1046 hat = SDL_HAT_RIGHT; 1047 break; 1048 case 3: 1049 hat = SDL_HAT_RIGHTDOWN; 1050 break; 1051 case 4: 1052 hat = SDL_HAT_DOWN; 1053 break; 1054 case 5: 1055 hat = SDL_HAT_LEFTDOWN; 1056 break; 1057 case 6: 1058 hat = SDL_HAT_LEFT; 1059 break; 1060 case 7: 1061 hat = SDL_HAT_LEFTUP; 1062 break; 1063 default: 1064 hat = SDL_HAT_CENTERED; 1065 break; 1066 } 1067 SDL_SendJoystickHat(timestamp, joystick, 0, hat); 1068 } 1069 } 1070 1071 if (ctx->last_state.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) { 1072 Uint8 data = packet->rgucButtonsHatAndCounter[1]; 1073 1074 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data & 0x01) != 0)); 1075 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data & 0x02) != 0)); 1076 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data & 0x10) != 0)); 1077 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data & 0x20) != 0)); 1078 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data & 0x40) != 0)); 1079 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data & 0x80) != 0)); 1080 } 1081 1082 /* Some fightsticks, ex: Victrix FS Pro will only this these digital trigger bits and not the analog values so this needs to run whenever the 1083 trigger is evaluated 1084 */ 1085 if (packet->rgucButtonsHatAndCounter[1] & 0x0C) { 1086 Uint8 data = packet->rgucButtonsHatAndCounter[1]; 1087 packet->ucTriggerLeft = (data & 0x04) && packet->ucTriggerLeft == 0 ? 255 : packet->ucTriggerLeft; 1088 packet->ucTriggerRight = (data & 0x08) && packet->ucTriggerRight == 0 ? 255 : packet->ucTriggerRight; 1089 } 1090 1091 if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) { 1092 Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03); 1093 1094 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data & 0x01) != 0)); 1095 SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_PS4_TOUCHPAD, ((data & 0x02) != 0)); 1096 } 1097 1098 axis = ((int)packet->ucTriggerLeft * 257) - 32768; 1099 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis); 1100 axis = ((int)packet->ucTriggerRight * 257) - 32768; 1101 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis); 1102 axis = ((int)packet->ucLeftJoystickX * 257) - 32768; 1103 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis); 1104 axis = ((int)packet->ucLeftJoystickY * 257) - 32768; 1105 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis); 1106 axis = ((int)packet->ucRightJoystickX * 257) - 32768; 1107 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis); 1108 axis = ((int)packet->ucRightJoystickY * 257) - 32768; 1109 SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); 1110 1111 if (size > 9 && ctx->report_battery && ctx->enhanced_reports) { 1112 SDL_PowerState state; 1113 int percent; 1114 Uint8 level = (packet->ucBatteryLevel & 0x0F); 1115 1116 if (packet->ucBatteryLevel & 0x10) { 1117 if (level <= 10) { 1118 state = SDL_POWERSTATE_CHARGING; 1119 percent = SDL_min(level * 10 + 5, 100); 1120 } else if (level == 11) { 1121 state = SDL_POWERSTATE_CHARGED; 1122 percent = 100; 1123 } else { 1124 state = SDL_POWERSTATE_UNKNOWN; 1125 percent = 0; 1126 } 1127 } else { 1128 state = SDL_POWERSTATE_ON_BATTERY; 1129 percent = SDL_min(level * 10 + 5, 100); 1130 } 1131 SDL_SendJoystickPowerInfo(joystick, state, percent); 1132 } 1133 1134 if (size > 9 && ctx->report_sensors) { 1135 Uint16 tick; 1136 Uint16 delta; 1137 Uint64 sensor_timestamp; 1138 float data[3]; 1139 1140 tick = LOAD16(packet->rgucTimestamp[0], packet->rgucTimestamp[1]); 1141 if (ctx->last_tick < tick) { 1142 delta = (tick - ctx->last_tick); 1143 } else { 1144 delta = (SDL_MAX_UINT16 - ctx->last_tick + tick + 1); 1145 } 1146 ctx->sensor_ticks += delta; 1147 ctx->last_tick = tick; 1148 1149 // Sensor timestamp is in 5.33us units 1150 sensor_timestamp = (ctx->sensor_ticks * SDL_NS_PER_US * 16) / 3; 1151 1152 data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1])); 1153 data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1])); 1154 data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1])); 1155 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, sensor_timestamp, data, 3); 1156 1157 data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1])); 1158 data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1])); 1159 data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1])); 1160 SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, sensor_timestamp, data, 3); 1161 } 1162 1163 SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); 1164} 1165 1166static bool VerifyCRC(Uint8 *data, int size) 1167{ 1168 Uint8 ubHdr = 0xA1; // hidp header is part of the CRC calculation 1169 Uint32 unCRC, unPacketCRC; 1170 Uint8 *packetCRC = data + size - sizeof(unPacketCRC); 1171 unCRC = SDL_crc32(0, &ubHdr, 1); 1172 unCRC = SDL_crc32(unCRC, data, (size_t)(size - sizeof(unCRC))); 1173 1174 unPacketCRC = LOAD32(packetCRC[0], 1175 packetCRC[1], 1176 packetCRC[2], 1177 packetCRC[3]); 1178 return (unCRC == unPacketCRC); 1179} 1180 1181static bool HIDAPI_DriverPS4_IsPacketValid(SDL_DriverPS4_Context *ctx, Uint8 *data, int size) 1182{ 1183 switch (data[0]) { 1184 case k_EPS4ReportIdUsbState: 1185 if (size == 10) { 1186 // This is non-enhanced mode, this packet is fine 1187 return true; 1188 } 1189 1190 if (ctx->is_nacon_dongle && size >= (1 + sizeof(PS4StatePacket_t))) { 1191 // The report timestamp doesn't change when the controller isn't connected 1192 PS4StatePacket_t *packet = (PS4StatePacket_t *)&data[1]; 1193 if (SDL_memcmp(packet->rgucTimestamp, ctx->last_state.rgucTimestamp, sizeof(packet->rgucTimestamp)) == 0) { 1194 return false; 1195 } 1196 if (ctx->last_state.rgucAccelX[0] == 0 && ctx->last_state.rgucAccelX[1] == 0 && 1197 ctx->last_state.rgucAccelY[0] == 0 && ctx->last_state.rgucAccelY[1] == 0 && 1198 ctx->last_state.rgucAccelZ[0] == 0 && ctx->last_state.rgucAccelZ[1] == 0) { 1199 // We don't have any state to compare yet, go ahead and copy it 1200 SDL_memcpy(&ctx->last_state, &data[1], sizeof(PS4StatePacket_t)); 1201 return false; 1202 } 1203 } 1204 1205 /* In the case of a DS4 USB dongle, bit[2] of byte 31 indicates if a DS4 is actually connected (indicated by '0'). 1206 * For non-dongle, this bit is always 0 (connected). 1207 * This is usually the ID over USB, but the DS4v2 that started shipping with the PS4 Slim will also send this 1208 * packet over BT with a size of 128 1209 */ 1210 if (size >= 64 && !(data[31] & 0x04)) { 1211 return true; 1212 } 1213 break; 1214 case k_EPS4ReportIdBluetoothState1: 1215 case k_EPS4ReportIdBluetoothState2: 1216 case k_EPS4ReportIdBluetoothState3: 1217 case k_EPS4ReportIdBluetoothState4: 1218 case k_EPS4ReportIdBluetoothState5: 1219 case k_EPS4ReportIdBluetoothState6: 1220 case k_EPS4ReportIdBluetoothState7: 1221 case k_EPS4ReportIdBluetoothState8: 1222 case k_EPS4ReportIdBluetoothState9: 1223 // Bluetooth state packets have two additional bytes at the beginning, the first notes if HID data is present 1224 if (size >= 78 && (data[1] & 0x80)) { 1225 if (VerifyCRC(data, 78)) { 1226 ++ctx->valid_crc_packets; 1227 } else { 1228 if (ctx->valid_crc_packets > 0) { 1229 --ctx->valid_crc_packets; 1230 } 1231 if (ctx->valid_crc_packets >= 3) { 1232 // We're generally getting valid CRC, but failed one 1233 return false; 1234 } 1235 } 1236 return true; 1237 } 1238 break; 1239 default: 1240 break; 1241 } 1242 return false; 1243} 1244 1245static bool HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device) 1246{ 1247 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 1248 SDL_Joystick *joystick = NULL; 1249 Uint8 data[USB_PACKET_LENGTH * 2]; 1250 int size; 1251 int packet_count = 0; 1252 Uint64 now = SDL_GetTicks(); 1253 1254 if (device->num_joysticks > 0) { 1255 joystick = SDL_GetJoystickFromID(device->joysticks[0]); 1256 } 1257 1258 while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { 1259#ifdef DEBUG_PS4_PROTOCOL 1260 HIDAPI_DumpPacket("PS4 packet: size = %d", data, size); 1261#endif 1262 if (!HIDAPI_DriverPS4_IsPacketValid(ctx, data, size)) { 1263 continue; 1264 } 1265 1266 ++packet_count; 1267 ctx->last_packet = now; 1268 1269 if (!joystick) { 1270 continue; 1271 } 1272 1273 switch (data[0]) { 1274 case k_EPS4ReportIdUsbState: 1275 HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1], size - 1); 1276 break; 1277 case k_EPS4ReportIdBluetoothState1: 1278 case k_EPS4ReportIdBluetoothState2: 1279 case k_EPS4ReportIdBluetoothState3: 1280 case k_EPS4ReportIdBluetoothState4: 1281 case k_EPS4ReportIdBluetoothState5: 1282 case k_EPS4ReportIdBluetoothState6: 1283 case k_EPS4ReportIdBluetoothState7: 1284 case k_EPS4ReportIdBluetoothState8: 1285 case k_EPS4ReportIdBluetoothState9: 1286 // This is the extended report, we can enable effects now in auto mode 1287 HIDAPI_DriverPS4_UpdateEnhancedModeOnEnhancedReport(ctx); 1288 1289 // Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present 1290 HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[3], size - 3); 1291 break; 1292 default: 1293#ifdef DEBUG_JOYSTICK 1294 SDL_Log("Unknown PS4 packet: 0x%.2x", data[0]); 1295#endif 1296 break; 1297 } 1298 } 1299 1300 if (device->is_bluetooth) { 1301 if (packet_count == 0) { 1302 // Check to see if it looks like the device disconnected 1303 if (now >= (ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) { 1304 // Send an empty output report to tickle the Bluetooth stack 1305 HIDAPI_DriverPS4_TickleBluetooth(device); 1306 ctx->last_packet = now; 1307 } 1308 } else { 1309 // Reconnect the Bluetooth device once the USB device is gone 1310 if (device->num_joysticks == 0 && 1311 !HIDAPI_HasConnectedUSBDevice(device->serial)) { 1312 HIDAPI_JoystickConnected(device, NULL); 1313 } 1314 } 1315 } 1316 1317 if (ctx->is_dongle || ctx->is_nacon_dongle) { 1318 if (packet_count == 0) { 1319 if (device->num_joysticks > 0) { 1320 // Check to see if it looks like the device disconnected 1321 if (now >= (ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) { 1322 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1323 } 1324 } 1325 } else { 1326 if (device->num_joysticks == 0) { 1327 char serial[18]; 1328 size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data)); 1329 if (size >= 7 && (data[1] || data[2] || data[3] || data[4] || data[5] || data[6])) { 1330 (void)SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", 1331 data[6], data[5], data[4], data[3], data[2], data[1]); 1332 HIDAPI_SetDeviceSerial(device, serial); 1333 } 1334 HIDAPI_JoystickConnected(device, NULL); 1335 } 1336 } 1337 } 1338 1339 if (packet_count == 0 && size < 0 && device->num_joysticks > 0) { 1340 // Read error, device is disconnected 1341 HIDAPI_JoystickDisconnected(device, device->joysticks[0]); 1342 } 1343 return (size >= 0); 1344} 1345 1346static void HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) 1347{ 1348 SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; 1349 1350 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL, 1351 SDL_PS4ReportIntervalHintChanged, ctx); 1352 SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, 1353 SDL_PS4EnhancedReportsChanged, ctx); 1354 1355 ctx->joystick = NULL; 1356 1357 ctx->report_sensors = false; 1358 ctx->enhanced_mode = false; 1359 ctx->enhanced_mode_available = false; 1360} 1361 1362static void HIDAPI_DriverPS4_FreeDevice(SDL_HIDAPI_Device *device) 1363{ 1364} 1365 1366SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = { 1367 SDL_HINT_JOYSTICK_HIDAPI_PS4, 1368 true, 1369 HIDAPI_DriverPS4_RegisterHints, 1370 HIDAPI_DriverPS4_UnregisterHints, 1371 HIDAPI_DriverPS4_IsEnabled, 1372 HIDAPI_DriverPS4_IsSupportedDevice, 1373 HIDAPI_DriverPS4_InitDevice, 1374 HIDAPI_DriverPS4_GetDevicePlayerIndex, 1375 HIDAPI_DriverPS4_SetDevicePlayerIndex, 1376 HIDAPI_DriverPS4_UpdateDevice, 1377 HIDAPI_DriverPS4_OpenJoystick, 1378 HIDAPI_DriverPS4_RumbleJoystick, 1379 HIDAPI_DriverPS4_RumbleJoystickTriggers, 1380 HIDAPI_DriverPS4_GetJoystickCapabilities, 1381 HIDAPI_DriverPS4_SetJoystickLED, 1382 HIDAPI_DriverPS4_SendJoystickEffect, 1383 HIDAPI_DriverPS4_SetJoystickSensorsEnabled, 1384 HIDAPI_DriverPS4_CloseJoystick, 1385 HIDAPI_DriverPS4_FreeDevice, 1386}; 1387 1388#endif // SDL_JOYSTICK_HIDAPI_PS4 1389 1390#endif // SDL_JOYSTICK_HIDAPI 1391
[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.