579 lines
19 KiB
C
579 lines
19 KiB
C
// Realtek RTL8139 network driver. This file owns PCI attachment, MMIO setup, RX/TX rings, and net-device registration.
|
|
|
|
#include "rtl8139.h"
|
|
|
|
const HAL_API* gHal;
|
|
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 STATUS_SUCCESS;
|
|
if (gHal->Timer.Stall)
|
|
gHal->Timer.Stall(1000);
|
|
}
|
|
return STATUS_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;
|
|
}
|
|
|
|
// Creates dma.
|
|
static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
|
r->RxPhys = MmAllocateContiguous(RTL8139_RX_PAGES);
|
|
r->TxPhys = MmAllocateContiguous(RTL8139_TX_PAGES);
|
|
if (!r->RxPhys || !r->TxPhys)
|
|
return STATUS_NO_MEMORY;
|
|
|
|
// RTL8139 has 32-bit DMA address registers. This is fine for typical QEMU
|
|
// and low-memory hobby OS configs, but make failure explicit.
|
|
if ((r->RxPhys + RTL8139_RX_ALLOC_SIZE - 1) > 0xFFFFFFFFULL ||
|
|
(r->TxPhys + RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE - 1) > 0xFFFFFFFFULL) {
|
|
DbgPrint("RTL8139: DMA allocation above 4GiB is not supported by this NIC\n");
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
r->RxBuffer = (uint8_t*)gHal->PhysToVirt(r->RxPhys);
|
|
r->TxBuffers = (uint8_t*)gHal->PhysToVirt(r->TxPhys);
|
|
memset(r->RxBuffer, 0, PAGE_SIZE * RTL8139_RX_PAGES);
|
|
memset(r->TxBuffers, 0, PAGE_SIZE * RTL8139_TX_PAGES);
|
|
|
|
DbgPrint("RTL8139: rx=0x%016llX tx=0x%016llX\n",
|
|
(unsigned long long)r->RxPhys,
|
|
(unsigned long long)r->TxPhys);
|
|
return STATUS_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->RxPhys);
|
|
r->RxOffset = 0;
|
|
Rtl8139Write16(r, RTL8139_REG_CAPR, 0xFFF0u); // CAPR is rxOffset - 16.
|
|
|
|
for (uint32_t i = 0; i < RTL8139_TX_SLOTS; i++) {
|
|
PHYSICAL_ADDRESS p = r->TxPhys + (PHYSICAL_ADDRESS)i * RTL8139_TX_BUF_SIZE;
|
|
Rtl8139Write32(r, RTL8139_REG_TSAD(i), (uint32_t)p);
|
|
}
|
|
|
|
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 STATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gRtl8139RegistryLock);
|
|
for (uint32_t i = 0; i < gRtl8139DeviceCount; i++) {
|
|
if (gRtl8139Devices[i] == r) {
|
|
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (gRtl8139DeviceCount >= RTL8139_MAX_DEVICES) {
|
|
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
gRtl8139Devices[gRtl8139DeviceCount++] = r;
|
|
KeReleaseSpinlock(&gRtl8139RegistryLock, flags);
|
|
return STATUS_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 STATUS_INVALID_PARAMETER;
|
|
if (length > NET_ETHERNET_MAX_FRAME)
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
if (length < NET_ETHERNET_HEADER_LENGTH)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
RTL8139_DEVICE* r = (RTL8139_DEVICE*)device->DriverContext;
|
|
if (!r)
|
|
return STATUS_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 STATUS_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 STATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the select Register Bar operation.
|
|
static KSTATUS Rtl8139SelectRegisterBar(RTL8139_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("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 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("RTL8139: 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;
|
|
}
|
|
|
|
// Starts device.
|
|
static KSTATUS Rtl8139StartDevice(RTL8139_DEVICE* r) {
|
|
if (!r || r->Magic != RTL8139_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("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) ? STATUS_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))
|
|
return st;
|
|
|
|
KeIrqRegister(r->Irq, Rtl8139Isr);
|
|
r->Started = true;
|
|
Rtl8139ProgramHardware(r);
|
|
r->Fdo->PnpState = DevicePnpStarted;
|
|
|
|
st = NetRegisterDevice(&r->NetDevice);
|
|
if (!KSUCCESS(st)) {
|
|
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
|
|
r->Started = false;
|
|
Rtl8139UnregisterDevice(r);
|
|
return st;
|
|
}
|
|
|
|
DbgPrint("RTL8139: initialized; RX/TX enabled on IRQ %u\n", r->Irq);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Dispatches pnp.
|
|
KSTATUS Rtl8139DispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
|
if (!device || !irp || !device->DeviceExtension)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
RTL8139_DEVICE* r = (RTL8139_DEVICE*)device->DeviceExtension;
|
|
if (r->Magic != RTL8139_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 Rtl8139StartDevice(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)
|
|
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
|
|
return STATUS_SUCCESS;
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
r->Removed = true;
|
|
device->PnpState = DevicePnpDeleted;
|
|
if (r->Started)
|
|
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
|
|
Rtl8139UnregisterDevice(r);
|
|
IoDetachDevice(device);
|
|
IoDeleteDevice(device);
|
|
return STATUS_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 STATUS_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 STATUS_FAILURE;
|
|
}
|
|
|
|
DbgPrint("RTL8139: 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("RTL8139: driver loading...\n");
|
|
gHal = hal;
|
|
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;
|
|
}
|