Fixed controller disconnect detection for PS4 and PS5 controllers over Bluetooth
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 4e32355..6b29a1d 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -46,6 +46,7 @@
#define GYRO_RES_PER_DEGREE 1024.0f
#define ACCEL_RES_PER_G 8192.0f
+#define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500
#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
@@ -131,6 +132,7 @@ typedef struct {
SDL_bool report_sensors;
SDL_bool hardware_calibration;
IMUCalibrationData calibration[6];
+ Uint32 last_packet;
int player_index;
Uint8 rumble_left;
Uint8 rumble_right;
@@ -441,6 +443,20 @@ HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)
}
static void
+HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)
+{
+ /* This is just a dummy packet that should have no effect, since we don't set the CRC */
+ Uint8 data[78];
+
+ SDL_zero(data);
+
+ data[0] = k_EPS4ReportIdBluetoothEffects;
+ data[1] = 0xC0; /* Magic value HID + CRC */
+
+ SDL_HIDAPI_SendRumble(device, data, sizeof(data));
+}
+
+static void
HIDAPI_DriverPS4_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
@@ -494,6 +510,7 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
}
ctx->device = device;
ctx->joystick = joystick;
+ ctx->last_packet = SDL_GetTicks();
device->dev = hid_open_path(device->path, 0);
if (!device->dev) {
@@ -804,6 +821,7 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
SDL_Joystick *joystick = NULL;
Uint8 data[USB_PACKET_LENGTH*2];
int size;
+ int packet_count = 0;
if (device->num_joysticks > 0) {
joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
@@ -816,6 +834,9 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
#ifdef DEBUG_PS4_PROTOCOL
HIDAPI_DumpPacket("PS4 packet: size = %d", data, size);
#endif
+ ++packet_count;
+ ctx->last_packet = SDL_GetTicks();
+
switch (data[0]) {
case k_EPS4ReportIdUsbState:
HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]);
@@ -846,6 +867,14 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
}
}
+ if (ctx->is_bluetooth && packet_count == 0) {
+ /* Check to see if it looks like the device disconnected */
+ if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) {
+ /* Send an empty output report to tickle the Bluetooth stack */
+ HIDAPI_DriverPS4_TickleBluetooth(device);
+ }
+ }
+
if (size < 0) {
/* Read error, device is disconnected */
HIDAPI_JoystickDisconnected(device, joystick->instance_id);
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index 5b7553b..974dc29 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -508,6 +508,20 @@ HIDAPI_DriverPS5_CheckPendingLEDReset(SDL_HIDAPI_Device *device)
}
static void
+HIDAPI_DriverPS5_TickleBluetooth(SDL_HIDAPI_Device *device)
+{
+ /* This is just a dummy packet that should have no effect, since we don't set the CRC */
+ Uint8 data[78];
+
+ SDL_zero(data);
+
+ data[0] = k_EPS5ReportIdBluetoothEffects;
+ data[1] = 0x02; /* Magic value */
+
+ SDL_HIDAPI_SendRumble(device, data, sizeof(data));
+}
+
+static void
HIDAPI_DriverPS5_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
{
SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
@@ -991,7 +1005,7 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device)
/* Check to see if it looks like the device disconnected */
if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_packet + BLUETOOTH_DISCONNECT_TIMEOUT_MS)) {
/* Send an empty output report to tickle the Bluetooth stack */
- HIDAPI_DriverPS5_UpdateEffects(device, k_EDS5EffectNone);
+ HIDAPI_DriverPS5_TickleBluetooth(device);
}
}