uint16_t special;
} __attribute__((packed));
+/* RX semaphore — signaled by IRQ handler, waited on by RX thread */
+#include "sync.h"
+extern ksem_t e1000_rx_sem;
+
/* Initialize the E1000 NIC. Returns 0 on success, -1 on failure. */
int e1000_init(void);
#include "console.h"
#include "utils.h"
#include "io.h"
+#include "sync.h"
#include <stddef.h>
static volatile uint32_t tx_tail = 0;
static volatile uint32_t rx_tail = 0;
+/* RX semaphore — signaled by IRQ handler, waited on by RX thread */
+ksem_t e1000_rx_sem;
+
/* Cached PCI device info */
static uint8_t e1000_bus, e1000_slot, e1000_func;
static void e1000_irq_handler(struct registers* regs) {
(void)regs;
uint32_t icr = e1000_read(E1000_ICR);
- (void)icr;
- /* Reading ICR clears the pending interrupt bits.
- * RX/TX processing is done via polling in e1000_recv/e1000_send
- * for simplicity. The interrupt just wakes the system. */
+ /* Wake the RX thread if a receive event occurred */
+ if (icr & (E1000_ICR_RXT0 | E1000_ICR_RXDMT0 | E1000_ICR_RXO)) {
+ ksem_signal(&e1000_rx_sem);
+ }
}
/* ------------------------------------------------------------------ */
/* Wait for reset to complete (spec says ~1us, be generous) */
for (volatile int i = 0; i < 100000; i++) { }
+ /* Init RX semaphore before enabling interrupts */
+ ksem_init(&e1000_rx_sem, 0);
+
/* Disable interrupts during setup */
e1000_write(E1000_IMC, 0xFFFFFFFF);
e1000_read(E1000_ICR); /* Clear pending */
struct e1000_tx_desc* txd = (struct e1000_tx_desc*)E1000_TX_DESC_VA;
uint32_t idx = tx_tail;
- /* Wait for descriptor to be available */
- int timeout = 100000;
- while (!(txd[idx].status & E1000_TXD_STAT_DD) && --timeout > 0) { }
- if (timeout <= 0) return -1;
+ /* Non-blocking: if descriptor not ready, return immediately */
+ if (!(txd[idx].status & E1000_TXD_STAT_DD))
+ return -1;
/* Copy data to TX buffer */
uintptr_t buf_va = E1000_TX_BUF_VA + (uintptr_t)(idx / 2) * 4096 +
* (primary/secondary x master/slave). */
(void)ata_pio_init();
- /* Disk-based filesystems (diskfs, FAT, ext2) are NOT auto-mounted.
- * They are mounted either by /etc/fstab entries or manually via the
- * kconsole 'mount' command. Parse /etc/fstab if it exists. */
+ /* If root= is specified on the kernel command line, mount that device
+ * as the disk root filesystem. The filesystem type is auto-detected
+ * by trying each supported type in order.
+ * Example: root=/dev/hda or root=/dev/hdb */
+ const char* root_dev = cmdline_get("root");
+ if (root_dev) {
+ int drive = -1;
+ if (strncmp(root_dev, "/dev/", 5) == 0)
+ drive = ata_name_to_drive(root_dev + 5);
+ if (drive >= 0 && ata_pio_drive_present(drive)) {
+ /* Try auto-detect: diskfs, fat, ext2 */
+ static const char* fstypes[] = { "diskfs", "fat", "ext2", NULL };
+ int mounted = 0;
+ for (int i = 0; fstypes[i]; i++) {
+ if (init_mount_fs(fstypes[i], drive, 0, "/disk") == 0) {
+ kprintf("[INIT] root=%s mounted as %s on /disk\n",
+ root_dev, fstypes[i]);
+ mounted = 1;
+ break;
+ }
+ }
+ if (!mounted)
+ kprintf("[INIT] root=%s: no supported filesystem found\n", root_dev);
+ } else {
+ kprintf("[INIT] root=%s: device not found\n", root_dev);
+ }
+ }
+
+ /* Disk-based filesystems can also be mounted via /etc/fstab entries
+ * or manually via the kconsole 'mount' command. */
init_parse_fstab();
if (!fs_root) {
kprintf("Welcome to AdrOS (x86/ARM/RISC-V/MIPS)!\n");
// Infinite loop acting as Idle Task
+ // RX is interrupt-driven (e1000_rx_thread), no polling needed.
for(;;) {
- net_poll();
hal_cpu_idle();
}
}
/*
* lwIP netif driver for the E1000 NIC.
* Bridges the E1000 hardware driver to lwIP's network interface abstraction.
+ *
+ * RX path: interrupt → e1000_rx_sem → rx_thread → e1000_recv → tcpip_input
+ * TX path: lwIP core → e1000_netif_output → e1000_send (non-blocking)
*/
#include "lwip/opt.h"
#include "lwip/netif.h"
#include "spinlock.h"
#include "e1000.h"
+#include "process.h"
#include "console.h"
#include "utils.h"
#define E1000_NETIF_MTU 1500
-/* Temporary receive buffer (stack-allocated per poll call) */
-static uint8_t rx_tmp[2048];
-
/* Forward declaration */
static err_t e1000_netif_output(struct netif* netif, struct pbuf* p);
/*
- * Low-level output: send a pbuf chain via E1000.
+ * Low-level output: send a pbuf chain via E1000 (non-blocking).
*/
static err_t e1000_netif_output(struct netif* netif, struct pbuf* p) {
(void)netif;
/*
* Netif init callback — called by netif_add().
*/
-err_t e1000_netif_init(struct netif* netif) {
+static err_t e1000_netif_init(struct netif* netif) {
netif->name[0] = 'e';
netif->name[1] = 'n';
netif->output = etharp_output;
return ERR_OK;
}
-/*
- * Poll the E1000 for received packets and feed them into lwIP.
- * Must be called periodically from the main loop.
- */
-void e1000_netif_poll(struct netif* netif) {
- for (;;) {
- int len = e1000_recv(rx_tmp, sizeof(rx_tmp));
- if (len <= 0) break;
-
- struct pbuf* p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
- if (!p) break;
-
- pbuf_take(p, rx_tmp, (u16_t)len);
-
- if (netif->input(p, netif) != ERR_OK) {
- pbuf_free(p);
- }
- }
-}
-
/* ---- Global network state ---- */
static struct netif e1000_nif;
tcpip_ready = 1;
}
+/*
+ * Dedicated RX thread — waits on the E1000 RX semaphore (signaled by
+ * the hardware interrupt handler) and drains all available packets into
+ * the lwIP TCP/IP stack via tcpip_input().
+ */
+static uint8_t rx_tmp[2048];
+
+static void e1000_rx_thread(void) {
+ for (;;) {
+ /* Block until the IRQ handler signals a receive event */
+ ksem_wait(&e1000_rx_sem);
+
+ /* Drain all available packets */
+ for (;;) {
+ int len = e1000_recv(rx_tmp, sizeof(rx_tmp));
+ if (len <= 0) break;
+
+ struct pbuf* p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
+ if (!p) break;
+
+ pbuf_take(p, rx_tmp, (u16_t)len);
+
+ if (e1000_nif.input(p, &e1000_nif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ }
+ }
+}
+
void net_init(void) {
if (!e1000_link_up()) {
kprintf("[NET] E1000 link down, skipping lwIP init.\n");
netif_set_default(&e1000_nif);
netif_set_up(&e1000_nif);
+ /* Start the dedicated RX thread */
+ process_create_kernel(e1000_rx_thread);
+
net_initialized = 1;
- kprintf("[NET] lwIP initialized (threaded), IP=10.0.2.15\n");
+ kprintf("[NET] lwIP initialized (interrupt-driven RX), IP=10.0.2.15\n");
}
void net_poll(void) {
- if (!net_initialized) return;
- e1000_netif_poll(&e1000_nif);
+ /* No-op: RX is now handled by the interrupt-driven rx_thread.
+ * Kept for backward compatibility — callers can safely remove. */
+ (void)0;
}
struct netif* net_get_netif(void) {