Browse Source

Power off controllers after USB host shutdown

Co-authored-by: safijari <5191844+safijari@users.noreply.github.com>
copilot-swe-agent[bot] 1 week ago
parent
commit
ba348f81e8
1 changed files with 27 additions and 16 deletions
  1. 27 16
      OpenPuck/haptics.cpp

+ 27 - 16
OpenPuck/haptics.cpp

@@ -860,34 +860,45 @@ void hapticTask()
 	}
 	// (No automatic re-init firing -- see hapticOnReconnect: the connect-time 0x81 storm was the click train
 	// the real puck never makes. hapticReinit is on-demand only, via the WebUSB "Clear stuck buzz" button.)
-	// 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
-	// selective-suspend (host idle power-management) resumes in <1s; firing the power-off on its edge
-	// powered the controllers off ourselves -> random drop/reconnect churn. Arm only on a genuine
-	// resume->suspend edge (wasSusp=true at boot suppresses a false fire on boot-into-suspended).
+	// Power off after a sustained suspend or USB unmount. Some hosts unconfigure USB during shutdown
+	// without reporting a suspend; only treat an unmount as host-off after this device was mounted.
 	static bool wasSusp = true;
-	static unsigned long suspSinceMs = 0;
-	static bool suspArmed = false;
+	static bool suspendObserved = false;
+	static bool hostWasMounted = false;
+	static unsigned long hostOffSinceMs = 0;
+	static bool hostOffArmed = false;
 	bool susp = USBDevice.suspended();
+	bool mounted = USBDevice.mounted();
 	bool vbus = (NRF_POWER->USBREGSTATUS &
 		     POWER_USBREGSTATUS_VBUSDETECT_Msk) != 0;
 	if (susp && !wasSusp) {
-		suspSinceMs = millis();
-		suspArmed = true;
+		hostOffSinceMs = millis();
+		hostOffArmed = true;
+		suspendObserved = true;
 		faultDiagTrace(FR_SUSP, 0);
 	}
 	if (!susp) {
 		if (wasSusp)
 			faultDiagTrace(FR_RESUME, 0);
-		suspArmed = false;
+		suspendObserved = false;
 	}
-	// g_suspendOff: opt out of the power-off entirely -- keeps the controller awake through host sleep so its
-	// short-Steam-press remote wakeup stays available (see haptics.h). Disarm either way so re-enabling it
-	// mid-suspend can't fire late.
-	if (suspArmed && vbus && (millis() - suspSinceMs) >= SUSPEND_OFF_MS) {
-		if (g_suspendOff)
+	if (mounted)
+		hostWasMounted = true;
+	bool hostUnavailable = suspendObserved || (hostWasMounted && !mounted);
+	static bool hostOffHandled = false;
+	if (!hostUnavailable) {
+		hostOffArmed = false;
+		hostOffHandled = false;
+	} else if (!hostOffArmed) {
+		hostOffSinceMs = millis();
+		hostOffArmed = true;
+	}
+	// Disarm even when disabled or VBUS is gone, so enabling mid-suspend cannot fire late.
+	if (hostOffArmed && !hostOffHandled &&
+	    (millis() - hostOffSinceMs) >= SUSPEND_OFF_MS) {
+		if (g_suspendOff && vbus)
 			hapticSendShutdown();
-		suspArmed = false; // fire once per suspend
+		hostOffHandled = true;
 	}
 	wasSusp = susp;
 	// Per-slot stuck-rumble watchdog: force zero after 2.5s without a refresh.