// Realtek RTL8139 network driver. This file owns PCI attachment, MMIO setup, RX/TX rings, and net-device registration. #include "rtl8139.h" const NTL_DDK* gRtl8139Ddk; static DRIVER_OBJECT gRtl8139Driver; static RTL8139_DEVICE* gRtl8139Devices[RTL8139_MAX_DEVICES]; static uint32_t gRtl8139DeviceCount; static SPINLOCK gRtl8139RegistryLock = SPINLOCK_INIT; static const IO_DEVICE_ID gRtl8139Ids[] = { { IoBusPci, { "PCI\\VEN_10EC&DEV_8139", sizeof("PCI\\VEN_10EC&DEV_8139") - 1 } }, }; static const IO_DRIVER_ID_TABLE gRtl8139IdTable = { gRtl8139Ids, sizeof(gRtl8139Ids) / sizeof(gRtl8139Ids[0]), }; // Implements the memory Barrier operation. static __forceinline void Rtl8139MemoryBarrier(void) { _ReadWriteBarrier(); _mm_mfence(); _ReadWriteBarrier(); } // Implements the read8 operation. static uint8_t Rtl8139Read8(RTL8139_DEVICE* r, uint32_t reg) { if (r->UseMmio) return *(volatile uint8_t*)(r->Mmio + reg); return __inbyte((unsigned short)(r->IoBase + reg)); } // Implements the read16 operation. static uint16_t Rtl8139Read16(RTL8139_DEVICE* r, uint32_t reg) { if (r->UseMmio) return *(volatile uint16_t*)(r->Mmio + reg); return __inword((unsigned short)(r->IoBase + reg)); } // Implements the read32 operation. static uint32_t Rtl8139Read32(RTL8139_DEVICE* r, uint32_t reg) { if (r->UseMmio) return *(volatile uint32_t*)(r->Mmio + reg); return __indword((unsigned short)(r->IoBase + reg)); } // Implements the write8 operation. static void Rtl8139Write8(RTL8139_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 write16 operation. static void Rtl8139Write16(RTL8139_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 write32 operation. static void Rtl8139Write32(RTL8139_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 reset operation. static KSTATUS Rtl8139Reset(RTL8139_DEVICE* r) { Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RST); for (uint32_t i = 0; i < 1000; i++) { if ((Rtl8139Read8(r, RTL8139_REG_CMD) & RTL8139_CMD_RST) == 0) return NTLSTATUS_SUCCESS; if (gRtl8139Ddk->Timer->Stall) gRtl8139Ddk->Timer->Stall(1000); } return NTLSTATUS_TIMEOUT; } // Returns mac. static void Rtl8139ReadMac(RTL8139_DEVICE* r) { for (uint32_t i = 0; i < 6; i++) r->NetDevice.HardwareAddress[i] = Rtl8139Read8(r, RTL8139_REG_IDR0 + i); r->NetDevice.HardwareAddressLength = 6; } // Stops bus mastering before DMA memory can be released. static void Rtl8139StopDma(RTL8139_DEVICE* r) { if (!r || !r->Started) return; r->Started = false; if (r->SurpriseRemoved) return; Rtl8139Write16(r, RTL8139_REG_IMR, 0); Rtl8139Write8(r, RTL8139_REG_CMD, 0); KSTATUS status = Rtl8139Reset(r); if (!KSUCCESS(status)) KePanic(KE_PANIC_CODE_DRIVER, "RTL8139: failed to stop DMA before release status=0x%x\n", status); } // Releases the common buffers and adapter owned by one RTL8139 device. static void Rtl8139ReleaseDma(RTL8139_DEVICE* r) { if (!r || !gRtl8139Ddk || !gRtl8139Ddk->Dma) return; gRtl8139Ddk->Dma->FreeCommonBuffer(r->TxDma); gRtl8139Ddk->Dma->FreeCommonBuffer(r->RxDma); r->TxDma = (IO_DMA_BUFFER*)0; r->RxDma = (IO_DMA_BUFFER*)0; r->TxBuffers = (uint8_t*)0; r->RxBuffer = (uint8_t*)0; r->TxDeviceAddress = 0; r->RxDeviceAddress = 0; gRtl8139Ddk->Dma->PutAdapter(r->DmaAdapter); r->DmaAdapter = (IO_DMA_ADAPTER*)0; } // Allocates the receive ring and transmit slots through the device DMA adapter. static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) { IO_DMA_REQUIREMENTS requirements; memset(&requirements, 0, sizeof(requirements)); requirements.MaximumDeviceAddress = 0xFFFFFFFFULL; requirements.Alignment = 4; requirements.MaximumSegmentLength = RTL8139_TX_BUF_SIZE; requirements.MaximumSegments = 1; KSTATUS status = gRtl8139Ddk->Dma->GetAdapter(r->Fdo, &requirements, &r->DmaAdapter); if (!KSUCCESS(status)) return status; status = gRtl8139Ddk->Dma->AllocateCommonBuffer(r->DmaAdapter, RTL8139_RX_ALLOC_SIZE, PAGE_SIZE, &r->RxDma); if (!KSUCCESS(status)) { Rtl8139ReleaseDma(r); return status; } status = gRtl8139Ddk->Dma->AllocateCommonBuffer(r->DmaAdapter, RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE, PAGE_SIZE, &r->TxDma); if (!KSUCCESS(status)) { Rtl8139ReleaseDma(r); return status; } r->RxBuffer = (uint8_t*)gRtl8139Ddk->Dma->GetCommonBufferAddress(r->RxDma); r->TxBuffers = (uint8_t*)gRtl8139Ddk->Dma->GetCommonBufferAddress(r->TxDma); r->RxDeviceAddress = gRtl8139Ddk->Dma->GetCommonBufferDeviceAddress(r->RxDma); r->TxDeviceAddress = gRtl8139Ddk->Dma->GetCommonBufferDeviceAddress(r->TxDma); DbgPrint("RTL8139: rx=0x%016llX tx=0x%016llX\n", (unsigned long long)r->RxDeviceAddress, (unsigned long long)r->TxDeviceAddress); return NTLSTATUS_SUCCESS; } // Implements the rx Read16 operation. static uint16_t Rtl8139RxRead16(RTL8139_DEVICE* r, uint32_t offset) { offset %= RTL8139_RX_RING_SIZE; uint16_t lo = r->RxBuffer[offset]; uint16_t hi = r->RxBuffer[(offset + 1u) % RTL8139_RX_RING_SIZE]; return (uint16_t)(lo | (hi << 8)); } // Implements the rx Copy operation. static void Rtl8139RxCopy(RTL8139_DEVICE* r, uint32_t offset, void* dst, uint32_t length) { uint8_t* out = (uint8_t*)dst; offset %= RTL8139_RX_RING_SIZE; uint32_t first = RTL8139_RX_RING_SIZE - offset; if (first > length) first = length; memcpy(out, r->RxBuffer + offset, first); if (first < length) memcpy(out + first, r->RxBuffer, length - first); } // Implements the program Hardware operation. static void Rtl8139ProgramHardware(RTL8139_DEVICE* r) { // Wake the device and clear stale interrupts before installing buffers. Rtl8139Write8(r, RTL8139_REG_CONFIG1, 0x00); Rtl8139Write16(r, RTL8139_REG_IMR, 0); Rtl8139Write16(r, RTL8139_REG_ISR, 0xFFFFu); Rtl8139Write32(r, RTL8139_REG_RBSTART, (uint32_t)r->RxDeviceAddress); r->RxOffset = 0; Rtl8139Write16(r, RTL8139_REG_CAPR, 0xFFF0u); // CAPR is rxOffset - 16. for (uint32_t i = 0; i < RTL8139_TX_SLOTS; i++) { uint64_t address = r->TxDeviceAddress + (uint64_t)i * RTL8139_TX_BUF_SIZE; Rtl8139Write32(r, RTL8139_REG_TSAD(i), (uint32_t)address); } Rtl8139Write32(r, RTL8139_REG_TCR, RTL8139_TCR_IFG_NORMAL | RTL8139_TCR_MXDMA_UNLIMITED); Rtl8139Write32(r, RTL8139_REG_RCR, RTL8139_RCR_APM | RTL8139_RCR_AB | RTL8139_RCR_AM | RTL8139_RCR_WRAP | RTL8139_RCR_RBLEN_8K | RTL8139_RCR_MXDMA_UNLIMITED); Rtl8139MemoryBarrier(); Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RE | RTL8139_CMD_TE); Rtl8139Write16(r, RTL8139_REG_ISR, 0xFFFFu); Rtl8139Write16(r, RTL8139_REG_IMR, RTL8139_INTR_ENABLES); } // Handles rx. static void Rtl8139HandleRx(RTL8139_DEVICE* r) { for (uint32_t budget = 0; budget < 64; budget++) { uint8_t cmd = Rtl8139Read8(r, RTL8139_REG_CMD); if (cmd & RTL8139_CMD_BUFE) break; uint32_t off = r->RxOffset % RTL8139_RX_RING_SIZE; uint16_t status = Rtl8139RxRead16(r, off); uint16_t packetLength = Rtl8139RxRead16(r, off + 2u); if (packetLength < 4 || packetLength > RTL8139_RX_WIRE_MAX) { DbgPrint("RTL8139: bad rx length %u status=0x%04x, resetting rx pointer\n", packetLength, status); r->RxOffset = Rtl8139Read16(r, RTL8139_REG_CBR) % RTL8139_RX_RING_SIZE; Rtl8139Write16(r, RTL8139_REG_CAPR, (uint16_t)(r->RxOffset - 16u)); break; } uint32_t frameLength = packetLength; if (frameLength > 4) frameLength -= 4; // Strip Ethernet FCS before upper-layer handoff. bool goodFrame = (status & RTL8139_RX_OK) && !(status & RTL8139_RX_BAD_BITS) && frameLength >= NET_ETHERNET_HEADER_LENGTH && frameLength <= NET_ETHERNET_MAX_FRAME; if (goodFrame) { if ((off + 4u + frameLength) <= RTL8139_RX_RING_SIZE) { NetReceiveFrame(&r->NetDevice, r->RxBuffer + off + 4u, frameLength); } else { Rtl8139RxCopy(r, off + 4u, r->RxScratch, frameLength); NetReceiveFrame(&r->NetDevice, r->RxScratch, frameLength); } } else { DbgPrint("RTL8139: rx error status=0x%04x len=%u\n", status, packetLength); } r->RxOffset = (r->RxOffset + 4u + packetLength + 3u) & ~3u; r->RxOffset %= RTL8139_RX_RING_SIZE; Rtl8139MemoryBarrier(); Rtl8139Write16(r, RTL8139_REG_CAPR, (uint16_t)(r->RxOffset - 16u)); } } // Implements the dpc Routine operation. static void Rtl8139DpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { (void)dpc; (void)systemArgument1; (void)systemArgument2; RTL8139_DEVICE* r = (RTL8139_DEVICE*)context; uint16_t status = (uint16_t)r->PendingIsr; r->PendingIsr = 0; if (status & (RTL8139_ISR_ROK | RTL8139_ISR_RER | RTL8139_ISR_RXOVW | RTL8139_ISR_FOVW)) Rtl8139HandleRx(r); if (status & (RTL8139_ISR_PUN | RTL8139_ISR_LENCHG)) DbgPrint("RTL8139: link/status change isr=0x%04x\n", status); if (status & (RTL8139_ISR_RER | RTL8139_ISR_TER | RTL8139_ISR_RXOVW | RTL8139_ISR_FOVW | RTL8139_ISR_SYSERR)) DbgPrint("RTL8139: interrupt error bits=0x%04x\n", status); } // Registers device. static KSTATUS Rtl8139RegisterDevice(RTL8139_DEVICE* r) { if (!r) return NTLSTATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock); for (uint32_t i = 0; i < gRtl8139DeviceCount; i++) { if (gRtl8139Devices[i] == r) { KeReleaseSpinlock(&gRtl8139RegistryLock, flags); return NTLSTATUS_SUCCESS; } } if (gRtl8139DeviceCount >= RTL8139_MAX_DEVICES) { KeReleaseSpinlock(&gRtl8139RegistryLock, flags); return NTLSTATUS_NO_MEMORY; } gRtl8139Devices[gRtl8139DeviceCount++] = r; KeReleaseSpinlock(&gRtl8139RegistryLock, flags); return NTLSTATUS_SUCCESS; } // Implements the unregister Device operation. static void Rtl8139UnregisterDevice(RTL8139_DEVICE* r) { if (!r) return; uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock); for (uint32_t i = 0; i < gRtl8139DeviceCount; i++) { if (gRtl8139Devices[i] == r) { gRtl8139Devices[i] = gRtl8139Devices[gRtl8139DeviceCount - 1u]; gRtl8139Devices[gRtl8139DeviceCount - 1u] = (RTL8139_DEVICE*)0; gRtl8139DeviceCount--; break; } } KeReleaseSpinlock(&gRtl8139RegistryLock, flags); } // Implements the isr operation. static void Rtl8139Isr(void) { RTL8139_DEVICE* snapshot[RTL8139_MAX_DEVICES]; uint32_t count = 0; uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock); count = gRtl8139DeviceCount; if (count > RTL8139_MAX_DEVICES) count = RTL8139_MAX_DEVICES; for (uint32_t i = 0; i < count; i++) snapshot[i] = gRtl8139Devices[i]; KeReleaseSpinlock(&gRtl8139RegistryLock, flags); for (uint32_t i = 0; i < count; i++) { RTL8139_DEVICE* r = snapshot[i]; if (!r || !r->Started || r->Removed || r->SurpriseRemoved) continue; uint16_t status = Rtl8139Read16(r, RTL8139_REG_ISR); status &= RTL8139_INTR_ENABLES; if (!status || status == 0xFFFFu) continue; Rtl8139Write16(r, RTL8139_REG_ISR, status); r->PendingIsr |= status; KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0); } } // Implements the send Frame operation. static KSTATUS Rtl8139SendFrame(NET_DEVICE* device, const void* frame, size_t length) { if (!device || !frame) return NTLSTATUS_INVALID_PARAMETER; if (length > NET_ETHERNET_MAX_FRAME) return NTLSTATUS_BUFFER_TOO_SMALL; if (length < NET_ETHERNET_HEADER_LENGTH) return NTLSTATUS_INVALID_PARAMETER; RTL8139_DEVICE* r = (RTL8139_DEVICE*)device->DriverContext; if (!r) return NTLSTATUS_INVALID_PARAMETER; uintptr_t flags = KeAcquireSpinlock(&r->TxLock); uint32_t slot = r->TxSlot; uint32_t tsd = Rtl8139Read32(r, RTL8139_REG_TSD(slot)); // RTL8139 TSD.OWN is 1 when the descriptor is idle/free. // Writing a command with OWN cleared hands the slot to the NIC. if (!(tsd & RTL8139_TSD_OWN)) { KeReleaseSpinlock(&r->TxLock, flags); return NTLSTATUS_QUEUE_FULL; } size_t wireLength = length < NET_ETHERNET_MIN_FRAME ? NET_ETHERNET_MIN_FRAME : length; uint8_t* buf = r->TxBuffers + slot * RTL8139_TX_BUF_SIZE; memcpy(buf, frame, length); if (wireLength > length) memset(buf + length, 0, wireLength - length); Rtl8139MemoryBarrier(); Rtl8139Write32(r, RTL8139_REG_TSD(slot), (uint32_t)wireLength & RTL8139_TSD_SIZE_MASK); r->TxSlot = (slot + 1u) % RTL8139_TX_SLOTS; KeReleaseSpinlock(&r->TxLock, flags); return NTLSTATUS_SUCCESS; } // Implements the select Register Bar operation. static KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r) { if (!r || !r->Pdo) return NTLSTATUS_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_CACHE_UC, (void**)&r->Mmio, &r->RegisterBytes); if (!KSUCCESS(st)) return st; r->RegisterBar = best; r->UseMmio = true; DbgPrint("RTL8139: 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 NTLSTATUS_SUCCESS; } if (best.Type == PciBarIo) { if (best.Base > 0xFFFFu) return NTLSTATUS_NOT_SUPPORTED; r->RegisterBar = best; r->RegisterBytes = best.Length; r->IoBase = (uint16_t)best.Base; r->UseMmio = false; DbgPrint("RTL8139: BAR%u I/O base=0x%04x len=0x%llx\n", best.Index, r->IoBase, (unsigned long long)r->RegisterBytes); return NTLSTATUS_SUCCESS; } return NTLSTATUS_NOT_FOUND; } // Starts device. static KSTATUS Rtl8139StartDevice(RTL8139_DEVICE* r) { if (!r || r->Magic != RTL8139_DEVICE_MAGIC || !r->Pdo || !r->Fdo) return NTLSTATUS_INVALID_PARAMETER; if (r->Started) return NTLSTATUS_SUCCESS; KSTATUS st = PciGetDeviceInfo(r->Pdo, &r->PciInfo); if (!KSUCCESS(st)) { DbgPrint("RTL8139: PciGetDeviceInfo failed: 0x%x\n", st); return st; } DbgPrint("RTL8139: 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 = Rtl8139SelectRegisterBar(r); if (!KSUCCESS(st)) { DbgPrint("RTL8139: 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("RTL8139: PciEnableDevice failed: 0x%x\n", st); return st; } st = PciGetInterrupt(r->Pdo, &r->Irq); if (!KSUCCESS(st) || r->Irq == 0xFFu) { DbgPrint("RTL8139: no legacy PCI IRQ line wired\n"); return KSUCCESS(st) ? NTLSTATUS_NOT_SUPPORTED : st; } Rtl8139Write8(r, RTL8139_REG_CONFIG1, 0x00); st = Rtl8139Reset(r); if (!KSUCCESS(st)) { DbgPrint("RTL8139: reset timed out\n"); return st; } Rtl8139ReadMac(r); DbgPrint("RTL8139: 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 = Rtl8139AllocateDma(r); if (!KSUCCESS(st)) return st; KeInitializeDpc(&r->Dpc, Rtl8139DpcRoutine, r); r->NetDevice.Mtu = NET_MTU_ETHERNET; r->NetDevice.DriverContext = r; r->NetDevice.SendFrame = Rtl8139SendFrame; st = Rtl8139RegisterDevice(r); if (!KSUCCESS(st)) { Rtl8139ReleaseDma(r); return st; } KeIrqRegister(r->Irq, Rtl8139Isr); r->Started = true; Rtl8139ProgramHardware(r); r->Fdo->PnpState = DevicePnpStarted; st = NetRegisterDevice(&r->NetDevice); if (!KSUCCESS(st)) { Rtl8139StopDma(r); Rtl8139UnregisterDevice(r); Rtl8139ReleaseDma(r); return st; } DbgPrint("RTL8139: initialized; RX/TX enabled on IRQ %u\n", r->Irq); return NTLSTATUS_SUCCESS; } // Dispatches pnp. KSTATUS Rtl8139DispatchPnp(DEVICE_OBJECT* device, IRP* irp) { if (!device || !irp || !device->DeviceExtension) return NTLSTATUS_INVALID_PARAMETER; RTL8139_DEVICE* r = (RTL8139_DEVICE*)device->DeviceExtension; if (r->Magic != RTL8139_DEVICE_MAGIC) return NTLSTATUS_INVALID_PARAMETER; IO_STACK_LOCATION* ios = IoGetCurrentIrpStackLocation(irp); if (!ios || ios->MajorFunction != IRP_MJ_PNP) return NTLSTATUS_INVALID_PARAMETER; switch ((IO_PNP_MINOR_FUNCTION)ios->MinorFunction) { case IRP_MN_START_DEVICE: return Rtl8139StartDevice(r); case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE: return NTLSTATUS_SUCCESS; case IRP_MN_SURPRISE_REMOVAL: r->SurpriseRemoved = true; r->Started = false; device->PnpState = DevicePnpSurpriseRemoved; return NTLSTATUS_SUCCESS; case IRP_MN_REMOVE_DEVICE: r->Removed = true; device->PnpState = DevicePnpDeleted; Rtl8139StopDma(r); Rtl8139UnregisterDevice(r); Rtl8139ReleaseDma(r); IoDetachDevice(device); IoDeleteDevice(device); return NTLSTATUS_SUCCESS; default: return IoForwardIrpToLower(device, irp); } } // Implements the add Device operation. KSTATUS Rtl8139AddDevice(DRIVER_OBJECT* driver, DEVICE_OBJECT* physicalDeviceObject) { if (!driver || !physicalDeviceObject || physicalDeviceObject->Type != DeviceObjectPhysical) return NTLSTATUS_INVALID_PARAMETER; DEVICE_OBJECT* fdo = (DEVICE_OBJECT*)0; KSTATUS st = IoCreateDevice(driver, sizeof(RTL8139_DEVICE), &fdo); if (!KSUCCESS(st)) return st; (void)IoSetDeviceName(fdo, SYS_SV_LITERAL("rtl8139")); RTL8139_DEVICE* r = (RTL8139_DEVICE*)fdo->DeviceExtension; memset(r, 0, sizeof(*r)); r->Magic = RTL8139_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 NTLSTATUS_FAILURE; } DbgPrint("RTL8139: FDO attached to %v\n", physicalDeviceObject->Name); return NTLSTATUS_SUCCESS; } // Implements the driver Entry operation. KSTATUS DriverEntry(const NTL_DDK* ddk, NTL_DRIVER_INIT* init) { (void)init; if (!ddk || !ddk->Dma || !ddk->Timer) return NTLSTATUS_INVALID_PARAMETER; DbgPrint("RTL8139: driver loading...\n"); gRtl8139Ddk = ddk; gRtl8139DeviceCount = 0; memset(&gRtl8139Driver, 0, sizeof(gRtl8139Driver)); IoInitializeDriverObject(&gRtl8139Driver, SYS_SV_LITERAL("rtl8139")); gRtl8139Driver.IdTable = &gRtl8139IdTable; gRtl8139Driver.DriverAddDevice = Rtl8139AddDevice; gRtl8139Driver.MajorFunction[IRP_MJ_PNP] = Rtl8139DispatchPnp; KSTATUS st = IoRegisterDriverObject(&gRtl8139Driver); if (!KSUCCESS(st)) DbgPrint("RTL8139: IoRegisterDriverObject failed: 0x%x\n", st); return st; }