From d7dbac7c61ca6ea909b2be6803708fd27818e384 Mon Sep 17 00:00:00 2001
From: Hauke Petersen <hauke.petersen@fu-berlin.de>
Date: Fri, 5 Aug 2016 14:44:09 +0200
Subject: [PATCH] drivers: added driver for W5100 Ethernet chips

---
 Makefile.dep                         |   1 +
 drivers/Makefile.dep                 |   4 +
 drivers/Makefile.include             |   3 +
 drivers/include/w5100.h              |  85 ++++++++
 drivers/w5100/Makefile               |   1 +
 drivers/w5100/include/w5100_params.h |  62 ++++++
 drivers/w5100/include/w5100_regs.h   | 140 ++++++++++++
 drivers/w5100/w5100.c                | 313 +++++++++++++++++++++++++++
 8 files changed, 609 insertions(+)
 create mode 100644 drivers/include/w5100.h
 create mode 100644 drivers/w5100/Makefile
 create mode 100644 drivers/w5100/include/w5100_params.h
 create mode 100644 drivers/w5100/include/w5100_regs.h
 create mode 100644 drivers/w5100/w5100.c

diff --git a/Makefile.dep b/Makefile.dep
index 802f98b2aa..e6f70295b4 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -21,6 +21,7 @@ endif
 
 ifneq (,$(filter gnrc_netdev_default,$(USEMODULE)))
   USEMODULE += gnrc_netif
+  USEMODULE += gnrc_netdev2
   USEMODULE += netdev_default
 endif
 
diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index 77d899f5e2..ce41876d3c 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -136,6 +136,10 @@ ifneq (,$(filter srf08,$(USEMODULE)))
   USEMODULE += xtimer
 endif
 
+ifneq (,$(filter w5100,$(USEMODULE)))
+  USEMODULE += netdev2_eth
+endif
+
 ifneq (,$(filter xbee,$(USEMODULE)))
   USEMODULE += ieee802154
   USEMODULE += xtimer
