app
LAN7800 EEPROM Programmer
Public Made by Adomby adom
MPLAB-Connect-parity EEPROM tool for the Microchip LAN7800 USB-3.1 to Gigabit-Ethernet controller (VID 0x0424 / PID 0x7800). Read an adapter's EEPROM to a .bin, edit MAC / VID / PID / bcdDevice / strings / serial and LED/GPIO with a byte-exact preview, and program hardware with verify-after-write. Reads are free; physical writes are gated behind --force-physical-write. Ships an AI-oriented CLI (single source of truth) plus a Hydrogen webapp that shells out to it.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
"""Build a synthetic-but-realistic LAN7800 EEPROM .bin fixture.
No hardware needed. Lays down: 0xA5 signature, a MAC, a string heap with a
manufacturer/product/serial USB string descriptor, and SS/HS/FS USB device
descriptor blocks carrying VID/PID/bcdDevice — matching the layout the parser
in eeprom.py expects, so round-trip tests have something valid to chew on.
"""
import struct
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lan7800.eeprom import (SIG_OFFSET, SIG_VALUE, MAC_OFFSET, STR_TABLE_OFFSET,
STR_NAMES, DESC_PTRS, MAC_LITTLE_ENDIAN)
def usb_string(s: str) -> bytes:
payload = s.encode("utf-16-le")
return bytes([len(payload) + 2, 0x03]) + payload
def device_descriptor(vid: int, pid: int, bcd: int) -> bytes:
# 18-byte standard USB device descriptor; only VID/PID/bcdDevice matter here.
return struct.pack("<BBHBBBBHHHBBBB",
0x12, 0x01, 0x0300, 0xFF, 0x00, 0x00, 64,
vid, pid, bcd, 1, 2, 3, 1)
def build(mac="00:80:0F:11:22:33", vid=0x0424, pid=0x7800, bcd=0x0300,
size=512) -> bytes:
buf = bytearray(size)
buf[SIG_OFFSET] = SIG_VALUE
octets = [int(x, 16) for x in mac.split(":")]
stored = octets[::-1] if MAC_LITTLE_ENDIAN else octets
buf[MAC_OFFSET:MAC_OFFSET + 6] = bytes(stored)
# --- string heap (word-aligned) ---
# Placed high (0xC0+) so it clears both the fixed LED/GPIO register offsets
# (0x07/0x0B-0x0D/0x0E/0x48-0x4F/0x58-0x59) and the descriptor blocks below.
heap = 0xC0
strings = {"manufacturer": "Microchip", "product": "LAN7800 USB 3.1 GbE",
"serial": "SN0000000001"}
for i, name in enumerate(STR_NAMES):
entry = STR_TABLE_OFFSET + i * 2
if name in strings:
desc = usb_string(strings[name])
buf[heap:heap + len(desc)] = desc
buf[entry] = len(desc) # length field (bytes)
buf[entry + 1] = heap // 2 # word offset
heap += (len(desc) + 1) & ~1 # keep word alignment
else:
buf[entry] = 0
buf[entry + 1] = 0
# --- descriptor blocks for SS/HS/FS ---
# Placed at 0x60/0x80/0xA0 (24-byte slots) so no 18-byte block overlaps the
# fixed GPIO config region (0x48-0x4F) or LED behavior (0x58-0x59). This
# matches how a real EEPROM's pointer table keeps descriptors clear of the
# low config/register bytes.
dd = device_descriptor(vid, pid, bcd)
block = 0x60
for kind, ptr_off in DESC_PTRS.items():
buf[block:block + len(dd)] = dd
buf[ptr_off] = (block // 2) & 0xFF
buf[ptr_off + 1] = ((block // 2) >> 8) & 0xFF
block += 24
return bytes(buf)
if __name__ == "__main__":
out = sys.argv[1] if len(sys.argv) > 1 else "fixture.bin"
open(out, "wb").write(build())
print(f"wrote {out}")