Procházet zdrojové kódy

fix(haptics): land SET_SETTINGS(0x09,0) to kill the autonomous touchpad buzz

Root cause, decoded from a real puck<->controller capture (sniff1.json): the
controller runs an autonomous touchpad haptic sequencer that buzzes unless it is
told to stop. The real puck LANDS SET_SETTINGS(index 0x09, value 0) -- on-air
E3 .. 01 87 03 09 00 00 -- on connect and RE-ASSERTS it every ~3s (observed at
+1950/+4951/+7953ms). OpenPuck never sent it: it discards all 0x87 by design,
and its own hapticSetPadEnabled was inert (SETTING_HAPTICS_ENABLED 0xFF, index
never found -- the team had guessed 0x18/0x2e/0x34/0x35). So the sequencer stays
on and the controller buzzes ~10-15s after connect.

- SETTING_HAPTICS_ENABLED = 0x09 (the real index, from the capture).
- Add reg 0x09 to the type-01 landing whitelist so the write actually reaches
  the controller (0x30/0x34/0x35 still stay legacy/discarded -- those land-bugs
  are unchanged).
- hapticReinit now sends the DISABLE (value 0), matching the real puck, instead
  of enabling the sequencer for default types.
- Re-assert SET_SETTINGS(0x09,0) every PAD_HAPTIC_REASSERT_MS (3s) per connected
  slot -- mirrors the real puck; the controller re-enables otherwise (which is
  why a one-shot disable lapsed into the 10-15s buzz). This is the reference
  puck's actual protocol, not a symptom-suppression timer.

Commanded haptics (report 0x82) are unaffected. Builds clean (152380 B);
hardware confirmation still needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Jariullah Safi před 4 měsíci
rodič
revize
1f8637dec8
2 změnil soubory, kde provedl 36 přidání a 17 odebrání
  1. 27 17
      OpenPuck/haptics.cpp
  2. 9 0
      OpenPuck/haptics.h

+ 27 - 17
OpenPuck/haptics.cpp

@@ -347,12 +347,15 @@ void rfConnFlushRelay(uint8_t ch, uint8_t s1)
 			// the controller only with the type-01 + inner-len form E3 [2+rl][01][rid][innerlen][data];
 			// the controller only with the type-01 + inner-len form E3 [2+rl][01][rid][innerlen][data];
 			// the legacy form E3 [1+rl][05][rid][data] makes the controller DISCARD any 0x87+ command.
 			// the legacy form E3 [1+rl][05][rid][data] makes the controller DISCARD any 0x87+ command.
 			//
 			//
-			// Whitelist type-01 to only two commands: LED brightness (0x87 reg 0x2D) and power-off
-			// (0x9F). Other 0x87 writes (e.g. reg 0x30 IMU enable, 0x34/0x35 haptic amplitude) must
-			// stay on legacy form -- landing 0x30 freezes the gyro; landing 0x34/0x35 causes the buzz.
+			// Type-01 (landing) whitelist: LED brightness (0x87 reg 0x2D), the touchpad-haptics
+			// disable (0x87 reg 0x09, sniff-confirmed), and power-off (0x9F). Other 0x87 regs (0x30 IMU
+			// enable, 0x34/0x35 amplitude) MUST stay on legacy/discarded form -- landing 0x30 freezes
+			// the gyro, landing 0x34/0x35 buzzes.
 			bool land01 =
 			bool land01 =
 				(m.rid == 0x9F) ||
 				(m.rid == 0x9F) ||
