From 4a723cfc0eee4d54ab886460ff2c9f0d4e017b8c Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Thu, 24 Sep 2026 15:13:27 +0200 Subject: [PATCH] net: a native RTL8169/8111 driver, because phylib is not a set of leaf calls The laptop's NIC is an RTL8111H, and the request was to add it to LDLS. Measuring it is what changed the answer, so the measurement is the commit message: r8169.ko (6.18.52) has **183 undefined symbols, 130 of them not implemented by ldls/kpi/, and 35 of those 130 are phylib** -- phy_connect_direct, phy_start, mdiobus_read, genphy_soft_reset and the rest. phylib is a subsystem with a link state machine, not a set of leaf calls, and LDLS's usual answer for an unimplemented symbol -- a stub that reports itself and returns zero -- is exactly wrong for it. phy_start returning zero means the link never comes up and there is nothing to look at. The 8139too that ldls.md uses as its model has no phylib dependency, which is why that one was tractable and this one is not. So: a native driver. It reaches the PHY through the chip's own MII window -- PHYAR is one register that is a whole MII bus -- and skips the subsystem. Less work than the port, no GPL entanglement, no firmware blob, no coupling to one Linux version, and it covers the whole RTL8111/8168 family because all of it answers to 10EC:8168. The register layout is a fact about the hardware, taken the way ldls.md section 3 says facts may be taken; the expression is this tree's, in the same shape as the e1000 beside it. **It has not run on hardware, and that is not a hedge.** QEMU emulates rtl8139 and nothing else from Realtek, and the 8139 is a different programming model -- a circular receive buffer rather than descriptors -- so it is deliberately not in the match table. What is verified: it builds, its presence changes nothing on a machine without the device, and with an rtl8139 on the bus it correctly leaves it unclaimed. What is not: every register write, on a real part. The probe therefore says everything a first bug report needs, with cprintf rather than kinfo so it reaches the screen of a machine with no serial port: r8169: xid 5c0, 1c:1b:b5:xx:xx:xx, irq 11, link up (bmsr 796d) xid is the hardware version from the top of TxConfig -- the PCI device id has been 8168 for twenty years and says nothing. bmsr is MII register 1: bit 2 is link, bit 5 is autonegotiation complete. Those four facts separate "the chip is not there" from "the PHY sees nothing" from "the rings are wrong". Two constants were wrong in the first draft and are recorded because both would have worked quietly. RxConfig had 0x7700 for "unlimited": MXDMA is bits 8-10 and RXFTH is bits 13-15, so that was one bit short of a full burst and a half-full FIFO threshold. And CPCR bit 3 was being set under a comment calling it "PCI multiple read/write" -- bit 3 is PCIDAC, 64-bit addressing, and bit 4 is what the comment described; neither is wanted, so the write is gone. Both supported configurations: one boot each, no traps, no panics, socktest and memfdtest green. Co-Authored-By: Claude Opus 5 --- Makefile | 1 + docs/architecture/networking.md | 69 ++++ kernel/drivers/net/r8169/r8169.c | 539 +++++++++++++++++++++++++++++++ kernel/drivers/registry.c | 2 + 4 files changed, 611 insertions(+) create mode 100644 kernel/drivers/net/r8169/r8169.c diff --git a/Makefile b/Makefile index a936bad..78cbeff 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_pci.o KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/vga/vga_fb.o KERN_DRIVERS += $(BUILD)/kernel/drivers/gpu/virtio_gpu/virtio_gpu.o KERN_DRIVERS += $(BUILD)/kernel/drivers/net/e1000/e1000.o +KERN_DRIVERS += $(BUILD)/kernel/drivers/net/r8169/r8169.o KERN_DRIVERS += $(BUILD)/kernel/drivers/audio/ac97.o KERN_DRIVERS += $(BUILD)/kernel/drivers/bus/xhci/xhci.o KERN_DRIVERS += $(BUILD)/kernel/drivers/usb/core/usb.o diff --git a/docs/architecture/networking.md b/docs/architecture/networking.md index 31f28fa..670d4e7 100644 --- a/docs/architecture/networking.md +++ b/docs/architecture/networking.md @@ -29,6 +29,75 @@ that completes a handshake and drops every byte. └────────────────────────────────────┘ ``` +## Realtek RTL8169/8168/8111 — `drivers/net/r8169/` + +The NIC in a very large number of laptops and desktop boards. Every +RTL8111/8168 variant answers to **10EC:8168**, which is why one match entry +covers two decades of silicon — and why the driver prints the chip's XID on +probe, since the PCI device id tells you almost nothing about which part +arrived. + +### Why it is native, when the plan said LDLS + +The first plan was to run Linux's `r8169.ko` under [ldls.md](ldls.md). Measuring +it is what changed the answer: the module has **183 undefined symbols, 130 of +them not implemented**, and 35 of those 130 are **phylib** — +`phy_connect_direct`, `phy_start`, `mdiobus_read`, `genphy_soft_reset`. + +phylib is a subsystem with a link state machine, not a set of leaf calls, and +LDLS's usual answer for an unimplemented symbol — a stub that reports itself +and returns zero — is precisely wrong for it. `phy_start` returning zero means +the link never comes up and there is nothing on screen to say why. The 8139too +that ldls.md uses as its model has no phylib dependency, which is why that one +was tractable. + +A native driver reaches the PHY through the chip's own MII window (`PHYAR`, +one register that is a whole MII bus) and skips the subsystem entirely. It is +less work than the port, it carries no GPL entanglement, it needs no firmware +blob, and it is not coupled to one Linux version. + +The register layout is a *fact about the hardware*, taken the way +[ldls.md](ldls.md) §3 says facts may be taken. The expression is this tree's: +a descriptor-ring driver in the same shape as the e1000 beside it. + +### It has not run on hardware + +**Stated plainly because it cannot be tested here.** QEMU emulates `rtl8139` +and nothing else from Realtek, and the 8139 is a different programming model — +a circular receive buffer rather than descriptors — so it is deliberately *not* +in the match table. Claiming it would break a NIC that works. + +What has been verified: the driver builds, its presence changes nothing on a +machine without the device, and with an `rtl8139` on the bus it correctly +leaves it unclaimed (`10ec:8139 network controller [-]`). + +What has not: every register write, on a real part. + +So the probe says everything a first bug report would need, with `cprintf` +rather than `kinfo` so it reaches the screen on a machine with no serial port: + +``` +r8169: xid 5c0, 1c:1b:b5:xx:xx:xx, irq 11, link up (bmsr 796d) +``` + +`xid` is the hardware version out of the top of `TxConfig`; `bmsr` is MII +register 1, where bit 2 is link and bit 5 is autonegotiation complete. Those +four facts separate "the chip is not there", "the chip is there and the PHY +sees nothing", and "everything is up and the rings are wrong". + +### Two constants that were wrong first + +Recorded because both would have worked, quietly: + +- `RxConfig` had `0x7700` for unlimited burst and threshold. `MXDMA` is bits + 8-10 and `RXFTH` is bits 13-15, so that is one bit short of a full burst and + a half-full FIFO threshold rather than none — slower, and invisible. +- `CPCR` bit 3 was being set under a comment calling it "PCI multiple + read/write". Bit 3 is PCIDAC, 64-bit DMA addressing; bit 4 is the one the + comment described. Neither is wanted, since the rings come from the DMA arena + below 4 GB, so the write is gone rather than corrected. + + ## The netdev class A driver publishes a [`struct netdev_ops`](../../include/netdev.h) and is then diff --git a/kernel/drivers/net/r8169/r8169.c b/kernel/drivers/net/r8169/r8169.c new file mode 100644 index 0000000..7e11589 --- /dev/null +++ b/kernel/drivers/net/r8169/r8169.c @@ -0,0 +1,539 @@ +// Realtek RTL8169/8168/8111 gigabit Ethernet. +// +// The NIC in a very large number of laptops and desktop boards: every +// RTL8111/8168 variant answers to 10EC:8168, and the RTL8111H in particular is +// what prompted this driver. +// +// **Native rather than through LDLS**, and the reason is worth recording +// because the first plan was the other way round. Running Linux's r8169.ko +// under kernel/../ldls would mean implementing 130 kernel functions it calls, +// and 35 of those are *phylib* -- `phy_connect_direct`, `phy_start`, +// `mdiobus_read`, `genphy_soft_reset` and the rest. phylib is a subsystem with +// a link state machine, not a set of leaf calls, and LDLS's usual answer for a +// symbol it does not implement -- a stub that reports itself and returns zero +// -- is exactly wrong for it: `phy_start` returning zero means the link never +// comes up and there is nothing to see. A native driver reaches the PHY +// through the chip's own MII window instead and skips the whole subsystem. +// +// The register layout below is a *fact about the hardware*, taken the way +// docs/architecture/ldls.md §3 says facts may be taken. None of the expression +// is Linux's: this is a descriptor-ring driver in the same shape as the e1000 +// beside it, because that is the shape this kernel's netdev class wants. + +#include "types.h" +#include "defs.h" +#include "param.h" +#include "x86.h" +#include "spinlock.h" +#include "sleeplock.h" +#include "fs.h" +#include "file.h" +#include "memlayout.h" +#include "mmu.h" +#include "pci.h" +#include "device.h" +#include "netdev.h" +#include "dma.h" +#include "irq.h" + +// --- registers -------------------------------------------------------------- + +#define R_IDR0 0x00 // station address, six bytes +#define R_MAR0 0x08 // multicast filter +#define R_TNPDS 0x20 // Tx normal-priority descriptors, 64-bit +#define R_CR 0x37 // command +#define R_TPPOLL 0x38 // tell the chip to look at the Tx ring +#define R_IMR 0x3C +#define R_ISR 0x3E +#define R_TCR 0x40 // Tx config +#define R_RCR 0x44 // Rx config +#define R_9346CR 0x50 // unlocks the config registers +#define R_CONFIG1 0x52 +#define R_PHYAR 0x60 // the MII window +#define R_PHYSTATUS 0x6C +#define R_RMS 0xDA // largest frame the chip will accept +#define R_CPCR 0xE0 // "C+" command +#define R_RDSAR 0xE4 // Rx descriptors, 64-bit +#define R_MTPS 0xEC + +#define CR_RST 0x10 +#define CR_RE 0x08 +#define CR_TE 0x04 + +#define TPPOLL_NPQ 0x40 + +#define C9346_UNLOCK 0xC0 +#define C9346_LOCK 0x00 + +// Interrupt causes. Write-1-to-clear, which is the detail that matters: a +// cause left set on a level-triggered line re-enters the handler for ever. +#define ISR_ROK 0x0001 +#define ISR_RER 0x0002 +#define ISR_TOK 0x0004 +#define ISR_TER 0x0008 +#define ISR_RDU 0x0010 // ran out of Rx descriptors +#define ISR_LINKCHG 0x0020 +#define ISR_TDU 0x0080 + +#define PHYSTATUS_LINK 0x02 + +// Rx config: accept frames addressed to us, broadcast and multicast, with the +// largest DMA burst and Rx FIFO threshold the chip offers. Promiscuous is +// deliberately not set -- a NIC that hands the stack every frame on the wire +// is a NIC that makes its own bugs look like somebody else's traffic. +#define RCR_AAP 0x00000001 // (not set) +#define RCR_APM 0x00000002 // physical match +#define RCR_AM 0x00000004 // multicast +#define RCR_AB 0x00000008 // broadcast +// MXDMA is bits 8-10 and RXFTH is bits 13-15, and "unlimited" is all ones in +// each: 0x700 | 0xE000. The first version of this had 0x7700, which sets bits +// 12-14 -- an Rx FIFO threshold of *half* rather than none, and one bit of +// MXDMA short. It would have worked and been slower, which is the worst kind +// of wrong to leave in a register constant. +#define RCR_UNLIMITED 0x0000E700 + +#define TCR_IFG_STD 0x03000000 +#define TCR_MXDMA 0x00000700 + +// --- descriptors ------------------------------------------------------------ +// +// Sixteen bytes, and the ring is a ring only because the last descriptor +// carries EOR. The chip walks forward and wraps when it sees it. + +struct rtl_desc { + uint32 opts1; + uint32 opts2; + uint64 addr; +}; + +#define DESC_OWN 0x80000000 // the chip owns it +#define DESC_EOR 0x40000000 // last descriptor in the ring +#define DESC_FS 0x20000000 // first fragment of a frame +#define DESC_LS 0x10000000 // last fragment +#define DESC_LEN 0x00003FFF + +#define NRX 32 +#define NTX 32 +#define BUFSZ 2048 // one frame, with room for a VLAN tag + +#define R8169_MAX_NICS 2 + +struct r8169 { + volatile uint8 *mmio; + struct rtl_desc *rx, *tx; + uint8 *rxbuf, *txbuf; + uint64 rx_phys, tx_phys, rxbuf_phys, txbuf_phys; + + int rx_next; // the descriptor we will look at next + int tx_next; + uint8 mac[6]; + int ready; + uint64 rx_packets, tx_packets; + + struct spinlock lock; + struct pci_dev *pdev; + struct device *ndev; +}; + +static struct r8169 nics[R8169_MAX_NICS]; + +static struct r8169 * +of(struct device *d) +{ + return d ? (struct r8169 *)d->priv : 0; +} + +static inline uint8 rd8 (struct r8169 *r, int o) { return *(volatile uint8 *)(r->mmio + o); } +static inline uint16 rd16(struct r8169 *r, int o) { return *(volatile uint16 *)(r->mmio + o); } +static inline uint32 rd32(struct r8169 *r, int o) { return *(volatile uint32 *)(r->mmio + o); } +static inline void wr8 (struct r8169 *r, int o, uint8 v) { *(volatile uint8 *)(r->mmio + o) = v; } +static inline void wr16(struct r8169 *r, int o, uint16 v) { *(volatile uint16 *)(r->mmio + o) = v; } +static inline void wr32(struct r8169 *r, int o, uint32 v) { *(volatile uint32 *)(r->mmio + o) = v; } + +static void +wr64(struct r8169 *r, int o, uint64 v) +{ + // Low half first. The chip latches the pair on the high write, so the other + // order hands it half of one address and half of the previous one. + wr32(r, o, (uint32)v); + wr32(r, o + 4, (uint32)(v >> 32)); +} + +// --- the PHY, through the chip's own window --------------------------------- +// +// This is the part that makes the driver native rather than a phylib port: +// PHYAR is a one-register MII bus. Write it with the register number and a +// value to write; read it back until the chip says the access finished. + +#define PHYAR_FLAG 0x80000000 + +static void +mii_write(struct r8169 *r, int reg, uint16 val) +{ + wr32(r, R_PHYAR, PHYAR_FLAG | ((reg & 0x1F) << 16) | val); + for (int i = 0; i < 1000; i++) { + microdelay(20); + if (!(rd32(r, R_PHYAR) & PHYAR_FLAG)) + return; + } +} + +static int +mii_read(struct r8169 *r, int reg) +{ + wr32(r, R_PHYAR, (reg & 0x1F) << 16); + for (int i = 0; i < 1000; i++) { + microdelay(20); + uint32 v = rd32(r, R_PHYAR); + if (v & PHYAR_FLAG) + return (int)(v & 0xFFFF); + } + return -1; +} + +// --- the class ops ---------------------------------------------------------- + +static const unsigned char * +r8169_mac_op(struct device *d) +{ + struct r8169 *r = of(d); + static const unsigned char zero[6]; + return r ? r->mac : zero; +} + +static int +r8169_link_up(struct device *d) +{ + struct r8169 *r = of(d); + return r ? (rd8(r, R_PHYSTATUS) & PHYSTATUS_LINK) != 0 : 0; +} + +static int +r8169_transmit(struct device *d, const void *frame, int len) +{ + struct r8169 *r = of(d); + struct rtl_desc *t; + int idx; + + if (!r || !r->ready) + return -1; + if (len <= 0 || len > BUFSZ) + return -1; + + acquire(&r->lock); + idx = r->tx_next; + t = &r->tx[idx]; + + // Still the chip's? Then the ring is full. Congestion rather than failure: + // the caller retries, which is what the netdev contract says -1 means. + if (t->opts1 & DESC_OWN) { + release(&r->lock); + return -1; + } + + // The runt rule. Ethernet's minimum frame is 60 bytes before the FCS, and + // this chip does not pad for you -- a shorter frame goes out short and the + // other end drops it. ARP requests are 42 bytes, so getting this wrong is + // not a corner case, it is "the machine cannot resolve anything". + int n = len < 60 ? 60 : len; + memmove(r->txbuf + (uint64)idx * BUFSZ, frame, len); + if (n > len) + memset(r->txbuf + (uint64)idx * BUFSZ + len, 0, n - len); + + t->addr = r->txbuf_phys + (uint64)idx * BUFSZ; + t->opts2 = 0; + __sync_synchronize(); + t->opts1 = DESC_OWN | DESC_FS | DESC_LS | (uint32)n | + (idx == NTX - 1 ? DESC_EOR : 0); + __sync_synchronize(); + + // Poke it. Without this the chip may sit on a ring it already owns until + // something else wakes it. + wr8(r, R_TPPOLL, TPPOLL_NPQ); + + r->tx_next = (idx + 1) % NTX; + r->tx_packets++; + release(&r->lock); + return 0; +} + +static int +r8169_receive(struct device *d, void *out, int cap) +{ + struct r8169 *r = of(d); + struct rtl_desc *rx; + int idx, len; + + if (!r || !r->ready) + return 0; + + acquire(&r->lock); + idx = r->rx_next; + rx = &r->rx[idx]; + + if (rx->opts1 & DESC_OWN) { // still the chip's: nothing arrived + release(&r->lock); + return 0; + } + + // The length includes the four-byte FCS, which the stack does not want and + // lwIP would otherwise carry to the end of the world as trailing garbage. + len = (int)(rx->opts1 & DESC_LEN) - 4; + if (len < 0) + len = 0; + if (len > cap) + len = cap; + if (len > 0) + memmove(out, r->rxbuf + (uint64)idx * BUFSZ, len); + + // Hand the descriptor back. Address and size first, OWN last: the chip may + // start filling it the instant OWN is set. + rx->addr = r->rxbuf_phys + (uint64)idx * BUFSZ; + rx->opts2 = 0; + __sync_synchronize(); + rx->opts1 = DESC_OWN | (uint32)BUFSZ | (idx == NRX - 1 ? DESC_EOR : 0); + __sync_synchronize(); + + r->rx_next = (idx + 1) % NRX; + r->rx_packets++; + release(&r->lock); + return len; +} + +// --- interrupt -------------------------------------------------------------- + +static void +r8169_intr(void *arg) +{ + struct r8169 *r = arg; + uint16 isr; + + if (!r || !r->mmio) + return; + + isr = rd16(r, R_ISR); + if (!isr) + return; + + // Write-1-to-clear, and it has to happen whatever else does: a cause left + // asserted on a level-triggered PCI line re-enters this handler until the + // machine does nothing else. See docs/history/kernel.md section 13 for the + // last time that was learned here. + wr16(r, R_ISR, isr); + + // Nothing is read from the ring here, for the same reason as the e1000: the + // frame is pulled by the network thread, which may sleep on the buffer cache + // and take the lwIP lock, and an interrupt handler may do neither. All this + // does is end that thread's wait early. + if (isr & (ISR_ROK | ISR_RDU)) + netdev_rx_signal(); +} + +// --- bring-up --------------------------------------------------------------- + +static int +rings_init(struct r8169 *r) +{ + // 256-byte alignment is the chip's requirement for both descriptor bases, + // and it is not advisory: a misaligned base is a ring the chip walks + // somewhere else entirely. + r->rx = dma_alloc(NRX * sizeof(struct rtl_desc), 256); + r->tx = dma_alloc(NTX * sizeof(struct rtl_desc), 256); + r->rxbuf = dma_alloc(NRX * BUFSZ, 8); + r->txbuf = dma_alloc(NTX * BUFSZ, 8); + if (!r->rx || !r->tx || !r->rxbuf || !r->txbuf) + return -1; + + r->rx_phys = dma_phys(r->rx); + r->tx_phys = dma_phys(r->tx); + r->rxbuf_phys = dma_phys(r->rxbuf); + r->txbuf_phys = dma_phys(r->txbuf); + + memset(r->rx, 0, NRX * sizeof(struct rtl_desc)); + memset(r->tx, 0, NTX * sizeof(struct rtl_desc)); + + for (int i = 0; i < NRX; i++) { + r->rx[i].addr = r->rxbuf_phys + (uint64)i * BUFSZ; + r->rx[i].opts1 = DESC_OWN | (uint32)BUFSZ | + (i == NRX - 1 ? DESC_EOR : 0); + } + for (int i = 0; i < NTX; i++) + r->tx[i].opts1 = (i == NTX - 1 ? DESC_EOR : 0); + + r->rx_next = 0; + r->tx_next = 0; + return 0; +} + +static int +r8169_probe(struct pci_dev *d) +{ + struct r8169 *r = 0; + uint64 bar = 0; + uint64 barlen = 0; + + for (int i = 0; i < R8169_MAX_NICS; i++) + if (!nics[i].ready) { r = &nics[i]; break; } + if (!r) + return -1; + + // BAR 2 on a PCIe part, BAR 1 on the older PCI ones, and BAR 0 is I/O on + // both. Picking the first memory BAR rather than a number is what makes one + // driver cover the family. + for (int i = 0; i < 6; i++) + if (d->bar[i].base && !d->bar[i].is_io) { + bar = d->bar[i].base; + barlen = d->bar[i].size; + break; + } + if (!bar) { + cprintf("r8169: no memory BAR; not driving this device\n"); + return -1; + } + + r->pdev = d; + initlock(&r->lock, "r8169"); + pci_enable(d, PCI_CMD_MEMORY | PCI_CMD_BUS_MASTER); + + // ioremap, not P2V: a 64-bit BAR on a laptop routinely sits above the + // 4 GB the direct map covers, and this is the same lesson the virtio-gpu + // driver's comment records. + r->mmio = ioremap(bar, barlen ? barlen : 0x1000); + if (!r->mmio) { + cprintf("r8169: cannot map registers at %p\n", (void *)bar); + return -1; + } + + // Soft reset, and wait for the chip to say it finished. + wr8(r, R_CR, CR_RST); + for (int i = 0; i < 1000; i++) { + microdelay(100); + if (!(rd8(r, R_CR) & CR_RST)) + break; + } + if (rd8(r, R_CR) & CR_RST) { + cprintf("r8169: the chip did not come out of reset\n"); + return -1; + } + + for (int i = 0; i < 6; i++) + r->mac[i] = rd8(r, R_IDR0 + i); + + if (rings_init(r) != 0) { + cprintf("r8169: no DMA memory for the rings\n"); + return -1; + } + + wr8(r, R_9346CR, C9346_UNLOCK); // config registers are writable now + + // CPCR is deliberately left alone. + // + // An earlier version set bit 3 here with a comment calling it "PCI multiple + // read/write". Bit 3 is PCIDAC -- dual address cycle, which is 64-bit DMA + // addressing -- and the comment was describing bit 4. Neither is wanted: + // this driver's rings come from the DMA arena, which is below 4 GB, so DAC + // would be asking the chip to do something it does not need to do on the + // strength of a constant nobody had checked. + // + // The descriptor registers written below *are* the C+ ones on this part; + // unlike the 8139 it has no other programming model to be in. + + wr16(r, R_RMS, BUFSZ); + wr8(r, R_MTPS, 0x3B); + + wr64(r, R_RDSAR, r->rx_phys); + wr64(r, R_TNPDS, r->tx_phys); + + wr32(r, R_TCR, TCR_IFG_STD | TCR_MXDMA); + wr32(r, R_RCR, RCR_APM | RCR_AM | RCR_AB | RCR_UNLIMITED); + + // Multicast: everything. The stack filters, and a hash filter that is wrong + // drops IPv6 neighbour discovery in a way that looks like a routing problem. + wr32(r, R_MAR0, 0xFFFFFFFF); + wr32(r, R_MAR0 + 4, 0xFFFFFFFF); + + wr8(r, R_CR, CR_RE | CR_TE); + wr8(r, R_9346CR, C9346_LOCK); + + // Restart autonegotiation. The PHY normally does this out of reset, and + // asking again costs nothing and covers the case where firmware left it + // parked -- BMCR bit 12 is autoneg enable, bit 9 restarts it. + { + int bmcr = mii_read(r, 0); + if (bmcr >= 0) + mii_write(r, 0, (uint16)(bmcr | 0x1200)); + } + + wr16(r, R_ISR, 0xFFFF); // clear anything left over + wr16(r, R_IMR, ISR_ROK | ISR_RER | ISR_TOK | ISR_TER | + ISR_RDU | ISR_LINKCHG); + + if (d->irq != 0xFF && d->irq < 24) + irq_register(d->irq, r8169_intr, r, "r8169", 1); + else + cprintf("r8169: no usable interrupt line; the link will be polled\n"); + + r->ready = 1; + + // Said with cprintf rather than kinfo, and said in full. + // + // This driver's first run is on a machine nobody here can attach a debugger + // to, so the one line it prints has to carry everything a bug report would + // need: which variant answered, what address it claims, whether the PHY sees + // anything, and which line it will interrupt on. XID is the hardware version + // -- the top bits of TxConfig -- and is how Realtek parts are told apart, + // because the PCI device id is 8168 for two decades of silicon. + { + uint32 xid = (rd32(r, R_TCR) >> 20) & 0xFCF; + int bmsr = mii_read(r, 1); + + cprintf("r8169: xid %x, %x:%x:%x:%x:%x:%x, irq %d, %s (bmsr %x)\n", + xid, + r->mac[0], r->mac[1], r->mac[2], r->mac[3], r->mac[4], r->mac[5], + d->irq, + (rd8(r, R_PHYSTATUS) & PHYSTATUS_LINK) ? "link up" : "link down", + bmsr); + } + return 0; +} + +static void +r8169_attached(struct pci_dev *d, struct device *dev) +{ + for (int i = 0; i < R8169_MAX_NICS; i++) + if (nics[i].ready && nics[i].pdev == d) { + dev->priv = &nics[i]; + nics[i].ndev = dev; + return; + } +} + +static const struct netdev_ops r8169_netdev_ops = { + .transmit = r8169_transmit, + .receive = r8169_receive, + .mac = r8169_mac_op, + .link_up = r8169_link_up, +}; + +// The whole RTL8111/8168 family answers to 8168, which is why one entry covers +// most of the laptops this will ever run on. The others are the older PCI +// 8169, the Fast Ethernet 8101/8106, and the 2.5G 8125 -- listed because they +// share this programming model, and untested because none of them is here. +static const struct pci_match r8169_match[] = { + { 0x10EC, 0x8168, 0, 0 }, // RTL8111/8168/8411 -- includes the 8111H + { 0x10EC, 0x8169, 0, 0 }, // the original gigabit part + { 0x10EC, 0x8136, 0, 0 }, // RTL8101/8102/8106, Fast Ethernet + { 0x10EC, 0x8161, 0, 0 }, + { 0x10EC, 0x2502, 0, 0 }, + { 0x10EC, 0x2600, 0, 0 }, + { 0, 0, 0, 0 }, +}; + +const struct pci_driver r8169_driver = { + .name = "r8169", + .devname = "eth", + .match = r8169_match, + .ops = &r8169_netdev_ops, + .probe = r8169_probe, + .attached = r8169_attached, +}; diff --git a/kernel/drivers/registry.c b/kernel/drivers/registry.c index 34653e0..b5d5b2c 100644 --- a/kernel/drivers/registry.c +++ b/kernel/drivers/registry.c @@ -33,6 +33,7 @@ extern const struct pci_driver xhci_driver; extern const struct pci_driver virtio_gpu_driver; extern const struct pci_driver e1000_driver; extern const struct pci_driver ac97_driver; +extern const struct pci_driver r8169_driver; // Order matters here in one respect: virtio-gpu is offered the bus before the // VBE framebuffer driver, so that on a machine with both, the device with real @@ -43,6 +44,7 @@ static const struct pci_driver *drivers[] = { &virtio_gpu_driver, &vga_pci_driver, &e1000_driver, + &r8169_driver, &xhci_driver, &ac97_driver, };