686 lines
23 KiB
C
686 lines
23 KiB
C
#include <ntlite/ddk/module.h>
|
|
// Realtek RTL816x network driver. This file owns PCI attachment, descriptor rings, and net-device registration.
|
|
|
|
#include "rtl816x.h"
|
|
|
|
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 NTLSTATUS_SUCCESS;
|
|
KeStallExecution(1000);
|
|
}
|
|
return NTLSTATUS_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;
|
|
}
|
|
|
|
// Stops bus mastering before DMA memory can be released.
|
|
static void RtlStopDma(RTL816X_DEVICE* r) {
|
|
if (!r || !r->Started)
|
|
return;
|
|
r->Started = false;
|
|
if (r->SurpriseRemoved)
|
|
return;
|
|
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
|
|
RtlWrite8(r, RTL_REG_CHIP_CMD, 0);
|
|
KSTATUS status = RtlReset(r);
|
|
if (!KSUCCESS(status))
|
|
KePanic(KE_PANIC_CODE_DRIVER,
|
|
"RTL816X: failed to stop DMA before release status=0x%x\n",
|
|
status);
|
|
}
|
|
|
|
// Releases the common buffers and adapter owned by one RTL816x device.
|
|
static void RtlReleaseDma(RTL816X_DEVICE* r) {
|
|
if (!r)
|
|
return;
|
|
IoDmaFreeCommonBuffer(r->TxBuffersDma);
|
|
IoDmaFreeCommonBuffer(r->RxBuffersDma);
|
|
IoDmaFreeCommonBuffer(r->TxRingDma);
|
|
IoDmaFreeCommonBuffer(r->RxRingDma);
|
|
r->TxBuffersDma = (IO_DMA_BUFFER*)0;
|
|
r->RxBuffersDma = (IO_DMA_BUFFER*)0;
|
|
r->TxRingDma = (IO_DMA_BUFFER*)0;
|
|
r->RxRingDma = (IO_DMA_BUFFER*)0;
|
|
r->TxBuffers = (uint8_t*)0;
|
|
r->RxBuffers = (uint8_t*)0;
|
|
r->TxRing = (RTL_DESC*)0;
|
|
r->RxRing = (RTL_DESC*)0;
|
|
r->TxBuffersDeviceAddress = 0;
|
|
r->RxBuffersDeviceAddress = 0;
|
|
r->TxRingDeviceAddress = 0;
|
|
r->RxRingDeviceAddress = 0;
|
|
IoDmaPutAdapter(r->DmaAdapter);
|
|
r->DmaAdapter = (IO_DMA_ADAPTER*)0;
|
|
}
|
|
|
|
// Allocates descriptor rings and packet buffers through the device DMA adapter.
|
|
static KSTATUS RtlAllocateDma(RTL816X_DEVICE* r) {
|
|
IO_DMA_REQUIREMENTS requirements;
|
|
memset(&requirements, 0, sizeof(requirements));
|
|
requirements.MaximumDeviceAddress = (uint64_t)-1;
|
|
requirements.Alignment = 1;
|
|
requirements.MaximumSegmentLength = RTL_BUF_SIZE;
|
|
requirements.MaximumSegments = RTL_TX_COUNT;
|
|
|
|
KSTATUS status = IoDmaGetAdapter(r->Fdo,
|
|
&requirements,
|
|
&r->DmaAdapter);
|
|
if (!KSUCCESS(status))
|
|
return status;
|
|
|
|
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
|
PAGE_SIZE * RTL_RING_PAGES,
|
|
PAGE_SIZE,
|
|
&r->RxRingDma);
|
|
if (!KSUCCESS(status)) goto fail;
|
|
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
|
PAGE_SIZE * RTL_RING_PAGES,
|
|
PAGE_SIZE,
|
|
&r->TxRingDma);
|
|
if (!KSUCCESS(status)) goto fail;
|
|
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
|
PAGE_SIZE * RTL_RXBUF_PAGES,
|
|
PAGE_SIZE,
|
|
&r->RxBuffersDma);
|
|
if (!KSUCCESS(status)) goto fail;
|
|
status = IoDmaAllocateCommonBuffer(r->DmaAdapter,
|
|
PAGE_SIZE * RTL_TXBUF_PAGES,
|
|
PAGE_SIZE,
|
|
&r->TxBuffersDma);
|
|
if (!KSUCCESS(status)) goto fail;
|
|
|
|
r->RxRing = (RTL_DESC*)IoDmaGetCommonBufferAddress(r->RxRingDma);
|
|
r->TxRing = (RTL_DESC*)IoDmaGetCommonBufferAddress(r->TxRingDma);
|
|
r->RxBuffers = (uint8_t*)IoDmaGetCommonBufferAddress(r->RxBuffersDma);
|
|
r->TxBuffers = (uint8_t*)IoDmaGetCommonBufferAddress(r->TxBuffersDma);
|
|
r->RxRingDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->RxRingDma);
|
|
r->TxRingDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->TxRingDma);
|
|
r->RxBuffersDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->RxBuffersDma);
|
|
r->TxBuffersDeviceAddress = IoDmaGetCommonBufferDeviceAddress(r->TxBuffersDma);
|
|
|
|
DbgPrint("RTL816X: rxRing=0x%016llX txRing=0x%016llX rxBuf=0x%016llX txBuf=0x%016llX\n",
|
|
(unsigned long long)r->RxRingDeviceAddress,
|
|
(unsigned long long)r->TxRingDeviceAddress,
|
|
(unsigned long long)r->RxBuffersDeviceAddress,
|
|
(unsigned long long)r->TxBuffersDeviceAddress);
|
|
return NTLSTATUS_SUCCESS;
|
|
|
|
fail:
|
|
RtlReleaseDma(r);
|
|
return status;
|
|
}
|
|
|
|
// Implements the rtl Init Descriptors operation.
|
|
static void RtlInitDescriptors(RTL816X_DEVICE* r) {
|
|
for (uint32_t i = 0; i < RTL_RX_COUNT; i++) {
|
|
uint64_t address = r->RxBuffersDeviceAddress + (uint64_t)i * RTL_BUF_SIZE;
|
|
r->RxRing[i].addrLo = (uint32_t)address;
|
|
r->RxRing[i].addrHi = (uint32_t)(address >> 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++) {
|
|
uint64_t address = r->TxBuffersDeviceAddress + (uint64_t)i * RTL_BUF_SIZE;
|
|
r->TxRing[i].addrLo = (uint32_t)address;
|
|
r->TxRing[i].addrHi = (uint32_t)(address >> 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->TxRingDeviceAddress);
|
|
RtlWrite32(r, RTL_REG_TNPDS_HI, (uint32_t)(r->TxRingDeviceAddress >> 32));
|
|
RtlWrite32(r, RTL_REG_THPDS_LO, (uint32_t)r->TxRingDeviceAddress);
|
|
RtlWrite32(r, RTL_REG_THPDS_HI, (uint32_t)(r->TxRingDeviceAddress >> 32));
|
|
RtlWrite32(r, RTL_REG_RDSAR_LO, (uint32_t)r->RxRingDeviceAddress);
|
|
RtlWrite32(r, RTL_REG_RDSAR_HI, (uint32_t)(r->RxRingDeviceAddress >> 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 NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uintptr_t flags = KeAcquireSpinlock(&gRtl816xRegistryLock);
|
|
for (uint32_t i = 0; i < gRtl816xDeviceCount; i++) {
|
|
if (gRtl816xDevices[i] == r) {
|
|
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (gRtl816xDeviceCount >= RTL816X_MAX_DEVICES) {
|
|
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
|
|
return NTLSTATUS_NO_MEMORY;
|
|
}
|
|
|
|
gRtl816xDevices[gRtl816xDeviceCount++] = r;
|
|
KeReleaseSpinlock(&gRtl816xRegistryLock, flags);
|
|
return NTLSTATUS_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 NTLSTATUS_INVALID_PARAMETER;
|
|
if (length > NET_ETHERNET_MAX_FRAME)
|
|
return NTLSTATUS_BUFFER_TOO_SMALL;
|
|
if (length < NET_ETHERNET_HEADER_LENGTH)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DriverContext;
|
|
if (!r || !r->Started || r->Removed || r->SurpriseRemoved)
|
|
return NTLSTATUS_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 NTLSTATUS_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 NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the rtl Select Register Bar operation.
|
|
static KSTATUS RtlSelectRegisterBar(RTL816X_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("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 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("RTL816X: 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;
|
|
}
|
|
|
|
// Configures a dedicated MSI vector and falls back to a routed INTx line.
|
|
static KSTATUS RtlConfigureInterrupt(RTL816X_DEVICE* r) {
|
|
if (!r || !r->Pdo)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
r->Irq = 0xFFu;
|
|
r->UsingMsi = false;
|
|
|
|
KSTATUS msiStatus = PciEnableMsiAuto(r->Pdo, &r->Irq);
|
|
if (KSUCCESS(msiStatus)) {
|
|
r->UsingMsi = true;
|
|
DbgPrint("RTL816X: using MSI IRQ slot %u\n", r->Irq);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
DbgPrint("RTL816X: MSI setup failed status=0x%x; attempting explicit INTx fallback\n",
|
|
msiStatus);
|
|
|
|
KSTATUS intxStatus = PciGetInterrupt(r->Pdo, &r->Irq);
|
|
if (!KSUCCESS(intxStatus))
|
|
return intxStatus;
|
|
if (r->Irq == 0xFFu || r->Irq >= 24u)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
|
|
intxStatus = PciEnableDevice(r->Pdo,
|
|
PCI_ENABLE_BUSMASTER |
|
|
PCI_ENABLE_INTX |
|
|
(r->UseMmio ? PCI_ENABLE_MEMORY : PCI_ENABLE_IO));
|
|
if (!KSUCCESS(intxStatus))
|
|
return intxStatus;
|
|
|
|
DbgPrint("RTL816X: using legacy INTx IRQ %u\n", r->Irq);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the rtl Start Device operation.
|
|
static KSTATUS RtlStartDevice(RTL816X_DEVICE* r) {
|
|
if (!r || r->Magic != RTL816X_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("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_DISABLE_INTX |
|
|
(r->UseMmio ? PCI_ENABLE_MEMORY : PCI_ENABLE_IO));
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("RTL816X: PciEnableDevice failed: 0x%x\n", st);
|
|
return st;
|
|
}
|
|
|
|
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
|
|
RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu);
|
|
|
|
st = RtlReset(r);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("RTL816X: reset timed out\n");
|
|
return st;
|
|
}
|
|
|
|
st = RtlConfigureInterrupt(r);
|
|
if (!KSUCCESS(st)) {
|
|
DbgPrint("RTL816X: no usable MSI or INTx interrupt status=0x%x\n", st);
|
|
return st;
|
|
}
|
|
|
|
RtlReadMac(r);
|
|
DbgPrint("RTL816X: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u mode=%s\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, r->UsingMsi ? "MSI" : "INTx");
|
|
|
|
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)) {
|
|
RtlReleaseDma(r);
|
|
return st;
|
|
}
|
|
|
|
KeIrqRegister(r->Irq, RtlIsr);
|
|
r->Started = true;
|
|
RtlProgramHardware(r);
|
|
r->Fdo->PnpState = DevicePnpStarted;
|
|
|
|
st = NetRegisterDevice(&r->NetDevice);
|
|
if (!KSUCCESS(st)) {
|
|
RtlStopDma(r);
|
|
RtlUnregisterDevice(r);
|
|
RtlReleaseDma(r);
|
|
return st;
|
|
}
|
|
|
|
DbgPrint("RTL816X: initialized; RX/TX enabled on %s IRQ %u\n",
|
|
r->UsingMsi ? "MSI" : "INTx", r->Irq);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Dispatches pnp.
|
|
KSTATUS Rtl816xDispatchPnp(DEVICE_OBJECT* device, IRP* irp) {
|
|
if (!device || !irp || !device->DeviceExtension)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DeviceExtension;
|
|
if (r->Magic != RTL816X_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 RtlStartDevice(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;
|
|
RtlStopDma(r);
|
|
RtlUnregisterDevice(r);
|
|
RtlReleaseDma(r);
|
|
IoDetachDevice(device);
|
|
IoDeleteDevice(device);
|
|
return NTLSTATUS_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 NTLSTATUS_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 NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
DbgPrint("RTL816X: FDO attached to %v\n", physicalDeviceObject->Name);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
static KSTATUS DriverInitialize(NTL_MODULE* module) {
|
|
(void)module;
|
|
|
|
DbgPrint("RTL816X: driver loading...\n");
|
|
|
|
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;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
static DRIVER_OBJECT* const gModuleIoDrivers[] = { &gRtl816xDriver };
|
|
NTL_DECLARE_IO_MODULE(DriverInitialize, gModuleIoDrivers);
|