176 lines
5.9 KiB
C
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);
|
|
}
|