diff --git a/drivers/Makefile.include b/drivers/Makefile.include
index 667b05794d..1d2a7feaf8 100644
--- a/drivers/Makefile.include
+++ b/drivers/Makefile.include
@@ -70,3 +70,6 @@ endif
 ifneq (,$(filter lpd8808,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/lpd8808/include
 endif
+ifneq (,$(filter w5100,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/w5100/include
+endif
diff --git a/drivers/include/w5100.h b/drivers/include/w5100.h
new file mode 100644
index 0000000000..9cd72076c9
--- /dev/null
+++ b/drivers/include/w5100.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup    drivers_w5100 W5100
+ * @ingroup     drivers_netdev_netdev2
+ * @brief       Driver for W5100 ethernet devices
+ *
+ * This device driver only exposes the MACRAW mode of W5100 devices, so it does
+ * not offer any support for the on-chip IPv4, UDP, and TCP capabilities of
+ * these chips. In connection with RIOT we are only interested in the RAW
+ * Ethernet packets, which we can use through netdev2 with any software network
+ * stack provided by RIOT (e.g. GNRC). This enables W5100 devices to communicate
+ * via IPv6, enables unlimited connections, and more...
+ *
+ * @note        This driver expects to be triggered by the external interrupt
+ *              line of the W5100 device. On some Arduino shields this is not
+ *              enabled by default, you have to close the corresponding solder
+ *              bridge to make it work...
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for the W5100 device driver
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef W5100_H
+#define W5100_H
+
+#include <stdint.h>
+
+#include "periph/spi.h"
+#include "periph/gpio.h"
+#include "net/netdev2.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   W5100 error codes
+ */
+enum {
+    W5100_ERR_BUS = -1,
+};
+
+/**
+ * @brief   W5100 device descriptor
+ */
+typedef struct {
+    spi_t spi;              /**< SPI bus used */
+    spi_speed_t spi_speed;  /**< clock speed used on the selected SPI bus */
+    gpio_t cs;              /**< pin connected to the chip select line */
+    gpio_t evt;             /**< pin connected to the INT line */
+} w5100_params_t;
+
+/**
+ * @brief   Device descriptor for W5100 devices
+ */
+typedef struct {
+    netdev2_t nd;           /**< extends the netdev2 structure */
+    w5100_params_t p;       /**< device configuration parameters */
+} w5100_t;
+
+/**
+ * @brief   So the initial device setup
+ *
+ * This function pre-initializes the netdev2 structure, saves the configuration
+ * parameters and finally initializes the SPI bus and the used GPIO pins.
+ */
+void w5100_setup(w5100_t *dev, const w5100_params_t *params);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* W5100_h */
+/* @} */
diff --git a/drivers/w5100/Makefile b/drivers/w5100/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/drivers/w5100/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/w5100/include/w5100_params.h b/drivers/w5100/include/w5100_params.h
new file mode 100644
index 0000000000..315bfd5b68
--- /dev/null
+++ b/drivers/w5100/include/w5100_params.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_w5100
+ * @{
+ *
+ * @file
+ * @brief       Default parameters for W5100 Ethernet devices
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef W5100_PARAMS_H
+#define W5100_PARAMS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Set default configuration parameters for the W5100 driver
+ * @{
+ */
+#ifndef W5100_PARAM_SPI
+#define W5100_PARAM_SPI         (SPI_0)
+#endif
+#ifndef W5100_PARAM_SPI_SPEED
+#define W5100_PARAM_SPI_SPEED   (SPI_SPEED_5MHZ)
+#endif
+#ifndef W5100_PARAM_CS
+#define W5100_PARAM_CS          (GPIO_PIN(0, 0))
+#endif
+#ifndef W5100_PARAM_EVT
+#define W5100_PARAM_EVT         (GPIO_PIN(0, 1))
+#endif
+/** @} */
+
+/**
+ * @brief   W5100 configuration
+ */
+static const  w5100_params_t w5100_params[] = {
+    {
+        .spi       = W5100_PARAM_SPI,
+        .spi_speed = W5100_PARAM_SPI_SPEED,
+        .cs        = W5100_PARAM_CS,
+        .evt       = W5100_PARAM_EVT
+    },
+};
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* W5100_PARAMS_H */
+/** @} */
diff --git a/drivers/w5100/include/w5100_regs.h b/drivers/w5100/include/w5100_regs.h
new file mode 100644
index 0000000000..7d4fe77566
--- /dev/null
+++ b/drivers/w5100/include/w5100_regs.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_w5100
+ * @{
+ *
+ * @file
+ * @brief       Register definitions for W5100 devices
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef W5100_REGS_H
+#define W5100_REGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   SPI commands
+ * @{
+ */
+#define CMD_READ            (0x0f)
+#define CMD_WRITE           (0xf0)
+/** @} */
+
+/**
+ * @brief   Common registers
+ */
+#define REG_MODE            (0x0000)    /**< mode */
+#define REG_GAR0            (0x0001)    /**< gateway address 0 */
+#define REG_GAR1            (0x0002)    /**< gateway address 1 */
+#define REG_GAR2            (0x0003)    /**< gateway address 2 */
+#define REG_GAR3            (0x0004)    /**< gateway address 3 */
+#define REG_SUB0            (0x0005)    /**< subnet mask 0 */
+#define REG_SUB1            (0x0006)    /**< subnet mask 1 */
+#define REG_SUB2            (0x0007)    /**< subnet mask 2 */
+#define REG_SUB3            (0x0008)    /**< subnet mask 3 */
+#define REG_SHAR0           (0x0009)    /**< source hardware address 0 */
+#define REG_SHAR1           (0x000a)    /**< source hardware address 1 */
+#define REG_SHAR2           (0x000b)    /**< source hardware address 2 */
+#define REG_SHAR3           (0x000c)    /**< source hardware address 3 */
+#define REG_SHAR4           (0x000d)    /**< source hardware address 4 */
+#define REG_SHAR5           (0x000e)    /**< source hardware address 5 */
+#define REG_SIPR0           (0x000f)    /**< source IP address 0 */
+#define REG_SIPR1           (0x0010)    /**< source IP address 1 */
+#define REG_SIPR2           (0x0011)    /**< source IP address 2 */
+#define REG_SIPR3           (0x0012)    /**< source IP address 3 */
+#define REG_IR              (0x0015)    /**< interrupt flags */
+#define REG_IMR             (0x0016)    /**< interrupt masks */
+#define REG_RTR0            (0x0017)    /**< retry time 0 */
+#define REG_RTR1            (0x0018)    /**< retry time 1 */
+#define REG_RCR             (0x0019)    /**< retry count */
+#define REG_RMSR            (0x001a)    /**< RX memory size */
+#define REG_TMSR            (0x001b)    /**< TX memory size */
+#define REG_PATR0           (0x001c)    /**< PPPoE auth type 0 */
+#define REG_PATR1           (0x001d)    /**< PPPoE auth type 1 */
+#define REG_PTIMER          (0x0028)    /**< PPP LCP request timer */
+#define REG_PMAGIC          (0x0029)    /**< PPP LCP magic number */
+#define REG_UIPR0           (0x002a)    /**< unreachable IP address 0 */
+#define REG_UIPR1           (0x002b)    /**< unreachable IP address 1 */
+#define REG_UIPR2           (0x002c)    /**< unreachable IP address 2 */
+#define REG_UIPR3           (0x002d)    /**< unreachable IP address 3 */
+#define REG_UPORT0          (0x00fe)    /**< unreachable port 0 */
+#define REG_UPORT1          (0x002f)    /**< unreachable port 1 */
+/** @} */
+
+/**
+ * @brief   Socket 0 registers
+ *
+ * As we are using the device in MACRAW mode, we only need socket 0.
+ */
+#define S0_MR               (0x0400)    /**< mode */
+#define S0_CR               (0x0401)    /**< control */
+#define S0_IR               (0x0402)    /**< interrupt flags */
+#define S0_SR               (0x0403)    /**< state */
+#define S0_DHAR0            (0x0406)    /**< destination hardware address 0 */
+#define S0_DHAR1            (0x0407)    /**< destination hardware address 1 */
+#define S0_DHAR2            (0x0408)    /**< destination hardware address 2 */
+#define S0_DHAR3            (0x0409)    /**< destination hardware address 3 */
+#define S0_DHAR4            (0x040a)    /**< destination hardware address 4 */
+#define S0_DHAR5            (0x040b)    /**< destination hardware address 5 */
+#define S0_DIPR0            (0x040c)    /**< destination IP address 0 */
+#define S0_DIPR1            (0x040d)    /**< destination IP address 1 */
+#define S0_DIPR2            (0x040e)    /**< destination IP address 2 */
+#define S0_DIPR3            (0x040f)    /**< destination IP address 3 */
+#define S0_DPORT0           (0x0410)    /**< destination port 0 */
+#define S0_DPORT1           (0x0411)    /**< destination port 1 */
+#define S0_MSSR0            (0x0412)    /**< maximum segment size 0 */
+#define S0_MSSR1            (0x0413)    /**< maximum segment size 1 */
+#define S0_PROTO            (0x0414)    /**< protocol in IP raw mode */
+#define S0_TOS              (0x0415)    /**< IP TOS */
+#define S0_TTL              (0x0416)    /**< IP TTL */
+#define S0_TX_FSR0          (0x0420)    /**< TX free size 0 */
+#define S0_TX_FSR1          (0x0421)    /**< TX free size 1 */
+#define S0_TX_RD0           (0x0422)    /**< TX read pointer 0 */
+#define S0_TX_RD1           (0x0423)    /**< TX read pointer 1 */
+#define S0_TX_WR0           (0x0424)    /**< TX write pointer 0 */
+#define S0_TX_WR1           (0x0425)    /**< TX write pointer 1 */
+#define S0_RX_RSR0          (0x0426)    /**< RX receive size 0 */
+#define S0_RX_RSR1          (0x0427)    /**< RX receive size 1 */
+#define S0_RX_RD0           (0x0428)    /**< RX read pointer 0 */
+#define S0_RX_RD1           (0x0429)    /**< RX read pointer 1 */
+/** @} */
+
+/**
+ * @brief   Some selected bitfield definitions
+ */
+#define MODE_RESET          (0x80)      /**< device mode: reset */
+
+#define RMSR_8KB_TO_S0      (0x03)      /**< receive memory size: 8kib */
+#define TMSR_8KB_TO_S0      (0x03)      /**< transmit memory size: 8kib */
+
+#define IMR_S0_INT          (0x01)      /**< global socket 0 interrupt mask */
+
+#define MR_UDP              (0x02)      /**< socket mode: UDP */
+#define MR_MACRAW           (0x04)      /**< socket mode: raw Ethernet */
+
+#define CR_OPEN             (0x01)      /**< socket command: open */
+#define CR_CLOSE            (0x10)      /**< socket command: close */
+#define CR_SEND_MAC         (0x21)      /**< socket command: send raw */
+#define CR_RECV             (0x40)      /**< socket command: receive new data */
+
+#define IR_SEND_OK          (0x10)      /**< socket interrupt: send ok */
+#define IR_RECV             (0x04)      /**< socket interrupt: data received */
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* W5100_REGS_H */
+/** @} */
diff --git a/drivers/w5100/w5100.c b/drivers/w5100/w5100.c
new file mode 100644
index 0000000000..3c953553f8
--- /dev/null
+++ b/drivers/w5100/w5100.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2016 Freie Universität Berlin
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     drivers_w5100
+ * @{
+ *
+ * @file
+ * @brief       Device driver implementation for w5100 Ethernet devices
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "log.h"
+#include "assert.h"
+#include "periph/cpuid.h"
+
+#include "net/ethernet.h"
+#include "net/netdev2/eth.h"
+
+#include "w5100.h"
+#include "w5100_regs.h"
+
+#define ENABLE_DEBUG        (0)
+#include "debug.h"
+
+
+#define SPI_CONF            SPI_CONF_FIRST_RISING
+#define RMSR_DEFAULT_VALUE  (0x55)
+#define MAC_SEED            (0x23)
+
+#define S0_MEMSIZE          (0x2000)
+#define S0_MASK             (S0_MEMSIZE - 1)
+#define S0_TX_BASE          (0x4000)
+#define S0_RX_BASE          (0x6000)
+
+static const netdev2_driver_t netdev2_driver_w5100;
+
+static inline void send_addr(w5100_t *dev, uint16_t addr)
+{
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+    spi_transfer_byte(dev->p.spi, (addr >> 8), NULL);
+    spi_transfer_byte(dev->p.spi, (addr & 0xff), NULL);
+#else
+    spi_transfer_byte(dev->p.spi, (addr & 0xff), NULL);
+    spi_transfer_byte(dev->p.spi, (addr >> 8), NULL);
+#endif
+}
+
+static uint8_t rreg(w5100_t *dev, uint16_t reg)
+{
+    uint8_t data;
+
+    gpio_clear(dev->p.cs);
+    spi_transfer_byte(dev->p.spi, CMD_READ, NULL);
+    send_addr(dev, reg++);
+    spi_transfer_byte(dev->p.spi, 0, (char *)&data);
+    gpio_set(dev->p.cs);
+
+    return data;
+}
+
+static uint16_t raddr(w5100_t *dev, uint16_t addr_high, uint16_t addr_low)
+{
+    uint16_t res = (rreg(dev, addr_high) << 8);
+    res |= rreg(dev, addr_low);
+    return res;
+}
+
+static void rchunk(w5100_t *dev, uint16_t addr, uint8_t *data, size_t len)
+{
+    /* reading a chunk must be split in multiple single byte reads, as the
+     * device does not support auto address increment via SPI */
+    for (int i = 0; i < (int)len; i++) {
+        data[i] = rreg(dev, addr++);
+    }
+}
+
+static void wreg(w5100_t *dev, uint16_t reg, uint8_t data)
+{
+    gpio_clear(dev->p.cs);
+    spi_transfer_byte(dev->p.spi, CMD_WRITE, NULL);
+    send_addr(dev, reg);
+    spi_transfer_byte(dev->p.spi, data, NULL);
+    gpio_set(dev->p.cs);
+}
+
+static void waddr(w5100_t *dev,
+                  uint16_t addr_high, uint16_t addr_low, uint16_t val)
+{
+    wreg(dev, addr_high, (uint8_t)(val >> 8));
+    wreg(dev, addr_low, (uint8_t)(val & 0xff));
+}
+
+static void wchunk(w5100_t *dev, uint16_t addr, uint8_t *data, size_t len)
+{
+    /* writing a chunk must be split in multiple single byte writes, as the
+     * device does not support auto address increment via SPI */
+    for (int i = 0; i < (int)len; i++) {
+        wreg(dev, addr++, data[i]);
+    }
+}
+
+static void extint(void *arg)
+{
+    w5100_t *dev = (w5100_t *)arg;
+
+    if (dev->nd.event_callback) {
+        dev->nd.event_callback(&dev->nd, NETDEV2_EVENT_ISR);
+    }
+}
+
+void w5100_setup(w5100_t *dev, const w5100_params_t *params)
+{
+    /* initialize netdev structure */
+    dev->nd.driver = &netdev2_driver_w5100;
+    dev->nd.event_callback = NULL;
+    dev->nd.context = dev;
+
+    /* initialize the device descriptor */
+    memcpy(&dev->p, params, sizeof(w5100_params_t));
+
+    /* initialize pins and SPI */
+    gpio_init(dev->p.cs, GPIO_OUT);
+    gpio_set(dev->p.cs);
+    spi_init_master(dev->p.spi, SPI_CONF, dev->p.spi_speed);
+
+    gpio_init_int(dev->p.evt, GPIO_IN, GPIO_FALLING, extint, dev);
+}
+
+static int init(netdev2_t *netdev)
+{
+    w5100_t *dev = (w5100_t *)netdev;
+    uint8_t tmp;
+    uint8_t hwaddr[ETHERNET_ADDR_LEN];
+#if CPUID_LEN
+    uint8_t cpuid[CPUID_LEN];
+#endif
+
+    /* test the SPI connection by reading the value of the RMSR register */
+    tmp = rreg(dev, REG_TMSR);
+    if (tmp != RMSR_DEFAULT_VALUE) {
+        LOG_ERROR("[w5100] error: no SPI connection\n");
+        return W5100_ERR_BUS;
+    }
+
+    /* reset the device */
+    wreg(dev, REG_MODE, MODE_RESET);
+    while (rreg(dev, REG_MODE) & MODE_RESET) {};
+
+    /* initialize the device, start with writing the MAC address */
+    memset(hwaddr, MAC_SEED, ETHERNET_ADDR_LEN);
+#if CPUID_LEN
+    cpuid_get(cpuid);
+    for (int i = 0; i < CPUID_LEN; i++) {
+        hwaddr[i % ETHERNET_ADDR_LEN] ^= cpuid[i];
+    }
+#endif
+    hwaddr[0] &= ~0x03;         /* no group address and not globally unique */
+    wchunk(dev, REG_SHAR0, hwaddr, ETHERNET_ADDR_LEN);
+
+    /* configure all memory to be used by socket 0 */
+    wreg(dev, REG_RMSR, RMSR_8KB_TO_S0);
+    wreg(dev, REG_TMSR, TMSR_8KB_TO_S0);
+
+    /* configure interrupt pin to trigger on socket 0 events */
+    wreg(dev, REG_IMR, IMR_S0_INT);
+
+    /* next we configure socket 0 to work in MACRAW mode */
+    wreg(dev, S0_MR, MR_MACRAW);
+    wreg(dev, S0_CR, CR_OPEN);
+
+    /* start receiving packets */
+    wreg(dev, S0_CR, CR_RECV);
+
+    return 0;
+}
+
+static uint16_t tx_upload(w5100_t *dev, uint16_t start, void *data, size_t len)
+{
+    if ((start + len) >= (S0_TX_BASE + S0_MEMSIZE)) {
+        size_t limit = ((S0_TX_BASE + S0_MEMSIZE) - start);
+        wchunk(dev, start, data, limit);
+        wchunk(dev, S0_TX_BASE, &((uint8_t *)data)[limit], len - limit);
+        return (S0_TX_BASE + limit);
+    }
+    else {
+        wchunk(dev, start, data, len);
+        waddr(dev, S0_TX_WR0, S0_TX_WR1, start + len);
+        return (start + len);
+    }
+}
+
+static int send(netdev2_t *netdev, const struct iovec *vector, int count)
+{
+    w5100_t *dev = (w5100_t *)netdev;
+    int sum = 0;
+
+    uint16_t pos = raddr(dev, S0_TX_WR0, S0_TX_WR1);
+
+    /* the register is only set correctly after the first send pkt, so we need
+     * this fix here */
+    if (pos == 0) {
+        pos = S0_TX_BASE;
+    }
+
+    for (int i = 0; i < count; i++) {
+        pos = tx_upload(dev, pos, vector[i].iov_base, vector[i].iov_len);
+        sum += vector[i].iov_len;
+    }
+
+    waddr(dev, S0_TX_WR0, S0_TX_WR1, pos);
+
+    /* trigger the sending process */
+    wreg(dev, S0_CR, CR_SEND_MAC);
+    while (!(rreg(dev, S0_IR) & IR_SEND_OK)) {};
+    wreg(dev, S0_IR, IR_SEND_OK);
+
+    DEBUG("[w5100] send: transferred %i byte (at 0x%04x)\n", sum, (int)pos);
+
+    return sum;
+}
+
+static int recv(netdev2_t *netdev, char *buf, int len, void *info)
+{
+    w5100_t *dev = (w5100_t *)netdev;
+    int n = 0;
+
+    uint16_t num = raddr(dev, S0_RX_RSR0, S0_RX_RSR1);
+
+    if (num > 0) {
+        /* find the size of the next packet in the RX buffer */
+        uint16_t rp = raddr(dev, S0_RX_RD0, S0_RX_RD1);
+        uint16_t psize = raddr(dev, (S0_RX_BASE + (rp & S0_MASK)),
+                                  (S0_RX_BASE + ((rp + 1) & S0_MASK)));
+        n = psize - 2;
+
+        DEBUG("[w5100] recv: got packet of %i byte (at 0x%04x)\n", n, (int)rp);
+
+        /* read the actual data into the given buffer if wanted */
+        if (buf != NULL) {
+            uint16_t pos = rp + 2;
+            len = (n <= len) ? n : len;
+            for (int i = 0; i < (int)len; i++) {
+                buf[i] = rreg(dev, (S0_RX_BASE + ((pos++) & S0_MASK)));
+            }
+
+            DEBUG("[w5100] recv: read %i byte from device (at 0x%04x)\n",
+                  n, (int)rp);
+
+            /* set the new read pointer address */
+            waddr(dev, S0_RX_RD0, S0_RX_RD1, rp += psize);
+            wreg(dev, S0_CR, CR_RECV);
+
+            /* if RX buffer now empty, clear RECV interrupt flag */
+            if ((num - psize) == 0) {
+                wreg(dev, S0_IR, IR_RECV);
+            }
+        }
+    }
+
+    return n;
+}
+
+static void isr(netdev2_t *netdev)
+{
+    w5100_t *dev = (w5100_t *)netdev;
+
+    /* we only react on RX events, and if we see one, we read from the RX buffer
+     * until it is empty */
+    while (rreg(dev, S0_IR) & IR_RECV) {
+        DEBUG("[w5100] netdev2 RX complete\n");
+        netdev->event_callback(netdev, NETDEV2_EVENT_RX_COMPLETE);
+    }
+}
+
+static int get(netdev2_t *netdev, netopt_t opt, void *value, size_t max_len)
+{
+    w5100_t *dev = (w5100_t *)netdev;
+    int res = 0;
+
+    switch (opt) {
+        case NETOPT_ADDRESS:
+            assert(max_len >= ETHERNET_ADDR_LEN);
+            rchunk(dev, REG_SHAR0, value, ETHERNET_ADDR_LEN);
+            res = ETHERNET_ADDR_LEN;
+            break;
+        default:
+            res = netdev2_eth_get(netdev, opt, value, max_len);
+            break;
+    }
+
+    return res;
+}
+
+static const netdev2_driver_t netdev2_driver_w5100 = {
+    .send = send,
+    .recv = recv,
+    .init = init,
+    .isr = isr,
+    .get = get,
+    .set = netdev2_eth_set,
+};
-- 
GitLab