From 6c80d905968601ea397d63809690b3c9754f3146 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20M=C3=B6ller?= <fredrik.moller@rt-labs.com>
Date: Tue, 15 Sep 2020 18:22:53 +0200
Subject: [PATCH] rtkernel for Profinet

Changes made to rt-kernel tree from rt-collab
Toolbox 2021.1 as to support the p-net Profinet
stack:
- lwip:
  - Build SNMP server.
  - Add SNMP callbacks for MIB-II system variables.
  - Fix SNMP ifDescr in ifTable.
  - Discard invalid UDP packets.
  - VLAN tag support.
  - Lock mutex while handling received frame
    instead of passing message to lwip thread
    (LWIP_TCPIP_CORE_LOCKING_INPUT).
  - Add hook function to be called when frame of
    unknown EtherType is received, such as LLDP
    and Profinet (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL).
- drivers/eth/dwmac1000:
  - Use zero copy for Rx.
  - Add ioctl for getting link status.
  - Lock mutex for Tx, as p-net stack may send
    frames outside of lwip context.
  - Report accurate ifSpeed to SNMP.
- drivers/eth/dwgmac:
  - Add ioctl for getting link status.
  - Lock mutex for Tx, as p-net stack may send
    frames outside of lwip context.
  - Report accurate ifSpeed to SNMP.
- drivers/eth/phy:
  - Add function for retrieving link capabilities.
- bsp/xmc48relax:
  - Static IP addressing.
  - Increase stack size for main task.
  - Increase stack size for Ethernet driver task.
  - Let Ethernet driver task have higher priority
    than lwip task.
  - Put .bss and heap in 256kB memory section.
- bsp/h750dk:
  - Increase stack size for main task.
  - Increase stack size for Ethernet driver task.
  - Let Ethernet driver task have higher priority
    than lwip task.

Change-Id: Ie335fd029b503db842d3ca16331629444db70b9a
---
 bsp/h750dk/include/config.h               |   2 +-
 bsp/h750dk/src/lwip.c                     |   8 +-
 bsp/xmc48relax/include/config.h           |  10 +-
 bsp/xmc48relax/src/lwip.c                 |   4 +-
 bsp/xmc48relax/xmc48relax.ld              |  24 +-
 drivers/eth/dwgmac.c                      |  89 ++++++-
 drivers/eth/dwmac1000.c                   | 162 ++++++++---
 drivers/eth/phy/mii.c                     |  13 +-
 include/drivers/eth/phy/mii.h             |   1 +
 include/drivers/eth/phy/phy.h             |   9 +
 include/drivers/ioctl.h                   |  51 ++++
 kern/arch/xmc4/hal.h                      |   2 +-
 lwip/CMakeLists.txt                       |   1 +
 lwip/src/apps/Makefile                    |  12 +
 lwip/src/apps/snmp/Makefile               |  16 ++
 lwip/src/apps/snmp/snmp_mib2_interfaces.c |   6 +-
 lwip/src/apps/snmp/snmp_mib2_system.c     | 310 ++--------------------
 lwip/src/core/lwip_hooks.c                |  23 ++
 lwip/src/core/udp.c                       |  12 +
 lwip/src/include/lwip/apps/snmp_mib2.h    |  24 +-
 lwip/src/include/lwip/lwip_hooks.h        |  41 +++
 lwip/src/include/lwip/lwipopts.h          |  39 ++-
 22 files changed, 511 insertions(+), 348 deletions(-)
 create mode 100644 lwip/src/apps/Makefile
 create mode 100644 lwip/src/apps/snmp/Makefile
 create mode 100644 lwip/src/core/lwip_hooks.c
 create mode 100644 lwip/src/include/lwip/lwip_hooks.h

diff --git a/bsp/h750dk/include/config.h b/bsp/h750dk/include/config.h
index 05ed51b8b..4963aad37 100644
--- a/bsp/h750dk/include/config.h
+++ b/bsp/h750dk/include/config.h
@@ -107,7 +107,7 @@
  * implemented in the user application and is the task that will be
  * started immediately after the kernel has finished initialising.
  */
-#define CFG_MAIN_STACK_SIZE     1280
+#define CFG_MAIN_STACK_SIZE     14374
 
 /* Configure the priority of the main task. */
 #define CFG_MAIN_PRIORITY       10
diff --git a/bsp/h750dk/src/lwip.c b/bsp/h750dk/src/lwip.c
index e22b17f33..9a7281005 100644
--- a/bsp/h750dk/src/lwip.c
+++ b/bsp/h750dk/src/lwip.c
@@ -26,8 +26,8 @@
 #define RX_BUFFERS 8            /* Number of Rx buffers */
 #define TX_BUFFERS 8            /* Number of Tx buffers */
 
-#define RX_BUFFER_SIZE 1520     /* Max size of Rx buffers */
-#define TX_BUFFER_SIZE 1520     /* Max size of Tx buffers */
+#define RX_BUFFER_SIZE PBUF_POOL_BUFSIZE     /* Max size of Rx buffers */
+#define TX_BUFFER_SIZE PBUF_POOL_BUFSIZE     /* Max size of Tx buffers */
 
 static dwgmac_descriptor_t rx_dma[RX_BUFFERS] CC_ATTRIBUTE_SECTION (".dma");
 static dwgmac_descriptor_t tx_dma[TX_BUFFERS] CC_ATTRIBUTE_SECTION (".dma");
@@ -114,8 +114,8 @@ err_t bsp_eth_init (struct netif * netif)
          .buffer_size = TX_BUFFER_SIZE,
          .num_buffers = TX_BUFFERS,
       },
-      .rx_task_prio  = TCPIP_THREAD_PRIO - 1,
-      .rx_task_stack = 1100,
+      .rx_task_prio  = TCPIP_THREAD_PRIO + 1,
+      .rx_task_stack = 6250,
       .mac_address   = CFG_LWIP_MAC_ADDRESS
    };
 
diff --git a/bsp/xmc48relax/include/config.h b/bsp/xmc48relax/include/config.h
index 91549e483..54cb9d39f 100644
--- a/bsp/xmc48relax/include/config.h
+++ b/bsp/xmc48relax/include/config.h
@@ -53,14 +53,14 @@
  *   CFG_LWIP_NAMESERVER        IP address of DNS server
  */
 #define CFG_LWIP_MAC_ADDRESS       {0x1E, 0x30, 0x6C, 0xA2, 0x45, 0x5E}
-#define CFG_LWIP_ADDRESS_DYNAMIC
+#undef CFG_LWIP_ADDRESS_DYNAMIC
 #if defined(CFG_LWIP_ADDRESS_DYNAMIC)
    #define CFG_LWIP_HOSTNAME       "xmc48relax"
 #else
-   #define CFG_LWIP_IPADDR()       IP4_ADDR (&ipaddr, 192, 168, 10, 171)
+   #define CFG_LWIP_IPADDR()       IP4_ADDR (&ipaddr, 192, 168, 0, 50)
    #define CFG_LWIP_NETMASK()      IP4_ADDR (&netmask, 255, 255, 255, 0)
-   #define CFG_LWIP_GATEWAY()      IP4_ADDR (&gw, 192, 168, 10, 1)
-   #define CFG_LWIP_NAMESERVER()   IP4_ADDR (&nameserver, 192, 168, 10, 4)
+   #define CFG_LWIP_GATEWAY()      IP4_ADDR (&gw, 192, 168, 0, 1)
+   #define CFG_LWIP_NAMESERVER()   IP4_ADDR (&nameserver, 0, 0, 0, 0)
 #endif
 
 /* Enable SD Card if defined */
