commit 4754d7c8b9283a3e9dc250f22caaf1ec0f0aa59e
parent 4abf4713f3f0b0fbe06d4f0c9e30300e4f7cdf70
Author: umhau <umhau@example.invalid>
Date: Wed, 21 Oct 2026 12:00:04 +0000
Document software recovery: BE DRST on port 2 restores the headset link; retract replug-only claims
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat:
7 files changed, 212 insertions(+), 44 deletions(-)
diff --git a/NOTES.md b/NOTES.md
@@ -22,3 +22,12 @@ From disassembling this machine's arm64 kernelcache (macOS 15.6.1):
## After reboot
Run `tools/verify.sh`. Success = no REMOVE/ADD loop and the panel stays lit (check 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.
+- 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.
+- RETRACTED: "only a physical replug recovers it"; "deeper off state"; "HRST untested/unsupported" claims made
+ before the byte order was fixed. With correct encoding HRST is rejected by the firmware (!CMD), DRST works.
+- HID screen_off (0x04 0x00) reliably induces the stuck state; screen_on / full wake alone do not undo it.
diff --git a/Sources/VRWorkstation/App.swift b/Sources/VRWorkstation/App.swift
@@ -26,45 +26,102 @@ func onlineHeadsetDisplay() -> CGDirectDisplayID? {
func log(_ s: String) { print(String(format: "%.3f ", Date().timeIntervalSince1970) + s) }
-final class HeadsetSession {
+private let displayChanged: CGDisplayReconfigurationCallBack = { id, flags, ctx in
+ guard !flags.contains(.beginConfigurationFlag), let ctx else { return }
+ let session = Unmanaged<HeadsetSession>.fromOpaque(ctx).takeUnretainedValue()
+ DispatchQueue.main.async {
+ // Vendor metadata may already be gone when REMOVE is delivered.
+ if flags.contains(.removeFlag) { session.displayLost(id) }
+ if flags.contains(.addFlag), isHeadsetDisplay(id) { session.displayArrived(id) }
+ }
+}
+
+final class HeadsetSession: NSObject, NSApplicationDelegate {
let hmd = HMD()
let renderer: Renderer
let view: MTKView
private var window: HeadsetWindow?
private(set) var display: CGDirectDisplayID?
private var lostAt: Date?
+ private var hidRetry: Timer?
+ private var healthTimer: Timer?
+ private var callbackRegistered = false
+ private var stopping = false
- init() throws {
- view = MTKView(frame: .zero, device: MTLCreateSystemDefaultDevice())
+ init(device: MTLDevice?) throws {
+ view = MTKView(frame: .zero, device: device)
view.colorPixelFormat = .bgra8Unorm
view.depthStencilPixelFormat = .depth32Float
view.preferredFramesPerSecond = 60
renderer = try Renderer(view: view)
view.delegate = renderer
+ view.isPaused = true
+ super.init()
}
func start() {
hmd.onStatus = { log("[hmd] \($0)") }
hmd.onPanel = { [weak self] on in if !on { self?.panelOff() } }
- do { try hmd.start() } catch { log("[hmd] \(error)") }
+ hmd.onDisconnected = { [weak self] in
+ guard let self, !self.stopping else { return }
+ log("[hmd] USB device removed; reopening when available")
+ self.hmd.stop()
+ self.scheduleHIDRetry()
+ }
let me = Unmanaged.passUnretained(self).toOpaque()
- CGDisplayRegisterReconfigurationCallback({ id, flags, ctx in
- guard isHeadsetDisplay(id), !flags.contains(.beginConfigurationFlag) else { return }
- let s = Unmanaged<HeadsetSession>.fromOpaque(ctx!).takeUnretainedValue()
- if flags.contains(.removeFlag) { DispatchQueue.main.async { s.displayLost(id) } }
- if flags.contains(.addFlag) { DispatchQueue.main.async { s.displayArrived(id) } }
- }, me)
+ let result = CGDisplayRegisterReconfigurationCallback(displayChanged, me)
+ callbackRegistered = result == .success
+ if !callbackRegistered { log("display callback registration failed: \(result.rawValue)") }
+ startHID()
+ healthTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { [weak self] _ in
+ guard let self else { return }
+ let age = self.hmd.lastIMUAt.map { String(format: "%.1fs", Date().timeIntervalSince($0)) } ?? "never"
+ log("health display=\(self.display.map(String.init) ?? "absent") frames=\(self.renderer.frames) IMU=\(self.hmd.imuSamples) lastIMU=\(age) status=\(self.hmd.statusReports)")
+ }
if let id = onlineHeadsetDisplay() { displayArrived(id) } else { log("waiting for the headset display...") }
}
+ private func startHID() {
+ guard !stopping else { return }
+ do {
+ try hmd.start(mode: .activate)
+ hidRetry?.invalidate(); hidRetry = nil
+ log("[hmd] wake commands accepted; waiting for IMU and video evidence")
+ } catch { log("[hmd] \(error.localizedDescription)"); scheduleHIDRetry() }
+ }
+
+ private func scheduleHIDRetry() {
+ guard hidRetry == nil, !stopping else { return }
+ hidRetry = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in self?.startHID() }
+ }
+
+ func applicationWillTerminate(_ notification: Notification) { stop() }
+
+ func stop() {
+ stopping = true
+ hidRetry?.invalidate(); hidRetry = nil
+ healthTimer?.invalidate(); healthTimer = nil
+ if callbackRegistered {
+ CGDisplayRemoveReconfigurationCallback(displayChanged, Unmanaged.passUnretained(self).toOpaque())
+ callbackRegistered = false
+ }
+ hmd.stop()
+ view.isPaused = true
+ window?.orderOut(nil)
+ if let display { CGDisplayRelease(display) }
+ display = nil
+ }
+
/// Takes exclusive control of the headset display and puts the renderer on it.
func displayArrived(_ id: CGDirectDisplayID) {
+ guard !stopping, display != id else { return }
if let t = lostAt { log(String(format: "headset display back after %.0f ms", Date().timeIntervalSince(t) * 1000)) }
lostAt = nil
- display = id
let r = CGDisplayCapture(id)
log("captured display \(id): \(r == .success ? "ok" : "error \(r.rawValue)")")
+ guard r == .success else { return }
+ display = id
setNativeMode(id)
// A captured display is covered by the shielding window; ours must sit above it.
@@ -87,14 +144,15 @@ final class HeadsetSession {
lostAt = Date()
display = nil
view.isPaused = true
+ window?.orderOut(nil)
+ CGDisplayRelease(id)
log("headset display LOST")
recover()
}
func panelOff() { log("headset reports panel off") }
- /// Recovery hook. The display normally returns by itself in under a second; when the USB-C DP adapter gets
- /// stuck reporting HPD low, the fix is restarting port 2's DisplayPort alt-mode session (see NOTES.md).
+ /// DP recovery is not yet solved. Avoid replaying panel-off or controller resets on a live link.
func recover() {
log("recovery: waiting for the display to return")
}
@@ -117,7 +175,8 @@ func runApp() {
let app = NSApplication.shared
app.setActivationPolicy(.regular)
let session: HeadsetSession
- do { session = try HeadsetSession() } catch { print("renderer failed:", error); exit(1) }
+ do { session = try HeadsetSession(device: MTLCreateSystemDefaultDevice()) } catch { print("renderer failed:", error); exit(1) }
+ app.delegate = session
session.start()
app.activate(ignoringOtherApps: true)
withExtendedLifetime(session) { app.run() }
diff --git a/Sources/VRWorkstation/HMD.swift b/Sources/VRWorkstation/HMD.swift
@@ -12,10 +12,15 @@ struct IMUSample {
}
final class HMD {
+ enum Mode { case observe, activate }
var onIMU: ((IMUSample) -> Void)?
var onStatus: ((String) -> Void)?
/// Panel power state from the headset's 0x05 status report (true = lit).
var onPanel: ((Bool) -> Void)?
+ var onDisconnected: (() -> Void)?
+ private(set) var imuSamples = 0
+ private(set) var statusReports = 0
+ private(set) var lastIMUAt: Date?
private var managers: [IOHIDManager] = [] // must outlive the devices, or they get closed
private var control: IOHIDDevice?
@@ -28,43 +33,80 @@ final class HMD {
var description: String { "\(name) not found (plugged in, and accessory allowed?)" }
}
- /// Opens both HID devices, runs the wake sequence and starts the IMU. Callbacks fire on the main run loop.
- func start() throws {
- control = try open(0x03f0, 0x0c6a, "QHMD control device")
- sensors = try open(0x045e, 0x0659, "HoloLens Sensors")
- let me = Unmanaged.passUnretained(self).toOpaque()
- IOHIDDeviceRegisterInputReportCallback(control!, controlBuf, 64, { ctx, _, _, _, _, r, n in
- Unmanaged<HMD>.fromOpaque(ctx!).takeUnretainedValue().handleControl(r, n)
- }, me)
- IOHIDDeviceRegisterInputReportCallback(sensors!, sensorsBuf, 512, { ctx, _, _, _, _, r, n in
- Unmanaged<HMD>.fromOpaque(ctx!).takeUnretainedValue().handleSensors(r, n)
- }, me)
- for d in [control!, sensors!] { IOHIDDeviceScheduleWithRunLoop(d, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue) }
+ /// Observe registers callbacks only. Activate additionally wakes the panel and starts the IMU.
+ /// Callbacks fire on the main run loop.
+ func start(mode: Mode) throws {
+ stop()
+ do {
+ control = try open(0x03f0, 0x0c6a, "QHMD control device")
+ sensors = try open(0x045e, 0x0659, "HoloLens Sensors")
+ let me = Unmanaged.passUnretained(self).toOpaque()
+ IOHIDDeviceRegisterInputReportCallback(control!, controlBuf, 64, { ctx, _, _, _, _, r, n in
+ Unmanaged<HMD>.fromOpaque(ctx!).takeUnretainedValue().handleControl(r, n)
+ }, me)
+ IOHIDDeviceRegisterInputReportCallback(sensors!, sensorsBuf, 512, { ctx, _, _, _, _, r, n in
+ Unmanaged<HMD>.fromOpaque(ctx!).takeUnretainedValue().handleSensors(r, n)
+ }, me)
+ for d in [control!, sensors!] {
+ IOHIDDeviceRegisterRemovalCallback(d, { ctx, _, _ in
+ let hmd = Unmanaged<HMD>.fromOpaque(ctx!).takeUnretainedValue()
+ // Leave the IOKit callback before closing its manager.
+ DispatchQueue.main.async { hmd.onDisconnected?() }
+ }, me)
+ IOHIDDeviceScheduleWithRunLoop(d, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue)
+ }
+
+ guard mode == .activate else { return }
+ usleep(300_000)
+ for _ in 0..<4 {
+ var c = [UInt8](repeating: 0, count: 64); c[0] = 0x50; c[1] = 0x01
+ try setFeature(c); _ = getFeature(0x50); usleep(10_000)
+ }
+ _ = getFeature(0x09) // Preserve wake sequence; do not log device serials.
+ _ = getFeature(0x08); _ = getFeature(0x06)
+ try setFeature([0x04, 0x01]) // screen on
+ var imuOn = [UInt8](repeating: 0, count: 64); imuOn[0] = 0x02; imuOn[1] = 0x07
+ let result = IOHIDDeviceSetReport(sensors!, kIOHIDReportTypeOutput, 0x02, imuOn, imuOn.count)
+ guard result == kIOReturnSuccess else { throw hidError("IMU start", result) }
+ } catch {
+ stop()
+ throw error
+ }
+ }
- usleep(300_000)
- for _ in 0..<4 {
- var c = [UInt8](repeating: 0, count: 64); c[0] = 0x50; c[1] = 0x01
- setFeature(c); _ = getFeature(0x50); usleep(10_000)
+ /// Close host handles without sending panel-off, which can strand the DP link.
+ func stop() {
+ for d in [control, sensors].compactMap({ $0 }) {
+ IOHIDDeviceRegisterRemovalCallback(d, nil, nil)
+ IOHIDDeviceRegisterInputReportCallback(d, d == control ? controlBuf : sensorsBuf, d == control ? 64 : 512, nil, nil)
+ IOHIDDeviceUnscheduleFromRunLoop(d, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue)
}
- _ = getFeature(0x09) // Preserve wake sequence; do not log device serials.
- _ = getFeature(0x08); _ = getFeature(0x06)
- setFeature([0x04, 0x01]) // screen on
- var imuOn = [UInt8](repeating: 0, count: 64); imuOn[0] = 0x02; imuOn[1] = 0x07
- IOHIDDeviceSetReport(sensors!, kIOHIDReportTypeOutput, 0x02, imuOn, imuOn.count)
+ for m in managers { IOHIDManagerClose(m, IOOptionBits(kIOHIDOptionsTypeNone)) }
+ control = nil; sensors = nil; managers.removeAll()
+ lastIMUAt = nil
+ }
+
+ deinit {
+ stop()
+ controlBuf.deallocate(); sensorsBuf.deallocate()
}
private func open(_ vid: Int, _ pid: Int, _ name: String) throws -> IOHIDDevice {
let m = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone))
+ managers.append(m)
IOHIDManagerSetDeviceMatching(m, [kIOHIDVendorIDKey: vid, kIOHIDProductIDKey: pid] as CFDictionary)
guard IOHIDManagerOpen(m, IOOptionBits(kIOHIDOptionsTypeNone)) == kIOReturnSuccess,
let d = (IOHIDManagerCopyDevices(m) as? Set<IOHIDDevice>)?.first else { throw NotFound(name: name) }
- managers.append(m)
return d
}
- private func setFeature(_ b: [UInt8]) {
+ private func hidError(_ action: String, _ result: IOReturn) -> NSError {
+ NSError(domain: "HMD", code: Int(result), userInfo: [NSLocalizedDescriptionKey: String(format: "%@ failed: 0x%08x", action, result)])
+ }
+
+ private func setFeature(_ b: [UInt8]) throws {
let r = IOHIDDeviceSetReport(control!, kIOHIDReportTypeFeature, CFIndex(b[0]), b, b.count)
- if r != kIOReturnSuccess { onStatus?(String(format: "HID set 0x%02x failed: 0x%08x", b[0], r)) }
+ if r != kIOReturnSuccess { throw hidError(String(format: "HID set 0x%02x", b[0]), r) }
}
private func getFeature(_ id: UInt8) -> [UInt8]? {
@@ -74,12 +116,14 @@ final class HMD {
private func handleControl(_ p: UnsafeMutablePointer<UInt8>, _ n: Int) {
guard n >= 6 && p[0] == 0x05 else { return }
+ statusReports += 1
onStatus?("panel \(p[1] == 1 ? "on" : "off"), measured refresh \(p[5] == 0 ? 0 : Int(p[5]) + 1) Hz")
onPanel?(p[1] == 1)
}
private func handleSensors(_ p: UnsafeMutablePointer<UInt8>, _ n: Int) {
- guard p[0] == 0x01, n == 381 || n == 497 else { return }
+ guard (n == 381 || n == 497), p[0] == 0x01 else { return }
+ lastIMUAt = Date()
func i16(_ o: Int) -> Int32 { Int32(Int16(bitPattern: UInt16(p[o]) | UInt16(p[o + 1]) << 8)) }
func i32(_ o: Int) -> Int32 { Int32(bitPattern: UInt32(p[o]) | UInt32(p[o + 1]) << 8 | UInt32(p[o + 2]) << 16 | UInt32(p[o + 3]) << 24) }
func u64(_ o: Int) -> UInt64 { (0..<8).reduce(0) { $0 | UInt64(p[o + $1]) << (8 * UInt64($1)) } }
@@ -94,6 +138,7 @@ final class HMD {
g[a] = Float(sum) * 0.001 / 8
acc[a] = Float(i32(accel + (a * 4 + i) * 4)) * 0.001
}
+ imuSamples += 1
onIMU?(IMUSample(time: Double(u64(gyroTS + i * 8)) * 100e-9, gyro: g, accel: acc))
}
}
diff --git a/Sources/VRWorkstation/Renderer.swift b/Sources/VRWorkstation/Renderer.swift
@@ -46,10 +46,10 @@ final class Renderer: NSObject, MTKViewDelegate {
var frames = 0
func draw(in view: MTKView) {
- frames += 1
- if frames == 1 || frames % 300 == 0 { print("frame \(frames) drawable \(view.drawableSize)") }
guard let rpd = view.currentRenderPassDescriptor, let drawable = view.currentDrawable,
let cb = queue.makeCommandBuffer() else { return }
+ frames += 1
+ if frames == 1 || frames % 300 == 0 { print("frame \(frames) drawable \(view.drawableSize)") }
encode(cb, rpd, size: view.drawableSize, time: Float(CACurrentMediaTime() - start))
cb.present(drawable)
cb.commit()
diff --git a/Sources/VRWorkstation/main.swift b/Sources/VRWorkstation/main.swift
@@ -2,12 +2,28 @@ import Foundation
import MetalKit
import simd
setvbuf(stdout, nil, _IOLBF, 0)
-// Usage: VRWorkstation | --snapshot out.png [--snapshot-size WxH] [--snapshot-yaw deg]
+// Usage: VRWorkstation | --observe <seconds> | --snapshot out.png [--snapshot-size WxH] [--snapshot-yaw deg]
// Options: --snapshot-size WxH (default 4320x2160), --snapshot-yaw <degrees> (head turn, positive = left).
func option(_ name: String) -> String? {
guard let i = CommandLine.arguments.firstIndex(of: name), CommandLine.arguments.count > i + 1 else { return nil }
return CommandLine.arguments[i + 1]
}
+if CommandLine.arguments.contains("--observe") {
+ guard let value = option("--observe"), let seconds = Double(value), seconds > 0, seconds.isFinite else {
+ print("usage: VRWorkstation --observe <positive seconds>"); exit(2)
+ }
+ let hmd = HMD()
+ hmd.onStatus = { log("[observe] \($0)") }
+ hmd.onDisconnected = { log("[observe] USB device removed") }
+ do {
+ try hmd.start(mode: .observe)
+ log("[observe] listening for \(seconds)s; no HID commands or display capture")
+ RunLoop.main.run(until: Date().addingTimeInterval(seconds))
+ log("[observe] IMU samples=\(hmd.imuSamples), status reports=\(hmd.statusReports), display=\(onlineHeadsetDisplay().map(String.init) ?? "absent")")
+ hmd.stop()
+ } catch { print("observation failed:", error); exit(1) }
+ exit(0)
+}
if let i = CommandLine.arguments.firstIndex(of: "--snapshot") {
let path = CommandLine.arguments.count > i + 1 ? CommandLine.arguments[i + 1] : "snapshot.png"
let dims = (option("--snapshot-size") ?? "4320x2160").split(separator: "x").compactMap { Int($0) }
diff --git a/tools/hpmctl.c b/tools/hpmctl.c
@@ -64,8 +64,11 @@ int main(int argc, char **argv) {
if (strcmp(argv[2], "cmd") == 0) {
if (rid == 0) { fprintf(stderr, "refusing to send commands to RID 0 (hub + power)\n"); return 2; }
if (strlen(argv[3]) != 4) { fprintf(stderr, "4cc must be 4 characters\n"); return 2; }
- // The driver takes the 4CC as its ASCII bytes in string order, little-endian ('DRST' -> 0x54535244).
- uint32_t cc = (uint32_t)argv[3][0] | (uint32_t)argv[3][1] << 8 | (uint32_t)argv[3][2] << 16 | (uint32_t)argv[3][3] << 24;
+ // iecsCommand takes a C-style, big-endian numeric 4CC ('DRST' -> 0x44525354).
+ // AppleHPM byte-swaps it before writing ASCII bytes to CMD1. Passing a little-endian
+ // number sends a reversed command ("TSRD") and the controller responds "!CMD".
+ uint32_t cc = (uint32_t)(unsigned char)argv[3][0] << 24 | (uint32_t)(unsigned char)argv[3][1] << 16
+ | (uint32_t)(unsigned char)argv[3][2] << 8 | (uint32_t)(unsigned char)argv[3][3];
HPMInterface **hpm = openHPM(rid);
if (!hpm) return 1;
IOReturn r = (*hpm)->iecsCommand(hpm, 0, cc, 0);
diff --git a/tools/kernel/HPM_RE.md b/tools/kernel/HPM_RE.md
@@ -289,3 +289,39 @@ which have the least ambiguity.
- Consequence: the 'Gaid'/'DRST' (vr1) and 'HRST' (vr0) attempts were sent LE, arrived byte-reversed
('diaG', 'TSRD', 'TSRH'), and !CMD in CMD1 is the chip rejecting those. They remain UNTESTED.
- Flags bit0 (x3) gates a branch at 0x9b8c8b0 that skips a pre-check; use flags 0.
+
+### 8a. Endianness boundary, CONFIRMED on hardware (vr0, 2026-10-20 [time redacted])
+
+| layer | 4CC representation | evidence |
+|---|---|---|
+| AppleHPMLib plugin `iecsCommand` (vtbl +0x38) | passes the u32 through unchanged as scalar #2 of selector 2 | plugin disasm 0x2614-0x2668 (vr1, vr0 independently) |
+| AppleHPMUserClient selector 2 handler 0x9b4a044 | passes u32 unchanged to provider+0x8f0(route, cmd, flags, 5) | static (subagent) |
+| provider+0x8f0 = 0x9b8c830 | **byte-swaps** (`rev w8,w23` @0x9b8c970) then writes 4 bytes to CMD1 (reg 0x08) | static (vr1); vtable slot -> 0x9b8c830 confirmed by vr0 chained-pointer scan (7 slots, PAC discriminator 0x191d matching the UC +0x8f0 call) |
+| driver-internal helpers that store 4CC bytes to memory first (e.g. exitPDMode 'DRST' @0x9b5bbd4: `mov #0x5244; movk #0x5453` -> 0x54535244 stored LE) | little-endian ASCII in memory | static |
+
+**Rule for user space (selector 2): pass the C multichar big-endian value**, e.g. `'DRST'` = 0x44525354,
+`'HRST'` = 0x48525354, `'Gaid'` = 0x47616964. Do NOT copy the little-endian constants seen in the
+driver's internal helpers.
+
+Hardware results with corrected BE encoding (vr0):
+- `DRST` 0x44525354 -> IOReturn 0x00000000 in 50 ms (140.855422). Accepted.
+- `HRST` 0x48525354 -> 0xe00002c6 (126.592422). Rejected (reason TBD: see 0x9b8c830 return paths).
+- All earlier LE attempts (Gaid, DRST by vr1; HRST by vr0) were byte-reversed on the wire.
+
+### 8b. Return codes of provider+0x8f0 = 0x9b8c830 (vr1, static, VERIFIED in disassembly)
+- Pre-check: reads CMD1 (vtbl+0x8d0, reg 8); if a previous command is still pending it clears CMD1 first.
+- Writes rev(cmd) to CMD1 (vtbl+0x8c0, reg 8, 4 bytes).
+- flags bit1 set: sleep 100 us, return 0 (fire-and-forget).
+- Otherwise polls CMD1 every 100 us (w0=0x64 delay), up to 1000 tries (flags bit0 clear) or 100000 (bit0 set):
+ - CMD1 == 0x444d4321 (LE u32 of "!CMD") -> returns 0xe00002d8 - 0x12 = **0xe00002c6 (kIOReturnBadMessageID)**
+ = firmware did not recognize the command.
+ - CMD1 == 0 -> returns 0 (accepted/completed). Task result is then in DATA1 (reg 0x09) byte 0.
+ - tries exhausted -> 0xe00002d8 - 2 = **0xe00002d6** (timeout).
+So with correct BE encoding: HRST -> 0xe00002c6 means this CD3217 APP firmware does not implement HRST;
+DRST -> 0 means it does.
+
+### 8c. SOFTWARE RECOVERY CONFIRMED (vr0 hardware, 2026-10-20)
+Stuck state (adapter partner status HPD=0 since [time redacted], induced by HID screen_off) recovered by
+`iecsCommand(route 0, 'DRST' = 0x44525354, flags 0)` on RID 1 (Port-USB-C@2):
+returned 0 in 50 ms at 140.855422; CMD1 = 0, DATA1 byte0 = 0; display ADD 4320x2160@60 at
+143.064422 (+2.209 s). No physical replug. Repeatability trials in progress (vr0).