Implement Ethernet subsystem with unified NIC abstraction (AI)

- Created src/ethernet.h: eth_iface_t interface struct, Ethernet header
  type, htons/ntohs/htonl/ntohl byte-order helpers, EtherType constants,
  send/recv with automatic header construction/stripping
- Created src/ethernet.c: interface table, char device ops routed
  through ethernet layer, sysfs /sys/net namespace exposing per-iface
  mac/ip/netmask/gateway/link files, IPv4 address parse/format
- NE2000 and 3C509B drivers now register through ethernet_register()
  instead of directly via devicefs_register_char(); removed redundant
  char device ops from both drivers
- Kernel calls ethernet_init() before init_drivers() so the subsystem
  is ready when NIC drivers probe
- Tested: NE2000 detected with NIC, 'eth1' registered via ethernet
  subsystem; clean boot without NIC
This commit is contained in:
AI
2026-02-24 07:24:56 +00:00
parent 35bce963be
commit 35bafdcad9
8 changed files with 626 additions and 87 deletions

View File

@@ -70,7 +70,7 @@ Once a task is completed, it should be checked off.
- [x] Create an app called `mkfs.fat32`. This app can be used to format a block into a FAT32 filesystem.
- [x] Create a network driver for the NE2000 NIC.
- [x] Create a network driver for the 3C509B NIC. It should only support RJ45 and 10base-T.
- [ ] Create an ethernet subsytsem. Each ethernet device should be shown as a character device with the name `ethN`.
- [x] Create an ethernet subsytsem. Each ethernet device should be shown as a character device with the name `ethN`.
- [ ] Create a IPv4 stack. Create the `ip` app that shows curernt IPv4 configuration. It should read this information from `/sys`
- [ ] Create a ARP subsystem. Create the `arp` command that shows current ARP tables. Again, this info should be found in `/sys`
- [ ] Create a DHCP subsystem. Create the `dhcp` command to show current DHCP status information.

View File

@@ -1,9 +1,4 @@
make: Warning: File 'Makefile' has modification time 48547 s in the future
make[1]: Warning: File 'CMakeFiles/Makefile2' has modification time 48547 s in the future
make[2]: Warning: File 'CMakeFiles/apps.dir/progress.make' has modification time 48547 s in the future
make[2]: warning: Clock skew detected. Your build may be incomplete.
make[2]: Warning: File 'CMakeFiles/apps.dir/progress.make' has modification time 48547 s in the future
[ 3%] Building user-mode applications
[ 2%] Building user-mode applications
Building app: cat
Built: /workspaces/claude-os/build/apps_bin/cat (310 bytes)
Building app: diskpart
@@ -33,23 +28,9 @@ Building app: sh
| ^~~~
1 warning generated.
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
make[2]: warning: Clock skew detected. Your build may be incomplete.
[ 3%] Built target apps
make[2]: Warning: File 'CMakeFiles/initrd.dir/progress.make' has modification time 48530 s in the future
make[2]: warning: Clock skew detected. Your build may be incomplete.
make[2]: Warning: File 'CMakeFiles/initrd.dir/progress.make' has modification time 48530 s in the future
make[2]: warning: Clock skew detected. Your build may be incomplete.
[ 6%] Built target initrd
make[2]: Warning: File 'src/CMakeFiles/kernel.dir/build.make' has modification time 48529 s in the future
make[2]: warning: Clock skew detected. Your build may be incomplete.
make[2]: Warning: File 'src/CMakeFiles/kernel.dir/build.make' has modification time 48529 s in the future
[ 9%] Building C object src/CMakeFiles/kernel.dir/ne2000.c.o
[ 12%] Linking C executable ../bin/kernel
make[2]: warning: Clock skew detected. Your build may be incomplete.
[ 96%] Built target kernel
make[2]: Warning: File 'CMakeFiles/iso.dir/progress.make' has modification time 48527 s in the future
make[2]: warning: Clock skew detected. Your build may be incomplete.
make[2]: Warning: File 'CMakeFiles/iso.dir/progress.make' has modification time 48527 s in the future
[ 2%] Built target apps
[ 5%] Built target initrd
[ 97%] Built target kernel
[100%] Generating bootable ISO image
xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project.
@@ -57,17 +38,14 @@ Drive current: -outdev 'stdio:/workspaces/claude-os/release/claude-os.iso'
Media current: stdio file, overwriteable
Media status : is blank
Media summary: 0 sessions, 0 data blocks, 0 data, 126g free
Added to ISO image: directory '/'='/tmp/grub.jahFGc'
Added to ISO image: directory '/'='/tmp/grub.KhfgHk'
xorriso : UPDATE : 581 files added in 1 seconds
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
xorriso : UPDATE : 586 files added in 1 seconds
xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img'
xorriso : UPDATE : Thank you for being patient. Working since 0 seconds.
ISO image produced: 5943 sectors
Written to medium : 5943 sectors at LBA 0
ISO image produced: 5967 sectors
Written to medium : 5967 sectors at LBA 0
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
make[2]: warning: Clock skew detected. Your build may be incomplete.
[100%] Built target iso
make[1]: warning: Clock skew detected. Your build may be incomplete.
make: warning: Clock skew detected. Your build may be incomplete.