@@ -132,7 +132,7 @@
  * implemented in the user application and is the task that will be
  * started immediately after the kernel has finished initialising.
  */
-#define CFG_MAIN_STACK_SIZE     1280
+#define CFG_MAIN_STACK_SIZE     14374
 
 /* Configure the priority of the main task. */
 #define CFG_MAIN_PRIORITY       10
diff --git a/bsp/xmc48relax/src/lwip.c b/bsp/xmc48relax/src/lwip.c
index f8b7d94af..858a794bc 100644
--- a/bsp/xmc48relax/src/lwip.c
+++ b/bsp/xmc48relax/src/lwip.c
@@ -94,8 +94,8 @@ err_t bsp_eth_init (struct netif * netif)
       .hclk          = CFG_ETHCLK_FREQUENCY,
       .rx_buffers    = 3,
       .tx_buffers    = 5,
-      .rx_task_prio  = TCPIP_THREAD_PRIO - 1,
-      .rx_task_stack = 1100,
+      .rx_task_prio  = TCPIP_THREAD_PRIO + 1,
+      .rx_task_stack = 6250,
       .mac_address   = CFG_LWIP_MAC_ADDRESS,
    };
 #ifdef CFG_RTC_INIT
diff --git a/bsp/xmc48relax/xmc48relax.ld b/bsp/xmc48relax/xmc48relax.ld
index 9deadfd32..3e1ac030a 100644
--- a/bsp/xmc48relax/xmc48relax.ld
+++ b/bsp/xmc48relax/xmc48relax.ld
@@ -18,6 +18,7 @@ EXTERN(vectors)
  * sram: The Code RAM is intended for user code or Operating System data storage. The
  * memory is accessed via the Bus Matrix and provides zero-wait-state access for the CPU
  * for code execution or data access.
+ * NOTE: May not be used by USB and ETH modules.
  *
  * sdsram: The System RAM is intended for general user data storage. The System RAM is
  * accessed via the Bus Matrix and provides zero-wait-state access for data.
@@ -30,8 +31,7 @@ MEMORY
 {
    flash : org = 0x08000000, len = 2M
    sram  : org = 0x1FFE8000, len = 96K
-   sdsram : org = 0x20000000, len = 128K
-   cdsram : org = 0x20020000, len = 128K
+   ram   : org = 0x20000000, len = 256K /* sdsram + cdsram */
 }
 
 SECTIONS
@@ -85,19 +85,23 @@ SECTIONS
 
    /* Put the IRQ-stack at the start of RAM as to
     * provoke CPU exception upon overflow. */
-   .bss :
+   .os_stuff :
    {
         KEEP (*(.irqstack));
         irq_stack_top = .;
+        event_start = .;
+        KEEP (*(.event));
+        event_end = .;
+   } > sram
+
+   .bss :
+   {
         bss_start = .;
         *(.bss*)
         *(COMMON)
         . = ALIGN(4);
         bss_end = .;
-        event_start = .;
-        KEEP (*(.event));
-        event_end = .;
-   } > sram
+   } > ram
 
    .data : AT (data_start_rom)
    {
@@ -106,13 +110,13 @@ SECTIONS
         *(.data*)
         . = ALIGN(4);
         data_end_ram = .;
-   } > sram
+   } > ram
 
    .sdsram (NOLOAD) :
    {
         heap_start = .;
-   } > sdsram
+   } > ram
 
-   heap_end = ORIGIN(sdsram) + LENGTH(sdsram);
+   heap_end = ORIGIN(ram) + LENGTH(ram);
    heap_size = heap_end - heap_start;
 }
diff --git a/drivers/eth/dwgmac.c b/drivers/eth/dwgmac.c
index 91518e412..e54bc5443 100644
--- a/drivers/eth/dwgmac.c
+++ b/drivers/eth/dwgmac.c
@@ -274,6 +274,7 @@ typedef struct dwgmac
    drv_t drv;
 
    irq_t irq;
+   mtx_t * mtx_tx;
    task_t * tRcv;
    phy_t * phy;
    volatile eth_mac_t * mac;
@@ -291,6 +292,19 @@ typedef struct dwgmac
 
    /* MIIAR CR divider */
    uint32_t cr;
+
+   /* Link state retrieved in last call to probe function
+    * See defines PHY_LINK_OK, PHY_LINK_100MBIT, etc.
+    */
+   uint8_t last_link_state;
+
+   /* Link auto-negotiation capabilities read from PHY at startup
+    * See defines PHY_CAPABILITY_100_FD, PHY_CAPABILITY_100, etc.
+    */
+   uint16_t link_capabilities;
+
+   /* lwip network interface handle */
+   struct netif * netif;
 } dwgmac_t;
 
 #ifdef DEBUG_DATA
@@ -325,6 +339,24 @@ static void dwgmac_pbuf_dump (struct pbuf *p)
 }
 #endif  /* DEBUG_DATA */
 
+#if MIB2_STATS
+static uint32_t dwgmac_snmp_link_speed (uint8_t link_state)
+{
+   if (link_state & PHY_LINK_10MBIT)
+   {
+      return 10 * 1000 * 1000;
+   }
+   else if (link_state & PHY_LINK_100MBIT)
+   {
+      return 100 * 1000 * 1000;
+   }
+   else
+   {
+      return 0;
+   }
+}
+#endif /* MIB2_STATS */
+
 static uint16_t dwgmac_read_phy (void * arg, uint8_t address, uint8_t reg)
 {
    dwgmac_t *dwgmac = arg;
@@ -490,6 +522,9 @@ static err_t dwgmac_hw_transmit_frame (struct netif *netif, struct pbuf *p)
       return ERR_OK;
    }
 
+   /* Lock TX handling */
+   mtx_lock (dwgmac->mtx_tx);
+
 #if ETH_PAD_SIZE
    pbuf_header (p, -ETH_PAD_SIZE);  /* drop the padding word */
 #endif
@@ -548,6 +583,7 @@ static err_t dwgmac_hw_transmit_frame (struct netif *netif, struct pbuf *p)
    pbuf_header (p, ETH_PAD_SIZE);   /* reclaim the padding word */
 #endif
 
+   mtx_unlock (dwgmac->mtx_tx);
    return ERR_OK;
 }
 
@@ -763,16 +799,64 @@ static dev_state_t dwgmac_probe (drv_t * drv)
 
    link_state = dwgmac->phy->ops->get_link_state (dwgmac->phy);
 
+   /* Save link state for IOCTL_ETH_GET_STATUS */
+   dwgmac->last_link_state = link_state;
+
+   /* Set ifSpeed in SNMP MIB-II ifTable */
+#if MIB2_STATS
+   dwgmac->netif->link_speed = dwgmac_snmp_link_speed (link_state);
+#endif /* MIB2_STATS */
+
+
    return (link_state & PHY_LINK_OK) ? StateAttached : StateDetached;
 }
 
