// net_device.c - minimal network-device registry and RX handoff. #include #include #include #include #include #include #include #include #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); }