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