Skip to main content

strat9_kernel/hardware/nic/
e1000_drv.rs

1//! Kernel adapter for the `e1000` crate (legacy 8254x family).
2//!
3//! Thin shim around the shared `KernelE1000Adapter`; only the device-ID
4//! list and driver name differ from the e1000e variant.
5
6use super::{
7    common::{probe_e1000_pci, KernelDma, KernelE1000Adapter},
8    register_device, set_nic_device,
9};
10use crate::{
11    arch::x86_64::{ioapic, msi},
12    hardware::pci_client as pci,
13    serial_println,
14};
15use e1000::E1000Nic;
16
17const LEGACY_E1000_IDS: &[u16] = &[pci::intel_eth::E1000_82540EM, pci::intel_eth::E1000_82545EM];
18
19pub fn init() {
20    let Some(probe) = probe_e1000_pci(LEGACY_E1000_IDS) else {
21        serial_println!("[E1000] no compatible device found");
22        return;
23    };
24
25    log::info!(
26        "E1000: PCI {:04x}:{:04x} at {:?}",
27        probe.pci_dev.vendor_id,
28        probe.pci_dev.device_id,
29        probe.pci_dev.address
30    );
31
32    let mut init_ok = None;
33    for attempt in 0..2 {
34        log::info!(
35            "E1000: init attempt {} mmio_phys={:#x} mmio_virt={:#x}",
36            attempt + 1,
37            probe.mmio_phys,
38            probe.mmio_virt
39        );
40        match E1000Nic::init(probe.mmio_virt, &KernelDma) {
41            Ok(nic) => {
42                log::info!("E1000: core init ok on attempt {}", attempt + 1);
43                init_ok = Some(nic);
44                break;
45            }
46            Err(e) => {
47                log::warn!("E1000: core init attempt {} failed: {}", attempt + 1, e);
48                if attempt == 0 {
49                    let mut cmd_retry = probe.pci_dev.read_config_u16(pci::config::COMMAND);
50                    cmd_retry |= pci::command::BUS_MASTER | pci::command::MEMORY_SPACE;
51                    cmd_retry &= !pci::command::INTERRUPT_DISABLE;
52                    probe
53                        .pci_dev
54                        .write_config_u16(pci::config::COMMAND, cmd_retry);
55                    continue;
56                }
57                log::error!("E1000: init failed: {}", e);
58            }
59        }
60    }
61
62    if let Some(nic) = init_ok {
63        let mac = nic.mac_address();
64        serial_println!(
65            "[E1000] Device initialized: MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
66            mac[0],
67            mac[1],
68            mac[2],
69            mac[3],
70            mac[4],
71            mac[5]
72        );
73        let dev = KernelE1000Adapter::new(nic, "e1000");
74        let iface = register_device(dev.clone());
75
76        // --- Wire up NIC interrupt: MSI → MSI-X → INTx fallback ---
77        // Legacy E1000 (82540EM/82545EM) only support MSI, not MSI-X.
78        // probe_and_enable tries MSI, falls back to INTx.
79        let (irq, vector) = msi::probe_and_enable(&probe.pci_dev, false);
80
81        if irq == 0 || irq == 0xFF {
82            log::warn!(
83                "[E1000] {}: no valid IRQ line, running in polling mode",
84                iface
85            );
86        } else {
87            // If MSI was enabled, INTx is disabled : skip IOAPIC routing.
88            // Check whether MSI is active by reading the PCI command register.
89            let cmd = probe.pci_dev.read_config_u16(pci::config::COMMAND);
90            let msi_active = (cmd & pci::command::INTERRUPT_DISABLE) != 0;
91
92            if !msi_active {
93                // Fallback: route through I/O APIC (INTx).
94                ioapic::route_nic_irq(irq, vector);
95                log::info!("[E1000] {}: INTx IRQ {} → vector {:#x}", iface, irq, vector);
96            } else {
97                log::info!("[E1000] {}: MSI active on vector {:#x}", iface, vector);
98            }
99
100            crate::arch::x86_64::idt::register_nic_irq(irq);
101            set_nic_device(dev, irq);
102        }
103    }
104}