Unplugged-cable behavior fixed end to end: heat grid rolls on the slot boundary (view computes the newest slot from the draw clock; history sampled every frame so slots color within a frame; noise phase displayed as commanded straight from the shared clock — radiating and the per-slot vote deleted), SNR ghost panic disambiguated via the PMA 1.1 latch (link dropped mid-read discards the batch, ghost on a proven-up link still dies), measure skips the 25s relink wait when the ECD verdict says faulted and the header says MEASURING instead of GOOD while a diag runs, RollBall mailbox deadline resized to 10s from bench measurement (<25ms linked, ~220ms no-link, >3s during a live plug's training; docs/modules/wiitek updated)

This commit is contained in:
flamingcow
2026-08-17 21:29:02 -07:00
parent 916c0150ef
commit be2bf56fa7
7 changed files with 81 additions and 40 deletions
+20 -4
View File
@@ -25,8 +25,10 @@ const (
rbReadDelayUs = 500
rbCmdPoll = 20 * time.Millisecond
// Matches the BCM allowance for a handler frozen by 10GBASE-T training.
rbCmdTimeout = 3 * time.Second
// Bench-measured: <25 ms linked, ~220 ms steady with no link, and >3 s
// while a live plug event's training ran — sized past a full ~6 s
// training, so a cable plugged mid-read reads busy, not dead.
rbCmdTimeout = 10 * time.Second
rbPHYIDHi uint16 = 0x002B
rbPHYIDLo uint16 = 0x0BF4
@@ -168,7 +170,12 @@ func (r *rollball) identify() (ident string, err error) {
return
}
func (r *rollball) snrMargins() (out [4]float64, err error) {
// An untrained PHY honestly serves 0x0000 here, so an out-of-band value is
// only a ghost if the link stayed up across the reads. The caller drained the
// PMA 1.1 latch when it checked the link, so one latched read now answers
// that: a drop since means the unplug race — discard the batch, ok false, and
// the next poll reads the link down.
func (r *rollball) snrMargins() (out [4]float64, ok bool, err error) {
r.exec(func() {
for i := range out {
var v uint16
@@ -177,10 +184,19 @@ func (r *rollball) snrMargins() (out [4]float64, err error) {
}
m := (float64(v) - 0x8000) / 10
if m < rbGhostLow || m > rbGhostHigh {
panic(fmt.Sprintf("%s: ghost SNR margin %.1f dB (1.%d=%#04x)", r.ifname, m, 133+i, v))
var link uint16
if link, err = r.mdioRead(1, 1); err != nil {
return
}
if link&0x0004 == 0 {
return
}
panic(fmt.Sprintf("%s: ghost SNR margin %.1f dB (1.%d=%#04x) with the link up",
r.ifname, m, 133+i, v))
}
out[i] = m
}
ok = true
})
return
}