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