Przeglądaj źródła

ps4 enumeration and gyro fixed

Jariullah Safi 4 miesięcy temu
rodzic
commit
c3b0fdaa85
3 zmienionych plików z 80 dodań i 5 usunięć
  1. 20 0
      OpenPuck/gamepad_util.cpp
  2. 14 0
      OpenPuck/gamepad_util.h
  3. 46 5
      OpenPuck/mode_hidgyro.cpp

+ 20 - 0
OpenPuck/gamepad_util.cpp

@@ -2,6 +2,26 @@
 #include "triton.h"
 #include "config.h"
 
+void psNeutralCalib(uint8_t *buf)
+{
+	// Payload offsets = kernel buf[] index minus 1. buf[0..5] gyro bias stays zero (caller memset). Symmetric
+	// ranges -> non-zero divisors on the host.
+	le16(buf + 6, 2844);
+	le16(buf + 8, -2844); // gyro pitch +/-
+	le16(buf + 10, 2844);
+	le16(buf + 12, -2844); // gyro yaw +/-
+	le16(buf + 14, 2844);
+	le16(buf + 16, -2844); // gyro roll +/-
+	le16(buf + 18, 2844);
+	le16(buf + 20, 2844); // gyro speed +/- (sum != 0)
+	le16(buf + 22, 8192);
+	le16(buf + 24, -8192); // accel X +/-
+	le16(buf + 26, 8192);
+	le16(buf + 28, -8192); // accel Y +/-
+	le16(buf + 30, 8192);
+	le16(buf + 32, -8192); // accel Z +/-
+}
+
 uint8_t swStick(int16_t v, bool invert)
 {
 	int32_t a = 0x80 + (invert ? -((int32_t)v >> 8) : ((int32_t)v >> 8));

+ 14 - 0
OpenPuck/gamepad_util.h

@@ -11,6 +11,20 @@
 // int16 stick (center 0) -> uint8 (center 0x80), optional axis invert (HID Y is down-positive).
 uint8_t swStick(int16_t v, bool invert);
 
+// Write a signed 16-bit value little-endian.
+static inline void le16(uint8_t *p, int16_t v)
+{
+	p[0] = (uint8_t)(v & 0xFF);
+	p[1] = (uint8_t)((v >> 8) & 0xFF);
+}
+
+// Fill a DS4/DualSense motion-calibration feature report PAYLOAD. The TinyUSB GET_REPORT path already wrote the
+// report id and hands us the buffer PAST it, so our offsets = the kernel's buf[] index minus one (bias at 0..5,
+// stays zero from the caller's memset). Zero bias + symmetric non-zero ranges so hid-playstation's/hid-sony's
+// divisor (|plus-bias|+|minus-bias|, and gyro speed_plus+speed_minus) is never zero. Shared by report 0x02 (DS4)
+// and 0x05 (DualSense) -- identical layout; the caller returns size-1 and leaves trailing bytes zero.
+void psNeutralCalib(uint8_t *buf);
+
 // Steam trackpad s16 coords -> absolute touch surface. TOUCH_PAD_W is split into left/right halves so both
 // pads can co-exist as two contacts on a single DualSense/DS4 touchpad.
 #define TOUCH_PAD_W 1920u

+ 46 - 5
OpenPuck/mode_hidgyro.cpp

@@ -27,6 +27,47 @@ static const uint8_t GYRO_HID_DESC[] = {
 static unsigned long g_gyroLastMs = 0;
 static Adafruit_USBD_HID g_hidGyro;
 
+static const uint8_t DS4_MAC[6] = { 0x00, 0x1B, 0xDC, 0x4F, 0x55, 0x53 };
+
+// GET_FEATURE handler. SteamOS drives the DS4 (054C:05C4) with hid-playstation (older kernels: hid-sony); both
+// issue GET_FEATURE requests during probe (calibration + MAC + firmware) and ABORT the bind if any STALLs or
+// returns the wrong length. ps_get_report enforces, on every bus: the response is the exact report size with the
+// report id as its first byte (data fields then start at buf[1]); CRC is only checked over Bluetooth. macOS/SDL
+// are lenient (re-derive), which is why the DS4 shows up there but not on a Deck. TinyUSB writes the report id
+// itself and hands us the buffer PAST it, so we fill only the PAYLOAD and return size-1. We satisfy both drivers'
+// report ids. (Sizes per drivers/hid/hid-playstation.c: 0x02=37, 0x12=16, 0xA3=49; legacy hid-sony MAC 0x81=7.)
+static uint16_t hidGyroGet(uint8_t rid, hid_report_type_t type, uint8_t *buf,
+			   uint16_t reqlen)
+{
+	if (type != HID_REPORT_TYPE_FEATURE || !buf || reqlen == 0)
+		return 0;
+	memset(buf, 0, reqlen);
+	switch (rid) {
+	case 0x02: // motion calibration (37 incl id)
+		if (reqlen < 36)
+			return 0;
+		psNeutralCalib(buf);
+		return 36;
+	case 0x12: // pairing info / MAC, hid-playstation (16 incl id)
+		if (reqlen < 15)
+			return 0;
+		memcpy(buf, DS4_MAC, 6);
+		return 15;
+	case 0x81: // MAC, legacy hid-sony USB (7 incl id)
+		if (reqlen < 6)
+			return 0;
+		memcpy(buf, DS4_MAC, 6);
+		return 6;
+	case 0xA3: // firmware / hardware info (49 incl id)
+		if (reqlen < 48)
+			return 0;
+		buf[0] = 0x01; // non-zero version (contents not validated over USB)
+		return 48;
+	default:
+		return 0;
+	}
+}
+
 static void hidGyroSet(uint8_t rid, hid_report_type_t type, uint8_t const *b,
 		       uint16_t n)
 {
@@ -71,10 +112,10 @@ static void hidGyroBuild(uint8_t out[63])
 	out[8] = g_in.rt;
 	out[12] = g_in.gx & 0xFF;
 	out[13] = g_in.gx >> 8;
-	out[14] = g_in.gy & 0xFF;
-	out[15] = g_in.gy >> 8;
-	out[16] = (-g_in.gz) & 0xFF;
-	out[17] = (-g_in.gz) >> 8;
+	out[14] = g_in.gz & 0xFF;
+	out[15] = g_in.gz >> 8;
+	out[16] = (-g_in.gy) & 0xFF;
+	out[17] = (-g_in.gy) >> 8;
 	out[18] = g_in.ax & 0xFF;
 	out[19] = g_in.ax >> 8;
 	out[20] = g_in.ay & 0xFF;
@@ -107,7 +148,7 @@ void HidGyroController::begin()
 	USBDevice.setManufacturerDescriptor("Sony Computer Entertainment");
 	USBDevice.setProductDescriptor("Wireless Controller");
 	g_hidGyro.enableOutEndpoint(true);
-	g_hidGyro.setReportCallback(NULL, hidGyroSet);
+	g_hidGyro.setReportCallback(hidGyroGet, hidGyroSet);
 	g_hidGyro.setReportDescriptor(GYRO_HID_DESC, sizeof GYRO_HID_DESC);
 
 	// 1ms bInterval so the RF rate is the only latency limit (matches Xbox)