Download

LAN7800 EEPROM Programmer (Core v1)

MPLAB-Connect-parity EEPROM tool for the Microchip LAN7800 USB-3.1→GbE controller (VID 0x0424 / PID 0x7800 only). Read the EEPROM to a .bin, edit MAC / VID / PID / bcdDevice / strings / serial with a byte-exact preview, and program a physical adapter with verify-after-write.

Webapp with an EEPROM loaded — byte-exact preview and field editor

Device scan — enumerate attached LAN7800 adapters

lan7800-programmer/
  docs/     register map, .bin layout, MPLAB parity checklist (read first)
  cli/      the engine — pluggable backends + AI-oriented CLI (single source of truth)
  webapp/   Hydrogen app; shells out to the CLI (holds no EEPROM logic)

Access method (decided)

Primary backend is the in-tree lan78xx driver via ethtool, exactly what Microchip's own "LAN78xx EEPROM/OTP Programming on Linux" article recommends:

  • Read: ethtool -e <iface> raw on
  • Write: ethtool -E <iface> magic 0x78A5 offset <o> value <v> (one byte/call; the driver handles EWEN internally)

The backend is pluggable (cli/lan7800/backends/) so a driver-independent libusb vendor-command path (for blank chips) can be added later without touching field logic.

Safety model (important)

  • Reads are free. Inspect, dump, preview, verify — none of these change hardware.
  • Physical writes are gated. The only commands that touch an adapter are program and erase, and both refuse unless --force-physical-write is passed. In the Symphony mesh, the operator/agent sets that flag only after a cleared mesh escalate PROCEED. The webapp never sets it — POST /program returns 423 with the exact command for a human to run.
  • Verify-after-write is mandatory (program re-reads and diffs; fails loud).
  • Anti-brick: byte0 signature 0xA5 is only written deliberately; a cleared signature is recoverable (device falls back to OTP/CSR defaults). Stay in 0–511. Keep a backup: lan7800prog read --iface <if> --out backup.bin.

CLI quickstart

cd cli
python3 lan7800prog scan                                           # FREE — enumerate LAN7800s
python3 lan7800prog read-device --out dump.bin                     # FREE — connected device
#   auto-picks the backend: a lan78xx netdev -> ethtool, else the driver-independent
#   libusb path (needs a one-time udev rule; run `lan7800prog udev-rule` to print it)
python3 lan7800prog new --out base.bin        # default LAN7800 template (or --blank)
python3 lan7800prog read --iface enx00800f780000 --out dump.bin    # FREE (specific iface)
python3 lan7800prog info --in dump.bin        # parse & print (fields, config flags, LED/GPIO)
python3 lan7800prog --json info --iface enp0s20u1                  # machine-readable

# edit fields -> new .bin (no hardware), then preview
python3 lan7800prog edit --in base.bin --out out.bin \
  --mac 00:80:0F:12:34:56 --vid 0x0424 --pid 0x7800 --serial SN0001 \
  --set-string product="LAN7800 GbE" --ensure-signature

# LED / GPIO (offsets authoritative; per-bit encoding VERIFY vs datasheet §15)
python3 lan7800prog edit --in base.bin --out out.bin \
  --set-led 0=10 --led-enable 0=1 --led-blink 2 \
  --set-gpio 3=out,push-pull,1 --gpio-wake 3=1,1 \
  --set-raw 0x48=01                # authoritative raw-byte fallback

# compare a device against an image (FREE)
python3 lan7800prog verify --iface enp0s20u1 --image out.bin

# GATED — only after a mesh-escalate PROCEED:
python3 lan7800prog program --iface enp0s20u1 --image out.bin --force-physical-write

Every command supports --json (machine output) and otherwise prints OK: / ERROR: + Hint: lines.

Webapp

cd webapp && PORT=8747 python3 server.py     # http://127.0.0.1:8747

Scan for devices enumerates attached LAN7800s (pick one to target). Load is a native file picker (uploads the .bin to the server). Edit MAC/VID/PID/ strings/serial and the LED/GPIO panel, watch the live byte-exact preview, save. Program to device is intentionally gated and returns the command to run under escalation. State is on the server (GET /state, GET /console, GET /scan) so it is AI-drivable.

Tests (offline, no hardware)

cd cli && python3 tests/test_eeprom.py       # 10/10 field round-trips on a synthetic .bin

Field layout & VERIFY caveats

Layout follows docs/01-eeprom-mechanism.md (datasheet DS00001992G Table 10-2 + lan78xx.c). Two assumptions are flagged for confirmation against a real dump and centralized in cli/lan7800/eeprom.py so they flip in one place:

  • MAC octet order (MAC_LITTLE_ENDIAN) — stored little-endian per the doc.
  • VID/PID location — inside the SS/HS/FS USB device-descriptor blocks pointed to by the word-unit pointers at 0x31/0x35/0x39; writes hit all present blocks.

Scope

Core v1 = read→dump, edit MAC/VID/PID/bcdDevice/strings/serial, signature 0xA5, verify-after-write, load/save raw .bin, device scan, file-picker load, and LED/GPIO editing (LED per-pin enable + 16 modes + blink; GPIO direction/ drive/output/wake). LED/GPIO byte offsets are authoritative (LED 0x0B–0x0D, 0x58–0x59; GPIO 0x07/0x0E/0x48–0x4F) but the per-bit encoding is best-effort — confirm against datasheet §15 or a real dump; a --set-raw byte-level fallback is always available. If a USB descriptor block is pointed into one of these fixed register ranges, info reports the collision and named LED/GPIO writes to the colliding byte are blocked (use --set-raw only if you truly intend it) so a descriptor is never silently corrupted. Not in v1 (see docs/02-mplab-connect-parity.md → ADVANCED): OTP, MAC ranges / mass production, PME/wake-frame filters, string-heap relocation (v1 edits strings in place only), INI/TOML source format.