View File

@@ -27,6 +27,7 @@ add_executable(kernel
floppy.c
ne2000.c
e3c509.c
ethernet.c
env.c
keyboard.c
interrupts.S

View File

@@ -16,6 +16,7 @@
#include "port_io.h"
#include "pic.h"
#include "devicefs.h"
#include "ethernet.h"
#include "driver.h"
#include <string.h>
@@ -217,28 +218,6 @@ void e3c509_irq(void) {
e3c509_cmd(base, CMD_ACK_INTR | STAT_INT_LATCH);
}
/* ================================================================
* Character device operations
* ================================================================ */
static int32_t e3c509_char_read(void *dev_data, uint32_t size, void *buf) {
e3c509_device_t *dev = (e3c509_device_t *)dev_data;
return (int32_t)e3c509_recv(dev, buf, size);
}
static int32_t e3c509_char_write(void *dev_data, uint32_t size, const void *buf) {
e3c509_device_t *dev = (e3c509_device_t *)dev_data;
if (e3c509_send(dev, buf, size) == 0) {
return (int32_t)size;
}
return -1;
}
static devicefs_char_ops_t e3c509_char_ops = {
.read = e3c509_char_read,
.write = e3c509_char_write,
};
/* ================================================================
* Initialization
* ================================================================ */
@@ -394,8 +373,11 @@ static int e3c509_driver_init(void) {
}
offset_print("\n");
/* Register as character device (shares "eth" class with NE2000) */
devicefs_register_char("eth", &e3c509_char_ops, &e3c509_dev);
/* Register with ethernet subsystem (creates /dev/ethN) */
ethernet_register(e3c509_dev.mac,
(eth_send_fn)e3c509_send,
(eth_recv_fn)e3c509_recv,
&e3c509_dev);
offset_print(" 3C509: 10base-T (RJ45) on I/O ");
print_hex(e3c509_dev.io_base);

393
src/ethernet.c Normal file
View File

@@ -0,0 +1,393 @@
/**
* @file ethernet.c
* @brief Ethernet subsystem implementation.
*
* Provides a unified layer over individual Ethernet NIC drivers.
* NIC drivers register through ethernet_register(), which creates the
* `/dev/ethN` char device and adds the interface to an internal table.
*
* The subsystem also exposes network interface info via sysfs at
* `/sys/net`, allowing userspace tools to query interface status.
*/
#include "ethernet.h"
#include "devicefs.h"
#include "sysfs.h"
#include <string.h>
/* Debug print helpers */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/* ================================================================
* Global state
* ================================================================ */
/** Interface table. */
static eth_iface_t ifaces[ETH_MAX_IFACES];
/** Number of registered interfaces. */
static uint32_t iface_count = 0;
/* ================================================================
* Character device operations
*
* Maps devicefs char read/write to the NIC driver send/recv.
* ================================================================ */
/**
* Read from `/dev/ethN`: receive a raw Ethernet frame.
*/
static int32_t eth_char_read(void *dev_data, uint32_t size, void *buf) {
eth_iface_t *iface = (eth_iface_t *)dev_data;
if (!iface || !iface->active || !iface->recv) return -1;
return (int32_t)iface->recv(iface->dev_data, buf, size);
}
/**
* Write to `/dev/ethN`: send a raw Ethernet frame.
*/
static int32_t eth_char_write(void *dev_data, uint32_t size, const void *buf) {
eth_iface_t *iface = (eth_iface_t *)dev_data;
if (!iface || !iface->active || !iface->send) return -1;
int ret = iface->send(iface->dev_data, buf, size);
return (ret == 0) ? (int32_t)size : -1;
}
/** Devicefs char ops shared by all ethernet interfaces. */
static devicefs_char_ops_t eth_char_ops = {
.read = eth_char_read,
.write = eth_char_write,
};
/* ================================================================
* Sysfs operations for /sys/net
*
* Directory layout:
* /sys/net/
* eth1/
* mac - MAC address as hex string
* ip - IPv4 address
* netmask - subnet mask
* gateway - default gateway
* link - "up" or "down"
* eth2/
* ...
* ================================================================ */
/**
* Format a MAC address as "XX:XX:XX:XX:XX:XX" into buf.
*/
static int format_mac(const uint8_t *mac, char *buf, uint32_t buf_size) {
if (buf_size < 18) return -1;
static const char hex[] = "0123456789ABCDEF";
int pos = 0;
for (int i = 0; i < 6; i++) {
if (i > 0) buf[pos++] = ':';
buf[pos++] = hex[(mac[i] >> 4) & 0xF];
buf[pos++] = hex[mac[i] & 0xF];
}
buf[pos++] = '\n';
buf[pos] = '\0';
return pos;
}
/**
* Format an IPv4 address as "A.B.C.D\n" into buf.
*/
static int format_ipv4(uint32_t ip, char *buf, uint32_t buf_size) {
if (buf_size < 16) return -1;
int pos = 0;
for (int i = 0; i < 4; i++) {
if (i > 0) buf[pos++] = '.';
uint8_t octet = (uint8_t)((ip >> (24 - i * 8)) & 0xFF);
if (octet >= 100) { buf[pos++] = (char)('0' + octet / 100); octet %= 100; buf[pos++] = (char)('0' + octet / 10); octet %= 10; }
else if (octet >= 10) { buf[pos++] = (char)('0' + octet / 10); octet %= 10; }
buf[pos++] = (char)('0' + octet);
}
buf[pos++] = '\n';
buf[pos] = '\0';
return pos;
}
/**
* Parse interface name from the path prefix.
* E.g., path "eth1/mac" → iface "eth1", subpath "mac".
* Returns the iface, or NULL if not found.
*/
static eth_iface_t *parse_iface_path(const char *path, const char **subpath) {
/* Find the first '/' */
const char *slash = NULL;
for (const char *p = path; *p; p++) {
if (*p == '/') { slash = p; break; }
}
char iface_name[16];
if (slash) {
uint32_t len = (uint32_t)(slash - path);
if (len >= sizeof(iface_name)) return NULL;
memcpy(iface_name, path, len);
iface_name[len] = '\0';
*subpath = slash + 1;
} else {
/* No slash — path is just the interface name */
uint32_t len = strlen(path);
if (len >= sizeof(iface_name)) return NULL;
memcpy(iface_name, path, len + 1);
*subpath = "";
}
return ethernet_find_iface(iface_name);
}
/**
* Sysfs list callback for /sys/net.
*/
static int net_sysfs_list(void *ctx, const char *path, uint32_t idx,
sysfs_entry_t *out) {
(void)ctx;
if (path[0] == '\0') {
/* Root of /sys/net — list interfaces */
uint32_t count = 0;
for (uint32_t i = 0; i < ETH_MAX_IFACES; i++) {
if (!ifaces[i].active) continue;
if (count == idx) {
memset(out, 0, sizeof(sysfs_entry_t));
strncpy(out->name, ifaces[i].name, SYSFS_MAX_NAME - 1);
out->is_dir = 1;
return 0;
}
count++;
}
return -1;
}
/* Listing inside an interface directory */
const char *subpath;
eth_iface_t *iface = parse_iface_path(path, &subpath);
if (!iface) return -1;
/* Files: mac, ip, netmask, gateway, link */
static const char *files[] = { "mac", "ip", "netmask", "gateway", "link" };
if (idx >= 5) return -1;
memset(out, 0, sizeof(sysfs_entry_t));
strncpy(out->name, files[idx], SYSFS_MAX_NAME - 1);
out->is_dir = 0;
return 0;
}
/**
* Sysfs read callback for /sys/net.
*/
static int net_sysfs_read(void *ctx, const char *path, char *buf,
uint32_t buf_size) {
(void)ctx;
const char *subpath;
eth_iface_t *iface = parse_iface_path(path, &subpath);
if (!iface) return -1;
if (strcmp(subpath, "mac") == 0) {
return format_mac(iface->mac, buf, buf_size);
}
if (strcmp(subpath, "ip") == 0) {
return format_ipv4(iface->ip_addr, buf, buf_size);
}
if (strcmp(subpath, "netmask") == 0) {
return format_ipv4(iface->netmask, buf, buf_size);
}
if (strcmp(subpath, "gateway") == 0) {
return format_ipv4(iface->gateway, buf, buf_size);
}
if (strcmp(subpath, "link") == 0) {
const char *s = iface->link_up ? "up\n" : "down\n";
strncpy(buf, s, buf_size);
return (int)strlen(s);
}
return -1;
}
/**
* Sysfs write callback for /sys/net.
* Allows setting IP address, netmask, gateway.
*/
static int net_sysfs_write(void *ctx, const char *path, const char *buf,
uint32_t size) {
(void)ctx;
(void)size;
const char *subpath;
eth_iface_t *iface = parse_iface_path(path, &subpath);
if (!iface) return -1;
/* Parse dotted-decimal IP from buf */
if (strcmp(subpath, "ip") == 0 || strcmp(subpath, "netmask") == 0 ||
strcmp(subpath, "gateway") == 0) {
uint32_t octets[4] = {0};
int octet_idx = 0;
for (uint32_t i = 0; i < size && octet_idx < 4; i++) {
if (buf[i] == '.' || buf[i] == '\n' || buf[i] == '\0') {
octet_idx++;
} else if (buf[i] >= '0' && buf[i] <= '9') {
octets[octet_idx] = octets[octet_idx] * 10 + (uint32_t)(buf[i] - '0');
}
}
uint32_t addr = (octets[0] << 24) | (octets[1] << 16) |
(octets[2] << 8) | octets[3];
if (strcmp(subpath, "ip") == 0) iface->ip_addr = addr;
if (strcmp(subpath, "netmask") == 0) iface->netmask = addr;
if (strcmp(subpath, "gateway") == 0) iface->gateway = addr;
return (int)size;
}
return -1;
}
/** Sysfs operations for /sys/net. */
static sysfs_ops_t net_sysfs_ops = {
.list = net_sysfs_list,
.read = net_sysfs_read,
.write = net_sysfs_write,
};
/* ================================================================
* Public API
* ================================================================ */
void ethernet_init(void) {
memset(ifaces, 0, sizeof(ifaces));
iface_count = 0;
sysfs_register("net", &net_sysfs_ops, NULL);
offset_print(" ETHERNET: subsystem initialized\n");
}
eth_iface_t *ethernet_register(const uint8_t *mac,
eth_send_fn send,
eth_recv_fn recv,
void *dev_data) {
if (iface_count >= ETH_MAX_IFACES) {
offset_print(" ETHERNET: too many interfaces\n");
return NULL;
}
/* Find free slot */
eth_iface_t *iface = NULL;
for (uint32_t i = 0; i < ETH_MAX_IFACES; i++) {
if (!ifaces[i].active) {
iface = &ifaces[i];
break;
}
}
if (!iface) return NULL;
memset(iface, 0, sizeof(eth_iface_t));
/* Copy MAC */
memcpy(iface->mac, mac, ETH_ALEN);
iface->send = send;
iface->recv = recv;
iface->dev_data = dev_data;
iface->link_up = 1; /* Assume link up after init */
iface->active = 1;
/* Register as /dev/ethN — devicefs assigns the number */
devicefs_device_t *devfs_dev = devicefs_register_char("eth",
&eth_char_ops,
iface);
if (devfs_dev) {
/* Copy the assigned name back */
strncpy(iface->name, devfs_dev->name, sizeof(iface->name) - 1);
} else {
/* Fallback name */
iface->name[0] = 'e'; iface->name[1] = 't'; iface->name[2] = 'h';
iface->name[3] = (char)('0' + iface_count + 1);
iface->name[4] = '\0';
}
iface_count++;
offset_print(" ETHERNET: registered ");
offset_print(iface->name);
offset_print(" MAC ");
for (int i = 0; i < 6; i++) {
if (i > 0) offset_print(":");
print_hex(mac[i]);
}
offset_print("\n");
return iface;
}
int ethernet_send(eth_iface_t *iface, const uint8_t *dst_mac,
uint16_t ethertype, const void *payload, uint32_t len) {
if (!iface || !iface->active || !iface->send) return -1;
if (len > ETH_MTU) return -1;
/* Build complete Ethernet frame on stack */
uint8_t frame[ETH_FRAME_LEN];
eth_header_t *hdr = (eth_header_t *)frame;
memcpy(hdr->dst, dst_mac, ETH_ALEN);
memcpy(hdr->src, iface->mac, ETH_ALEN);
hdr->ethertype = htons(ethertype);
memcpy(frame + ETH_HLEN, payload, len);
uint32_t frame_len = ETH_HLEN + len;
if (frame_len < 60) frame_len = 60; /* Minimum Ethernet frame */
return iface->send(iface->dev_data, frame, frame_len);
}
int ethernet_recv(eth_iface_t *iface, void *buf, uint32_t bufsize,
uint8_t *src_mac, uint16_t *ethertype) {
if (!iface || !iface->active || !iface->recv) return -1;
/* Receive into temporary buffer */
uint8_t frame[ETH_FRAME_LEN];
int ret = iface->recv(iface->dev_data, frame, sizeof(frame));
if (ret <= 0) return ret;
if ((uint32_t)ret < ETH_HLEN) return -1; /* Runt frame */
eth_header_t *hdr = (eth_header_t *)frame;
/* Copy out src MAC and ethertype if requested */
if (src_mac) memcpy(src_mac, hdr->src, ETH_ALEN);
if (ethertype) *ethertype = ntohs(hdr->ethertype);
/* Copy payload */
uint32_t payload_len = (uint32_t)ret - ETH_HLEN;
if (payload_len > bufsize) payload_len = bufsize;
memcpy(buf, frame + ETH_HLEN, payload_len);
return (int)payload_len;
}
eth_iface_t *ethernet_get_iface(uint32_t index) {
uint32_t count = 0;
for (uint32_t i = 0; i < ETH_MAX_IFACES; i++) {
if (!ifaces[i].active) continue;
if (count == index) return &ifaces[i];
count++;
}
return NULL;
}
uint32_t ethernet_get_iface_count(void) {
return iface_count;
}
eth_iface_t *ethernet_find_iface(const char *name) {
for (uint32_t i = 0; i < ETH_MAX_IFACES; i++) {
if (!ifaces[i].active) continue;
if (strcmp(ifaces[i].name, name) == 0) return &ifaces[i];
}
return NULL;
}

208
src/ethernet.h Normal file
View File

@@ -0,0 +1,208 @@
/**
* @file ethernet.h
* @brief Ethernet subsystem.
*
* Provides a unified abstraction over individual Ethernet NIC drivers.
* Each NIC driver registers itself with the ethernet subsystem, which
* then creates the `/dev/ethN` character device and exposes interface
* information via `/sys/net`.
*
* Higher-level protocols (IPv4, ARP, etc.) use this subsystem to send
* and receive Ethernet frames without knowing which NIC driver is in use.
*/
#ifndef ETHERNET_H
#define ETHERNET_H
#include <stdint.h>
/** Maximum number of Ethernet interfaces. */
#define ETH_MAX_IFACES 8
/** Ethernet MAC address length. */
#define ETH_ALEN 6
/** Ethernet header size (dst + src + ethertype). */
#define ETH_HLEN 14
/** Maximum Ethernet payload size. */
#define ETH_MTU 1500
/** Maximum Ethernet frame size (header + payload). */
#define ETH_FRAME_LEN (ETH_HLEN + ETH_MTU)
/* ================================================================
* EtherType constants
* ================================================================ */
#define ETHERTYPE_IPV4 0x0800 /**< IPv4 */
#define ETHERTYPE_ARP 0x0806 /**< ARP */
/* ================================================================
* Ethernet header (14 bytes)
* ================================================================ */
/**
* Ethernet frame header.
*/
typedef struct __attribute__((packed)) eth_header {
uint8_t dst[ETH_ALEN]; /**< Destination MAC address. */
uint8_t src[ETH_ALEN]; /**< Source MAC address. */
uint16_t ethertype; /**< EtherType (big-endian). */
} eth_header_t;
/* ================================================================
* NIC driver callbacks
* ================================================================ */
/**
* Callback to send a raw Ethernet frame (complete with header).
*
* @param dev_data Driver-specific device pointer.
* @param frame Complete Ethernet frame (header + payload).
* @param len Frame length in bytes.
* @return 0 on success, -1 on failure.
*/
typedef int (*eth_send_fn)(void *dev_data, const void *frame, uint32_t len);
/**
* Callback to receive a raw Ethernet frame (complete with header).
*
* @param dev_data Driver-specific device pointer.
* @param buf Buffer for received frame.
* @param bufsize Buffer size.
* @return Number of bytes received, 0 if none, -1 on error.
*/
typedef int (*eth_recv_fn)(void *dev_data, void *buf, uint32_t bufsize);
/* ================================================================
* Ethernet interface
* ================================================================ */
/**
* Represents one Ethernet network interface.
*/
typedef struct eth_iface {
char name[16]; /**< Interface name (e.g., "eth1"). */
uint8_t mac[ETH_ALEN]; /**< MAC address. */
uint8_t active; /**< 1 if registered, 0 if free. */
uint8_t link_up; /**< 1 if link is up. */
uint32_t ip_addr; /**< IPv4 address (0 if not configured). */
uint32_t netmask; /**< Subnet mask (0 if not configured). */
uint32_t gateway; /**< Default gateway (0 if not configured). */
eth_send_fn send; /**< NIC driver send callback. */
eth_recv_fn recv; /**< NIC driver receive callback. */
void *dev_data; /**< Opaque pointer to NIC driver state. */
} eth_iface_t;
/* ================================================================
* Public API
* ================================================================ */
/**
* Initialize the ethernet subsystem.
* Registers sysfs namespace "net" and prepares the interface table.
*/
void ethernet_init(void);
/**
* Register a NIC with the ethernet subsystem.
*
* Creates a `/dev/ethN` character device and adds the interface to the
* internal table. Called by NIC drivers during initialization.
*
* @param mac 6-byte MAC address.
* @param send Send callback function.
* @param recv Receive callback function.
* @param dev_data Opaque NIC device pointer (passed to callbacks).
* @return Pointer to the registered interface, or NULL on failure.
*/
eth_iface_t *ethernet_register(const uint8_t *mac,
eth_send_fn send,
eth_recv_fn recv,
void *dev_data);
/**
* Send an Ethernet frame through an interface.
*
* Constructs the Ethernet header (src MAC from interface, dst from caller)
* and dispatches through the NIC driver.
*
* @param iface Interface to send through.
* @param dst_mac Destination MAC address (6 bytes).
* @param ethertype EtherType (host byte order, will be converted to big-endian).
* @param payload Frame payload.
* @param len Payload length (max ETH_MTU).
* @return 0 on success, -1 on failure.
*/
int ethernet_send(eth_iface_t *iface, const uint8_t *dst_mac,
uint16_t ethertype, const void *payload, uint32_t len);
/**
* Receive an Ethernet frame from an interface.
*
* @param iface Interface to receive from.
* @param buf Buffer for frame payload (Ethernet header is stripped).
* @param bufsize Buffer size.
* @param src_mac Output: source MAC of received frame (6 bytes, can be NULL).
* @param ethertype Output: EtherType of received frame (host byte order, can be NULL).
* @return Payload length in bytes, 0 if no packet, -1 on error.
*/
int ethernet_recv(eth_iface_t *iface, void *buf, uint32_t bufsize,
uint8_t *src_mac, uint16_t *ethertype);
/**
* Get an interface by index.
*
* @param index 0-based index.
* @return Pointer to interface, or NULL if index out of range.
*/
eth_iface_t *ethernet_get_iface(uint32_t index);
/**
* Get the number of registered interfaces.
*
* @return Number of active Ethernet interfaces.
*/
uint32_t ethernet_get_iface_count(void);
/**
* Find an interface by name (e.g., "eth1").
*
* @param name Interface name.
* @return Pointer to interface, or NULL if not found.
*/
eth_iface_t *ethernet_find_iface(const char *name);
/**
* Convert a 16-bit value from host to network byte order (big-endian).
*/
static inline uint16_t htons(uint16_t h) {
return (uint16_t)((h >> 8) | (h << 8));
}
/**
* Convert a 16-bit value from network to host byte order.
*/
static inline uint16_t ntohs(uint16_t n) {
return htons(n);
}
/**
* Convert a 32-bit value from host to network byte order (big-endian).
*/
static inline uint32_t htonl(uint32_t h) {
return ((h >> 24) & 0x000000FF) |
((h >> 8) & 0x0000FF00) |
((h << 8) & 0x00FF0000) |
((h << 24) & 0xFF000000);
}
/**
* Convert a 32-bit value from network to host byte order.
*/
static inline uint32_t ntohl(uint32_t n) {
return htonl(n);
}
#endif /* ETHERNET_H */

View File

@@ -22,6 +22,7 @@
#include "mbr.h"
#include "fat32.h"
#include "keyboard.h"
#include "ethernet.h"
#include "framebuffer.h"
/* Global framebuffer info, parsed from multiboot2 tags. */
@@ -415,6 +416,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
fb_info.pitch = 80 * 2;
}
ethernet_init();
offset_print("Ethernet subsystem initialized\n");
init_drivers();
EARLY_PRINT("DRV ");
offset_print("Drivers initialized\n");

