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