// rtl816x.c - first-pass Realtek RTL8111/8168/8411 PCIe Gigabit NIC driver. // // Scope for this version: // * RTL8168/RTL8169-compatible PCI IDs, legacy INTx interrupt delivery. // * MMIO preferred, I/O BAR fallback. // * Descriptor-mode RX/TX with plain Ethernet frames. // * No checksum offload, TSO, VLAN, jumbo frames, MSI/MSI-X, or EEE. // // The driver registers a NET_DEVICE so a later ARP/IPv4/ICMP stack can attach // without knowing Realtek register details. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RTL_VENDOR_REALTEK 0x10ECu #define RTL_DEVICE_8168 0x8168u #define RTL_DEVICE_8169 0x8169u #define PCI_REG_COMMAND 0x04u #define PCI_REG_BAR0 0x10u #define PCI_REG_IRQ 0x3Cu #define PCI_COMMAND_IO (1u << 0) #define PCI_COMMAND_MEMORY (1u << 1) #define PCI_COMMAND_MASTER (1u << 2) #define PCI_COMMAND_INTX_DISABLE (1u << 10) #define RTL_REG_MAC0 0x00u #define RTL_REG_MAR0 0x08u #define RTL_REG_TNPDS_LO 0x20u #define RTL_REG_TNPDS_HI 0x24u #define RTL_REG_THPDS_LO 0x28u #define RTL_REG_THPDS_HI 0x2Cu #define RTL_REG_CHIP_CMD 0x37u #define RTL_REG_TX_POLL 0x38u #define RTL_REG_INTR_MASK 0x3Cu #define RTL_REG_INTR_STAT 0x3Eu #define RTL_REG_TX_CONFIG 0x40u #define RTL_REG_RX_CONFIG 0x44u #define RTL_REG_CFG9346 0x50u #define RTL_REG_CONFIG1 0x52u #define RTL_REG_PHY_STATUS 0x6Cu #define RTL_REG_RX_MAXSIZE 0xDAu #define RTL_REG_CPLUS_CMD 0xE0u #define RTL_REG_RDSAR_LO 0xE4u #define RTL_REG_RDSAR_HI 0xE8u #define RTL_CMD_RESET 0x10u #define RTL_CMD_RX_ENABLE 0x08u #define RTL_CMD_TX_ENABLE 0x04u #define RTL_TXPOLL_NPQ 0x40u #define RTL_ISR_ROK 0x0001u #define RTL_ISR_RER 0x0002u #define RTL_ISR_TOK 0x0004u #define RTL_ISR_TER 0x0008u #define RTL_ISR_RXOVW 0x0010u #define RTL_ISR_LINKCHG 0x0020u #define RTL_ISR_FOVW 0x0040u #define RTL_ISR_TDU 0x0080u #define RTL_ISR_SWINT 0x0100u #define RTL_ISR_TIMEOUT 0x4000u #define RTL_ISR_SYSERR 0x8000u #define RTL_INTR_ENABLES (RTL_ISR_ROK | RTL_ISR_RER | RTL_ISR_TOK | RTL_ISR_TER | \ RTL_ISR_RXOVW | RTL_ISR_LINKCHG | RTL_ISR_FOVW | \ RTL_ISR_TDU | RTL_ISR_SYSERR) #define RTL_RXCFG_AAP (1u << 0) // accept all physical packets/promiscuous #define RTL_RXCFG_APM (1u << 1) // accept physical match #define RTL_RXCFG_AM (1u << 2) // accept multicast #define RTL_RXCFG_AB (1u << 3) // accept broadcast #define RTL_RXCFG_AR (1u << 4) // accept runt #define RTL_RXCFG_AER (1u << 5) // accept errored #define RTL_RXCFG_DMA_UNLIMITED (7u << 8) #define RTL_TXCFG_DMA_UNLIMITED (7u << 8) #define RTL_TXCFG_IFG (3u << 24) #define RTL_DESC_OWN (1u << 31) #define RTL_DESC_EOR (1u << 30) #define RTL_DESC_FS (1u << 29) #define RTL_DESC_LS (1u << 28) #define RTL_RXDESC_RES (1u << 21) #define RTL_RXDESC_RWT (1u << 22) #define RTL_RXDESC_CRC (1u << 19) #define RTL_RXDESC_RUNT (1u << 20) #define RTL_DESC_LENGTH_MASK 0x00003FFFu #define RTL_RX_COUNT 64u #define RTL_TX_COUNT 64u #define RTL_BUF_SIZE 2048u #define RTL_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u) #define RTL_RING_PAGES 1u #define RTL_RXBUF_PAGES ((RTL_RX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u) #define RTL_TXBUF_PAGES ((RTL_TX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u) #define RTL_MMIO_PAGES 1u #pragma pack(push, 1) typedef struct RTL_DESC { volatile uint32_t opts1; volatile uint32_t opts2; volatile uint32_t addrLo; volatile uint32_t addrHi; } RTL_DESC; #pragma pack(pop) typedef enum RTL_BAR_KIND { RtlBarNone = 0, RtlBarIo, RtlBarMmio, } RTL_BAR_KIND; typedef struct RTL_BAR { RTL_BAR_KIND Kind; uint64_t Base; uint8_t Index; } RTL_BAR; typedef struct RTL816X_DEVICE { uint8_t Bus; uint8_t Dev; uint8_t Fn; uint8_t Irq; uint16_t DeviceId; bool UseMmio; volatile uint8_t* Mmio; uint16_t IoBase; RTL_DESC* RxRing; RTL_DESC* TxRing; PHYSICAL_ADDRESS RxRingPhys; PHYSICAL_ADDRESS TxRingPhys; uint8_t* RxBuffers; uint8_t* TxBuffers; PHYSICAL_ADDRESS RxBuffersPhys; PHYSICAL_ADDRESS TxBuffersPhys; uint32_t RxIndex; uint32_t TxIndex; SPINLOCK TxLock; KDPC Dpc; volatile uint16_t PendingIsr; NET_DEVICE NetDevice; } RTL816X_DEVICE; static const HAL_API* gHal; static RTL816X_DEVICE gDevice; static bool gDevicePresent = false; static __forceinline void RtlMemoryBarrier(void) { _ReadWriteBarrier(); _mm_mfence(); _ReadWriteBarrier(); } static uint8_t RtlRead8(RTL816X_DEVICE* r, uint32_t reg) { if (r->UseMmio) return *(volatile uint8_t*)(r->Mmio + reg); return __inbyte((unsigned short)(r->IoBase + reg)); } static uint16_t RtlRead16(RTL816X_DEVICE* r, uint32_t reg) { if (r->UseMmio) return *(volatile uint16_t*)(r->Mmio + reg); return __inword((unsigned short)(r->IoBase + reg)); } static uint32_t RtlRead32(RTL816X_DEVICE* r, uint32_t reg) { if (r->UseMmio) return *(volatile uint32_t*)(r->Mmio + reg); return __indword((unsigned short)(r->IoBase + reg)); } static void RtlWrite8(RTL816X_DEVICE* r, uint32_t reg, uint8_t value) { if (r->UseMmio) *(volatile uint8_t*)(r->Mmio + reg) = value; else __outbyte((unsigned short)(r->IoBase + reg), value); } static void RtlWrite16(RTL816X_DEVICE* r, uint32_t reg, uint16_t value) { if (r->UseMmio) *(volatile uint16_t*)(r->Mmio + reg) = value; else __outword((unsigned short)(r->IoBase + reg), value); } static void RtlWrite32(RTL816X_DEVICE* r, uint32_t reg, uint32_t value) { if (r->UseMmio) *(volatile uint32_t*)(r->Mmio + reg) = value; else __outdword((unsigned short)(r->IoBase + reg), value); } static bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId) { if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8168, outBus, outDev, outFn)) { *outDeviceId = RTL_DEVICE_8168; return true; } if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8169, outBus, outDev, outFn)) { *outDeviceId = RTL_DEVICE_8169; return true; } return false; } static bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out) { uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u); uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg); out->Kind = RtlBarNone; out->Base = 0; out->Index = index; if (lo == 0 || lo == 0xFFFFFFFFu) return false; if (lo & 1u) { uint32_t base = lo & ~0x3u; if (!base) return false; out->Kind = RtlBarIo; out->Base = base; return true; } uint64_t base = (uint64_t)(lo & ~0xFu); uint32_t type = (lo >> 1) & 0x3u; if (type == 0x2u && index < 5) { uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u)); base |= ((uint64_t)hi << 32); } if (!base) return false; out->Kind = RtlBarMmio; out->Base = base; return true; } static KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio) { uint64_t pageBase = physBase & PAGE_MASK; uint64_t pageOff = physBase - pageBase; for (uint32_t i = 0; i < RTL_MMIO_PAGES; i++) { PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE; uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys); KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE); if (st == STATUS_ALREADY_EXISTS) st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE); if (!KSUCCESS(st)) return st; } *outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff); return STATUS_SUCCESS; } static KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) { RTL_BAR best = { RtlBarNone, 0, 0 }; RTL_BAR fallback = { RtlBarNone, 0, 0 }; for (uint8_t i = 0; i < 6; i++) { RTL_BAR bar; if (!RtlReadBar(r->Bus, r->Dev, r->Fn, i, &bar)) continue; if (bar.Kind == RtlBarMmio) { best = bar; break; } if (bar.Kind == RtlBarIo && fallback.Kind == RtlBarNone) fallback = bar; // Skip the high dword of 64-bit memory BARs. if (bar.Kind == RtlBarMmio) { uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u)); if (((lo >> 1) & 0x3u) == 0x2u) i++; } } if (best.Kind == RtlBarNone) best = fallback; if (best.Kind == RtlBarMmio) { KSTATUS st = RtlMapMmio(best.Base, &r->Mmio); if (!KSUCCESS(st)) return st; r->UseMmio = true; DbgPrint("RTL816X: BAR%u MMIO base=0x%llx virt=%p\n", best.Index, (unsigned long long)best.Base, r->Mmio); return STATUS_SUCCESS; } if (best.Kind == RtlBarIo) { if (best.Base > 0xFFFFu) return STATUS_NOT_SUPPORTED; r->IoBase = (uint16_t)best.Base; r->UseMmio = false; DbgPrint("RTL816X: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase); return STATUS_SUCCESS; } return STATUS_NOT_FOUND; } static void RtlEnablePci(RTL816X_DEVICE* r) { uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND); cmd |= PCI_COMMAND_MASTER; if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY; else cmd |= PCI_COMMAND_IO; cmd &= ~PCI_COMMAND_INTX_DISABLE; gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd); } static KSTATUS RtlReset(RTL816X_DEVICE* r) { RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RESET); for (uint32_t i = 0; i < 1000; i++) { if ((RtlRead8(r, RTL_REG_CHIP_CMD) & RTL_CMD_RESET) == 0) return STATUS_SUCCESS; if (gHal->Timer.Stall) gHal->Timer.Stall(1000); } return STATUS_TIMEOUT; } static void RtlReadMac(RTL816X_DEVICE* r) { for (uint32_t i = 0; i < 6; i++) r->NetDevice.HardwareAddress[i] = RtlRead8(r, RTL_REG_MAC0 + i); r->NetDevice.HardwareAddressLength = 6; } static KSTATUS RtlAllocateDma(RTL816X_DEVICE* r) { r->RxRingPhys = MmAllocateContiguous(RTL_RING_PAGES); r->TxRingPhys = MmAllocateContiguous(RTL_RING_PAGES); r->RxBuffersPhys = MmAllocateContiguous(RTL_RXBUF_PAGES); r->TxBuffersPhys = MmAllocateContiguous(RTL_TXBUF_PAGES); if (!r->RxRingPhys || !r->TxRingPhys || !r->RxBuffersPhys || !r->TxBuffersPhys) return STATUS_NO_MEMORY; r->RxRing = (RTL_DESC*)gHal->PhysToVirt(r->RxRingPhys); r->TxRing = (RTL_DESC*)gHal->PhysToVirt(r->TxRingPhys); r->RxBuffers = (uint8_t*)gHal->PhysToVirt(r->RxBuffersPhys); r->TxBuffers = (uint8_t*)gHal->PhysToVirt(r->TxBuffersPhys); memset(r->RxRing, 0, PAGE_SIZE * RTL_RING_PAGES); memset(r->TxRing, 0, PAGE_SIZE * RTL_RING_PAGES); memset(r->RxBuffers, 0, PAGE_SIZE * RTL_RXBUF_PAGES); memset(r->TxBuffers, 0, PAGE_SIZE * RTL_TXBUF_PAGES); DbgPrint("RTL816X: rxRing=0x%llx txRing=0x%llx rxBuf=0x%llx txBuf=0x%llx\n", (unsigned long long)r->RxRingPhys, (unsigned long long)r->TxRingPhys, (unsigned long long)r->RxBuffersPhys, (unsigned long long)r->TxBuffersPhys); return STATUS_SUCCESS; } static void RtlInitDescriptors(RTL816X_DEVICE* r) { for (uint32_t i = 0; i < RTL_RX_COUNT; i++) { PHYSICAL_ADDRESS p = r->RxBuffersPhys + (PHYSICAL_ADDRESS)i * RTL_BUF_SIZE; r->RxRing[i].addrLo = (uint32_t)p; r->RxRing[i].addrHi = (uint32_t)(p >> 32); r->RxRing[i].opts2 = 0; r->RxRing[i].opts1 = RTL_DESC_OWN | RTL_BUF_SIZE | ((i == RTL_RX_COUNT - 1) ? RTL_DESC_EOR : 0); } for (uint32_t i = 0; i < RTL_TX_COUNT; i++) { PHYSICAL_ADDRESS p = r->TxBuffersPhys + (PHYSICAL_ADDRESS)i * RTL_BUF_SIZE; r->TxRing[i].addrLo = (uint32_t)p; r->TxRing[i].addrHi = (uint32_t)(p >> 32); r->TxRing[i].opts2 = 0; r->TxRing[i].opts1 = (i == RTL_TX_COUNT - 1) ? RTL_DESC_EOR : 0; } r->RxIndex = 0; r->TxIndex = 0; RtlMemoryBarrier(); } static void RtlProgramHardware(RTL816X_DEVICE* r) { // Unlock the 9346-style config register window while programming. RtlWrite8(r, RTL_REG_CFG9346, 0xC0); // Disable interrupts while rings are being installed. RtlWrite16(r, RTL_REG_INTR_MASK, 0); RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu); // DMA ring base addresses. RtlWrite32(r, RTL_REG_TNPDS_LO, (uint32_t)r->TxRingPhys); RtlWrite32(r, RTL_REG_TNPDS_HI, (uint32_t)(r->TxRingPhys >> 32)); RtlWrite32(r, RTL_REG_THPDS_LO, (uint32_t)r->TxRingPhys); RtlWrite32(r, RTL_REG_THPDS_HI, (uint32_t)(r->TxRingPhys >> 32)); RtlWrite32(r, RTL_REG_RDSAR_LO, (uint32_t)r->RxRingPhys); RtlWrite32(r, RTL_REG_RDSAR_HI, (uint32_t)(r->RxRingPhys >> 32)); // Plain packet mode. Keep C+ features mostly disabled for bring-up. RtlWrite16(r, RTL_REG_CPLUS_CMD, RtlRead16(r, RTL_REG_CPLUS_CMD) & ~(uint16_t)0x00E0u); RtlWrite16(r, RTL_REG_RX_MAXSIZE, RTL_BUF_SIZE); RtlWrite32(r, RTL_REG_TX_CONFIG, RTL_TXCFG_IFG | RTL_TXCFG_DMA_UNLIMITED); RtlWrite32(r, RTL_REG_RX_CONFIG, RTL_RXCFG_APM | RTL_RXCFG_AB | RTL_RXCFG_AM | RTL_RXCFG_DMA_UNLIMITED); RtlMemoryBarrier(); // Enable receiver and transmitter. RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RX_ENABLE | RTL_CMD_TX_ENABLE); // Lock config space again and enable selected interrupts. RtlWrite8(r, RTL_REG_CFG9346, 0x00); RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu); RtlWrite16(r, RTL_REG_INTR_MASK, RTL_INTR_ENABLES); } static void RtlHandleRx(RTL816X_DEVICE* r) { for (uint32_t budget = 0; budget < RTL_RX_COUNT; budget++) { RTL_DESC* d = &r->RxRing[r->RxIndex]; uint32_t opts = d->opts1; if (opts & RTL_DESC_OWN) break; uint32_t keepEor = opts & RTL_DESC_EOR; uint32_t len = opts & RTL_DESC_LENGTH_MASK; bool good = ((opts & (RTL_RXDESC_RES | RTL_RXDESC_RWT | RTL_RXDESC_CRC | RTL_RXDESC_RUNT)) == 0) && ((opts & (RTL_DESC_FS | RTL_DESC_LS)) == (RTL_DESC_FS | RTL_DESC_LS)) && len >= NET_ETHERNET_HEADER_LENGTH && len <= RTL_RX_WIRE_MAX; if (good) { // Realtek reports the Ethernet FCS in the descriptor length on many // chips. Remove it when present so upper layers see a normal frame. if (len > 4) len -= 4; if (len > NET_ETHERNET_MAX_FRAME) { DbgPrint("RTL816X: dropping oversize frame len=%u\n", len); } else { NetReceiveFrame(&r->NetDevice, r->RxBuffers + r->RxIndex * RTL_BUF_SIZE, len); } } else { DbgPrint("RTL816X: rx descriptor %u error opts=0x%08x len=%u\n", r->RxIndex, opts, len); } d->opts2 = 0; RtlMemoryBarrier(); d->opts1 = RTL_DESC_OWN | RTL_BUF_SIZE | keepEor; RtlMemoryBarrier(); r->RxIndex = (r->RxIndex + 1) % RTL_RX_COUNT; } } static void RtlDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { (void)dpc; (void)systemArgument1; (void)systemArgument2; RTL816X_DEVICE* r = (RTL816X_DEVICE*)context; uint16_t status = (uint16_t)r->PendingIsr; r->PendingIsr = 0; if (status & (RTL_ISR_ROK | RTL_ISR_RER | RTL_ISR_RXOVW | RTL_ISR_FOVW)) RtlHandleRx(r); if (status & RTL_ISR_LINKCHG) { uint8_t phy = RtlRead8(r, RTL_REG_PHY_STATUS); DbgPrint("RTL816X: link change phy=0x%02x\n", phy); } if (status & (RTL_ISR_TER | RTL_ISR_SYSERR)) DbgPrint("RTL816X: interrupt status error bits=0x%04x\n", status); } static void RtlIsr(void) { if (!gDevicePresent) return; RTL816X_DEVICE* r = &gDevice; uint16_t status = RtlRead16(r, RTL_REG_INTR_STAT); if (!status || status == 0xFFFFu) return; RtlWrite16(r, RTL_REG_INTR_STAT, status); r->PendingIsr |= status; KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0); } static KSTATUS RtlSendFrame(NET_DEVICE* device, const void* frame, size_t length) { if (!device || !frame) return STATUS_INVALID_PARAMETER; if (length > NET_ETHERNET_MAX_FRAME) return STATUS_BUFFER_TOO_SMALL; if (length < NET_ETHERNET_HEADER_LENGTH) return STATUS_INVALID_PARAMETER; RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DriverContext; if (!r) return STATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&r->TxLock); uint32_t index = r->TxIndex; RTL_DESC* d = &r->TxRing[index]; uint32_t opts = d->opts1; if (opts & RTL_DESC_OWN) { KeReleaseSpinlock(&r->TxLock, flags); return STATUS_QUEUE_FULL; } size_t wireLength = length < NET_ETHERNET_MIN_FRAME ? NET_ETHERNET_MIN_FRAME : length; uint8_t* buf = r->TxBuffers + index * RTL_BUF_SIZE; memcpy(buf, frame, length); if (wireLength > length) memset(buf + length, 0, wireLength - length); uint32_t eor = opts & RTL_DESC_EOR; d->opts2 = 0; RtlMemoryBarrier(); d->opts1 = RTL_DESC_OWN | RTL_DESC_FS | RTL_DESC_LS | eor | (uint32_t)wireLength; RtlMemoryBarrier(); RtlWrite8(r, RTL_REG_TX_POLL, RTL_TXPOLL_NPQ); r->TxIndex = (index + 1) % RTL_TX_COUNT; KeReleaseSpinlock(&r->TxLock, flags); return STATUS_SUCCESS; } static KSTATUS RtlInitialize(const HAL_API* hal) { gHal = hal; memset(&gDevice, 0, sizeof(gDevice)); gDevice.TxLock = (SPINLOCK)SPINLOCK_INIT; uint16_t deviceId; if (!RtlFindDevice(&gDevice.Bus, &gDevice.Dev, &gDevice.Fn, &deviceId)) { DbgPrint("RTL816X: no RTL8168/RTL8169 PCI device found; skipping\n"); return STATUS_SUCCESS; } gDevice.DeviceId = deviceId; DbgPrint("RTL816X: found Realtek PCI device 10ec:%04x at %u:%u.%u\n", deviceId, gDevice.Bus, gDevice.Dev, gDevice.Fn); KSTATUS st = RtlSelectRegisterBar(&gDevice); if (!KSUCCESS(st)) { DbgPrint("RTL816X: no usable register BAR (0x%x)\n", st); return st; } RtlEnablePci(&gDevice); gDevice.Irq = hal->Pci.GetIrqLine(gDevice.Bus, gDevice.Dev, gDevice.Fn); if (gDevice.Irq == 0xFFu) { DbgPrint("RTL816X: no legacy PCI IRQ line wired\n"); return STATUS_NOT_SUPPORTED; } st = RtlReset(&gDevice); if (!KSUCCESS(st)) { DbgPrint("RTL816X: reset timed out\n"); return st; } RtlReadMac(&gDevice); DbgPrint("RTL816X: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u\n", gDevice.NetDevice.HardwareAddress[0], gDevice.NetDevice.HardwareAddress[1], gDevice.NetDevice.HardwareAddress[2], gDevice.NetDevice.HardwareAddress[3], gDevice.NetDevice.HardwareAddress[4], gDevice.NetDevice.HardwareAddress[5], gDevice.Irq); st = RtlAllocateDma(&gDevice); if (!KSUCCESS(st)) return st; RtlInitDescriptors(&gDevice); KeInitializeDpc(&gDevice.Dpc, RtlDpcRoutine, &gDevice); gDevice.NetDevice.Mtu = NET_MTU_ETHERNET; gDevice.NetDevice.DriverContext = &gDevice; gDevice.NetDevice.SendFrame = RtlSendFrame; RtlProgramHardware(&gDevice); gDevicePresent = true; KeIrqRegister(gDevice.Irq, RtlIsr); st = NetRegisterDevice(&gDevice.NetDevice); if (!KSUCCESS(st)) return st; DbgPrint("RTL816X: initialized; RX/TX enabled on IRQ %u\n", gDevice.Irq); return STATUS_SUCCESS; } KSTATUS DriverEntry(const HAL_API* hal) { DbgPrint("RTL816X: driver loading...\n"); KSTATUS st = RtlInitialize(hal); if (!KSUCCESS(st)) DbgPrint("RTL816X: init failed: 0x%x\n", st); return st; }