Files
nt-lite/code/kernel/net_device.c
T

176 lines
5.9 KiB
C

// net_device.c - minimal network-device registry and RX handoff.
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <net_device.h>
#include <ethernet.h>
#include <ke_debug.h>
#include <ke_spinlock.h>
#define NET_MAX_DEVICES 8u
#define NET_RX_LOG_LIMIT 16u
static NET_DEVICE* gNetDevices[NET_MAX_DEVICES];
static uint32_t gNetDeviceCount = 0;
static SPINLOCK gNetLock = SPINLOCK_INIT;
static void NetMakeDefaultName(NET_DEVICE* device, uint32_t index) {
if (!device || device->Name[0])
return;
// The registry is currently capped at eight devices, so ethN is enough and
// keeps the name stable for early boot diagnostics.
device->Name[0] = 'e';
device->Name[1] = 't';
device->Name[2] = 'h';
device->Name[3] = (char)('0' + (index % 10u));
device->Name[4] = '\0';
}
static bool NetIsDeviceRegisteredLocked(const NET_DEVICE* device) {
for (uint32_t i = 0; i < gNetDeviceCount; ++i) {
if (gNetDevices[i] == device)
return true;
}
return false;
}
static bool NetDeviceHasEthernetAddress(const NET_DEVICE* device) {
return device && device->HardwareAddressLength == NET_ETHERNET_ADDRESS_LENGTH;
}
static void NetLogHardwareAddress(const NET_DEVICE* device) {
if (NetDeviceHasEthernetAddress(device)) {
DbgPrint("%02x:%02x:%02x:%02x:%02x:%02x",
device->HardwareAddress[0], device->HardwareAddress[1],
device->HardwareAddress[2], device->HardwareAddress[3],
device->HardwareAddress[4], device->HardwareAddress[5]);
} else {
DbgPrint("hwlen=%u", (unsigned)device->HardwareAddressLength);
}
}
__declspec(dllexport) KSTATUS NetRegisterDevice(NET_DEVICE* device) {
if (!device || !device->SendFrame)
return STATUS_INVALID_PARAMETER;
if (device->HardwareAddressLength > NET_HWADDR_MAX)
return STATUS_INVALID_PARAMETER;
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
if (NetIsDeviceRegisteredLocked(device)) {
KeReleaseSpinlock(&gNetLock, flags);
return STATUS_ALREADY_EXISTS;
}
if (gNetDeviceCount >= NET_MAX_DEVICES) {
KeReleaseSpinlock(&gNetLock, flags);
return STATUS_QUEUE_FULL;
}
uint32_t index = gNetDeviceCount;
device->Index = index;
if (device->Mtu == 0)
device->Mtu = NET_MTU_ETHERNET;
memset(&device->Stats, 0, sizeof(device->Stats));
NetMakeDefaultName(device, index);
gNetDevices[index] = device;
gNetDeviceCount++;
KeReleaseSpinlock(&gNetLock, flags);
DbgPrint("NET: registered %s index=%u mac=", device->Name, device->Index);
NetLogHardwareAddress(device);
DbgPrint(" mtu=%u\n", device->Mtu);
NetEthernetAttachDevice(device);
return STATUS_SUCCESS;
}
__declspec(dllexport) uint32_t NetGetDeviceCount(void) {
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
uint32_t count = gNetDeviceCount;
KeReleaseSpinlock(&gNetLock, flags);
return count;
}
__declspec(dllexport) NET_DEVICE* NetGetDevice(uint32_t index) {
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
NET_DEVICE* device = (index < gNetDeviceCount) ? gNetDevices[index] : (NET_DEVICE*)0;
KeReleaseSpinlock(&gNetLock, flags);
return device;
}
__declspec(dllexport) KSTATUS NetGetDeviceStats(uint32_t index,
NET_DEVICE_STATS* stats) {
if (!stats)
return STATUS_INVALID_PARAMETER;
NET_DEVICE* device = NetGetDevice(index);
if (!device)
return STATUS_NOT_FOUND;
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
memcpy(stats, &device->Stats, sizeof(*stats));
KeReleaseSpinlock(&gNetLock, flags);
return STATUS_SUCCESS;
}
__declspec(dllexport) KSTATUS NetSendFrame(uint32_t index,
const void* frame,
size_t length) {
NET_DEVICE* device = NetGetDevice(index);
if (!device || !device->SendFrame)
return STATUS_NOT_FOUND;
if (!frame || length < NET_ETHERNET_HEADER_LENGTH)
return STATUS_INVALID_PARAMETER;
if (length > (size_t)device->Mtu + NET_ETHERNET_HEADER_LENGTH)
return STATUS_BUFFER_TOO_SMALL;
KSTATUS st = device->SendFrame(device, frame, length);
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
if (KSUCCESS(st)) {
device->Stats.TxPackets++;
device->Stats.TxBytes += length;
} else {
device->Stats.TxErrors++;
}
KeReleaseSpinlock(&gNetLock, flags);
return st;
}
__declspec(dllexport) void NetReceiveFrame(NET_DEVICE* device,
const void* frame,
size_t length) {
static volatile uint32_t sLoggedFrames = 0;
if (!device || !frame || length < NET_ETHERNET_HEADER_LENGTH) {
if (device) {
uintptr_t dropFlags = KeAcquireSpinlock(&gNetLock);
device->Stats.RxDrops++;
KeReleaseSpinlock(&gNetLock, dropFlags);
}
return;
}
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
device->Stats.RxPackets++;
device->Stats.RxBytes += length;
KeReleaseSpinlock(&gNetLock, flags);
// Temporary observability until lwIP is attached. Avoid spamming the
// console on busy LANs. Log before protocol handling so RX/TX ordering is
// easier to read when an interrupt fires during a send path.
if (sLoggedFrames < NET_RX_LOG_LIMIT) {
const uint8_t* b = (const uint8_t*)frame;
uint16_t etherType = ((uint16_t)b[12] << 8) | b[13];
DbgPrint("NET: %s rx len=%u ethertype=0x%04x dst=%02x:%02x:%02x:%02x:%02x:%02x src=%02x:%02x:%02x:%02x:%02x:%02x\n",
device->Name,
(unsigned)length,
etherType,
b[0], b[1], b[2], b[3], b[4], b[5],
b[6], b[7], b[8], b[9], b[10], b[11]);
sLoggedFrames++;
}
NetEthernetReceive(device, frame, length);
}