// Realtek RTL816x network driver. This file owns PCI attachment, descriptor rings, and net-device registration. #include "rtl816x.h" const HAL_API* gHal; static DRIVER_OBJECT gRtl816xDriver; static RTL816X_DEVICE* gRtl816xDevices[RTL816X_MAX_DEVICES]; static uint32_t gRtl816xDeviceCount; static SPINLOCK gRtl816xRegistryLock = SPINLOCK_INIT; static const IO_DEVICE_ID gRtl816xIds[] = { { IoBusPci, { "PCI\\VEN_10EC&DEV_8168", sizeof("PCI\\VEN_10EC&DEV_8168") - 1 } }, { IoBusPci, { "PCI\\VEN_10EC&DEV_8169", sizeof("PCI\\VEN_10EC&DEV_8169") - 1 } }, }; static const IO_DRIVER_ID_TABLE gRtl816xIdTable = { gRtl816xIds, sizeof(gRtl816xIds) / sizeof(gRtl816xIds[0]), }; // Implements the rtl Memory Barrier operation. static __forceinline void RtlMemoryBarrier(void) { _ReadWriteBarrier(); _mm_mfence(); _ReadWriteBarrier(); } // Implements the rtl Read8 operation. 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)); } // Implements the rtl Read16 operation. 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)); } // Implements the rtl Read32 operation. 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)); } // Implements the rtl Write8 operation. 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); } // Implements the rtl Write16 operation. 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); } // Implements the rtl Write32 operation. 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); } // Implements the rtl Reset operation. 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; } // Implements the rtl Read Mac operation. 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; } // Implements the rtl Allocate Dma operation. static KSTATUS RtlAllocateDma(RTL816X_DEVICE* r) { r->RxRingPhys = MmPfnAllocateContiguous(RTL_RING_PAGES, MmPfnUseDma); r->TxRingPhys = MmPfnAllocateContiguous(RTL_RING_PAGES, MmPfnUseDma); r->RxBuffersPhys = MmPfnAllocateContiguous(RTL_RXBUF_PAGES, MmPfnUseDma); r->TxBuffersPhys = MmPfnAllocateContiguous(RTL_TXBUF_PAGES, MmPfnUseDma); 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%016llX txRing=0x%016llX rxBuf=0x%016llX txBuf=0x%016llX\n", (unsigned long long)r->RxRingPhys, (unsigned long long)r->TxRingPhys, (unsigned long long)r->RxBuffersPhys, (unsigned long long)r->TxBuffersPhys); return STATUS_SUCCESS; } // Implements the rtl Init Descriptors operation. 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(); } // Implements the rtl Program Hardware operation. static void RtlProgramHardware(RTL816X_DEVICE* r) { RtlWrite8(r, RTL_REG_CFG9346, 0xC0); RtlWrite16(r, RTL_REG_INTR_MASK, 0); RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu); 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)); 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(); RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RX_ENABLE | RTL_CMD_TX_ENABLE); RtlWrite8(r, RTL_REG_CFG9346, 0x00); RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu); RtlWrite16(r, RTL_REG_INTR_MASK, RTL_INTR_ENABLES); } // Implements the rtl Handle Rx operation. 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) { 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; } } // Implements the rtl Dpc Routine operation. static void RtlDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { (void)dpc; (void)systemArgument1; (void)systemArgument2; RTL816X_DEVICE* r = (RTL816X_DEVICE*)context; if (!r || r->Removed || r->SurpriseRemoved) return; 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); } // Implements the rtl Register Device operation. static KSTATUS RtlRegisterDevice(RTL816X_DEVICE* r) { if (!r) return STATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock); for (uint32_t i = 0; i < gRtl816xDeviceCount; i++) { if (gRtl816xDevices[i] == r) { KeReleaseSpinlock(&gRtl816xRegistryLock, flags); return STATUS_SUCCESS; } } if (gRtl816xDeviceCount >= RTL816X_MAX_DEVICES) { KeReleaseSpinlock(&gRtl816xRegistryLock, flags); return STATUS_NO_MEMORY; } gRtl816xDevices[gRtl816xDeviceCount++] = r; KeReleaseSpinlock(&gRtl816xRegistryLock, flags); return STATUS_SUCCESS; } // Implements the rtl Unregister Device operation. static void RtlUnregisterDevice(RTL816X_DEVICE* r) { if (!r) return; uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock); for (uint32_t i = 0; i < gRtl816xDeviceCount; i++) { if (gRtl816xDevices[i] == r) { gRtl816xDevices[i] = gRtl816xDevices[gRtl816xDeviceCount - 1u]; gRtl816xDevices[gRtl816xDeviceCount - 1u] = (RTL816X_DEVICE*)0; gRtl816xDeviceCount--; break; } } KeReleaseSpinlock(&gRtl816xRegistryLock, flags); } // Implements the rtl Isr operation. static void RtlIsr(void) { RTL816X_DEVICE* snapshot[RTL816X_MAX_DEVICES]; uint32_t count = 0; uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock); count = gRtl816xDeviceCount; if (count > RTL816X_MAX_DEVICES) count = RTL816X_MAX_DEVICES; for (uint32_t i = 0; i < count; i++) snapshot[i] = gRtl816xDevices[i]; KeReleaseSpinlock(&gRtl816xRegistryLock, flags); for (uint32_t i = 0; i < count; i++) { RTL816X_DEVICE* r = snapshot[i]; if (!r || !r->Started || r->Removed || r->SurpriseRemoved) continue; uint16_t status = RtlRead16(r, RTL_REG_INTR_STAT); status &= RTL_INTR_ENABLES; if (!status || status == 0xFFFFu) continue; RtlWrite16(r, RTL_REG_INTR_STAT, status); r->PendingIsr |= status; KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0); } } // Implements the rtl Send Frame operation. 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 || !r->Started || r->Removed || r->SurpriseRemoved) return STATUS_OBJECT_DEAD; 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; } // Implements the rtl Select Register Bar operation. static KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) { if (!r || !r->Pdo) return STATUS_INVALID_PARAMETER; PCI_BAR best; PCI_BAR fallback; memset(&best, 0, sizeof(best)); memset(&fallback, 0, sizeof(fallback)); best.Type = PciBarUnused; fallback.Type = PciBarUnused; for (uint8_t i = 0; i < 6; i++) { PCI_BAR bar; KSTATUS st = PciGetBar(r->Pdo, i, &bar); if (!KSUCCESS(st)) continue; if (bar.Type == PciBarMemory32 || bar.Type == PciBarMemory64) { best = bar; break; } if (bar.Type == PciBarIo && fallback.Type == PciBarUnused) fallback = bar; } if (best.Type == PciBarUnused) best = fallback; if (best.Type == PciBarMemory32 || best.Type == PciBarMemory64) { KSTATUS st = PciMapBar(r->Pdo, best.Index, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE, (void**)&r->Mmio, &r->RegisterBytes); if (!KSUCCESS(st)) return st; r->RegisterBar = best; r->UseMmio = true; DbgPrint("RTL816X: BAR%u MMIO base=0x%016llX len=0x%llx virt=0x%016llX\n", best.Index, (unsigned long long)best.Base, (unsigned long long)r->RegisterBytes, r->Mmio); return STATUS_SUCCESS; } if (best.Type == PciBarIo) { if (best.Base > 0xFFFFu) return STATUS_NOT_SUPPORTED; r->RegisterBar = best; r->RegisterBytes = best.Length; r->IoBase = (uint16_t)best.Base; r->UseMmio = false; DbgPrint("RTL816X: BAR%u I/O base=0x%04x len=0x%llx\n", best.Index, r->IoBase, (unsigned long long)r->RegisterBytes); return STATUS_SUCCESS; } return STATUS_NOT_FOUND; } // Implements the rtl Start Device operation. static KSTATUS RtlStartDevice(RTL816X_DEVICE* r) { if (!r || r->Magic != RTL816X_DEVICE_MAGIC || !r->Pdo || !r->Fdo) return STATUS_INVALID_PARAMETER; if (r->Started) return STATUS_SUCCESS; KSTATUS st = PciGetDeviceInfo(r->Pdo, &r->PciInfo); if (!KSUCCESS(st)) { DbgPrint("RTL816X: PciGetDeviceInfo failed: 0x%x\n", st); return st; } DbgPrint("RTL816X: controller at PCI %02x:%02x.%x vendor=%04x device=%04x\n", r->PciInfo.Address.Bus, r->PciInfo.Address.Device, r->PciInfo.Address.Function, r->PciInfo.VendorId, r->PciInfo.DeviceId); st = RtlSelectRegisterBar(r); if (!KSUCCESS(st)) { DbgPrint("RTL816X: no usable register BAR (0x%x)\n", st); return st; } st = PciEnableDevice(r->Pdo, PCI_ENABLE_BUSMASTER | PCI_ENABLE_INTX | (r->UseMmio ? PCI_ENABLE_MEMORY : PCI_ENABLE_IO)); if (!KSUCCESS(st)) { DbgPrint("RTL816X: PciEnableDevice failed: 0x%x\n", st); return st; } st = PciGetInterrupt(r->Pdo, &r->Irq); if (!KSUCCESS(st) || r->Irq == 0xFFu) { DbgPrint("RTL816X: no legacy PCI IRQ line wired\n"); return KSUCCESS(st) ? STATUS_NOT_SUPPORTED : st; } st = RtlReset(r); if (!KSUCCESS(st)) { DbgPrint("RTL816X: reset timed out\n"); return st; } RtlReadMac(r); DbgPrint("RTL816X: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u\n", r->NetDevice.HardwareAddress[0], r->NetDevice.HardwareAddress[1], r->NetDevice.HardwareAddress[2], r->NetDevice.HardwareAddress[3], r->NetDevice.HardwareAddress[4], r->NetDevice.HardwareAddress[5], r->Irq); st = RtlAllocateDma(r); if (!KSUCCESS(st)) return st; RtlInitDescriptors(r); KeInitializeDpc(&r->Dpc, RtlDpcRoutine, r); r->NetDevice.Mtu = NET_MTU_ETHERNET; r->NetDevice.DriverContext = r; r->NetDevice.SendFrame = RtlSendFrame; st = RtlRegisterDevice(r); if (!KSUCCESS(st)) return st; KeIrqRegister(r->Irq, RtlIsr); r->Started = true; RtlProgramHardware(r); r->Fdo->PnpState = DevicePnpStarted; st = NetRegisterDevice(&r->NetDevice); if (!KSUCCESS(st)) { RtlWrite16(r, RTL_REG_INTR_MASK, 0); r->Started = false; RtlUnregisterDevice(r); return st; } DbgPrint("RTL816X: initialized; RX/TX enabled on IRQ %u\n", r->Irq); return STATUS_SUCCESS; } // Dispatches pnp. KSTATUS Rtl816xDispatchPnp(DEVICE_OBJECT* device, IRP* irp) { if (!device || !irp || !device->DeviceExtension) return STATUS_INVALID_PARAMETER; RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DeviceExtension; if (r->Magic != RTL816X_DEVICE_MAGIC) return STATUS_INVALID_PARAMETER; IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); if (!ios || ios->MajorFunction != IRP_MJ_PNP) return STATUS_INVALID_PARAMETER; switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) { case IRP_MN_START_DEVICE: return RtlStartDevice(r); case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: return STATUS_SUCCESS; case IRP_MN_SURPRISE_REMOVAL: r->SurpriseRemoved = true; device->PnpState = DevicePnpSurpriseRemoved; if (r->Started) RtlWrite16(r, RTL_REG_INTR_MASK, 0); return STATUS_SUCCESS; case IRP_MN_REMOVE_DEVICE: r->Removed = true; device->PnpState = DevicePnpDeleted; if (r->Started) RtlWrite16(r, RTL_REG_INTR_MASK, 0); RtlUnregisterDevice(r); IoDetachDevice(device); IoDeleteDevice(device); return STATUS_SUCCESS; default: return IoForwardIrpToLower(device, irp); } } // Implements the add Device operation. KSTATUS Rtl816xAddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) { if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical) return STATUS_INVALID_PARAMETER; DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0; KSTATUS st = IoCreateDevice(driver, sizeof(RTL816X_DEVICE), &fdo); if (!KSUCCESS(st)) return st; (void)IoSetDeviceName(fdo, SYS_SV_LITERAL("rtl816x")); RTL816X_DEVICE* r = (RTL816X_DEVICE*)fdo->DeviceExtension; memset(r, 0, sizeof(*r)); r->Magic = RTL816X_DEVICE_MAGIC; r->Fdo = fdo; r->Pdo = physicalDeviceObject; r->Irq = 0xFFu; r->TxLock = (SPINLOCK)SPINLOCK_INIT; r->LowerDevice = IoAttachDeviceToDeviceStack(fdo, physicalDeviceObject); if (!r->LowerDevice) { IoDeleteDevice(fdo); return STATUS_FAILURE; } DbgPrint("RTL816X: FDO attached to %v\n", physicalDeviceObject->Name); return STATUS_SUCCESS; } // Implements the driver Entry operation. KSTATUS DriverEntry(const HAL_API* hal) { if (!hal) return STATUS_INVALID_PARAMETER; DbgPrint("RTL816X: driver loading...\n"); gHal = hal; gRtl816xDeviceCount = 0; memset(&gRtl816xDriver, 0, sizeof(gRtl816xDriver)); IoInitializeDriverObject(&gRtl816xDriver, SYS_SV_LITERAL("rtl816x")); gRtl816xDriver.IdTable = &gRtl816xIdTable; gRtl816xDriver.DriverAddDevice = Rtl816xAddDevice; gRtl816xDriver.MajorFunction[IRP_MJ_PNP] = Rtl816xDispatchPnp; KSTATUS st = IoRegisterDriverObject(&gRtl816xDriver); if (!KSUCCESS(st)) DbgPrint("RTL816X: IoRegisterDriverObject failed: 0x%x\n", st); return st; }