+static void dwgmac_get_status (
+   dwgmac_t * dwgmac,
+   struct netif * netif,
+   ioctl_eth_status_t * link)
+{
+   if (dwgmac->phy->loopback_mode)
+   {
+      link->is_autonegotiation_supported = false;
+      link->is_autonegotiation_enabled = false;
+      link->capabilities = 0x0000;
+   }
+   else
+   {
+      link->is_autonegotiation_supported = true;
+      link->is_autonegotiation_enabled = true;
+      link->capabilities = dwgmac->link_capabilities;
+   }
+
+   link->state = dwgmac->last_link_state;
+   link->is_operational = netif_is_up (netif) && netif_is_link_up (netif);
+}
+
+static int dwgmac_ioctl (drv_t * drv, void * arg, int req, void * param)
+{
+   dwgmac_t * dwgmac = (dwgmac_t *)drv;
+   int status = EARG;
+
+   switch (req)
+   {
+   case IOCTL_ETH_GET_STATUS:
+      dwgmac_get_status (dwgmac, arg, param);
+      return 0;
+   default:
+      UASSERT (0, EARG);
+   }
+
+   return status;
+}
+
 static const drv_ops_t dwgmac_ops =
 {
    .open    = NULL,
    .read    = NULL,
    .write   = NULL,
    .close   = NULL,
-   .ioctl   = NULL,
+   .ioctl   = dwgmac_ioctl,
    .hotplug = dwgmac_hotplug,
 };
 
@@ -802,6 +886,7 @@ drv_t * dwgmac_init (const char * name, const dwgmac_cfg_t * cfg,
    /* Initialise driver state */
    dwgmac->phy = phy;
    dwgmac->irq = cfg->irq;
+   dwgmac->mtx_tx = mtx_create();
    dwgmac->mac = (eth_mac_t *)cfg->base;
    dwgmac->mtl = (eth_mtl_t *)(cfg->base + 0xC00);
    dwgmac->dma = (eth_dma_t *)(cfg->base + 0x1000);
@@ -813,6 +898,8 @@ drv_t * dwgmac_init (const char * name, const dwgmac_cfg_t * cfg,
    dwgmac->tx.num_buffers = cfg->tx.num_buffers;
    dwgmac->rx.ix = 0;
    dwgmac->tx.ix = 0;
+   dwgmac->last_link_state = 0x00;
+   dwgmac->netif = netif;
 
    /* Create receive task */
    dwgmac->tRcv = task_spawn ("EthRcv",
diff --git a/drivers/eth/dwmac1000.c b/drivers/eth/dwmac1000.c
index ece017a59..456986211 100644
--- a/drivers/eth/dwmac1000.c
+++ b/drivers/eth/dwmac1000.c
@@ -26,7 +26,13 @@
 
 #include <string.h>
 
-#define BUFFER_SIZE 1520
+/* Align DMA buffer with lwIP POOL buf size
+ * One RX DMA buffer will fit in one POOL buf.
+ */
+#define BUFFER_SIZE PBUF_POOL_BUFSIZE
+COMPILETIME_ASSERT (BUFFER_SIZE >= 1520);
+COMPILETIME_ASSERT ((BUFFER_SIZE % 4) == 0);
+
 #define RX_MAX_BUFFERS 10
 #define TX_MAX_BUFFERS 10
 
@@ -194,6 +200,7 @@ typedef struct dma_descriptor
    uint32_t des1;
    uint8_t * buff;
    volatile struct dma_descriptor * next;
+   struct pbuf * pbuf;
 } dma_descriptor_t;
 
 #define xDES0_OWN BIT(31)        /* Owned by DMA engine */
@@ -215,6 +222,7 @@ typedef struct dwmac1000
    drv_t drv;
 
    irq_t irq;
+   mtx_t * mtx_tx;
    task_t * tRcv;
    phy_t * phy;
    volatile eth_mac_t * mac;
@@ -229,6 +237,19 @@ typedef struct dwmac1000
 
    /* MIIAR CR divider */
    uint32_t cr;
+
+   /* Link state retrieved in last call to probe function
+    * See defines PHY_LINK_OK, PHY_LINK_100MBIT, etc.
+    */
+   uint8_t last_link_state;
+
+   /* Link auto-negotiation capabilities read from PHY at startup
+    * See defines PHY_CAPABILITY_100_FD, PHY_CAPABILITY_100, etc.
+    */
+   uint16_t link_capabilities;
+
+   /* lwip network interface handle */
+   struct netif * netif;
 } dwmac1000_t;
 
 #ifdef DEBUG_DATA
@@ -263,6 +284,24 @@ static void dwmac1000_pbuf_dump (struct pbuf *p)
 }
 #endif  /* DEBUG_DATA */
 
+#if MIB2_STATS
+static uint32_t dwmac1000_snmp_link_speed (uint8_t link_state)
+{
+   if (link_state & PHY_LINK_10MBIT)
+   {
+      return 10 * 1000 * 1000;
+   }
+   else if (link_state & PHY_LINK_100MBIT)
+   {
+      return 100 * 1000 * 1000;
+   }
+   else
+   {
+      return 0;
+   }
+}
+#endif /* MIB2_STATS */
+
 static uint16_t dwmac1000_read_phy (void * arg, uint8_t address, uint8_t reg)
 {
    dwmac1000_t *dwmac1000 = arg;
@@ -326,6 +365,13 @@ static void dwmac1000_hw_init (struct netif *netif, const dwmac1000_cfg_t * cfg)
    DEBUG_ASSERT (dwmac1000->phy->ops->start != NULL);
    dwmac1000->phy->ops->start (dwmac1000->phy);
 
+   /* Read auto-negotiation capabilities from PHY */
+   if (dwmac1000->phy->ops->get_link_capabilities)
+   {
+      dwmac1000->link_capabilities =
+         dwmac1000->phy->ops->get_link_capabilities (dwmac1000->phy);
+   }
+
    /* Enable receiver, transmitter */
    mac->cr = CR_RE | CR_TE;
 
@@ -352,13 +398,18 @@ static void dwmac1000_hw_init (struct netif *netif, const dwmac1000_cfg_t * cfg)
    /* Setup Rx descriptors using chained mode */
    for (ix = 0; ix < cfg->rx_buffers; ix++)
    {
-      uint8_t * p = malloc (BUFFER_SIZE);
+      /* Allocate a pbuf from the pool.
+       * Note that pbuf will be unchained (pbuf->next==NULL) as allocated size
+       * fits in a single pool buffer.
+       */
+      struct pbuf * p = pbuf_alloc (PBUF_RAW, BUFFER_SIZE, PBUF_POOL);
       UASSERT (p != NULL, EMEM);
 
       dwmac1000->rx[ix].des0 = xDES0_OWN;
       dwmac1000->rx[ix].des1 = RDES1_RCH | BUFFER_SIZE;
-      dwmac1000->rx[ix].buff = p;
+      dwmac1000->rx[ix].buff = p->payload;
       dwmac1000->rx[ix].next = &dwmac1000->rx[ix + 1];
+      dwmac1000->rx[ix].pbuf = p;
    }
    dwmac1000->rx[ix - 1].next = &dwmac1000->rx[0]; /* Point to first */
    dwmac1000->pRx = &dwmac1000->rx[0];             /* Driver index */
@@ -373,6 +424,7 @@ static void dwmac1000_hw_init (struct netif *netif, const dwmac1000_cfg_t * cfg)
       dwmac1000->tx[ix].des0 = TDES0_TCH;
       dwmac1000->tx[ix].buff = p;
       dwmac1000->tx[ix].next = &dwmac1000->tx[ix + 1];
+      dwmac1000->tx[ix].pbuf = NULL;
    }
    dwmac1000->tx[ix - 1].next = &dwmac1000->tx[0]; /* Point to first */
    dwmac1000->pTx = &dwmac1000->tx[0];             /* Driver index */
