commit 36a78784056136bbd61fff6c185d9343c7391fd6
parent 20e97b315a4973ea847052d2b81c25d4aa805210
Author: umhau <umhau@example.invalid>
Date: Wed, 21 Oct 2026 12:00:06 +0000
Passive-only verify.sh; remove polling linkwatch/recovery_test; correct status-byte and polling-causation claims
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat:
5 files changed, 46 insertions(+), 38 deletions(-)
diff --git a/NOTES.md b/NOTES.md
@@ -18,13 +18,24 @@ From disassembling this machine's arm64 kernelcache (macOS 15.6.1):
## HID (works)
- Control 03f0:0c6a: 4x feature [0x50,0x01]+get, get 0x09/0x08/0x06, feature [0x04,0x01] = screen on.
- IMU 045e:0659: output [0x02,0x07] starts 1000 Hz IMU (381-byte reports).
-- Status 0x05 (11 bytes): [1],[4] panel on; [5] measured refresh-1 (0 = no video).
+- Status 0x05 (11 bytes): [1],[4] appear to track panel on. [5] is NOT a reliable refresh measurement (nonzero in some
+ sessions, 0 while video was visibly running in others); do not infer refresh from it.
## After reboot
-Run `tools/verify.sh`. Success = no REMOVE/ADD loop and the panel stays lit (check physically).
+Run `tools/verify.sh` (passive, launches nothing). Success = HDCP TX Protocols=() and headset display online; panel lit
+must still be confirmed physically.
## Link reliability (2026-10-20, vr1 + vr0)
-- Periodic ~70-80 s drops were caused by polling `ioreg -c IOPortTransportStateDisplayPort` (1 Hz). Never poll it.
+- Periodic ~70-80 s drops ([time redacted]-[time redacted]) happened while `ioreg -c IOPortTransportStateDisplayPort` was polled at 1 Hz and a
+ 287 s run without polling had none, but causation is UNPROVEN. Signature: sink pulls HPD fully low (DP [HPD=0,IRQ=0]).
+ Still: never poll that class.
+- Post-DRST transient (2 of 3 trials, +5.05 s after link-up): headset raises IRQ_HPD (DP [HPD=1,IRQ=1]); DCP
+ DCPDP13Service::handleCheckLink reports INTERLANE_ALIGN_DONE=0 -> "link unstable, retraining" -> terminates the video
+ interface (macOS sees REMOVE) -> retrains at HBR3 x4 -> ADD ~0.63 s later. A link-integrity failure, different from
+ the HPD=0 drops. Hypothesis: HBR3 margin through the adapter; 60 Hz fits HBR2 (~15.2 of 17.28 Gbps).
+- DCP firmware has per-port link limits (max_link_rate, max_link_rate_4_lanes, min_link_rate, max_lane_count) read
+ from boot-args with default prefix "appledptx" (format strings "%s-%s" present, so likely `appledptx-max_link_rate=`);
+ separator and value encoding NOT yet verified. Parked; would need a reboot.
- Stuck state (headset display gone, adapter reports HPD=0 to the Mac): **recoverable in software** with a USB-PD
Data Reset on port 2's controller: `sudo tools/hpmctl 1 cmd DRST` (4CC passed big-endian, see
tools/kernel/HPM_RE.md section 8). First success: display back 2.2 s after the command, no replug.
diff --git a/SETUP.md b/SETUP.md
@@ -79,10 +79,14 @@ reports `05 01 01 01 01 ...` (panel on), with no further `05 00` and no drops.
## Gotchas
- Keep `IOHIDManager` objects alive as long as you use their devices; freeing the manager closes the device.
- In zsh, `log` is a builtin; use `/usr/bin/log`.
-- The headset's status report `0x05`: byte 1 = panel on, byte 5 = measured refresh rate minus 1 (0 = no video).
+- The headset's status report `0x05`: byte 1 appears to track panel-on (unreliable on the G2 per Wintch/reverb-g2). Byte 5 is
+ NOT a reliable refresh measurement: it read 0x59/0x3b in some sessions but 0 while video was visibly running in others.
+ Judge the display by the native mode in use and actual Metal presentation counts, not by this byte.
## Tools (`tools/`)
- `hmdwake`: wake sequence + IMU on; logs status reports. `passive` arg = listen only. SIGUSR1 = screen on.
-- `displaywatch`, `linkwatch.sh`: log display add/remove and DP link rate/lanes.
+- `displaywatch`: passively logs display add/remove (CoreGraphics callbacks).
+- Never poll `ioreg -c IOPortTransportStateDisplayPort` (the old `linkwatch.sh` did; removed).
- `testpattern`: red LEFT / blue RIGHT fullscreen pattern on the headset.
-- `verify.sh`: boot-args check + 60 s of the above, summarized.
+- `verify.sh`: passive one-shot check (boot-args, HDCP TX capability, display online, log summaries). Launches nothing.
+- `hpmctl` / `recovery_trials.py` / the app's LinkRecovery helper: see NOTES.md "Link reliability" (vr0 owns these).
diff --git a/tools/linkwatch.sh b/tools/linkwatch.sh
@@ -1,8 +0,0 @@
-#!/bin/zsh
-# Logs DisplayPort link state of each USB-C port, on change.
-prev=""
-while true; do
- cur=$(ioreg -r -c IOPortTransportStateDisplayPort -w0 | grep -E '"TransportDescription"|"LinkRateDescription"|"LaneCount"' | awk -F'= ' '{print $2}' | tr '\n' ' ')
- [[ "$cur" != "$prev" ]] && { echo "$(date +%s) $cur"; prev="$cur"; }
- sleep 1
-done
diff --git a/tools/recovery_test.sh b/tools/recovery_test.sh
@@ -1,13 +0,0 @@
-#!/bin/zsh
-# Measures how the headset recovers its DP link: off -> wait D s -> on/wake -> time until HPD/link returns.
-# Usage: recovery_test.sh <delay_s> <on|wake>
-cd "$(dirname "$0")"
-D=$1; CMD=${2:-on}
-link() { ioreg -r -c IOPortTransportStateDisplayPort -w0 | grep -q HBR; }
-now() { python3 -c 'import time;print(f"{time.time():.2f}")'; }
-link || { echo "link not up at start; aborting"; exit 1; }
-t0=$(now); ./hidcmd off
-until ! link; do sleep 0.1; done; t1=$(now); echo "link down after $(( t1 - t0 ))s"
-sleep $D; t2=$(now); ./hidcmd $CMD
-until link; do sleep 0.2; [ $(( $(now) - t2 )) -gt 200 ] && { echo "NO RECOVERY within 200s"; exit 2; }; done
-t3=$(now); echo "RESULT delay=$D cmd=$CMD -> link back $(( t3 - t2 ))s after $CMD"
diff --git a/tools/verify.sh b/tools/verify.sh
@@ -1,14 +1,28 @@
#!/bin/zsh
-# Post-reboot check: boot-args, then wake headset + loggers for 60 s, then summarize.
+# Passive, one-shot health check. It launches, kills, wakes and polls NOTHING:
+# - never reads USB-C/DisplayPort port state (ioreg IOPortTransportStateDisplayPort) — polling it destabilised the link
+# - never sends HID commands or port-controller (hpmctl) commands
+# It reports boot-args, HDCP transmit capability (one registry read), and summarises existing passive logs.
cd "$(dirname "$0")"
+
echo "kern.bootargs: $(sysctl -n kern.bootargs)"
-pkill -f './hmdwake|./displaywatch|linkwatch.sh' 2>/dev/null
-./displaywatch > displaywatch.log 2>&1 &
-./linkwatch.sh > linkwatch.log 2>&1 &
-./hmdwake > hmdwake.log 2>&1 &
-echo "watching 60 s... (replug DP if the link doesn't come up)"
-sleep 60
-echo "--- link:"; cat linkwatch.log
-echo "--- display events:"; grep -E "ADD|REMOVE" displaywatch.log
-echo "--- panel status:"; grep status hmdwake.log
-echo "--- HDCP log lines (last 2 min):"; /usr/bin/log show --last 2m --info --debug --style compact 2>/dev/null | grep -i -E "hdcp|EnableEncryption" | tail -15
+case " $(sysctl -n kern.bootargs) " in
+ *" hdcp-fuse-mask=0 "*) echo " OK: hdcp-fuse-mask=0 active" ;;
+ *) echo " PROBLEM: hdcp-fuse-mask=0 not active; the headset will blank (see SETUP.md)" ;;
+esac
+
+# Expect {"Protocols"=()} on every channel when HDCP is off.
+echo "HDCP transmit capabilities:"
+ioreg -l -w0 | grep '"HDCPTXCapabilities"' | sort | uniq -c | sed 's/^/ /'
+
+# Headset display currently online? (CoreGraphics view, no port access)
+if system_profiler SPDisplaysDataType 2>/dev/null | grep -q "4320 x 2160"; then
+ echo "headset display: online"
+else
+ echo "headset display: not online"
+fi
+
+# Summaries of passive logs, if an observer is already running (start tools/displaywatch yourself if wanted).
+[ -f displaywatch.log ] && { echo "--- last display events (displaywatch.log):"; grep -E "ADD|REMOVE" displaywatch.log | tail -5; }
+[ -f hmdwake.log ] && { echo "--- last headset 0x05 reports (hmdwake.log):"; grep status hmdwake.log | tail -3; }
+true