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