@@ -420,6 +472,9 @@ static err_t dwmac1000_hw_transmit_frame (struct netif *netif, struct pbuf *p)
       return ERR_OK;
    }
 
+   /* Lock TX handling */
+   mtx_lock (dwmac1000->mtx_tx);
+
 #if ETH_PAD_SIZE
    pbuf_header (p, -ETH_PAD_SIZE);  /* drop the padding word */
 #endif
@@ -478,6 +533,7 @@ static err_t dwmac1000_hw_transmit_frame (struct netif *netif, struct pbuf *p)
    pbuf_header (p, ETH_PAD_SIZE);   /* reclaim the padding word */
 #endif
 
+   mtx_unlock (dwmac1000->mtx_tx);
    return ERR_OK;
 }
 
@@ -513,7 +569,7 @@ static struct pbuf *dwmac1000_hw_get_received_frame (struct netif * netif)
    struct pbuf *p = NULL;
    uint16_t length;
    volatile dma_descriptor_t * pRx;
-   uint8_t * buffer;
+   struct pbuf * q;
    uint32_t des0;
 
    pRx = dwmac1000->pRx;
@@ -527,39 +583,33 @@ static struct pbuf *dwmac1000_hw_get_received_frame (struct netif * netif)
    if ((des0 & (RDES0_FS | RDES0_LS)) != (RDES0_FS | RDES0_LS))
       goto done;
 
-   buffer = pRx->buff;
+   /* Fetch received pbuf */
+   p = pRx->pbuf;
+   p->payload = pRx->buff;
    length = (des0 >> 16) & 0x3FFF;
-   length -= 4;                 /* Drop CRC */
+   length -= 4; /* Drop CRC */
+   p->len = p->tot_len = length;
+
+   DPRINT ("in (%d):\n", p->len);
+#ifdef DEBUG_DATA
+   dwmac1000_pbuf_dump (q);
+#endif /* DEBUG_DATA */
 
 #if ETH_PAD_SIZE
 #error "ETH_PAD_SIZE not supported"
 #endif
 
-   /* We allocate a pbuf chain of pbufs from the pool. */
-   p = pbuf_alloc (PBUF_RAW, length, PBUF_POOL);
+   /* Allocate a pbuf from the pool to replace the one currently use by DMA.
+    * Note that pbuf will be unchained (pbuf->next==NULL) as allocated size
+    * fits in a single pool buffer.
+    */
+   q = pbuf_alloc (PBUF_RAW, BUFFER_SIZE, PBUF_POOL);
 
-   if (p != NULL)
+   if (q != NULL)
    {
-      struct pbuf *q;
-
-      /* We iterate over the pbuf chain until we have read the entire
-       * packet into the pbuf.
-       */
-      for (q = p; q != NULL; q = q->next)
-      {
-         /* Read enough bytes to fill this pbuf in the chain. The
-          * available data in the pbuf is given by the q->len
-          * variable.
-          */
-
-         memcpy (q->payload, buffer, q->len);
-         buffer += q->len;
-
-         DPRINT ("in (%d):\n", q->len);
-#ifdef DEBUG_DATA
-         dwmac1000_pbuf_dump (q);
-#endif  /* DEBUG_DATA */
-      }
+      ASSERT (q->len == q->tot_len);
+      pRx->buff = q->payload;
+      pRx->pbuf = q;
 
       MIB2_STATS_NETIF_ADD (netif, ifinoctets, p->tot_len);
       if (IS_UNICAST (p))
@@ -575,6 +625,7 @@ static struct pbuf *dwmac1000_hw_get_received_frame (struct netif * netif)
    }
    else
    {
+      p = NULL;
       LINK_STATS_INC (link.memerr);
       LINK_STATS_INC (link.drop);
       MIB2_STATS_NETIF_INC (netif, ifindiscards);
@@ -694,16 +745,63 @@ static dev_state_t dwmac1000_probe (drv_t * drv)
 
    link_state = dwmac1000->phy->ops->get_link_state (dwmac1000->phy);
 
+   /* Save link state for IOCTL_ETH_GET_STATUS */
+   dwmac1000->last_link_state = link_state;
+
+   /* Set ifSpeed in SNMP MIB-II ifTable */
+#if MIB2_STATS
+   dwmac1000->netif->link_speed = dwmac1000_snmp_link_speed (link_state);
+#endif /* MIB2_STATS */
+
    return (link_state & PHY_LINK_OK) ? StateAttached : StateDetached;
 }
 
+static void dwmac1000_get_status (
+   dwmac1000_t * dwmac1000,
+   struct netif * netif,
+   ioctl_eth_status_t * link)
+{
+   if (dwmac1000->phy->loopback_mode)
+   {
+      link->is_autonegotiation_supported = false;
+      link->is_autonegotiation_enabled = false;
+      link->capabilities = 0x0000;
+   }
+   else
+   {
+      link->is_autonegotiation_supported = true;
+      link->is_autonegotiation_enabled = true;
+      link->capabilities = dwmac1000->link_capabilities;
+   }
+
+   link->state = dwmac1000->last_link_state;
+   link->is_operational = netif_is_up (netif) && netif_is_link_up (netif);
+}
+
+static int dwmac1000_ioctl (drv_t * drv, void * arg, int req, void * param)
+{
+   dwmac1000_t * dwmac1000 = (dwmac1000_t *)drv;
+   int status = EARG;
+
+   switch (req)
+   {
+   case IOCTL_ETH_GET_STATUS:
+      dwmac1000_get_status (dwmac1000, arg, param);
+      return 0;
+   default:
+      UASSERT (0, EARG);
+   }
+
+   return status;
+}
+
 static const drv_ops_t dwmac1000_ops =
 {
    .open    = NULL,
    .read    = NULL,
    .write   = NULL,
    .close   = NULL,
-   .ioctl   = NULL,
+   .ioctl   = dwmac1000_ioctl,
    .hotplug = dwmac1000_hotplug,
 };
 
