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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user