View File

@@ -20,6 +20,7 @@
#include "port_io.h"
#include "pic.h"
#include "devicefs.h"
#include "ethernet.h"
#include "driver.h"
#include <string.h>
@@ -309,37 +310,6 @@ void ne2k_irq(void) {
}
}
/* ================================================================
* Devicefs character device operations
* ================================================================ */
/**
* Read from the NE2000 character device.
* Returns one received Ethernet frame.
*/
static int32_t ne2k_char_read(void *dev_data, uint32_t size, void *buf) {
ne2k_device_t *dev = (ne2k_device_t *)dev_data;
return (int32_t)ne2k_recv(dev, buf, size);
}
/**
* Write to the NE2000 character device.
* Sends an Ethernet frame.
*/
static int32_t ne2k_char_write(void *dev_data, uint32_t size, const void *buf) {
ne2k_device_t *dev = (ne2k_device_t *)dev_data;
if (ne2k_send(dev, buf, size) == 0) {
return (int32_t)size;
}
return -1;
}
/** Character device operations for NE2000. */
static devicefs_char_ops_t ne2k_char_ops = {
.read = ne2k_char_read,
.write = ne2k_char_write,
};
/* ================================================================
* Initialization
* ================================================================ */
@@ -535,8 +505,11 @@ static int ne2k_driver_init(void) {
}
offset_print("\n");
/* Register as character device */
devicefs_register_char("eth", &ne2k_char_ops, &ne2k_dev);
/* Register with ethernet subsystem (creates /dev/ethN) */
ethernet_register(ne2k_dev.mac,
(eth_send_fn)ne2k_send,
(eth_recv_fn)ne2k_recv,
&ne2k_dev);
offset_print(" NE2K: initialized on I/O ");
print_hex(ne2k_dev.io_base);