Ver Fonte

Fix Steam battery always showing 100%: defer 0x43 send when HID endpoint busy

copilot-swe-agent[bot] há 4 meses atrás
pai
commit
45380d5ced
1 ficheiros alterados com 27 adições e 1 exclusões
  1. 27 1
      OpenPuck/puck_hid.cpp

+ 27 - 1
OpenPuck/puck_hid.cpp

@@ -261,16 +261,37 @@ void SteamPuckController::onReport45(const uint8_t* rep, bool fresh, uint8_t bod
   }
 }
 
+// ---- pending aux report: 0x43 (battery) always arrives in the same RF frame as 0x45 (input). onReport45 sends
+// first; by the time onAuxReport runs, the HID endpoint is still busy with the 0x45 IN transfer (ready()==false)
+// and the aux report would be silently dropped. Queue it here and retry from task() on the next 4 ms cycle,
+// when the host has had time to poll and the endpoint is free again. One slot is enough: task() drains it every
+// 4 ms and aux reports arrive at most once every ~2 s, so a second report can never arrive before the first
+// is drained.
+static uint8_t g_pendingAuxRid = 0;        // 0 = nothing pending
+static uint8_t g_pendingAuxData[16];
+static uint8_t g_pendingAuxLen = 0;
+
 // Forward the controller's NON-input status reports (0x43 power/battery, 0x44) to Steam verbatim -- the real
 // puck does this and it's how Steam reads battery. OpenPuck used to drop everything but 0x45, so Steam never got
 // the power report (-> default/unknown battery). Same host-asleep / post-resume gating as 0x45; no lizard path
 // (status reports aren't input, so they forward regardless of the lizard decision).
+//
+// 0x43 arrives in the same RF frame as 0x45; onReport45 sends 0x45 first, so by the time we arrive here the HID
+// endpoint is still busy (ready()==false). Queue the report and let task() deliver it on the next cycle instead
+// of dropping it silently (which caused Steam to always show 100% battery).
 void SteamPuckController::onAuxReport(uint8_t rid, const uint8_t* data, uint8_t n){
   if (USBDevice.suspended()) return;
   if (g_resumeMs && millis()-g_resumeMs < POST_RESUME_MUTE_MS) return;
   if (g_connSlot < 0 || g_connSlot >= NSLOT) return;
-  if (g_slot[g_connSlot].used && hid[g_connSlot].ready())
+  if (!g_slot[g_connSlot].used) return;
+  if (hid[g_connSlot].ready()){
     hid[g_connSlot].sendReport(rid, data, n);
+  } else {
+    uint8_t len = n < sizeof(g_pendingAuxData) ? n : (uint8_t)sizeof(g_pendingAuxData);
+    g_pendingAuxRid  = rid;
+    g_pendingAuxLen  = len;
+    memcpy(g_pendingAuxData, data, len);
+  }
 }
 
 // ---- wake nudge: a bare USB resume signal is NOT enough to wake some hosts (Windows in particular) -- they
@@ -320,6 +341,11 @@ void SteamPuckController::task(){
     if (conn!=usbConn || (conn && !steamAcked && millis()-last79>=750)){
       if (conn && !usbConn) connEdgeMs = millis();
       uint8_t st=conn?0x02:0x01; hid[g_connSlot].sendReport(0x79,&st,1); usbConn=conn; last79=millis();
+    } else if (g_pendingAuxRid && g_slot[g_connSlot].used) {
+      // Drain queued aux report (0x43 battery, 0x44, …) -- deferred from onAuxReport because the endpoint
+      // was busy with a 0x45 send at the time. Deliver before the periodic 0x7B so battery reaches Steam.
+      hid[g_connSlot].sendReport(g_pendingAuxRid, g_pendingAuxData, g_pendingAuxLen);
+      g_pendingAuxRid = 0;
     } else if (conn && millis()-last7B>=2000){
       // 0x7B status, live-captured template. Byte 8 is the controller->puck signal strength as signed dBm
       // (capture showed 0xDD = -35; replaying it verbatim is why Steam pinned -35dBm forever) -- patch in the