-				(m.rid == 0x87 && rl >= 1 && m.data[0] == 0x2D);
+				(m.rid == 0x87 && rl >= 1 &&
+				 (m.data[0] == 0x2D ||
+				  m.data[0] == SETTING_HAPTICS_ENABLED));
 			uint8_t p[5 + RELAY_MAXP], plen;
 			uint8_t p[5 + RELAY_MAXP], plen;
 			if (land01) {
 			if (land01) {
 				p[0] = g_relayOp;
 				p[0] = g_relayOp;
@@ -390,19 +393,13 @@ void rfConnFlushRelay(uint8_t ch, uint8_t s1)
 // touchpad haptic sequences autonomously (haptics-sequencer-touchpad) gated by settings/haptics/enabled; in the
 // touchpad haptic sequences autonomously (haptics-sequencer-touchpad) gated by settings/haptics/enabled; in the
 // emulated (non-Steam) modes the host never sends SC haptic reports, so the only way to silence the pad buzz is
 // emulated (non-Steam) modes the host never sends SC haptic reports, so the only way to silence the pad buzz is
 // to write this setting. Wire format [id][val u16 LE] -- confirmed from the controller's SET_SETTINGS handler
 // to write this setting. Wire format [id][val u16 LE] -- confirmed from the controller's SET_SETTINGS handler
-// (decomp FUN_0001f61c; id 0x30 == settings/sensors/imu/mode is the cross-check anchor in the same id space).
-// The exact id for settings/haptics/enabled needs a one-time `scmd labels` HW read (candidates from the captured
-// reinit: 0x18 / 0x2e / 0x34 / 0x35). Until it is set, SETTING_HAPTICS_ENABLED stays 0xFF and this is inert.
-#define SETTING_HAPTICS_ENABLED 0xFF
+// SET_SETTINGS(SETTING_HAPTICS_ENABLED, on?1:0) on the connected slot. Wire format [idx][u16 value LE]; landed
+// via the type-01 whitelist in rfConnFlushRelay. on=false (value 0) is what the real puck lands to quiet the
+// autonomous touchpad sequencer (sniff1.json); commanded 0x82 haptics are unaffected. See SETTING_HAPTICS_ENABLED.
 void hapticSetPadEnabled(uint8_t slot, bool on)
 void hapticSetPadEnabled(uint8_t slot, bool on)
 {
 {
-#if SETTING_HAPTICS_ENABLED != 0xFF
 	uint8_t pl[3] = { SETTING_HAPTICS_ENABLED, (uint8_t)(on ? 1 : 0), 0 };
 	uint8_t pl[3] = { SETTING_HAPTICS_ENABLED, (uint8_t)(on ? 1 : 0), 0 };
 	relayEnqueue(0x87, pl, 3, slot);
 	relayEnqueue(0x87, pl, 3, slot);
-#else
-	(void)slot;
-	(void)on;
-#endif
 }
 }
 void hapticReinit(uint8_t slot)
 void hapticReinit(uint8_t slot)
 {
 {
@@ -428,10 +425,12 @@ void hapticReinit(uint8_t slot)
 	relayEnqueue(0x87, H35, sizeof H35, slot);
 	relayEnqueue(0x87, H35, sizeof H35, slot);
 	relayEnqueue(0x81, T81A, sizeof T81A, slot);
 	relayEnqueue(0x81, T81A, sizeof T81A, slot);
 	relayEnqueue(0x81, T81B, sizeof T81B, slot);
 	relayEnqueue(0x81, T81B, sizeof T81B, slot);
-	// Re-apply the active emulated type's trackpad-haptics preference last (after the haptic-config writes
-	// above, which would otherwise re-enable it). Default-on types send "enable"; Switch (padHaptics=0)
-	// disables. Inert until the setting id is captured.
-	hapticSetPadEnabled(slot, g_padHaptics != 0);
+	// Silence the controller's autonomous touchpad haptic sequencer -- the connect buzz. The real puck lands
+	// SET_SETTINGS(0x09, 0) here (sniff1.json); commanded 0x82 haptics still work. Sent last so the 0x87
+	// config writes above can't re-enable it. (Was gated on g_padHaptics, which left it ENABLED for the
+	// default types -> buzz; the autonomous sequencer is now always disabled to match the reference puck.)
+	(void)g_padHaptics;
+	hapticSetPadEnabled(slot, false);
 }
 }
 void hapticInit()
 void hapticInit()
 {
 {
@@ -487,6 +486,17 @@ void hapticTask()
 				     (millis() + HAPTIC_REINIT_GAP_MS) :
 				     (millis() + HAPTIC_REINIT_GAP_MS) :
 				     0;
 				     0;
 	}
 	}
+	// Re-assert the touchpad-haptics-disable: the controller re-enables its autonomous sequencer, so the
+	// connect-time disable lapses and the buzz returns ~10-15s in. The real puck re-lands SET_SETTINGS(0x09,0)
+	// every ~3s (sniff1.json) -- we mirror that exactly, per connected slot.
+	static unsigned long lastPadReassert = 0;
+	if (anySlotLinkUp() &&
+	    millis() - lastPadReassert >= PAD_HAPTIC_REASSERT_MS) {
+		lastPadReassert = millis();
+		for (int s = 0; s < NSLOT; s++)
+			if (g_slot[s].used && hapticLinkUp(s))
+				hapticSetPadEnabled((uint8_t)s, false);
+	}
 	// Power-off on host sleep: only when VBUS is present (genuine sleep, not a cable unplug which also
 	// Power-off on host sleep: only when VBUS is present (genuine sleep, not a cable unplug which also
 	// trips the suspend edge briefly) AND the suspend has PERSISTED >= SUSPEND_OFF_MS. A brief USB
 	// trips the suspend edge briefly) AND the suspend has PERSISTED >= SUSPEND_OFF_MS. A brief USB
 	// selective-suspend (host idle power-management) resumes in <1s; firing the power-off on its edge
 	// selective-suspend (host idle power-management) resumes in <1s; firing the power-off on its edge

+ 9 - 0
OpenPuck/haptics.h

@@ -36,6 +36,15 @@
 // during use (a buzz that starts seconds after connect and won't self-clear). Long enough not to fire between
 // during use (a buzz that starts seconds after connect and won't self-clear). Long enough not to fire between
 // rapid in-game haptics; short enough to clear a stuck buzz soon after the user pauses.
 // rapid in-game haptics; short enough to clear a stuck buzz soon after the user pauses.
 #define HAPTIC_CLEAR_IDLE_MS 1200u
 #define HAPTIC_CLEAR_IDLE_MS 1200u
+// Settings index written to 0 to silence the controller's AUTONOMOUS touchpad haptic sequencer -- the connect
+// buzz. Decoded from a real puck<->controller capture (sniff1.json): the puck LANDS SET_SETTINGS(0x09, 0)
+// (on-air E3 .. 01 87 03 09 00 00) on connect and re-asserts it every ~3s. Commanded haptics (report 0x82)
+// are unaffected. The OpenPuck team had guessed 0x18/0x2e/0x34/0x35; the real index is 0x09.
+#define SETTING_HAPTICS_ENABLED 0x09
+// Re-assert cadence for the disable. The controller re-enables the sequencer, so a one-shot disable lapses
+// ~10-15s after connect (the reported buzz). Matches the real puck's observed +1950/+4951/+7953ms re-sends
+// in sniff1.json -- the reference puck's protocol, not a symptom-suppression timer.
+#define PAD_HAPTIC_REASSERT_MS 3000u
 // Controller power-off: hapticSendShutdown() relays Steam's confirmed "turn off controller" command (feature-0x01
 // Controller power-off: hapticSendShutdown() relays Steam's confirmed "turn off controller" command (feature-0x01
 // cmd 0x9F, payload "off!" -- captured from the real puck). Sent as a small burst because the RF relay is NO-ACK.
 // cmd 0x9F, payload "off!" -- captured from the real puck). Sent as a small burst because the RF relay is NO-ACK.
 #define HAPTIC_SHUTDOWN_SHOTS 3u
 #define HAPTIC_SHUTDOWN_SHOTS 3u