[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[tyndur-devel] [PATCH] CDI NE2K driver



This patch adds a CDI driver for the NE2K (PCI) NIC.

Signed-off-by: Matthew Iselin <matthew@xxxxxxxxxxxxxx>
---
 src/modules/cdi/ne2k/Makefile.all       |    6 +
 src/modules/cdi/ne2k/include/ethernet.h |   43 ++++
 src/modules/cdi/ne2k/include/ne2k.h     |  105 +++++++++
 src/modules/cdi/ne2k/main.c             |  118 ++++++++++
 src/modules/cdi/ne2k/ne2k.c             |  355 +++++++++++++++++++++++++++++++
 5 files changed, 627 insertions(+), 0 deletions(-)
 create mode 100644 src/modules/cdi/ne2k/Makefile.all
 create mode 100644 src/modules/cdi/ne2k/include/ethernet.h
 create mode 100644 src/modules/cdi/ne2k/include/ne2k.h
 create mode 100644 src/modules/cdi/ne2k/main.c
 create mode 100644 src/modules/cdi/ne2k/ne2k.c

diff --git a/src/modules/cdi/ne2k/Makefile.all b/src/modules/cdi/ne2k/Makefile.all
new file mode 100644
index 0000000..250d3c3
--- /dev/null
+++ b/src/modules/cdi/ne2k/Makefile.all
@@ -0,0 +1,6 @@
+shopt -s extglob
+source $LOST_BUILDMK_ROOT/config.sh
+
+echo "LD   $1/modules/ne2k"
+$LOST_TOOLS_LD -Ttext=0x40000000 -one2k.mod  *.o --start-group $2 --end-group
+$LOST_TOOLS_STRIP -s ne2k.mod -o $1/modules/ne2k
diff --git a/src/modules/cdi/ne2k/include/ethernet.h b/src/modules/cdi/ne2k/include/ethernet.h
new file mode 100644
index 0000000..a8159d1
--- /dev/null
+++ b/src/modules/cdi/ne2k/include/ethernet.h
@@ -0,0 +1,43 @@
+/*  
+ * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Kevin Wolf.
+ *
+ * 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 HOLDERS 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.
+ */
+
+#ifndef _ETHERNET_H_
+#define _ETHERNET_H_
+
+#include <stdint.h>
+
+#define ETH_TYPE_IP 0x0008
+#define ETH_TYPE_ARP 0x0608
+
+struct eth_packet_header {
+    uint64_t    dest    : 48;
+    uint64_t    src     : 48;
+    uint16_t    type;
+} __attribute__ ((packed));
+
+#endif
diff --git a/src/modules/cdi/ne2k/include/ne2k.h b/src/modules/cdi/ne2k/include/ne2k.h
new file mode 100644
index 0000000..a3c78e3
--- /dev/null
+++ b/src/modules/cdi/ne2k/include/ne2k.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Matthew Iselin.
+ *
+ * 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 HOLDERS 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.
+ */
+
+#ifndef _NE2K_H_
+#define _NE2K_H_
+
+#include <stdint.h>
+
+#include "cdi.h"
+#include "cdi/net.h"
+#include "cdi/pci.h"
+
+#define NE_CMD      0
+#define NE_PSTART   1
+#define NE_PSTOP    2
+#define NE_BNDRY    3
+
+#define NE_TSR      4 // R
+#define NE_TPSR     4 // W
+
+#define NE_TBCR0    5
+#define NE_TBCR1    6
+
+#define NE_ISR      7
+
+#define NE_RSAR0    8
+#define NE_RSAR1    9
+#define NE_RBCR0    10
+#define NE_RBCR1    11
+
+#define NE_RCR      12
+#define NE_TCR      13
+#define NE_DCR      14
+#define NE_IMR      15
+
+// Register page 1
+#define NE_PAR      1 // PAR0..5
+#define NE_CURR     7
+#define NE_MAR      8
+
+// Packet ring buffer offsets
+#define PAGE_TX     0x40
+#define PAGE_RX     0x50
+#define PAGE_STOP   0x80
+
+#define NE_RESET    0x1F
+#define NE_DATA     0x10
+
+#define PHYS(netcard, field) \
+    ((uintptr_t) netcard->phys + offsetof(struct rtl8139_device, field))
+
+#define RX_BUFFER_SIZE 0x2000
+#define TX_BUFFER_SIZE 0x1000
+
+typedef struct {
+    void* virt;
+    uintptr_t phys;
+} cdi_dma_mem_ptr_t;
+
+struct ne2k_device {
+    struct cdi_net_device       net;
+    struct cdi_pci_device*      pci;
+
+    void*                       phys;
+    uint16_t                    port_base;
+    
+    uint8_t                     next_packet;
+    
+    int                         tx_in_progress;
+
+    cdi_list_t                  pending_sends;
+};
+
+void ne2k_init_device(struct cdi_device* device);
+void ne2k_remove_device(struct cdi_device* device);
+
+void ne2k_send_packet
+    (struct cdi_net_device* device, void* data, size_t size);
+
+#endif
diff --git a/src/modules/cdi/ne2k/main.c b/src/modules/cdi/ne2k/main.c
new file mode 100644
index 0000000..66ec9a8
--- /dev/null
+++ b/src/modules/cdi/ne2k/main.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Matthew Iselin.
+ *
+ * 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 HOLDERS 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 <stdio.h>
+
+#include "cdi/net.h"
+#include "cdi/pci.h"
+#include "cdi/misc.h"
+
+#include "ne2k.h"
+
+struct module_options {
+    uint32_t ip;
+};
+
+static struct {
+    struct cdi_net_driver net;
+} driver;
+
+static const char* driver_name = "ne2k";
+
+static int ne2k_driver_init(int argc, char* argv[]);
+static void ne2k_driver_destroy(struct cdi_driver* driver);
+
+#ifdef CDI_STANDALONE
+int main(int argc, char* argv[])
+#else
+int init_ne2k(int argc, char* argv[])
+#endif
+{
+    cdi_init();
+
+    ne2k_driver_init(argc, argv);
+    cdi_driver_register((struct cdi_driver*) &driver);
+
+    cdi_run_drivers();
+
+    return 0;
+}
+
+static int ne2k_driver_init(int argc, char* argv[])
+{
+    // TODO Auf pci-Service warten
+    // TODO Auf tcpip-Service warten
+
+    // Konstruktor der Vaterklasse
+    cdi_net_driver_init((struct cdi_net_driver*) &driver);
+
+    // Namen setzen
+    driver.net.drv.name = driver_name;
+
+    // Funktionspointer initialisieren
+    driver.net.drv.destroy         = ne2k_driver_destroy;
+    driver.net.drv.init_device     = ne2k_init_device;
+    driver.net.drv.remove_device   = ne2k_remove_device;
+
+    // Passende PCI-Geraete suchen
+    cdi_list_t pci_devices = cdi_list_create();
+    cdi_pci_get_all_devices(pci_devices);
+
+    struct cdi_pci_device* dev;
+    int i;
+    for (i = 0; (dev = cdi_list_get(pci_devices, i)); i++) {
+        if ((dev->vendor_id == 0x10ec) && (dev->device_id == 0x8029)) {
+            void* phys_device;
+            struct ne2k_device* device;
+
+            cdi_alloc_phys_mem(sizeof(*device),
+                (void**) &device, &phys_device);
+            memset(device, 0, sizeof(*device));
+
+            device->phys = phys_device;
+            device->pci = dev;
+            cdi_list_push(driver.net.drv.devices, device);
+        } else {
+            cdi_pci_device_destroy(dev);
+        }
+    }
+
+    cdi_list_destroy(pci_devices);
+
+    return 0;
+}
+
+/**
+ * Deinitialisiert die Datenstrukturen fuer den ne2k-Treiber
+ */
+static void ne2k_driver_destroy(struct cdi_driver* driver)
+{
+    cdi_net_driver_destroy((struct cdi_net_driver*) driver);
+
+    // TODO Alle Karten deinitialisieren
+}
diff --git a/src/modules/cdi/ne2k/ne2k.c b/src/modules/cdi/ne2k/ne2k.c
new file mode 100644
index 0000000..2e54f28
--- /dev/null
+++ b/src/modules/cdi/ne2k/ne2k.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2007 The tyndur Project. All rights reserved.
+ *
+ * This code is derived from software contributed to the tyndur Project
+ * by Matthew Iselin.
+ *
+ * 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 HOLDERS 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "cdi/net.h"
+#include "cdi/pci.h"
+#include "cdi/io.h"
+#include "cdi/misc.h"
+
+#include "ne2k.h"
+#include "ethernet.h"
+
+//Hier koennen die Debug-Nachrichten aktiviert werden
+// #define DEBUG_MSG(s) printf("[ne2k] debug: %s() '%s'\n", __FUNCTION__, s)
+#define DEBUG_MSG(s) //
+
+static void ne2k_handle_interrupt(struct cdi_device* device);
+static void receive_ok_handler(struct ne2k_device* netcard);
+static void transmit_ok_handler(struct ne2k_device* netcard);
+
+static inline void write_register_byte(struct ne2k_device* netcard, uint8_t reg, uint8_t value)
+{
+    cdi_outb(netcard->port_base + reg, value);
+}
+
+static inline void write_register_word(struct ne2k_device* netcard, uint8_t reg, uint16_t value)
+{
+    cdi_outw(netcard->port_base + reg, value);
+}
+
+static inline void write_register_dword(struct ne2k_device* netcard, uint8_t reg, uint32_t value)
+{
+    cdi_outl(netcard->port_base + reg, value);
+}
+
+static inline uint8_t read_register_byte(struct ne2k_device* netcard, uint8_t reg)
+{
+    return cdi_inb(netcard->port_base + reg);
+}
+
+static inline uint16_t read_register_word(struct ne2k_device* netcard, uint8_t reg)
+{
+    return cdi_inw(netcard->port_base + reg);
+}
+
+static inline uint32_t read_register_dword(struct ne2k_device* netcard, uint8_t reg)
+{
+    return cdi_inl(netcard->port_base + reg);
+}
+
+void ne2k_init_device(struct cdi_device* device)
+{
+    struct ne2k_device* netcard = (struct ne2k_device*) device;
+    netcard->net.send_packet = ne2k_send_packet;
+
+    // PCI-bezogenes Zeug initialisieren
+    DEBUG_MSG("Interrupthandler und Ports registrieren");
+    cdi_register_irq(netcard->pci->irq, ne2k_handle_interrupt, device);
+    cdi_pci_alloc_ioports(netcard->pci);
+
+    cdi_list_t reslist = netcard->pci->resources;
+    struct cdi_pci_resource* res;
+    int i;
+    for (i = 0; (res = cdi_list_get(reslist, i)); i++) {
+        if (res->type == CDI_PCI_IOPORTS) {
+            netcard->port_base = res->start;
+        }
+    }
+    
+    // Reset the card
+    DEBUG_MSG("Resetting the card");
+    write_register_byte(netcard, NE_CMD, 0x21);
+    
+    // Enable 16-bit transfer, turn on monitor mode to avoid receiving packets
+    // and turn on loopback just in case. Receive logic is not ready yet.
+    write_register_byte(netcard, NE_DCR, 0x09);
+    write_register_byte(netcard, NE_RCR, 0x20);
+    write_register_byte(netcard, NE_TCR, 0x02);
+    
+    // Disable card interrupts
+    write_register_byte(netcard, NE_ISR, 0xFF);
+    write_register_byte(netcard, NE_IMR, 0);
+    
+    // Read the MAC address from PROM
+    write_register_byte(netcard, NE_RSAR0, 0);
+    write_register_byte(netcard, NE_RSAR1, 0);
+    
+    write_register_byte(netcard, NE_RBCR0, 32);
+    write_register_byte(netcard, NE_RBCR1, 0);
+    
+    write_register_byte(netcard, NE_CMD, 0x0A);
+
+    uint16_t prom[16];
+    uint8_t mac[6];
+    for(i = 0; i < 16; i++) {
+        prom[i] = read_register_word(netcard, NE_DATA);
+    }
+    
+    // Set the MAC address for the system use
+    for(i = 0; i < 6; i++) {
+        mac[i] = (uint8_t) (prom[i] & 0xFF);
+    }
+    netcard->net.mac = (*(uint32_t*) (&mac[0])) | ((uint64_t) ((*(uint32_t*) (&mac[4])) & 0xFFFF) << 32);
+    
+    // Setup the packet ring buffer
+    write_register_byte(netcard, NE_CMD, 0x61);
+    netcard->next_packet = PAGE_RX + 1;
+    write_register_byte(netcard, NE_CURR, netcard->next_packet);
+    
+    write_register_byte(netcard, NE_CMD, 0x21);
+    
+    write_register_byte(netcard, NE_PSTART, PAGE_RX);
+    write_register_byte(netcard, NE_BNDRY, PAGE_RX);
+    write_register_byte(netcard, NE_PSTOP, PAGE_STOP);
+    
+    // Accept broadcast and runt packets
+    write_register_byte(netcard, NE_RCR, 0x06);
+    write_register_byte(netcard, NE_TCR, 0);
+    
+    // Clear pending interrupts, enable them all, and begin card operation
+    write_register_byte(netcard, NE_ISR, 0xFF);
+    write_register_byte(netcard, NE_IMR, 0x3F);
+    write_register_byte(netcard, NE_CMD, 0x22);
+
+    netcard->pending_sends = cdi_list_create();
+
+    cdi_net_device_init((struct cdi_net_device*) device);
+    DEBUG_MSG("Fertig initialisiert");
+}
+
+void ne2k_remove_device(struct cdi_device* device)
+{
+}
+
+void ne2k_send_packet
+    (struct cdi_net_device* device, void* data, size_t size)
+{
+    struct ne2k_device* netcard = (struct ne2k_device*) device;
+
+    if (size > 0x700) {
+        // Spezialfall - keine Lust
+        printf("ne2k: size ist boese\n");
+        return;
+    }
+    
+    if (!__sync_lock_test_and_set(&netcard->tx_in_progress, 1)) {
+        netcard->tx_in_progress = 1;
+    } else {
+        DEBUG_MSG("Tx-Buffer ist schon besetzt");
+
+        void* pending = malloc(size + sizeof(uint32_t));
+        cdi_list_insert(netcard->pending_sends,
+            cdi_list_size(netcard->pending_sends), pending);
+        *((uint32_t*) pending) = size;
+        pending += sizeof(uint32_t);
+        memcpy(pending, data, size);
+
+        return;
+    }
+    
+    // Send the length/address for this write
+    write_register_byte(netcard, NE_RSAR0, 0);
+    write_register_byte(netcard, NE_RSAR1, PAGE_TX);
+    
+    write_register_byte(netcard, NE_RBCR0, (size > 64) ? (size & 0xff) : 64);
+    write_register_byte(netcard, NE_RBCR1, size >> 8);
+    
+    write_register_byte(netcard, NE_CMD, 0x12);
+    
+    // Write to the NIC
+    uint16_t *p = (uint16_t*) data;
+    size_t i;
+    for(i = 0; (i + 1) < size; i += 2) {
+        write_register_word(netcard, NE_DATA, p[i/2]);
+    }
+    
+    // Handle odd bytes
+    if(size & 1) {
+        write_register_byte(netcard, NE_DATA, p[i/2]);
+        i++;
+    }
+    
+    // Pad to 64 bytes, if needed
+    for(; i < 64; i += 2) {
+        write_register_word(netcard, NE_DATA, 0);
+    }
+    
+    // Await the transfer completion and then transmit
+    // FIXME: Should wait upon a mutex or something which is unlocked when
+    // the IRQ fires.
+    while(!(read_register_byte(netcard, NE_ISR) & 0x40));
+    write_register_byte(netcard, NE_ISR, 0x40);
+    
+    write_register_byte(netcard, NE_TBCR0, (size > 64) ? (size & 0xff) : 64);
+    write_register_byte(netcard, NE_TBCR1, size >> 8);
+    
+    write_register_byte(netcard, NE_TPSR, PAGE_TX);
+    
+    write_register_byte(netcard, NE_CMD, 0x26);
+}
+
+static void receive_ok_handler(struct ne2k_device* netcard)
+{
+    DEBUG_MSG("ROK");
+    
+    write_register_byte(netcard, NE_ISR, 0x1);
+    
+    write_register_byte(netcard, NE_CMD, 0x61);
+    uint8_t current = read_register_byte(netcard, NE_CURR);
+    write_register_byte(netcard, NE_CMD, 0x21);
+
+    while (netcard->next_packet != current)
+    {
+        write_register_byte(netcard, NE_RSAR0, 0);
+        write_register_byte(netcard, NE_RSAR1, netcard->next_packet);
+        
+        // 4 bytes - 2 for status, 2 for length
+        write_register_byte(netcard, NE_RBCR0, 4);
+        write_register_byte(netcard, NE_RBCR1, 0);
+        
+        write_register_byte(netcard, NE_CMD, 0x0A);
+        
+        uint16_t status = read_register_word(netcard, NE_DATA);
+        uint16_t length = read_register_word(netcard, NE_DATA);
+        
+        if(!length) {
+            break;
+        }
+        
+        // Remove status and length bytes
+        length -= 3;
+
+        if (length >= sizeof(struct eth_packet_header))
+        {
+            // Verify status
+            while(!(read_register_byte(netcard, NE_ISR) & 0x40));
+            write_register_byte(netcard, NE_ISR, 0x40);
+        
+            // Read the packet
+            write_register_byte(netcard, NE_RSAR0, 4);
+            write_register_byte(netcard, NE_RSAR1, netcard->next_packet);
+            write_register_byte(netcard, NE_RBCR0, length & 0xFF);
+            write_register_byte(netcard, NE_RBCR1, (length >> 8) & 0xFF);
+            
+            write_register_byte(netcard, NE_CMD, 0x0A);
+            
+            uint16_t data[(length / 2) + 1];
+            int i, words = (length / 2);
+            for(i = 0; i < words; ++i) {
+                data[i] = read_register_word(netcard, NE_DATA);
+            }
+            if(length & 1) {
+                data[i] = read_register_word(netcard, NE_DATA) & 0xFF;
+            }
+            
+            // Verify status
+            while(!(read_register_byte(netcard, NE_ISR) & 0x40));
+            write_register_byte(netcard, NE_ISR, 0x40);
+            
+            netcard->next_packet = status >> 8;
+            write_register_byte(netcard, NE_BNDRY, (netcard->next_packet == PAGE_RX) ? (PAGE_STOP - 1) : (netcard->next_packet - 1));
+
+            cdi_net_receive(
+                (struct cdi_net_device*) netcard,
+                (uint8_t*) data,
+                length);
+        }
+    }
+}
+
+static void transmit_ok_handler(struct ne2k_device* netcard)
+{
+    DEBUG_MSG("TOK");
+    netcard->tx_in_progress = 0;
+}
+
+static void ne2k_handle_interrupt(struct cdi_device* device)
+{
+    DEBUG_MSG("<ne2k Interrupt>");
+
+    struct ne2k_device* netcard = (struct ne2k_device*) device;
+    uint32_t isr = read_register_word(netcard, NE_ISR);
+    
+    // Packet received?
+    if(isr & 0x05) {
+        DEBUG_MSG("ne2k: received packet");
+        if(!(isr & 0x04)) {
+            write_register_byte(netcard, NE_IMR, 0x3A);
+            isr &= ~1;
+            receive_ok_handler(netcard);
+            write_register_byte(netcard, NE_IMR, 0x3F);
+        }
+        else {
+            DEBUG_MSG("ne2k: receive failed");
+        }
+    }
+    
+    // Packet transmitted?
+    if(isr & 0x0A) {
+        if(!(isr & 0x8)) {
+            transmit_ok_handler(netcard);
+        }
+    }
+    
+    // Overflows
+    if(isr & 0x10) {
+        DEBUG_MSG("ne2K: receive buffer overflow");
+    }
+    if(isr & 0x20) {
+        DEBUG_MSG("ne2K: counter overflow");
+    }
+    
+    write_register_byte(netcard, NE_ISR, isr);
+
+    // Falls noch Pakete zu senden waren, die nicht gesendet werden
+    // konnten, weil die Karte beschaeftigt war, das jetzt nachholen
+    void* pending = cdi_list_pop(netcard->pending_sends);
+    if (pending) {
+        uint32_t size = *((uint32_t*) pending);
+        pending += sizeof(uint32_t);
+        DEBUG_MSG("Sende Paket aus der pending-Queue");
+        ne2k_send_packet((struct cdi_net_device*) device, pending, size);
+        free(pending - sizeof(uint32_t));
+    }
+    
+    DEBUG_MSG("interrupt returns");
+}
-- 
1.5.6.3