@@ -714,6 +812,7 @@ drv_t * dwmac1000_init (const char * name, const dwmac1000_cfg_t * cfg,
 
    UASSERT (cfg->hclk >= 20 * 1000 * 1000, EARG);
    UASSERT (cfg->rx_buffers <= RX_MAX_BUFFERS, EARG);
+   UASSERT (cfg->rx_buffers < PBUF_POOL_SIZE, EARG);
    UASSERT (cfg->tx_buffers <= TX_MAX_BUFFERS, EARG);
 
    dwmac1000 = malloc (sizeof (dwmac1000_t));
@@ -735,9 +834,12 @@ drv_t * dwmac1000_init (const char * name, const dwmac1000_cfg_t * cfg,
    /* Initialise driver state */
    dwmac1000->phy = phy;
    dwmac1000->irq = cfg->irq;
+   dwmac1000->mtx_tx = mtx_create();
    dwmac1000->mac = (eth_mac_t *)cfg->base;
    dwmac1000->mmc = (eth_mmc_t *)(cfg->base + 0x100);
    dwmac1000->dma = (eth_dma_t *)(cfg->base + 0x1000);
+   dwmac1000->last_link_state = 0x00;
+   dwmac1000->netif = netif;
 
    /* Create receive task */
    dwmac1000->tRcv = task_spawn ("EthRcv",
diff --git a/drivers/eth/phy/mii.c b/drivers/eth/phy/mii.c
index 2cbaef613..7eb31315b 100644
--- a/drivers/eth/phy/mii.c
+++ b/drivers/eth/phy/mii.c
@@ -305,11 +305,18 @@ uint8_t mii_get_link_state (phy_t * phy)
 	return link_state;
 }
 
+uint16_t  mii_get_link_capabilities (struct phy * phy)
+{
+   uint16_t anar = phy->read (phy->arg, phy->address, MII_ANAR);
+   return anar;
+}
+
 static phy_ops_t mii_ops =
 {
-   .reset          = mii_reset,
-   .start          = mii_start,
-   .get_link_state = mii_get_link_state
+   .reset                 = mii_reset,
+   .start                 = mii_start,
+   .get_link_state        = mii_get_link_state,
+   .get_link_capabilities = mii_get_link_capabilities,
 };
 
 phy_t * mii_init (const phy_cfg_t * cfg)
diff --git a/include/drivers/eth/phy/mii.h b/include/drivers/eth/phy/mii.h
index 57364bdc6..dd69ceaee 100644
--- a/include/drivers/eth/phy/mii.h
+++ b/include/drivers/eth/phy/mii.h
@@ -19,6 +19,7 @@ phy_t * mii_init (const phy_cfg_t * cfg);
 void mii_reset (phy_t * phy);
 void mii_start (phy_t * phy);
 uint8_t mii_get_link_state (phy_t * phy);
+uint16_t mii_get_link_capabilities (phy_t * phy);
 
 /** \internal Exposed for testing purposes only
  *
diff --git a/include/drivers/eth/phy/phy.h b/include/drivers/eth/phy/phy.h
index 22aea4ccf..a34a0a616 100644
--- a/include/drivers/eth/phy/phy.h
+++ b/include/drivers/eth/phy/phy.h
@@ -19,6 +19,14 @@
 #define PHY_LINK_1000MBIT    BIT (3)
 #define PHY_LINK_FULL_DUPLEX BIT (4)
 
+/* Capabilities advertised to link partner during auto-negotiation.
+ * Bit patterns are the same as the Auto-Negotiation Advertisement Register.
+ */
+#define PHY_CAPABILITY_100_FD  BIT (8) /* Can do 100BASE-TX full duplex */
+#define PHY_CAPABILITY_100     BIT (7) /* Can do 100BASE-TX */
+#define PHY_CAPABILITY_10_FD   BIT (6) /* Can do 10BASE-T full duplex */
+#define PHY_CAPABILITY_10      BIT (5) /* Can do 10BASE-T */
+
 struct phy;
 
 typedef struct phy_ops
@@ -26,6 +34,7 @@ typedef struct phy_ops
    void (*reset) (struct phy * phy);
    void (*start) (struct phy * phy);
    uint8_t  (*get_link_state) (struct phy * phy);
+   uint16_t (*get_link_capabilities) (struct phy * phy);
 } phy_ops_t;
 
 typedef struct phy
diff --git a/include/drivers/ioctl.h b/include/drivers/ioctl.h
index 4471b4ae6..d7be0d261 100644
--- a/include/drivers/ioctl.h
+++ b/include/drivers/ioctl.h
@@ -142,6 +142,57 @@ typedef struct ioctl_i2c_access
 
 #define IOCTL_CAN_LOOPBACK 0x701 /**< Set CAN module in loopback state */
 
+/**
+ * Status of Ethernet link
+ */
+typedef struct ioctl_eth_status
+{
+   /** Capabilities advertised to link partner during auto-negotiation
+    *
+    * This forms a bit pattern. Valid bit values (see driver/eth/phy/phy.h):
+    * - PHY_CAPABILITY_100_FD,
+    * - PHY_CAPABILITY_100,
+    * - PHY_CAPABILITY_10_FD,
+    * - PHY_CAPABILITY_10.
+    */
+   uint16_t capabilities;
+
+   /** Is auto-negotiation process supported by physical layer? */
+   bool is_autonegotiation_supported;
+
+   /** Is auto-negotiation process enabled for physical layer? */
+   bool is_autonegotiation_enabled;
+
+   /**
+    * Is link operational?
+    *
+    * True if link is up and network interface is administratively up.
+    * False if any of those are down.
+    */
+   bool is_operational;
+
+   /**
+    * Link state
+    *
+    * Bit pattern of the following:
+    * - PHY_LINK_OK is set if link is up,
+    * - PHY_LINK_10MBIT, PHY_LINK_100MBIT or PHY_LINK_1000MBIT is the speed,
+    * - PHY_LINK_FULL_DUPLEX is set if link is full-duplex and cleared if
+    *   it is half-duplex.
+    */
+   uint8_t state;
+} ioctl_eth_status_t;
+
+/**
+ * Get status of network interface
+ *
+ * \code
+ * ioctl_eth_status_t status;
+ * int error = ioctl (fd, IOCTL_ETH_GET_STATUS, &status);
+ * \endcode
+ */
+#define IOCTL_ETH_GET_STATUS 0x801
+
 /* ioctl is not defined by newlib */
 int ioctl (int fd, int request, ...);
 
diff --git a/kern/arch/xmc4/hal.h b/kern/arch/xmc4/hal.h
index 20694b6bb..58fd606d9 100644
--- a/kern/arch/xmc4/hal.h
+++ b/kern/arch/xmc4/hal.h
@@ -18,7 +18,7 @@
 
 /* Set the maximum allocation size in bytes per malloc.
  * Maximum size is 2^HAL_MAXIMUM_ALLOCATION_SIZE_POWER bytes */
-#define HAL_MAXIMUM_ALLOCATION_SIZE_POWER 17 /* 128 kB */
+#define HAL_MAXIMUM_ALLOCATION_SIZE_POWER 18 /* 256 kB */
 
 /* This macro enables interrupts globally. */
 #define HAL_INTERRUPT_UNLOCK()                  \
diff --git a/lwip/CMakeLists.txt b/lwip/CMakeLists.txt
index 1e5a3d2bd..122fde5fb 100644
--- a/lwip/CMakeLists.txt
+++ b/lwip/CMakeLists.txt
@@ -16,6 +16,7 @@ file(GLOB SOURCES CONFIGURE_DEPENDS
   src/core/*.c
   src/core/ipv4/*.c
   src/netif/*.c
+  src/apps/snmp/*.c
   )
 add_library(lwip ${SOURCES})
 
diff --git a/lwip/src/apps/Makefile b/lwip/src/apps/Makefile
new file mode 100644
index 000000000..e92bb4eed
--- /dev/null
+++ b/lwip/src/apps/Makefile
@@ -0,0 +1,12 @@
+#********************************************************************
+#        _       _         _
+#  _ __ | |_  _ | |  __ _ | |__   ___
+# | '__|| __|(_)| | / _` || '_ \ / __|
+# | |   | |_  _ | || (_| || |_) |\__ \
+# |_|    \__|(_)|_| \__,_||_.__/ |___/
+#
+# http://www.rt-labs.com
+# Copyright 2013 rt-labs AB, Sweden.
+# See LICENSE file in the project root for full license information.
+#*******************************************************************/
+include $(PRJ_ROOT)/make/subdir.mk
diff --git a/lwip/src/apps/snmp/Makefile b/lwip/src/apps/snmp/Makefile
new file mode 100644
index 000000000..e1383bfe1
--- /dev/null
+++ b/lwip/src/apps/snmp/Makefile
@@ -0,0 +1,16 @@
+#********************************************************************
+#        _       _         _
+#  _ __ | |_  _ | |  __ _ | |__   ___
+# | '__|| __|(_)| | / _` || '_ \ / __|
+# | |   | |_  _ | || (_| || |_) |\__ \
+# |_|    \__|(_)|_| \__,_||_.__/ |___/
+#
+# http://www.rt-labs.com
+# Copyright 2013 rt-labs AB, Sweden.
+# See LICENSE file in the project root for full license information.
+#*******************************************************************/
+OBJDIR = ../../../obj/$(ARCH)/$(VARIANT)/$(CPU)
+
+override EXTRA_CFLAGS += -Wno-enum-compare
+
+include $(PRJ_ROOT)/make/subdir.mk
diff --git a/lwip/src/apps/snmp/snmp_mib2_interfaces.c b/lwip/src/apps/snmp/snmp_mib2_interfaces.c
index 5f12dd552..85e80f9b0 100644
--- a/lwip/src/apps/snmp/snmp_mib2_interfaces.c
+++ b/lwip/src/apps/snmp/snmp_mib2_interfaces.c
@@ -159,6 +159,7 @@ interfaces_Table_get_value(struct snmp_node_instance *instance, void *value)
   struct netif *netif = (struct netif *)instance->reference.ptr;
   u32_t *value_u32 = (u32_t *)value;
   s32_t *value_s32 = (s32_t *)value;
+  char *value_string = (char *)value;
   u16_t value_len;
 
   switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) {
@@ -167,8 +168,9 @@ interfaces_Table_get_value(struct snmp_node_instance *instance, void *value)
       value_len = sizeof(*value_s32);
       break;
     case 2: /* ifDescr */
-      value_len = sizeof(netif->name);
-      MEMCPY(value, netif->name, value_len);
+      value_len = sizeof(netif->name) + 1;
+      MEMCPY(value_string, netif->name, sizeof(netif->name));
+      value_string[sizeof(netif->name)] = '0' + netif->num;
       break;
     case 3: /* ifType */
       *value_s32 = netif->link_type;
diff --git a/lwip/src/apps/snmp/snmp_mib2_system.c b/lwip/src/apps/snmp/snmp_mib2_system.c
index 71c1c29c0..9331b6080 100644
--- a/lwip/src/apps/snmp/snmp_mib2_system.c
+++ b/lwip/src/apps/snmp/snmp_mib2_system.c
@@ -56,309 +56,57 @@
 
 /* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
 
-/** mib-2.system.sysDescr */
-static const u8_t   sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
-static const u8_t  *sysdescr           = sysdescr_default;
-static const u16_t *sysdescr_len       = NULL; /* use strlen for determining len */
-
-/** mib-2.system.sysContact */
-static const u8_t   syscontact_default[]     = SNMP_LWIP_MIB2_SYSCONTACT;
-static const u8_t  *syscontact               = syscontact_default;
-static const u16_t *syscontact_len           = NULL; /* use strlen for determining len */
-static u8_t        *syscontact_wr            = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
-static u16_t       *syscontact_wr_len        = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
-static u16_t        syscontact_bufsize       = 0;    /* 0=not writable */
-
-/** mib-2.system.sysName */
-static const u8_t   sysname_default[]        = SNMP_LWIP_MIB2_SYSNAME;
-static const u8_t  *sysname                  = sysname_default;
-static const u16_t *sysname_len              = NULL; /* use strlen for determining len */
-static u8_t        *sysname_wr               = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
-static u16_t       *sysname_wr_len           = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
-static u16_t        sysname_bufsize          = 0;    /* 0=not writable */
-
-/** mib-2.system.sysLocation */
-static const u8_t   syslocation_default[]    = SNMP_LWIP_MIB2_SYSLOCATION;
-static const u8_t  *syslocation              = syslocation_default;
-static const u16_t *syslocation_len           = NULL; /* use strlen for determining len */
-static u8_t        *syslocation_wr            = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
-static u16_t       *syslocation_wr_len        = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
-static u16_t        syslocation_bufsize       = 0;    /* 0=not writable */
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysDescr pointers.
- *
- * @param str if non-NULL then copy str pointer
- * @param len points to string length, excluding zero terminator
- */
-void
-snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
-{
-  if (str != NULL) {
-    sysdescr     = str;
-    sysdescr_len = len;
-  }
-}
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysContact pointers
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator.
- *        if set to NULL it is assumed that ocstr is NULL-terminated.
- * @param bufsize size of the buffer in bytes.
- *        (this is required because the buffer can be overwritten by snmp-set)
- *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
- *        otherwise complete buffer is used for string.
- *        if bufsize is set to 0, the value is regarded as read-only.
- */
-void
-snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
-{
-  if (ocstr != NULL) {
-    syscontact         = ocstr;
-    syscontact_wr      = ocstr;
-    syscontact_len     = ocstrlen;
-    syscontact_wr_len  = ocstrlen;
-    syscontact_bufsize = bufsize;
-  }
-}
-
-/**
- * @ingroup snmp_mib2
- * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
- */
-void
-snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+static snmp_mib2_get_callback_fct snmp_mib2_system_get;
+static snmp_mib2_test_set_callback_fct snmp_mib2_system_test_set;
+static snmp_mib2_set_callback_fct snmp_mib2_system_set;
+
+void snmp_mib2_system_set_callbacks(
+  snmp_mib2_get_callback_fct get,
+  snmp_mib2_test_set_callback_fct test_set,
+  snmp_mib2_set_callback_fct set)
 {
-  if (ocstr != NULL) {
-    syscontact         = ocstr;
-    syscontact_len     = ocstrlen;
-    syscontact_wr      = NULL;
-    syscontact_wr_len  = NULL;
-    syscontact_bufsize = 0;
-  }
+  snmp_mib2_system_get = get;
+  snmp_mib2_system_test_set = test_set;
+  snmp_mib2_system_set = set;
 }
 
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysName pointers
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator.
- *        if set to NULL it is assumed that ocstr is NULL-terminated.
- * @param bufsize size of the buffer in bytes.
- *        (this is required because the buffer can be overwritten by snmp-set)
- *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
- *        otherwise complete buffer is used for string.
- *        if bufsize is set to 0, the value is regarded as read-only.
- */
-void
-snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
-{
-  if (ocstr != NULL) {
-    sysname         = ocstr;
-    sysname_wr      = ocstr;
-    sysname_len     = ocstrlen;
-    sysname_wr_len  = ocstrlen;
-    sysname_bufsize = bufsize;
-  }
-}
-
-/**
- * @ingroup snmp_mib2
- * see \ref snmp_mib2_set_sysname but set pointer to readonly memory
- */
-void
-snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
-{
-  if (ocstr != NULL) {
-    sysname         = ocstr;
-    sysname_len     = ocstrlen;
-    sysname_wr      = NULL;
-    sysname_wr_len  = NULL;
-    sysname_bufsize = 0;
-  }
-}
-
-/**
- * @ingroup snmp_mib2
- * Initializes sysLocation pointers
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator.
- *        if set to NULL it is assumed that ocstr is NULL-terminated.
- * @param bufsize size of the buffer in bytes.
- *        (this is required because the buffer can be overwritten by snmp-set)
- *        if ocstrlen is NULL buffer needs space for terminating 0 byte.
- *        otherwise complete buffer is used for string.
- *        if bufsize is set to 0, the value is regarded as read-only.
- */
-void
-snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
-{
-  if (ocstr != NULL) {
-    syslocation         = ocstr;
-    syslocation_wr      = ocstr;
-    syslocation_len     = ocstrlen;
-    syslocation_wr_len  = ocstrlen;
-    syslocation_bufsize = bufsize;
-  }
-}
-
-/**
- * @ingroup snmp_mib2
- * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
- */
-void
-snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
-{
-  if (ocstr != NULL) {
-    syslocation         = ocstr;
-    syslocation_len     = ocstrlen;
-    syslocation_wr      = NULL;
-    syslocation_wr_len  = NULL;
-    syslocation_bufsize = 0;
-  }
-}
-
-
 static s16_t
 system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
 {
-  const u8_t  *var = NULL;
-  const s16_t *var_len;
-  u16_t result;
-
-  switch (node->oid) {
-    case 1: /* sysDescr */
-      var     = sysdescr;
-      var_len = (const s16_t *)sysdescr_len;
-      break;
-    case 2: { /* sysObjectID */
-      const struct snmp_obj_id *dev_enterprise_oid = snmp_get_device_enterprise_oid();
-      MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
-      return dev_enterprise_oid->len * sizeof(u32_t);
-    }
-    case 3: /* sysUpTime */
-      MIB2_COPY_SYSUPTIME_TO((u32_t *)value);
-      return sizeof(u32_t);
-    case 4: /* sysContact */
-      var     = syscontact;
-      var_len = (const s16_t *)syscontact_len;
-      break;
-    case 5: /* sysName */
-      var     = sysname;
-      var_len = (const s16_t *)sysname_len;
-      break;
-    case 6: /* sysLocation */
-      var     = syslocation;
-      var_len = (const s16_t *)syslocation_len;
-      break;
-    case 7: /* sysServices */
-      *(s32_t *)value = SNMP_SYSSERVICES;
-      return sizeof(s32_t);
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_get_value(): unknown id: %"S32_F"\n", node->oid));
-      return 0;
+  if (snmp_mib2_system_get == NULL)
+  {
+    return SNMP_ERR_GENERROR;
   }
-
-  /* handle string values (OID 1,4,5 and 6) */
-  LWIP_ASSERT("", (value != NULL));
-  if (var_len == NULL) {
-    result = (s16_t)strlen((const char *)var);
-  } else {
-    result = *var_len;
+  else
+  {
+    return snmp_mib2_system_get(node->oid, value, SNMP_MAX_VALUE_SIZE);
   }
-  MEMCPY(value, var, result);
-  return result;
 }
 
 static snmp_err_t
 system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
 {
-  snmp_err_t ret = SNMP_ERR_WRONGVALUE;
-  const u16_t *var_bufsize  = NULL;
-  const u16_t *var_wr_len;
-
-  LWIP_UNUSED_ARG(value);
-
-  switch (node->oid) {
-    case 4: /* sysContact */
-      var_bufsize  = &syscontact_bufsize;
-      var_wr_len   = syscontact_wr_len;
-      break;
-    case 5: /* sysName */
-      var_bufsize  = &sysname_bufsize;
-      var_wr_len   = sysname_wr_len;
-      break;
-    case 6: /* sysLocation */
-      var_bufsize  = &syslocation_bufsize;
-      var_wr_len   = syslocation_wr_len;
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_test(): unknown id: %"S32_F"\n", node->oid));
-      return ret;
+  if (snmp_mib2_system_test_set == NULL)
+  {
+    return SNMP_ERR_GENERROR;
   }
-
-  /* check if value is writable at all */
-  if (*var_bufsize > 0) {
-    if (var_wr_len == NULL) {
-      /* we have to take the terminating 0 into account */
-      if (len < *var_bufsize) {
-        ret = SNMP_ERR_NOERROR;
-      }
-    } else {
-      if (len <= *var_bufsize) {
-        ret = SNMP_ERR_NOERROR;
-      }
-    }
-  } else {
-    ret = SNMP_ERR_NOTWRITABLE;
+  else
+  {
+    return snmp_mib2_system_test_set(node->oid, value, len);
   }
-
-  return ret;
 }
 
 static snmp_err_t
 system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
 {
-  u8_t  *var_wr = NULL;
-  u16_t *var_wr_len;
-
-  switch (node->oid) {
-    case 4: /* sysContact */
-      var_wr     = syscontact_wr;
-      var_wr_len = syscontact_wr_len;
-      break;
-    case 5: /* sysName */
-      var_wr     = sysname_wr;
-      var_wr_len = sysname_wr_len;
-      break;
-    case 6: /* sysLocation */
-      var_wr     = syslocation_wr;
-      var_wr_len = syslocation_wr_len;
-      break;
-    default:
-      LWIP_DEBUGF(SNMP_MIB_DEBUG, ("system_set_value(): unknown id: %"S32_F"\n", node->oid));
-      return SNMP_ERR_GENERROR;
+  if (snmp_mib2_system_set == NULL)
+  {
+    return SNMP_ERR_GENERROR;
   }
-
-  /* no need to check size of target buffer, this was already done in set_test method */
-  LWIP_ASSERT("", var_wr != NULL);
-  MEMCPY(var_wr, value, len);
-
-  if (var_wr_len == NULL) {
-    /* add terminating 0 */
-    var_wr[len] = 0;
-  } else {
-    *var_wr_len = len;
+  else
+  {
+    return snmp_mib2_system_set(node->oid, value, len);
   }
-
-  return SNMP_ERR_NOERROR;
 }
 
 static const struct snmp_scalar_array_node_def system_nodes[] = {
diff --git a/lwip/src/core/lwip_hooks.c b/lwip/src/core/lwip_hooks.c
new file mode 100644
index 000000000..ad6229f82
--- /dev/null
+++ b/lwip/src/core/lwip_hooks.c
@@ -0,0 +1,23 @@
+#include "lwip/lwip_hooks.h"
+
+#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+static netif_input_fn lwip_hook_for_unknown_eth_protocol;
+
+err_enum_t lwip_hook_unknown_eth_protocol(struct pbuf *pbuf, struct netif *netif)
+{
+  if(lwip_hook_for_unknown_eth_protocol == NULL)
+  {
+    /* Not handled. User needs to free pbuf */
+    return ERR_IF;
+  }
+  else
+  {
+    return lwip_hook_for_unknown_eth_protocol(pbuf, netif);
+  }
+}
+
+void lwip_set_hook_for_unknown_eth_protocol(struct netif *netif, netif_input_fn hook)
+{
+  lwip_hook_for_unknown_eth_protocol = hook;
+}
+#endif /* LWIP_HOOK_UNKNOWN_ETH_PROTOCOL */
diff --git a/lwip/src/core/udp.c b/lwip/src/core/udp.c
index 0b609d333..05166989c 100644
--- a/lwip/src/core/udp.c
+++ b/lwip/src/core/udp.c
@@ -372,6 +372,18 @@ udp_input(struct pbuf *p, struct netif *inp)
       goto end;
     }
 
+    /* rtlaka: discard invalid UDP packets to pass PNIO tester */
+    if ((ntohs(udphdr->len) - UDP_HLEN) != p->len) {
+       /* drop invalid packets */
+      LWIP_DEBUGF(UDP_DEBUG,
+                  ("udp_input: invalid UDP length (%"U16_F" bytes) discarded\n", p->tot_len));
+      UDP_STATS_INC(udp.lenerr);
+      UDP_STATS_INC(udp.drop);
+      MIB2_STATS_INC(mib2.udpinerrors);
+      pbuf_free(p);
+      goto end;
+    }
+
     if (pcb != NULL) {
       MIB2_STATS_INC(mib2.udpindatagrams);
 #if SO_REUSE && SO_REUSE_RXTOALL
diff --git a/lwip/src/include/lwip/apps/snmp_mib2.h b/lwip/src/include/lwip/apps/snmp_mib2.h
index 2f4a68935..1df6bb0dc 100644
--- a/lwip/src/include/lwip/apps/snmp_mib2.h
+++ b/lwip/src/include/lwip/apps/snmp_mib2.h
@@ -60,13 +60,23 @@ extern struct snmp_threadsync_instance snmp_mib2_lwip_locks;
 #define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
 #endif
 
-void snmp_mib2_set_sysdescr(const u8_t* str, const u16_t* len); /* read-only be defintion */
-void snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
-void snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
-void snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
-void snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
-void snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
-void snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+typedef s16_t(*snmp_mib2_get_callback_fct)(
+   u32_t column,
+   void *value,
+   size_t size);
+typedef snmp_err_t(*snmp_mib2_test_set_callback_fct)(
+   u32_t column,
+   const void *value,
+   size_t size);
+typedef snmp_err_t(*snmp_mib2_set_callback_fct)(
+   u32_t column,
+   const void *value,
+   size_t size);
+
+void snmp_mib2_system_set_callbacks(
+  snmp_mib2_get_callback_fct get, 
+  snmp_mib2_test_set_callback_fct test_set,
+  snmp_mib2_set_callback_fct set);
 
 #endif /* SNMP_LWIP_MIB2 */
 #endif /* LWIP_SNMP */
diff --git a/lwip/src/include/lwip/lwip_hooks.h b/lwip/src/include/lwip/lwip_hooks.h
new file mode 100644
index 000000000..c48f1c57b
--- /dev/null
+++ b/lwip/src/include/lwip/lwip_hooks.h
@@ -0,0 +1,41 @@
+/**
+ * Hook functions
+ *
+ * Declares hook functions called by lwip.
+ * Also declared API for configuring hook functions.
+ *
+ * The name of this file is specified as LWIP_HOOK_FILENAME in lwipopts.h.
+ */
+
+#ifndef LWIP_HOOKS_H
+#define LWIP_HOOKS_H
+
+#include "lwip/netif.h"
+
+/**
+ * LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+ *
+ * Called from ethernet_input() when an unknown eth type is encountered.
+ *
+ * By default, this will do nothing and return ERR_IF.
+ * If a hook function has been set in lwip_set_hook_for_unknown_eth_protocol(),
+ * then that function will be called.
+ *
+ * \param pbuf  Payload points to ethernet header!
+ * \param netif Network interface.
+ * \return ERR_OK if packet is accepted and freed, 
+ *         ERR_IF otherwise.
+ */
+err_enum_t lwip_hook_unknown_eth_protocol(struct pbuf *pbuf, struct netif *netif);
+
+/**
+ * Configure function to be called by lwip_hook_unknown_eth_protocol()
+ *
+ *\param netif Network interface.
+ *\param hook  Hook function to be called when frame with unknown eth type
+ *             is encountered. Should return ERR_OK for accepted and freed
+ *             frames, ERR_IF otherwise.
+ */
+void lwip_set_hook_for_unknown_eth_protocol(struct netif *netif, netif_input_fn hook);
+
+#endif /* LWIP_HOOKS_H */
diff --git a/lwip/src/include/lwip/lwipopts.h b/lwip/src/include/lwip/lwipopts.h
index 34d44ecc0..480045bc3 100644
--- a/lwip/src/include/lwip/lwipopts.h
+++ b/lwip/src/include/lwip/lwipopts.h
@@ -44,12 +44,33 @@
 #define LWIP_NETIF_LINK_CALLBACK    1
 #define LWIP_NETIF_STATUS_CALLBACK  1
 #define LWIP_NETIF_LOOPBACK         1
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 1
 #define LWIP_SOCKET                 1
 #define LWIP_IGMP                   1
 #define LWIP_TCP_KEEPALIVE          1
 #define LWIP_SO_RCVTIMEO            1
 #define LWIP_SO_SNDTIMEO            1
+#define LWIP_SNMP                   1
+#define SNMP_USE_NETCONN            1
+#define SNMP_USE_RAW                0
+#define MIB2_STATS                  1
 #define SO_REUSE                    1
+#define ETHARP_SUPPORT_VLAN         1
+
+/**
+ * LWIP_HOOK_FILENAME: Custom filename to #include in files that provide hooks.
+ * Declare your hook function prototypes in there, you may also #include all headers
+ * providing data types that are need in this file.
+ */
+#define LWIP_HOOK_FILENAME "lwip/lwip_hooks.h"
+
+/**
+ * LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif):
+ * Called from ethernet_input() when an unknown eth type is encountered.
+ * Return ERR_OK if packet is accepted, any error code otherwise.
+ * Payload points to ethernet header!
+ */
+#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL lwip_hook_unknown_eth_protocol
 
 /**
  * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes
@@ -60,20 +81,33 @@
  */
 #define LWIP_DHCP_AUTOIP_COOP_TRIES 2
 
+/* Supported services. Should be 78 for Profinet. See IETF RFC 3418. */
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | (1 << 2) | (1 << 1))
+
 /* Various memory size options */
 #define MEM_SIZE                    (6 * 1024)
 #define MEMP_NUM_UDP_PCB            6
-#define PBUF_POOL_SIZE              8
+#define PBUF_POOL_SIZE              (8 + 3)
 #define TCP_MSS                     1460 /* Should be 1460 for Ethernet IPv4 */
 #define TCP_SND_BUF                 (2 * TCP_MSS)
+#define SNMP_MAX_OBJ_ID_LEN         32
+
+/* Max size + VLAN tag + extra PNET buffer */
+#define PROFINET_INFO_SIZE          0
+#define PBUF_FCS_SIZE               4
+#define PBUF_POOL_BUFSIZE           LWIP_MEM_ALIGN_SIZE(1518 + PBUF_FCS_SIZE  + PROFINET_INFO_SIZE)
 
 /* Stack sizes */
 #define DEFAULT_THREAD_STACKSIZE    1024
 #define TCPIP_THREAD_STACKSIZE      1768
+#define SNMP_STACK_SIZE             4000
 
 /* TCPIP thread priority */
 #define TCPIP_THREAD_PRIO           5
 
+/* SNMP server thread priority */
+#define SNMP_THREAD_PRIO            3
+
 /* Mailbox sizes */
 #define TCPIP_MBOX_SIZE             128
 #define DEFAULT_RAW_RECVMBOX_SIZE   5
@@ -106,6 +140,7 @@
  *    LWIP_DBG_OFF
  *    LWIP_DBG_ON
  */
+#define PBUF_DEBUG                  LWIP_DBG_OFF
 #define IP_DEBUG                    LWIP_DBG_OFF
 #define IGMP_DEBUG                  LWIP_DBG_OFF
 #define TCPIP_DEBUG                 LWIP_DBG_OFF
@@ -120,5 +155,7 @@
 #define TCP_FR_DEBUG                LWIP_DBG_OFF
 #define TCP_QLEN_DEBUG              LWIP_DBG_OFF
 #define TCP_RST_DEBUG               LWIP_DBG_OFF
+#define SNMP_DEBUG                  LWIP_DBG_OFF
+#define SNMP_MIB_DEBUG              LWIP_DBG_OFF
 
 #endif  /* __LWIPOPTS_H__ */
-- 
2.25.1

