summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/CMakeLists.txt1
-rw-r--r--Kernel/Debug.h.in4
-rw-r--r--Kernel/Net/NE2000NetworkAdapter.cpp476
-rw-r--r--Kernel/Net/NE2000NetworkAdapter.h78
-rw-r--r--Kernel/init.cpp2
-rw-r--r--Meta/CMake/all_the_debug_macros.cmake1
6 files changed, 562 insertions, 0 deletions
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt
index c9fcb9074f..ec0d2840cb 100644
--- a/Kernel/CMakeLists.txt
+++ b/Kernel/CMakeLists.txt
@@ -87,6 +87,7 @@ set(KERNEL_SOURCES
Net/IPv4Socket.cpp
Net/LocalSocket.cpp
Net/LoopbackAdapter.cpp
+ Net/NE2000NetworkAdapter.cpp
Net/NetworkAdapter.cpp
Net/NetworkTask.cpp
Net/RTL8139NetworkAdapter.cpp
diff --git a/Kernel/Debug.h.in b/Kernel/Debug.h.in
index fe7a0542b9..ca6000b907 100644
--- a/Kernel/Debug.h.in
+++ b/Kernel/Debug.h.in
@@ -182,6 +182,10 @@
#cmakedefine01 MULTIPROCESSOR_DEBUG
#endif
+#ifndef NE2000_DEBUG
+#cmakedefine01 NE2000_DEBUG
+#endif
+
#ifndef NETWORK_TASK_DEBUG
#cmakedefine01 NETWORK_TASK_DEBUG
#endif
diff --git a/Kernel/Net/NE2000NetworkAdapter.cpp b/Kernel/Net/NE2000NetworkAdapter.cpp
new file mode 100644
index 0000000000..457415e193
--- /dev/null
+++ b/Kernel/Net/NE2000NetworkAdapter.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2021, the SerenityOS developers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <AK/MACAddress.h>
+#include <Kernel/Debug.h>
+#include <Kernel/IO.h>
+#include <Kernel/Net/NE2000NetworkAdapter.h>
+
+namespace Kernel {
+
+/**
+ * The NE2000 is an ancient 10 Mib/s Ethernet network card standard by Novell
+ * from the late 80s. Based on National Semiconductor's DP8390 Ethernet chip
+ * or compatible, they were known to be extremely bare-bones but also very
+ * cheap entry-level cards.
+ *
+ * QEMU supports them with the ne2k_{isa,pci} devices, physical incarnations
+ * were available from different manufacturers for the ISA bus and later on
+ * the PCI bus, including:
+ * - Realtek's RTL8029
+ * - VIA Technologies, Inc.'s VT86C926
+ *
+ * Official documentation from National Semiconductor includes:
+ * - Datasheet "DP8390D/NS32490D NIC Network Interface Controller"
+ * - Application Note 874 "Writing Drivers for the DP8390 NIC Family of Ethernet Controllers"
+ *
+ * This driver supports only the PCI variant.
+ *
+ * Remember, friends don't let friends use NE2000 network cards :^)
+ */
+
+// Page 0 registers
+static constexpr u8 REG_RW_COMMAND = 0x00;
+static constexpr u8 BIT_COMMAND_STOP = (0b1 << 0);
+static constexpr u8 BIT_COMMAND_START = (0b1 << 1);
+static constexpr u8 BIT_COMMAND_TXP = (0b1 << 2);
+static constexpr u8 BIT_COMMAND_DMA_READ = (0b001 << 3);
+static constexpr u8 BIT_COMMAND_DMA_WRITE = (0b010 << 3);
+static constexpr u8 BIT_COMMAND_DMA_SEND = (0b011 << 3);
+static constexpr u8 BIT_COMMAND_DMA_ABORT = (0b100 << 3);
+static constexpr u8 BIT_COMMAND_DMA_FIELD = (0b111 << 3);
+static constexpr u8 BIT_COMMAND_PAGE1 = (0b01 << 6);
+static constexpr u8 BIT_COMMAND_PAGE2 = (0b10 << 6);
+static constexpr u8 BIT_COMMAND_PAGE_FIELD = (0b11 << 6);
+
+static constexpr u8 REG_WR_PAGESTART = 0x01;
+static constexpr u8 REG_WR_PAGESTOP = 0x02;
+static constexpr u8 REG_RW_BOUNDARY = 0x03;
+static constexpr u8 REG_RD_TRANSMITSTATUS = 0x04;
+static constexpr u8 REG_WR_TRANSMITPAGE = 0x04;
+static constexpr u8 REG_RD_NCR = 0x05;
+static constexpr u8 REG_WR_TRANSMITBYTECOUNT0 = 0x05;
+static constexpr u8 REG_WR_TRANSMITBYTECOUNT1 = 0x06;
+static constexpr u8 REG_RW_INTERRUPTSTATUS = 0x07;
+static constexpr u8 REG_RD_CRDMA0 = 0x08;
+static constexpr u8 REG_WR_REMOTESTARTADDRESS0 = 0x08;
+static constexpr u8 REG_RD_CRDMA1 = 0x09;
+static constexpr u8 REG_WR_REMOTESTARTADDRESS1 = 0x09;
+static constexpr u8 REG_WR_REMOTEBYTECOUNT0 = 0x0a;
+static constexpr u8 REG_WR_REMOTEBYTECOUNT1 = 0x0b;
+
+static constexpr u8 REG_RD_RECEIVESTATUS = 0x0c;
+static constexpr u8 BIT_RECEIVESTATUS_PRX = (0b1 << 0);
+static constexpr u8 BIT_RECEIVESTATUS_CRC = (0b1 << 1);
+static constexpr u8 BIT_RECEIVESTATUS_FAE = (0b1 << 2);
+static constexpr u8 BIT_RECEIVESTATUS_FO = (0b1 << 3);
+static constexpr u8 BIT_RECEIVESTATUS_MPA = (0b1 << 4);
+
+static constexpr u8 REG_WR_RECEIVECONFIGURATION = 0x0c;
+static constexpr u8 BIT_RECEIVECONFIGURATION_SEP = (0b1 << 0);
+static constexpr u8 BIT_RECEIVECONFIGURATION_AR = (0b1 << 1);
+static constexpr u8 BIT_RECEIVECONFIGURATION_AB = (0b1 << 2);
+static constexpr u8 BIT_RECEIVECONFIGURATION_AM = (0b1 << 3);
+static constexpr u8 BIT_RECEIVECONFIGURATION_PRO = (0b1 << 4);
+static constexpr u8 BIT_RECEIVECONFIGURATION_MON = (0b1 << 5);
+
+static constexpr u8 REG_RD_FAE_TALLY = 0x0d;
+
+static constexpr u8 REG_WR_TRANSMITCONFIGURATION = 0x0d;
+static constexpr u8 BIT_WR_TRANSMITCONFIGURATION_LOOPBACK = (0b10 << 0);
+
+static constexpr u8 REG_RD_CRC_TALLY = 0x0e;
+
+static constexpr u8 REG_WR_DATACONFIGURATION = 0x0e;
+static constexpr u8 BIT_DATACONFIGURATION_WTS = (0b1 << 0);
+static constexpr u8 BIT_DATACONFIGURATION_BOS = (0b1 << 1);
+static constexpr u8 BIT_DATACONFIGURATION_LS = (0b1 << 2);
+static constexpr u8 BIT_DATACONFIGURATION_FIFO_8B = (0b10 << 5);
+
+static constexpr u8 REG_RD_MISS_PKT_TALLY = 0x0f;
+
+static constexpr u8 REG_WR_INTERRUPTMASK = 0x0f;
+static constexpr u8 BIT_INTERRUPTMASK_PRX = (0b1 << 0);
+static constexpr u8 BIT_INTERRUPTMASK_PTX = (0b1 << 1);
+static constexpr u8 BIT_INTERRUPTMASK_RXE = (0b1 << 2);
+static constexpr u8 BIT_INTERRUPTMASK_TXE = (0b1 << 3);
+static constexpr u8 BIT_INTERRUPTMASK_OVW = (0b1 << 4);
+static constexpr u8 BIT_INTERRUPTMASK_CNT = (0b1 << 5);
+static constexpr u8 BIT_INTERRUPTMASK_RDC = (0b1 << 6);
+static constexpr u8 BIT_INTERRUPTMASK_RST = (0b1 << 7);
+
+static constexpr u8 REG_RW_IOPORT = 0x10;
+
+// Page 1 registers
+static constexpr u8 REG_RW_PHYSICALADDRESS0 = 0x01;
+static constexpr u8 REG_RW_CURRENT = 0x07;
+
+static constexpr int NE2K_PAGE_SIZE = 256;
+
+static constexpr int NE2K_RAM_BEGIN = 16384;
+static constexpr int NE2K_RAM_END = 32768;
+static constexpr int NE2K_RAM_SIZE = NE2K_RAM_END - NE2K_RAM_BEGIN;
+
+static constexpr int NE2K_RAM_SEND_BEGIN = 16384;
+static constexpr int NE2K_RAM_SEND_END = 16384 + 6 * 256;
+static constexpr int NE2K_RAM_SEND_SIZE = NE2K_RAM_SEND_END - NE2K_RAM_SEND_BEGIN;
+
+static constexpr int NE2K_RAM_RECV_BEGIN = NE2K_RAM_SEND_END;
+static constexpr int NE2K_RAM_RECV_END = NE2K_RAM_END;
+static constexpr int NE2K_RAM_RECV_SIZE = NE2K_RAM_RECV_END - NE2K_RAM_RECV_BEGIN;
+
+static_assert(NE2K_RAM_BEGIN % NE2K_PAGE_SIZE == 0);
+static_assert(NE2K_RAM_END % NE2K_PAGE_SIZE == 0);
+static_assert(NE2K_RAM_SEND_BEGIN % NE2K_PAGE_SIZE == 0);
+static_assert(NE2K_RAM_SEND_END % NE2K_PAGE_SIZE == 0);
+static_assert(NE2K_RAM_RECV_BEGIN % NE2K_PAGE_SIZE == 0);
+static_assert(NE2K_RAM_RECV_END % NE2K_PAGE_SIZE == 0);
+
+struct [[gnu::packed]] received_packet_header {
+ u8 status;
+ u8 next_packet_page;
+ u16 length;
+};
+
+void NE2000NetworkAdapter::detect()
+{
+ static const auto ne2k_ids = Array<PCI::ID, 11> {
+ PCI::ID { 0x10EC, 0x8029 }, // RealTek RTL-8029(AS)
+
+ // List of clones, taken from Linux's ne2k-pci.c
+ PCI::ID { 0x1050, 0x0940 }, // Winbond 89C940
+ PCI::ID { 0x11f6, 0x1401 }, // Compex RL2000
+ PCI::ID { 0x8e2e, 0x3000 }, // KTI ET32P2
+ PCI::ID { 0x4a14, 0x5000 }, // NetVin NV5000SC
+ PCI::ID { 0x1106, 0x0926 }, // Via 86C926
+ PCI::ID { 0x10bd, 0x0e34 }, // SureCom NE34
+ PCI::ID { 0x1050, 0x5a5a }, // Winbond W89C940F
+ PCI::ID { 0x12c3, 0x0058 }, // Holtek HT80232
+ PCI::ID { 0x12c3, 0x5598 }, // Holtek HT80229
+ PCI::ID { 0x8c4a, 0x1980 }, // Winbond W89C940 (misprogrammed)
+ };
+ PCI::enumerate([&](const PCI::Address& address, PCI::ID id) {
+ if (address.is_null())
+ return;
+ if (!ne2k_ids.span().contains_slow(id))
+ return;
+ u8 irq = PCI::get_interrupt_line(address);
+ [[maybe_unused]] auto& unused = adopt(*new NE2000NetworkAdapter(address, irq)).leak_ref();
+ });
+}
+
+NE2000NetworkAdapter::NE2000NetworkAdapter(PCI::Address address, u8 irq)
+ : PCI::Device(address, irq)
+ , m_io_base(PCI::get_BAR0(pci_address()) & ~3)
+{
+ set_interface_name("ne2k");
+
+ dmesgln("NE2000: Found @ {}", pci_address());
+
+ m_interrupt_line = PCI::get_interrupt_line(pci_address());
+ dmesgln("NE2000: Port base: {}", m_io_base);
+ dmesgln("NE2000: Interrupt line: {}", m_interrupt_line);
+
+ int ram_errors = ram_test();
+ dmesgln("NE2000: RAM test {}, got {} byte errors", (ram_errors > 0 ? "OK" : "KO"), ram_errors);
+
+ reset();
+ set_mac_address(m_mac_address);
+ dmesgln("NE2000: MAC address: {}", m_mac_address.to_string().characters());
+ enable_irq();
+}
+
+NE2000NetworkAdapter::~NE2000NetworkAdapter()
+{
+}
+
+void NE2000NetworkAdapter::handle_irq(const RegisterState&)
+{
+ u8 status = in8(REG_RW_INTERRUPTSTATUS);
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Got interrupt, status=0x{}", String::format("%02x", status));
+
+ if (status & BIT_INTERRUPTMASK_PRX) {
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Interrupt for packet received");
+ }
+ if (status & BIT_INTERRUPTMASK_PTX) {
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Interrupt for packet sent");
+ }
+ if (status & BIT_INTERRUPTMASK_RXE) {
+ u8 fae = in8(REG_RD_FAE_TALLY);
+ u8 crc = in8(REG_RD_CRC_TALLY);
+ u8 miss = in8(REG_RD_MISS_PKT_TALLY);
+ dmesgln("NE2000NetworkAdapter: Packet reception error framing={} crc={} missed={}", fae, crc, miss);
+ // TODO: handle counters
+ }
+ if (status & BIT_INTERRUPTMASK_TXE) {
+ dmesgln("NE2000NetworkAdapter: Packet transmission error");
+ }
+ if (status & BIT_INTERRUPTMASK_OVW) {
+ dmesgln("NE2000NetworkAdapter: Ring buffer reception overflow error");
+ // TODO: handle counters
+ }
+ if (status & BIT_INTERRUPTMASK_CNT) {
+ dmesgln("NE2000NetworkAdapter: Counter overflow error");
+ // TODO: handle counters
+ }
+ if (status & BIT_INTERRUPTMASK_RST) {
+ dmesgln("NE2000NetworkAdapter: NIC requires reset due to packet reception overflow");
+ // TODO: proper reset procedure
+ reset();
+ }
+
+ receive();
+ m_wait_queue.wake_all();
+
+ out8(REG_RW_INTERRUPTSTATUS, status);
+}
+
+int NE2000NetworkAdapter::ram_test()
+{
+ IOAddress io(PCI::get_BAR0(pci_address()) & ~3);
+ int errors = 0;
+
+ out8(REG_RW_COMMAND, BIT_COMMAND_DMA_ABORT | BIT_COMMAND_STOP);
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ out8(REG_WR_DATACONFIGURATION, BIT_DATACONFIGURATION_FIFO_8B | BIT_DATACONFIGURATION_WTS);
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ out8(REG_WR_DATACONFIGURATION, BIT_DATACONFIGURATION_FIFO_8B | BIT_DATACONFIGURATION_BOS | BIT_DATACONFIGURATION_WTS);
+#else
+# error Unknown byte order
+#endif
+ out8(REG_WR_REMOTEBYTECOUNT0, 0x00);
+ out8(REG_WR_REMOTEBYTECOUNT1, 0x00);
+ out8(REG_WR_RECEIVECONFIGURATION, BIT_RECEIVECONFIGURATION_MON);
+ out8(REG_RW_COMMAND, BIT_COMMAND_DMA_ABORT | BIT_COMMAND_START);
+ auto buffer = ByteBuffer::create_uninitialized(NE2K_RAM_SIZE);
+
+ const u8 patterns[3] = { 0x5a, 0xff, 0x00 };
+ for (int i = 0; i < 3; ++i) {
+ for (size_t j = 0; j < buffer.size(); ++j)
+ buffer[j] = patterns[i];
+
+ rdma_write(NE2K_RAM_BEGIN, buffer);
+ rdma_read(NE2K_RAM_BEGIN, buffer);
+
+ for (size_t j = 0; j < buffer.size(); ++j) {
+ if (buffer[j] != patterns[i]) {
+ if (errors < 16)
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Bad adapter RAM @ {} expected={} got={}", PhysicalAddress(NE2K_RAM_BEGIN + j), patterns[i], buffer[j]);
+ else if (errors == 16)
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Too many RAM errors, silencing further output");
+ errors++;
+ }
+ }
+ }
+
+ return errors;
+}
+
+void NE2000NetworkAdapter::reset()
+{
+ const u8 interrupt_mask = BIT_INTERRUPTMASK_PRX | BIT_INTERRUPTMASK_PTX | BIT_INTERRUPTMASK_RXE | BIT_INTERRUPTMASK_TXE | BIT_INTERRUPTMASK_OVW | BIT_INTERRUPTMASK_CNT;
+ u8 prom[32];
+
+ // Taken from DP8390D's datasheet section 11.0, "Initialization Procedures"
+ out8(REG_RW_COMMAND, BIT_COMMAND_DMA_ABORT | BIT_COMMAND_STOP);
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ out8(REG_WR_DATACONFIGURATION, BIT_DATACONFIGURATION_FIFO_8B | BIT_DATACONFIGURATION_WTS);
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ out8(REG_WR_DATACONFIGURATION, BIT_DATACONFIGURATION_FIFO_8B | BIT_DATACONFIGURATION_BOS | BIT_DATACONFIGURATION_WTS);
+#else
+# error Unknown byte order
+#endif
+ out8(REG_WR_REMOTEBYTECOUNT0, 0x00);
+ out8(REG_WR_REMOTEBYTECOUNT1, 0x00);
+ out8(REG_WR_RECEIVECONFIGURATION, BIT_RECEIVECONFIGURATION_AB | BIT_RECEIVECONFIGURATION_AR);
+ out8(REG_WR_TRANSMITCONFIGURATION, BIT_WR_TRANSMITCONFIGURATION_LOOPBACK);
+ m_ring_read_ptr = NE2K_RAM_RECV_BEGIN >> 8;
+ out8(REG_WR_PAGESTART, NE2K_RAM_RECV_BEGIN >> 8);
+ out8(REG_RW_BOUNDARY, NE2K_RAM_RECV_BEGIN >> 8);
+ out8(REG_WR_PAGESTOP, NE2K_RAM_RECV_END >> 8);
+ out8(REG_RW_INTERRUPTSTATUS, 0xff);
+ out8(REG_WR_INTERRUPTMASK, interrupt_mask);
+ rdma_read(0, Bytes(prom, sizeof(prom)));
+ for (int i = 0; i < 6; i++) {
+ m_mac_address[i] = prom[i * 2];
+ }
+
+ out8(REG_RW_COMMAND, BIT_COMMAND_PAGE1 | BIT_COMMAND_DMA_ABORT | BIT_COMMAND_STOP);
+ for (int i = 0; i < 6; i++) {
+ out8(REG_RW_PHYSICALADDRESS0 + i, m_mac_address[i]);
+ }
+ out8(REG_RW_CURRENT, NE2K_RAM_RECV_BEGIN >> 8);
+
+ out8(REG_RW_COMMAND, BIT_COMMAND_DMA_ABORT | BIT_COMMAND_START);
+ out8(REG_WR_TRANSMITCONFIGURATION, 0xe0);
+}
+
+void NE2000NetworkAdapter::rdma_read(size_t address, Bytes payload)
+{
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: DMA read @ {} length={}", PhysicalAddress(address), payload.size());
+
+ u8 command = in8(REG_RW_COMMAND) & ~(BIT_COMMAND_PAGE_FIELD | BIT_COMMAND_DMA_FIELD);
+ out8(REG_RW_COMMAND, command | BIT_COMMAND_DMA_ABORT);
+ out8(REG_RW_INTERRUPTSTATUS, BIT_INTERRUPTMASK_RDC);
+
+ out8(REG_WR_REMOTEBYTECOUNT0, payload.size());
+ out8(REG_WR_REMOTEBYTECOUNT1, payload.size() >> 8);
+ out8(REG_WR_REMOTESTARTADDRESS0, address);
+ out8(REG_WR_REMOTESTARTADDRESS1, address >> 8);
+
+ command = in8(REG_RW_COMMAND) & ~(BIT_COMMAND_DMA_FIELD);
+ out8(REG_RW_COMMAND, command | BIT_COMMAND_DMA_READ);
+
+ for (size_t i = 0; i < payload.size(); i += 2) {
+ u16 data = in16(REG_RW_IOPORT);
+ payload[i] = data;
+ if (i != payload.size() - 1)
+ payload[i + 1] = data >> 8;
+ }
+
+ while (!(in8(REG_RW_INTERRUPTSTATUS) & BIT_INTERRUPTMASK_RDC))
+ ;
+}
+
+void NE2000NetworkAdapter::rdma_write(size_t address, ReadonlyBytes payload)
+{
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: DMA write @ {} length={}", PhysicalAddress(address), payload.size());
+
+ u8 command = in8(REG_RW_COMMAND) & ~(BIT_COMMAND_PAGE_FIELD | BIT_COMMAND_DMA_FIELD);
+ out8(REG_RW_COMMAND, command | BIT_COMMAND_DMA_ABORT);
+ out8(REG_RW_INTERRUPTSTATUS, BIT_INTERRUPTMASK_RDC);
+
+ out8(REG_WR_REMOTEBYTECOUNT0, payload.size());
+ out8(REG_WR_REMOTEBYTECOUNT1, payload.size() >> 8);
+ out8(REG_WR_REMOTESTARTADDRESS0, address);
+ out8(REG_WR_REMOTESTARTADDRESS1, address >> 8);
+
+ command = in8(REG_RW_COMMAND) & ~(BIT_COMMAND_DMA_FIELD);
+ out8(REG_RW_COMMAND, command | BIT_COMMAND_DMA_WRITE);
+
+ for (size_t i = 0; i < payload.size(); i += 2) {
+ u16 data = payload[i];
+ if (i != payload.size() - 1)
+ data |= payload[i + 1] << 8;
+ out16(REG_RW_IOPORT, data);
+ }
+
+ while (!(in8(REG_RW_INTERRUPTSTATUS) & BIT_INTERRUPTMASK_RDC))
+ ;
+}
+
+void NE2000NetworkAdapter::send_raw(ReadonlyBytes payload)
+{
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Sending packet length={}", payload.size());
+
+ if (payload.size() > NE2K_RAM_SEND_SIZE) {
+ dmesgln("NE2000NetworkAdapter: Packet to send was too big; discarding");
+ return;
+ }
+
+ while (in8(REG_RW_COMMAND) & BIT_COMMAND_TXP)
+ m_wait_queue.wait_on({}, "NE2000NetworkAdapter");
+
+ disable_irq();
+ size_t packet_size = payload.size();
+ if (packet_size < 64)
+ packet_size = 64;
+ rdma_write(NE2K_RAM_SEND_BEGIN, payload);
+ out8(REG_WR_TRANSMITPAGE, NE2K_RAM_SEND_BEGIN >> 8);
+ out8(REG_WR_TRANSMITBYTECOUNT0, packet_size);
+ out8(REG_WR_TRANSMITBYTECOUNT1, packet_size >> 8);
+ out8(REG_RW_COMMAND, BIT_COMMAND_DMA_ABORT | BIT_COMMAND_TXP | BIT_COMMAND_START);
+
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Packet submitted for transmission");
+
+ enable_irq();
+}
+
+void NE2000NetworkAdapter::receive()
+{
+ while (true) {
+ out8(REG_RW_COMMAND, BIT_COMMAND_PAGE1 | in8(REG_RW_COMMAND));
+ u8 current = in8(REG_RW_CURRENT);
+ out8(REG_RW_COMMAND, in8(REG_RW_COMMAND) & ~BIT_COMMAND_PAGE_FIELD);
+ if (m_ring_read_ptr == current)
+ break;
+
+ size_t header_address = m_ring_read_ptr << 8;
+ received_packet_header header;
+ rdma_read(header_address, Bytes(reinterpret_cast<u8*>(&header), sizeof(header)));
+
+ bool packet_ok = header.status & BIT_RECEIVESTATUS_PRX;
+ dbgln<NE2000_DEBUG>("NE2000NetworkAdapter: Packet received {} length={}", (packet_ok ? "intact" : "damaged"), header.length);
+
+ if (packet_ok) {
+ auto packet = ByteBuffer::create_uninitialized(sizeof(received_packet_header) + header.length);
+ int bytes_left = packet.size();
+ int current_offset = 0;
+ int ring_offset = header_address;
+
+ while (bytes_left > 0) {
+ int copy_size = min(bytes_left, NE2K_PAGE_SIZE);
+ rdma_read(ring_offset, packet.span().slice(current_offset, copy_size));
+ current_offset += copy_size;
+ ring_offset += copy_size;
+ bytes_left -= copy_size;
+ if (ring_offset == NE2K_RAM_RECV_END)
+ ring_offset = NE2K_RAM_RECV_BEGIN;
+ }
+
+ did_receive(packet.span().slice(sizeof(received_packet_header)));
+ }
+
+ if (header.next_packet_page == NE2K_RAM_RECV_BEGIN)
+ out8(REG_RW_BOUNDARY, (NE2K_RAM_RECV_END >> 8) - 1);
+ else
+ out8(REG_RW_BOUNDARY, header.next_packet_page - 1);
+ m_ring_read_ptr = header.next_packet_page;
+ }
+}
+
+void NE2000NetworkAdapter::out8(u16 address, u8 data)
+{
+ m_io_base.offset(address).out(data);
+}
+
+void NE2000NetworkAdapter::out16(u16 address, u16 data)
+{
+ m_io_base.offset(address).out(data);
+}
+
+u8 NE2000NetworkAdapter::in8(u16 address)
+{
+ u8 data = m_io_base.offset(address).in<u8>();
+ return data;
+}
+
+u16 NE2000NetworkAdapter::in16(u16 address)
+{
+ return m_io_base.offset(address).in<u16>();
+}
+
+}
diff --git a/Kernel/Net/NE2000NetworkAdapter.h b/Kernel/Net/NE2000NetworkAdapter.h
new file mode 100644
index 0000000000..9a49d78753
--- /dev/null
+++ b/Kernel/Net/NE2000NetworkAdapter.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021, the SerenityOS developers
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/OwnPtr.h>
+#include <Kernel/IO.h>
+#include <Kernel/Net/NetworkAdapter.h>
+#include <Kernel/PCI/Access.h>
+#include <Kernel/PCI/Device.h>
+#include <Kernel/Random.h>
+
+namespace Kernel {
+
+class NE2000NetworkAdapter final : public NetworkAdapter
+ , public PCI::Device {
+public:
+ static void detect();
+
+ NE2000NetworkAdapter(PCI::Address, u8 irq);
+ virtual ~NE2000NetworkAdapter() override;
+
+ virtual void send_raw(ReadonlyBytes) override;
+ virtual bool link_up() override { return m_link_up; }
+
+ virtual const char* purpose() const override { return class_name(); }
+
+private:
+ virtual void handle_irq(const RegisterState&) override;
+ virtual const char* class_name() const override { return "NE2000NetworkAdapter"; }
+
+ int ram_test();
+ void reset();
+
+ void rdma_read(size_t address, Bytes payload);
+ void rdma_write(size_t address, ReadonlyBytes payload);
+
+ void receive();
+
+ void out8(u16 address, u8 data);
+ void out16(u16 address, u16 data);
+ u8 in8(u16 address);
+ u16 in16(u16 address);
+
+ IOAddress m_io_base;
+ int m_ring_read_ptr;
+ u8 m_interrupt_line { 0 };
+ bool m_link_up { false };
+
+ MACAddress m_mac_address;
+ EntropySource m_entropy_source;
+
+ WaitQueue m_wait_queue;
+};
+}
diff --git a/Kernel/init.cpp b/Kernel/init.cpp
index 429a1216ee..a00de591e5 100644
--- a/Kernel/init.cpp
+++ b/Kernel/init.cpp
@@ -55,6 +55,7 @@
#include <Kernel/Multiboot.h>
#include <Kernel/Net/E1000NetworkAdapter.h>
#include <Kernel/Net/LoopbackAdapter.h>
+#include <Kernel/Net/NE2000NetworkAdapter.h>
#include <Kernel/Net/NetworkTask.h>
#include <Kernel/Net/RTL8139NetworkAdapter.h>
#include <Kernel/PCI/Access.h>
@@ -257,6 +258,7 @@ void init_stage2(void*)
DMIExpose::initialize();
E1000NetworkAdapter::detect();
+ NE2000NetworkAdapter::detect();
RTL8139NetworkAdapter::detect();
LoopbackAdapter::the();
diff --git a/Meta/CMake/all_the_debug_macros.cmake b/Meta/CMake/all_the_debug_macros.cmake
index f919588e1a..097ae7a555 100644
--- a/Meta/CMake/all_the_debug_macros.cmake
+++ b/Meta/CMake/all_the_debug_macros.cmake
@@ -146,6 +146,7 @@ set(OFFD_DEBUG ON)
set(PTHREAD_DEBUG ON)
set(REACHABLE_DEBUG ON)
set(ROUTING_DEBUG ON)
+set(NE2000_DEBUG ON)
set(RTL8139_DEBUG ON)
set(SB16_DEBUG ON)
set(SH_DEBUG ON)