SDL  2.0
SDL_hidapi_ps5.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.h"
26 #include "SDL_events.h"
27 #include "SDL_timer.h"
28 #include "SDL_joystick.h"
29 #include "SDL_gamecontroller.h"
30 #include "../SDL_sysjoystick.h"
31 #include "SDL_hidapijoystick_c.h"
32 #include "SDL_hidapi_rumble.h"
33 
34 
35 #ifdef SDL_JOYSTICK_HIDAPI_PS5
36 
37 /* Define this if you want to log all packets from the controller */
38 /*#define DEBUG_PS5_PROTOCOL*/
39 
40 /* Define this if you want to log calibration data */
41 /*#define DEBUG_PS5_CALIBRATION*/
42 
43 #define GYRO_RES_PER_DEGREE 1024.0f
44 #define ACCEL_RES_PER_G 8192.0f
45 #define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500
46 
47 #define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
48 
49 typedef enum
50 {
51  k_EPS5ReportIdState = 0x01,
52  k_EPS5ReportIdUsbEffects = 0x02,
53  k_EPS5ReportIdBluetoothEffects = 0x31,
54  k_EPS5ReportIdBluetoothState = 0x31,
55 } EPS5ReportId;
56 
57 typedef enum
58 {
59  k_EPS5FeatureReportIdCalibration = 0x05,
60  k_EPS5FeatureReportIdSerialNumber = 0x09,
61 } EPS5FeatureReportId;
62 
63 typedef struct
64 {
65  Uint8 ucLeftJoystickX;
66  Uint8 ucLeftJoystickY;
67  Uint8 ucRightJoystickX;
68  Uint8 ucRightJoystickY;
69  Uint8 rgucButtonsHatAndCounter[3];
70  Uint8 ucTriggerLeft;
71  Uint8 ucTriggerRight;
72 } PS5SimpleStatePacket_t;
73 
74 typedef struct
75 {
76  Uint8 ucLeftJoystickX; /* 0 */
77  Uint8 ucLeftJoystickY; /* 1 */
78  Uint8 ucRightJoystickX; /* 2 */
79  Uint8 ucRightJoystickY; /* 3 */
80  Uint8 ucTriggerLeft; /* 4 */
81  Uint8 ucTriggerRight; /* 5 */
82  Uint8 ucCounter; /* 6 */
83  Uint8 rgucButtonsAndHat[3]; /* 7 */
84  Uint8 ucZero; /* 10 */
85  Uint8 rgucPacketSequence[4]; /* 11 - 32 bit little endian */
86  Uint8 rgucGyroX[2]; /* 15 */
87  Uint8 rgucGyroY[2]; /* 17 */
88  Uint8 rgucGyroZ[2]; /* 19 */
89  Uint8 rgucAccelX[2]; /* 21 */
90  Uint8 rgucAccelY[2]; /* 23 */
91  Uint8 rgucAccelZ[2]; /* 25 */
92  Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */
93  Uint8 ucBatteryTemp; /* 31 */
94  Uint8 ucTouchpadCounter1; /* 32 - high bit clear + counter */
95  Uint8 rgucTouchpadData1[3]; /* 33 - X/Y, 12 bits per axis */
96  Uint8 ucTouchpadCounter2; /* 36 - high bit clear + counter */
97  Uint8 rgucTouchpadData2[3]; /* 37 - X/Y, 12 bits per axis */
98  Uint8 rgucUnknown1[8]; /* 40 */
99  Uint8 rgucTimer2[4]; /* 48 - 32 bit little endian */
100  Uint8 ucBatteryLevel; /* 52 */
101  Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x01 = headphone */
102 
103  /* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */
104 } PS5StatePacket_t;
105 
106 typedef struct
107 {
108  Uint8 ucEnableBits1; /* 0 */
109  Uint8 ucEnableBits2; /* 1 */
110  Uint8 ucRumbleRight; /* 2 */
111  Uint8 ucRumbleLeft; /* 3 */
112  Uint8 ucHeadphoneVolume; /* 4 */
113  Uint8 ucSpeakerVolume; /* 5 */
114  Uint8 ucMicrophoneVolume; /* 6 */
115  Uint8 ucAudioEnableBits; /* 7 */
116  Uint8 ucMicLightMode; /* 8 */
117  Uint8 ucAudioMuteBits; /* 9 */
118  Uint8 rgucRightTriggerEffect[11]; /* 10 */
119  Uint8 rgucLeftTriggerEffect[11]; /* 21 */
120  Uint8 rgucUnknown1[6]; /* 32 */
121  Uint8 ucLedFlags; /* 38 */
122  Uint8 rgucUnknown2[2]; /* 39 */
123  Uint8 ucLedAnim; /* 41 */
124  Uint8 ucLedBrightness; /* 42 */
125  Uint8 ucPadLights; /* 43 */
126  Uint8 ucLedRed; /* 44 */
127  Uint8 ucLedGreen; /* 45 */
128  Uint8 ucLedBlue; /* 46 */
129 } DS5EffectsState_t;
130 
131 typedef enum {
132  k_EDS5EffectNone,
133  k_EDS5EffectRumbleStart,
134  k_EDS5EffectRumble,
135  k_EDS5EffectLEDReset,
136  k_EDS5EffectLED,
137  k_EDS5EffectPadLights,
138  k_EDS5EffectMicLight,
139 } EDS5Effect;
140 
141 typedef enum {
142  k_EDS5LEDResetStateNone,
143  k_EDS5LEDResetStatePending,
144  k_EDS5LEDResetStateComplete,
145 } EDS5LEDResetState;
146 
147 typedef struct {
148  Sint16 bias;
149  float sensitivity;
150 } IMUCalibrationData;
151 
152 typedef struct {
153  SDL_bool is_bluetooth;
154  SDL_bool report_sensors;
155  SDL_bool hardware_calibration;
156  IMUCalibrationData calibration[6];
157  Uint32 last_packet;
158  int player_index;
159  Uint8 rumble_left;
160  Uint8 rumble_right;
161  SDL_bool color_set;
162  Uint8 led_red;
163  Uint8 led_green;
164  Uint8 led_blue;
165  EDS5LEDResetState led_reset_state;
166  union
167  {
168  PS5SimpleStatePacket_t simple;
169  PS5StatePacket_t state;
170  } last_state;
171 } SDL_DriverPS5_Context;
172 
173 
174 static SDL_bool
175 HIDAPI_DriverPS5_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
176 {
178 }
179 
180 static const char *
181 HIDAPI_DriverPS5_GetDeviceName(Uint16 vendor_id, Uint16 product_id)
182 {
183  if (vendor_id == USB_VENDOR_SONY) {
184  return "PS5 Controller";
185  }
186  return NULL;
187 }
188 
189 static int ReadFeatureReport(hid_device *dev, Uint8 report_id, Uint8 *report, size_t length)
190 {
191  SDL_memset(report, 0, length);
192  report[0] = report_id;
193  return hid_get_feature_report(dev, report, length);
194 }
195 
196 static void
197 SetLedsForPlayerIndex(DS5EffectsState_t *effects, int player_index)
198 {
199  /* This list is the same as what hid-sony.c uses in the Linux kernel.
200  The first 4 values correspond to what the PS4 assigns.
201  */
202  static const Uint8 colors[7][3] = {
203  { 0x00, 0x00, 0x40 }, /* Blue */
204  { 0x40, 0x00, 0x00 }, /* Red */
205  { 0x00, 0x40, 0x00 }, /* Green */
206  { 0x20, 0x00, 0x20 }, /* Pink */
207  { 0x02, 0x01, 0x00 }, /* Orange */
208  { 0x00, 0x01, 0x01 }, /* Teal */
209  { 0x01, 0x01, 0x01 } /* White */
210  };
211 
212  if (player_index >= 0) {
213  player_index %= SDL_arraysize(colors);
214  } else {
215  player_index = 0;
216  }
217 
218  effects->ucLedRed = colors[player_index][0];
219  effects->ucLedGreen = colors[player_index][1];
220  effects->ucLedBlue = colors[player_index][2];
221 }
222 
223 static SDL_bool
224 HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device)
225 {
227 }
228 
229 static int
230 HIDAPI_DriverPS5_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
231 {
232  return -1;
233 }
234 
235 static void
236 HIDAPI_DriverPS5_LoadCalibrationData(SDL_HIDAPI_Device *device)
237 {
238  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
239  int i, size;
241 
242  size = ReadFeatureReport(device->dev, k_EPS5FeatureReportIdCalibration, data, sizeof(data));
243  if (size < 35) {
244 #ifdef DEBUG_PS5_CALIBRATION
245  SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
246 #endif
247  return;
248  }
249 
250  {
251  Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias;
252  Sint16 sGyroPitchPlus, sGyroPitchMinus;
253  Sint16 sGyroYawPlus, sGyroYawMinus;
254  Sint16 sGyroRollPlus, sGyroRollMinus;
255  Sint16 sGyroSpeedPlus, sGyroSpeedMinus;
256 
257  Sint16 sAccXPlus, sAccXMinus;
258  Sint16 sAccYPlus, sAccYMinus;
259  Sint16 sAccZPlus, sAccZMinus;
260 
261  float flNumerator;
262  Sint16 sRange2g;
263 
264 #ifdef DEBUG_PS5_CALIBRATION
265  HIDAPI_DumpPacket("PS5 calibration packet: size = %d", data, size);
266 #endif
267 
268  sGyroPitchBias = LOAD16(data[1], data[2]);
269  sGyroYawBias = LOAD16(data[3], data[4]);
270  sGyroRollBias = LOAD16(data[5], data[6]);
271 
272  sGyroPitchPlus = LOAD16(data[7], data[8]);
273  sGyroPitchMinus = LOAD16(data[9], data[10]);
274  sGyroYawPlus = LOAD16(data[11], data[12]);
275  sGyroYawMinus = LOAD16(data[13], data[14]);
276  sGyroRollPlus = LOAD16(data[15], data[16]);
277  sGyroRollMinus = LOAD16(data[17], data[18]);
278 
279  sGyroSpeedPlus = LOAD16(data[19], data[20]);
280  sGyroSpeedMinus = LOAD16(data[21], data[22]);
281 
282  sAccXPlus = LOAD16(data[23], data[24]);
283  sAccXMinus = LOAD16(data[25], data[26]);
284  sAccYPlus = LOAD16(data[27], data[28]);
285  sAccYMinus = LOAD16(data[29], data[30]);
286  sAccZPlus = LOAD16(data[31], data[32]);
287  sAccZMinus = LOAD16(data[33], data[34]);
288 
289  flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE;
290  ctx->calibration[0].bias = sGyroPitchBias;
291  ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus);
292 
293  ctx->calibration[1].bias = sGyroYawBias;
294  ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus);
295 
296  ctx->calibration[2].bias = sGyroRollBias;
297  ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus);
298 
299  sRange2g = sAccXPlus - sAccXMinus;
300  ctx->calibration[3].bias = sAccXPlus - sRange2g / 2;
301  ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
302 
303  sRange2g = sAccYPlus - sAccYMinus;
304  ctx->calibration[4].bias = sAccYPlus - sRange2g / 2;
305  ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
306 
307  sRange2g = sAccZPlus - sAccZMinus;
308  ctx->calibration[5].bias = sAccZPlus - sRange2g / 2;
309  ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
310 
311  ctx->hardware_calibration = SDL_TRUE;
312  for (i = 0; i < 6; ++i) {
313  float divisor = (i < 3 ? 64.0f : 1.0f);
314 #ifdef DEBUG_PS5_CALIBRATION
315  SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity);
316 #endif
317  /* Some controllers have a bad calibration */
318  if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabs(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) {
319 #ifdef DEBUG_PS5_CALIBRATION
320  SDL_Log("invalid calibration, ignoring\n");
321 #endif
322  ctx->hardware_calibration = SDL_FALSE;
323  }
324  }
325  }
326 }
327 
328 static float
329 HIDAPI_DriverPS5_ApplyCalibrationData(SDL_DriverPS5_Context *ctx, int index, Sint16 value)
330 {
331  float result;
332 
333  if (ctx->hardware_calibration) {
334  IMUCalibrationData *calibration = &ctx->calibration[index];
335 
336  result = (value - calibration->bias) * calibration->sensitivity;
337  } else if (index < 3) {
338  result = value * 64.f;
339  } else {
340  result = value;
341  }
342 
343  /* Convert the raw data to the units expected by SDL */
344  if (index < 3) {
345  result = (result / GYRO_RES_PER_DEGREE) * (float)M_PI / 180.0f;
346  } else {
347  result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY;
348  }
349  return result;
350 }
351 
352 static int
353 HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, EDS5Effect effect)
354 {
355  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
356  DS5EffectsState_t *effects;
357  Uint8 data[78];
358  int report_size, offset;
359  Uint8 *pending_data;
360  int *pending_size;
361  int maximum_size;
362 
363 
364  SDL_zero(data);
365 
366  if (ctx->is_bluetooth) {
367  data[0] = k_EPS5ReportIdBluetoothEffects;
368  data[1] = 0x02; /* Magic value */
369 
370  report_size = 78;
371  offset = 2;
372  } else {
373  data[0] = k_EPS5ReportIdUsbEffects;
374 
375  report_size = 48;
376  offset = 1;
377  }
378  effects = (DS5EffectsState_t *)&data[offset];
379 
380  /* Make sure the Bluetooth connection sequence has completed before sending LED color change */
381  if (effect == k_EDS5EffectLED && ctx->is_bluetooth) {
382  if (ctx->led_reset_state != k_EDS5LEDResetStateComplete) {
383  ctx->led_reset_state = k_EDS5LEDResetStatePending;
384  return 0;
385  }
386  }
387 
388  if (ctx->rumble_left || ctx->rumble_right) {
389  effects->ucEnableBits1 |= 0x01; /* Enable rumble emulation */
390  effects->ucEnableBits1 |= 0x02; /* Disable audio haptics */
391 
392  /* Shift to reduce effective rumble strength to match Xbox controllers */
393  effects->ucRumbleLeft = ctx->rumble_left >> 1;
394  effects->ucRumbleRight = ctx->rumble_right >> 1;
395  } else {
396  /* Leaving emulated rumble bits off will restore audio haptics */
397  }
398 
399  switch (effect) {
400  case k_EDS5EffectRumbleStart:
401  effects->ucEnableBits1 |= 0x02; /* Disable audio haptics */
402  break;
403  case k_EDS5EffectRumble:
404  /* Already handled above */
405  break;
406  case k_EDS5EffectLEDReset:
407  effects->ucEnableBits2 |= 0x08; /* Reset LED state */
408  break;
409  case k_EDS5EffectLED:
410  effects->ucEnableBits2 |= 0x04; /* Enable LED color */
411 
412  /* Populate the LED state with the appropriate color from our lookup table */
413  if (ctx->color_set) {
414  effects->ucLedRed = ctx->led_red;
415  effects->ucLedGreen = ctx->led_green;
416  effects->ucLedBlue = ctx->led_blue;
417  } else {
418  SetLedsForPlayerIndex(effects, ctx->player_index);
419  }
420  break;
421  case k_EDS5EffectPadLights:
422  effects->ucEnableBits2 |= 0x10; /* Enable touchpad lights */
423 
424  effects->ucPadLights = 0x00; /* Bitmask, 0x1F enables all lights, 0x20 changes instantly instead of fade */
425  break;
426  case k_EDS5EffectMicLight:
427  effects->ucEnableBits2 |= 0x01; /* Enable microphone light */
428 
429  effects->ucMicLightMode = 0; /* Bitmask, 0x00 = off, 0x01 = solid, 0x02 = pulse */
430  break;
431  default:
432  break;
433  }
434 
435  if (ctx->is_bluetooth) {
436  /* Bluetooth reports need a CRC at the end of the packet (at least on Linux) */
437  Uint8 ubHdr = 0xA2; /* hidp header is part of the CRC calculation */
438  Uint32 unCRC;
439  unCRC = SDL_crc32(0, &ubHdr, 1);
440  unCRC = SDL_crc32(unCRC, data, (size_t)(report_size - sizeof(unCRC)));
441  SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
442  }
443 
444  if (SDL_HIDAPI_LockRumble() < 0) {
445  return -1;
446  }
447 
448  /* See if we can update an existing pending request */
449  if (SDL_HIDAPI_GetPendingRumbleLocked(device, &pending_data, &pending_size, &maximum_size)) {
450  DS5EffectsState_t *pending_effects = (DS5EffectsState_t *)&pending_data[offset];
451  if (report_size == *pending_size &&
452  effects->ucEnableBits1 == pending_effects->ucEnableBits1 &&
453  effects->ucEnableBits2 == pending_effects->ucEnableBits2) {
454  /* We're simply updating the data for this request */
455  SDL_memcpy(pending_data, data, report_size);
456  SDL_HIDAPI_UnlockRumble();
457  return 0;
458  }
459  }
460 
461  return SDL_HIDAPI_SendRumbleAndUnlock(device, data, report_size);
462 }
463 
464 static void
465 HIDAPI_DriverPS5_SetBluetooth(SDL_HIDAPI_Device *device, SDL_bool is_bluetooth)
466 {
467  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
468 
469  if (ctx->is_bluetooth != is_bluetooth) {
470  ctx->is_bluetooth = is_bluetooth;
471  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
472  }
473 }
474 
475 static void
476 HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
477 {
478  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
479  const PS5StatePacket_t *packet = &ctx->last_state.state;
480 
481  /* Check the timer to make sure the Bluetooth connection LED animation is complete */
482  const Uint32 connection_complete = 10000000;
483  Uint32 timer = ((Uint32)packet->rgucTimer1[0] << 0) |
484  ((Uint32)packet->rgucTimer1[1] << 8) |
485  ((Uint32)packet->rgucTimer1[2] << 16) |
486  ((Uint32)packet->rgucTimer1[3] << 24);
487  if (timer >= connection_complete) {
488  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLEDReset);
489 
490  ctx->led_reset_state = k_EDS5LEDResetStateComplete;
491 
492  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
493  }
494 }
495 
496 static void
497 HIDAPI_DriverPS5_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
498 {
499  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
500 
501  if (!ctx) {
502  return;
503  }
504 
505  ctx->player_index = player_index;
506 
507  /* This will set the new LED state based on the new player index */
508  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
509 }
510 
511 static SDL_bool
512 HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
513 {
514  SDL_DriverPS5_Context *ctx;
516 
517  ctx = (SDL_DriverPS5_Context *)SDL_calloc(1, sizeof(*ctx));
518  if (!ctx) {
519  SDL_OutOfMemory();
520  return SDL_FALSE;
521  }
522  ctx->last_packet = SDL_GetTicks();
523 
524  device->dev = hid_open_path(device->path, 0);
525  if (!device->dev) {
526  SDL_free(ctx);
527  SDL_SetError("Couldn't open %s", device->path);
528  return SDL_FALSE;
529  }
530  device->context = ctx;
531 
532  /* Read the serial number (Bluetooth address in reverse byte order)
533  This will also enable enhanced reports over Bluetooth
534  */
535  if (ReadFeatureReport(device->dev, k_EPS5FeatureReportIdSerialNumber, data, sizeof(data)) >= 7) {
536  char serial[18];
537 
538  SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
539  data[6], data[5], data[4], data[3], data[2], data[1]);
540  joystick->serial = SDL_strdup(serial);
541  }
542 
543  /* Initialize player index (needed for setting LEDs) */
544  ctx->player_index = SDL_JoystickGetPlayerIndex(joystick);
545 
546  /* Initialize LED and effect state */
547  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
548 
549  /* Initialize the joystick capabilities */
550  joystick->nbuttons = 17;
552 
556 
557  return SDL_TRUE;
558 }
559 
560 static int
561 HIDAPI_DriverPS5_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
562 {
563  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
564 
565  if (!ctx->rumble_left && !ctx->rumble_right) {
566  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectRumbleStart);
567  }
568 
569  ctx->rumble_left = (low_frequency_rumble >> 8);
570  ctx->rumble_right = (high_frequency_rumble >> 8);
571 
572  return HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectRumble);
573 }
574 
575 static int
576 HIDAPI_DriverPS5_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
577 {
578  return SDL_Unsupported();
579 }
580 
581 static SDL_bool
582 HIDAPI_DriverPS5_HasJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
583 {
584  return SDL_FALSE;
585 }
586 
587 static int
588 HIDAPI_DriverPS5_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
589 {
590  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
591 
592  ctx->color_set = SDL_TRUE;
593  ctx->led_red = red;
594  ctx->led_green = green;
595  ctx->led_blue = blue;
596 
597  return HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectLED);
598 }
599 
600 static int
601 HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled)
602 {
603  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
604 
605  if (enabled) {
606  HIDAPI_DriverPS5_LoadCalibrationData(device);
607  }
608  ctx->report_sensors = enabled;
609 
610  return 0;
611 }
612 
613 static void
614 HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet)
615 {
616  Sint16 axis;
617 
618  if (ctx->last_state.simple.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
619  {
620  Uint8 data = (packet->rgucButtonsHatAndCounter[0] >> 4);
621 
626  }
627  {
628  Uint8 data = (packet->rgucButtonsHatAndCounter[0] & 0x0F);
629  SDL_bool dpad_up = SDL_FALSE;
630  SDL_bool dpad_down = SDL_FALSE;
631  SDL_bool dpad_left = SDL_FALSE;
632  SDL_bool dpad_right = SDL_FALSE;
633 
634  switch (data) {
635  case 0:
636  dpad_up = SDL_TRUE;
637  break;
638  case 1:
639  dpad_up = SDL_TRUE;
640  dpad_right = SDL_TRUE;
641  break;
642  case 2:
643  dpad_right = SDL_TRUE;
644  break;
645  case 3:
646  dpad_right = SDL_TRUE;
647  dpad_down = SDL_TRUE;
648  break;
649  case 4:
650  dpad_down = SDL_TRUE;
651  break;
652  case 5:
653  dpad_left = SDL_TRUE;
654  dpad_down = SDL_TRUE;
655  break;
656  case 6:
657  dpad_left = SDL_TRUE;
658  break;
659  case 7:
660  dpad_up = SDL_TRUE;
661  dpad_left = SDL_TRUE;
662  break;
663  default:
664  break;
665  }
670  }
671  }
672 
673  if (ctx->last_state.simple.rgucButtonsHatAndCounter[1] != packet->rgucButtonsHatAndCounter[1]) {
674  Uint8 data = packet->rgucButtonsHatAndCounter[1];
675 
682  }
683 
684  if (ctx->last_state.simple.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) {
685  Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03);
686 
689  }
690 
691  axis = ((int)packet->ucTriggerLeft * 257) - 32768;
693  axis = ((int)packet->ucTriggerRight * 257) - 32768;
695  axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
697  axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
699  axis = ((int)packet->ucRightJoystickX * 257) - 32768;
701  axis = ((int)packet->ucRightJoystickY * 257) - 32768;
703 
704  SDL_memcpy(&ctx->last_state.simple, packet, sizeof(ctx->last_state.simple));
705 }
706 
707 static void
708 HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet)
709 {
710  static const float TOUCHPAD_SCALEX = 1.0f / 1920;
711  static const float TOUCHPAD_SCALEY = 1.0f / 1070;
712  Sint16 axis;
713  Uint8 touchpad_state;
714  int touchpad_x, touchpad_y;
715 
716  if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) {
717  {
718  Uint8 data = (packet->rgucButtonsAndHat[0] >> 4);
719 
724  }
725  {
726  Uint8 data = (packet->rgucButtonsAndHat[0] & 0x0F);
727  SDL_bool dpad_up = SDL_FALSE;
728  SDL_bool dpad_down = SDL_FALSE;
729  SDL_bool dpad_left = SDL_FALSE;
730  SDL_bool dpad_right = SDL_FALSE;
731 
732  switch (data) {
733  case 0:
734  dpad_up = SDL_TRUE;
735  break;
736  case 1:
737  dpad_up = SDL_TRUE;
738  dpad_right = SDL_TRUE;
739  break;
740  case 2:
741  dpad_right = SDL_TRUE;
742  break;
743  case 3:
744  dpad_right = SDL_TRUE;
745  dpad_down = SDL_TRUE;
746  break;
747  case 4:
748  dpad_down = SDL_TRUE;
749  break;
750  case 5:
751  dpad_left = SDL_TRUE;
752  dpad_down = SDL_TRUE;
753  break;
754  case 6:
755  dpad_left = SDL_TRUE;
756  break;
757  case 7:
758  dpad_up = SDL_TRUE;
759  dpad_left = SDL_TRUE;
760  break;
761  default:
762  break;
763  }
768  }
769  }
770 
771  if (ctx->last_state.state.rgucButtonsAndHat[1] != packet->rgucButtonsAndHat[1]) {
772  Uint8 data = packet->rgucButtonsAndHat[1];
773 
780  }
781 
782  if (ctx->last_state.state.rgucButtonsAndHat[2] != packet->rgucButtonsAndHat[2]) {
783  Uint8 data = packet->rgucButtonsAndHat[2];
784 
788  }
789 
790  axis = ((int)packet->ucTriggerLeft * 257) - 32768;
792  axis = ((int)packet->ucTriggerRight * 257) - 32768;
794  axis = ((int)packet->ucLeftJoystickX * 257) - 32768;
796  axis = ((int)packet->ucLeftJoystickY * 257) - 32768;
798  axis = ((int)packet->ucRightJoystickX * 257) - 32768;
800  axis = ((int)packet->ucRightJoystickY * 257) - 32768;
802 
803  if (packet->ucBatteryLevel & 0x10) {
804  /* 0x20 set means fully charged */
806  } else {
807  /* Battery level ranges from 0 to 10 */
808  int level = (packet->ucBatteryLevel & 0xF);
809  if (level == 0) {
811  } else if (level <= 2) {
813  } else if (level <= 7) {
815  } else {
817  }
818  }
819 
820  touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
821  touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
822  touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
823  SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
824 
825  touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
826  touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
827  touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
828  SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
829 
830  if (ctx->report_sensors) {
831  float data[3];
832 
833  data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1]));
834  data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1]));
835  data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1]));
837 
838  data[0] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1]));
839  data[1] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1]));
840  data[2] = HIDAPI_DriverPS5_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1]));
842  }
843 
844  SDL_memcpy(&ctx->last_state.state, packet, sizeof(ctx->last_state.state));
845 }
846 
847 static SDL_bool
848 HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
849 {
850  SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
851  SDL_Joystick *joystick = NULL;
853  int size;
854  int packet_count = 0;
855 
856  if (device->num_joysticks > 0) {
857  joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
858  }
859  if (!joystick) {
860  return SDL_FALSE;
861  }
862 
863  while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
864 #ifdef DEBUG_PS5_PROTOCOL
865  HIDAPI_DumpPacket("PS5 packet: size = %d", data, size);
866 #endif
867  ++packet_count;
868  ctx->last_packet = SDL_GetTicks();
869 
870  switch (data[0]) {
871  case k_EPS5ReportIdState:
872  if (size == 10) {
873  HIDAPI_DriverPS5_SetBluetooth(device, SDL_TRUE); /* Simple state packet over Bluetooth */
874  HIDAPI_DriverPS5_HandleSimpleStatePacket(joystick, device->dev, ctx, (PS5SimpleStatePacket_t *)&data[1]);
875  } else {
876  HIDAPI_DriverPS5_SetBluetooth(device, SDL_FALSE);
877  HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[1]);
878  }
879  break;
880  case k_EPS5ReportIdBluetoothState:
881  HIDAPI_DriverPS5_SetBluetooth(device, SDL_TRUE);
882  HIDAPI_DriverPS5_HandleStatePacket(joystick, device->dev, ctx, (PS5StatePacket_t *)&data[2]);
883  if (ctx->led_reset_state == k_EDS5LEDResetStatePending) {
884  HIDAPI_DriverPS5_CheckPendingLEDReset(device);
885  }
886  break;
887  default:
888 #ifdef DEBUG_JOYSTICK
889  SDL_Log("Unknown PS5 packet: 0x%.2x\n", data[0]);
890 #endif
891  break;
892  }
893  }
894 
895  if (ctx->is_bluetooth && packet_count == 0) {
896  /* Check to see if it looks like the device disconnected */
897  if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) {
898  /* Send an empty output report to tickle the Bluetooth stack */
899  HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectNone);
900  }
901  }
902 
903  if (size < 0) {
904  /* Read error, device is disconnected */
906  }
907  return (size >= 0);
908 }
909 
910 static void
911 HIDAPI_DriverPS5_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
912 {
913  hid_close(device->dev);
914  device->dev = NULL;
915 
916  SDL_free(device->context);
917  device->context = NULL;
918 }
919 
920 static void
921 HIDAPI_DriverPS5_FreeDevice(SDL_HIDAPI_Device *device)
922 {
923 }
924 
926 {
928  SDL_TRUE,
929  HIDAPI_DriverPS5_IsSupportedDevice,
930  HIDAPI_DriverPS5_GetDeviceName,
931  HIDAPI_DriverPS5_InitDevice,
932  HIDAPI_DriverPS5_GetDevicePlayerIndex,
933  HIDAPI_DriverPS5_SetDevicePlayerIndex,
934  HIDAPI_DriverPS5_UpdateDevice,
935  HIDAPI_DriverPS5_OpenJoystick,
936  HIDAPI_DriverPS5_RumbleJoystick,
937  HIDAPI_DriverPS5_RumbleJoystickTriggers,
938  HIDAPI_DriverPS5_HasJoystickLED,
939  HIDAPI_DriverPS5_SetJoystickLED,
940  HIDAPI_DriverPS5_SetJoystickSensorsEnabled,
941  HIDAPI_DriverPS5_CloseJoystick,
942  HIDAPI_DriverPS5_FreeDevice,
943 };
944 
945 #endif /* SDL_JOYSTICK_HIDAPI_PS5 */
946 
947 #endif /* SDL_JOYSTICK_HIDAPI */
948 
949 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_SetError
#define SDL_memset
#define SDL_abs
#define SDL_JoystickGetPlayerIndex
#define SDL_crc32
#define SDL_fabs
#define SDL_JoystickFromInstanceID
#define SDL_free
#define SDL_strdup
#define SDL_memcpy
#define SDL_Log
#define SDL_snprintf
#define SDL_calloc
#define SDL_OutOfMemory()
Definition: SDL_error.h:88
#define SDL_Unsupported()
Definition: SDL_error.h:89
#define SDL_RELEASED
Definition: SDL_events.h:49
#define SDL_PRESSED
Definition: SDL_events.h:50
@ SDL_CONTROLLER_AXIS_LEFTX
@ SDL_CONTROLLER_AXIS_TRIGGERRIGHT
@ SDL_CONTROLLER_AXIS_RIGHTY
@ SDL_CONTROLLER_AXIS_RIGHTX
@ SDL_CONTROLLER_AXIS_MAX
@ SDL_CONTROLLER_AXIS_TRIGGERLEFT
@ SDL_CONTROLLER_AXIS_LEFTY
@ SDL_CONTROLLER_BUTTON_B
@ SDL_CONTROLLER_BUTTON_BACK
@ SDL_CONTROLLER_BUTTON_LEFTSTICK
@ SDL_CONTROLLER_BUTTON_START
@ SDL_CONTROLLER_BUTTON_DPAD_LEFT
@ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ SDL_CONTROLLER_BUTTON_DPAD_DOWN
@ SDL_CONTROLLER_BUTTON_DPAD_UP
@ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
@ SDL_CONTROLLER_BUTTON_GUIDE
@ SDL_CONTROLLER_BUTTON_DPAD_RIGHT
@ SDL_CONTROLLER_BUTTON_X
@ SDL_CONTROLLER_BUTTON_RIGHTSTICK
@ SDL_CONTROLLER_BUTTON_Y
@ SDL_CONTROLLER_BUTTON_A
SDL_GameControllerType
@ SDL_CONTROLLER_TYPE_PS5
const GLubyte GLuint red
Definition: SDL_glfuncs.h:80
#define USB_PACKET_LENGTH
void HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size)
SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5
#define SDL_HINT_JOYSTICK_HIDAPI_PS5
A variable controlling whether the HIDAPI driver for PS5 controllers should be used.
Definition: SDL_hints.h:616
int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger, Uint8 state, float x, float y, float pressure)
void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)
int SDL_PrivateJoystickSensor(SDL_Joystick *joystick, SDL_SensorType type, const float *data, int num_values)
void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type)
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
@ SDL_JOYSTICK_POWER_FULL
Definition: SDL_joystick.h:103
@ SDL_JOYSTICK_POWER_MEDIUM
Definition: SDL_joystick.h:102
@ SDL_JOYSTICK_POWER_EMPTY
Definition: SDL_joystick.h:100
@ SDL_JOYSTICK_POWER_WIRED
Definition: SDL_joystick.h:104
@ SDL_JOYSTICK_POWER_LOW
Definition: SDL_joystick.h:101
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
GLint level
Definition: SDL_opengl.h:1572
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLbyte GLbyte blue
GLuint64EXT * result
GLfloat bias
GLintptr offset
GLuint index
GLuint divisor
GLuint const GLchar * name
GLsizeiptr size
GLuint GLsizei GLsizei * length
GLbyte green
GLsizei const GLfloat * value
#define SDL_STANDARD_GRAVITY
Definition: SDL_sensor.h:99
@ SDL_SENSOR_GYRO
Definition: SDL_sensor.h:74
@ SDL_SENSOR_ACCEL
Definition: SDL_sensor.h:73
uint16_t Uint16
Definition: SDL_stdinc.h:197
int16_t Sint16
Definition: SDL_stdinc.h:191
#define SDL_zero(x)
Definition: SDL_stdinc.h:426
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:121
uint8_t Uint8
Definition: SDL_stdinc.h:185
uint32_t Uint32
Definition: SDL_stdinc.h:209
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
struct xkb_state * state
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:163
EGLContext ctx
Definition: eglext.h:208
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *device, unsigned char *data, size_t length, int milliseconds)
Read an Input report from a HID device with timeout.
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length)
Get a feature report from a HID device.
HID_API_EXPORT hid_device *HID_API_CALL hid_open_path(const char *path, int bExclusive)
Open a HID device by its path name.
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device)
Close a HID device.
struct hid_device_ hid_device
Definition: hidapi.h:54
static SDL_AudioDeviceID device
Definition: loopwave.c:37
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld if[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
SDL_Texture * axis
static int colors[7]
Definition: testgesture.c:41
static SDL_Joystick * joystick
Definition: testjoystick.c:37
#define USB_VENDOR_SONY
Definition: usb_ids.h:34
typedef int(__stdcall *FARPROC)()