Bugs-evident cleanup: cable-measure failure is fatal with its reason (failed/fail pretend-path and dead relinked plumbing deleted), bcm.command refuses partial SETs (0 or 5 params, stale-DATA trap made structural), BOOT gains a patched-ixgbe check naming stock-driver boots, panel fault rows matched by label and console rows strict on column count, phy.go split into sff/bcm/rollball and main.go's direction machinery into direction.go (pure moves), input events decoded via encoding/binary, gofmt; verified on hardware (20.00G, zero errors, ECD 41m)

This commit is contained in:
flamingcow
2026-08-17 18:56:55 -07:00
parent 38c2cc4da2
commit 916c0150ef
12 changed files with 1109 additions and 1049 deletions
+125
View File
@@ -0,0 +1,125 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"golang.org/x/sys/unix"
)
type sff struct {
ifname string
path string
// Every transport touch happens on the loop goroutine: requests execute
// one at a time, each admitted by the module type's own gate first.
reqs chan func()
admit func()
}
func openSFF(ifname string) (*sff, error) {
devLink, err := os.Readlink("/sys/class/net/" + ifname + "/device")
if err != nil {
return nil, fmt.Errorf("%s: %w", ifname, err)
}
drv, err := ifDriver(ifname)
if err != nil {
return nil, fmt.Errorf("%s: %w", ifname, err)
}
if drv != "ixgbe" {
return nil, fmt.Errorf("%s: no module I2C transport for driver %s", ifname, drv)
}
s := &sff{
ifname: ifname,
path: "/sys/kernel/debug/ixgbe/" + filepath.Base(devLink) + "/sff_i2c",
reqs: make(chan func()),
admit: func() {},
}
if _, err := os.Stat(s.path); err != nil {
return nil, fmt.Errorf("%s: %w (patched ixgbe?)", ifname, err)
}
go s.loop()
return s, nil
}
func (s *sff) loop() {
defer holdPanic()
for fn := range s.reqs {
s.admit()
fn()
}
}
func (s *sff) exec(fn func()) {
done := make(chan struct{})
s.reqs <- func() { fn(); close(done) }
<-done
}
func (s *sff) name() string { return s.ifname }
func (s *sff) op(cmd string) (string, error) {
fd, err := unix.Open(s.path, unix.O_RDWR, 0)
if err != nil {
return "", fmt.Errorf("%s: %w", s.path, err)
}
defer unix.Close(fd)
if _, err := unix.Write(fd, []byte(cmd)); err != nil {
return "", fmt.Errorf("%s %q: %w", s.ifname, cmd, err)
}
buf := make([]byte, 256)
n, err := unix.Read(fd, buf)
if err != nil {
return "", fmt.Errorf("%s %q: %w", s.ifname, cmd, err)
}
resp := strings.TrimSpace(string(buf[:n]))
if !strings.HasPrefix(resp, "ok") {
return "", fmt.Errorf("%s %q: %s", s.ifname, cmd, resp)
}
return strings.TrimSpace(resp[2:]), nil
}
func parseHexBytes(s string, n int) ([]byte, error) {
fields := strings.Fields(s)
if len(fields) != n {
return nil, fmt.Errorf("want %d bytes, got %q", n, s)
}
out := make([]byte, n)
for i, f := range fields {
var v byte
if _, err := fmt.Sscanf(f, "%x", &v); err != nil {
return nil, fmt.Errorf("byte %q in %q", f, s)
}
out[i] = v
}
return out, nil
}
// A single bus hold; split write/read ops would let the driver's own SFP
// traffic consume the bridge's pending read.
func (s *sff) compound(waddr, raddr byte, delayUs, n int, wdata []byte) ([]byte, error) {
var sb strings.Builder
fmt.Fprintf(&sb, "x %02x %02x %d %x", waddr, raddr, delayUs, n)
for _, v := range wdata {
fmt.Fprintf(&sb, " %02x", v)
}
resp, err := s.op(sb.String())
if err != nil {
return nil, err
}
return parseHexBytes(resp, n)
}
func (s *sff) eeprom(off byte, n int) ([]byte, error) {
return s.compound(0xA0, 0xA1, 500, n, []byte{off})
}
func (s *sff) vendorPN() (string, error) {
pn, err := s.eeprom(40, 16)
if err != nil {
return "", err
}
return strings.TrimSpace(string(pn)), nil
}