aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George2019-02-22 22:20:48 +1100
committerDamien George2019-02-26 23:32:19 +1100
commitc950a1a35db0bee6ee2d1da514021885496c4a45 (patch)
tree8f9aac17f8c026d924f50bb793686e805ed9eaba
parentb6791ffbbef5ecf7e7d42ee19d87ff3253a44eda (diff)
stm32/eth: Add low-level Ethernet MAC driver.
-rw-r--r--ports/stm32/Makefile2
-rw-r--r--ports/stm32/eth.c613
-rw-r--r--ports/stm32/eth.h39
-rw-r--r--ports/stm32/lwip_inc/lwipopts.h1
4 files changed, 655 insertions, 0 deletions
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index 20a26f234..f50924668 100644
--- a/ports/stm32/Makefile
+++ b/ports/stm32/Makefile
@@ -110,6 +110,7 @@ SRC_LIB = $(addprefix lib/,\
oofatfs/option/unicode.c \
mp-readline/readline.c \
netutils/netutils.c \
+ netutils/trace.c \
timeutils/timeutils.c \
utils/pyexec.c \
utils/interrupt_char.c \
@@ -240,6 +241,7 @@ SRC_C = \
can.c \
usb.c \
wdt.c \
+ eth.c \
gccollect.c \
help.c \
machine_i2c.c \
diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c
new file mode 100644
index 000000000..c3a18bde7
--- /dev/null
+++ b/ports/stm32/eth.c
@@ -0,0 +1,613 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+#include "py/mphal.h"
+#include "py/mperrno.h"
+#include "lib/netutils/netutils.h"
+#include "lwip/etharp.h"
+#include "lwip/dns.h"
+#include "lwip/dhcp.h"
+#include "netif/ethernet.h"
+#include "pin_static_af.h"
+#include "modnetwork.h"
+#include "eth.h"
+
+#if defined(MICROPY_HW_ETH_MDC)
+
+// ETH PHY register definitions (for LAN8742)
+
+#undef PHY_BCR
+#define PHY_BCR (0x0000)
+#define PHY_BCR_SOFT_RESET (0x8000)
+#define PHY_BCR_AUTONEG_EN (0x1000)
+
+#undef PHY_BSR
+#define PHY_BSR (0x0001)
+#define PHY_BSR_LINK_STATUS (0x0004)
+#define PHY_BSR_AUTONEG_DONE (0x0020)
+
+#define PHY_SCSR (0x001f)
+#define PHY_SCSR_SPEED_Pos (2)
+#define PHY_SCSR_SPEED_Msk (7 << PHY_SCSR_SPEED_Pos)
+#define PHY_SCSR_SPEED_10HALF (1 << PHY_SCSR_SPEED_Pos)
+#define PHY_SCSR_SPEED_10FULL (5 << PHY_SCSR_SPEED_Pos)
+#define PHY_SCSR_SPEED_100HALF (2 << PHY_SCSR_SPEED_Pos)
+#define PHY_SCSR_SPEED_100FULL (6 << PHY_SCSR_SPEED_Pos)
+
+// ETH DMA RX and TX descriptor definitions
+
+#define RX_DESCR_0_OWN_Pos (31)
+#define RX_DESCR_0_FL_Pos (16)
+#define RX_DESCR_0_FL_Msk (0x3fff << RX_DESCR_0_FL_Pos)
+#define RX_DESCR_1_RER_Pos (15)
+#define RX_DESCR_1_RCH_Pos (14)
+#define RX_DESCR_1_RBS2_Pos (16)
+#define RX_DESCR_1_RBS1_Pos (0)
+
+#define TX_DESCR_0_OWN_Pos (31)
+#define TX_DESCR_0_LS_Pos (29)
+#define TX_DESCR_0_FS_Pos (28)
+#define TX_DESCR_0_DP_Pos (26)
+#define TX_DESCR_0_CIC_Pos (22)
+#define TX_DESCR_0_TER_Pos (21)
+#define TX_DESCR_0_TCH_Pos (20)
+#define TX_DESCR_1_TBS1_Pos (0)
+
+// Configuration values
+
+#define PHY_INIT_TIMEOUT_MS (10000)
+
+#define RX_BUF_SIZE (1524) // includes 4-byte CRC at end
+#define TX_BUF_SIZE (1524)
+
+#define RX_BUF_NUM (5)
+#define TX_BUF_NUM (5)
+
+typedef struct _eth_dma_rx_descr_t {
+ volatile uint32_t rdes0, rdes1, rdes2, rdes3;
+} eth_dma_rx_descr_t;
+
+typedef struct _eth_dma_tx_descr_t {
+ volatile uint32_t tdes0, tdes1, tdes2, tdes3;
+} eth_dma_tx_descr_t;
+
+typedef struct _eth_dma_t {
+ eth_dma_rx_descr_t rx_descr[RX_BUF_NUM];
+ eth_dma_tx_descr_t tx_descr[TX_BUF_NUM];
+ uint8_t rx_buf[RX_BUF_NUM * RX_BUF_SIZE] __attribute__((aligned(4)));
+ uint8_t tx_buf[TX_BUF_NUM * TX_BUF_SIZE] __attribute__((aligned(4)));
+ size_t rx_descr_idx;
+ size_t tx_descr_idx;
+ uint8_t padding[16384 - 15408];
+} eth_dma_t;
+
+typedef struct _eth_t {
+ mod_network_nic_type_t base;
+ uint32_t trace_flags;
+ struct netif netif;
+ struct dhcp dhcp_struct;
+} eth_t;
+
+static eth_dma_t eth_dma __attribute__((aligned(16384)));
+
+eth_t eth_instance;
+
+STATIC void eth_mac_deinit(eth_t *self);
+STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf);
+
+STATIC void eth_phy_write(uint32_t reg, uint32_t val) {
+ while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
+ }
+ ETH->MACMIIDR = val;
+ uint32_t ar = ETH->MACMIIAR;
+ ar = reg << ETH_MACMIIAR_MR_Pos | (ar & ETH_MACMIIAR_CR_Msk) | ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
+ ETH->MACMIIAR = ar;
+ while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
+ }
+}
+
+STATIC uint32_t eth_phy_read(uint32_t reg) {
+ while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
+ }
+ uint32_t ar = ETH->MACMIIAR;
+ ar = reg << ETH_MACMIIAR_MR_Pos | (ar & ETH_MACMIIAR_CR_Msk) | ETH_MACMIIAR_MB;
+ ETH->MACMIIAR = ar;
+ while (ETH->MACMIIAR & ETH_MACMIIAR_MB) {
+ }
+ return ETH->MACMIIDR;
+}
+
+STATIC void mpu_config(uint32_t region, uint32_t base_addr, uint32_t size) {
+ __DMB();
+
+ // Disable MPU
+ SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
+ MPU->CTRL = 0;
+
+ // Config MPU region
+ MPU->RNR = region;
+ MPU->RBAR = base_addr;
+ MPU->RASR =
+ MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RASR_XN_Pos
+ | MPU_REGION_FULL_ACCESS << MPU_RASR_AP_Pos
+ | MPU_TEX_LEVEL1 << MPU_RASR_TEX_Pos
+ | MPU_ACCESS_SHAREABLE << MPU_RASR_S_Pos
+ | MPU_ACCESS_NOT_CACHEABLE << MPU_RASR_C_Pos
+ | MPU_ACCESS_NOT_BUFFERABLE << MPU_RASR_B_Pos
+ | 0x00 << MPU_RASR_SRD_Pos
+ | size << MPU_RASR_SIZE_Pos
+ | MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos;
+
+ // Enable MPU
+ MPU->CTRL = MPU_PRIVILEGED_DEFAULT | MPU_CTRL_ENABLE_Msk;
+ SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
+
+ __DSB();
+ __ISB();
+}
+
+void eth_init(eth_t *self, int mac_idx) {
+ mp_hal_get_mac(mac_idx, &self->netif.hwaddr[0]);
+ self->netif.hwaddr_len = 6;
+}
+
+void eth_set_trace(eth_t *self, uint32_t value) {
+ self->trace_flags = value;
+}
+
+STATIC int eth_mac_init(eth_t *self) {
+ // Configure MPU
+ mpu_config(MPU_REGION_NUMBER0, (uint32_t)&eth_dma, MPU_REGION_SIZE_16KB);
+
+ // Configure GPIO
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDC, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDC);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDIO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDIO);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_REF_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_REF_CLK);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_CRS_DV, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_CRS_DV);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD0);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD1);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TX_EN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TX_EN);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD0);
+ mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD1);
+
+ __HAL_RCC_ETH_CLK_ENABLE();
+ __HAL_RCC_ETHMAC_FORCE_RESET();
+
+ // Select RMII interface
+ __HAL_RCC_SYSCFG_CLK_ENABLE();
+ SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
+
+ __HAL_RCC_ETHMAC_RELEASE_RESET();
+
+ __HAL_RCC_ETHMAC_CLK_SLEEP_ENABLE();
+ __HAL_RCC_ETHMACTX_CLK_SLEEP_ENABLE();
+ __HAL_RCC_ETHMACRX_CLK_SLEEP_ENABLE();
+
+ // Do a soft reset of the MAC core
+ ETH->DMABMR = ETH_DMABMR_SR;
+ mp_hal_delay_ms(2);
+
+ // Wait for soft reset to finish
+ uint32_t t0 = mp_hal_ticks_ms();
+ while (ETH->DMABMR & ETH_DMABMR_SR) {
+ if (mp_hal_ticks_ms() - t0 > 1000) {
+ return -MP_ETIMEDOUT;
+ }
+ }
+
+ // Set MII clock range
+ uint32_t hclk = HAL_RCC_GetHCLKFreq();
+ uint32_t cr_div;
+ if (hclk < 35000000) {
+ cr_div = ETH_MACMIIAR_CR_Div16;
+ } else if (hclk < 60000000) {
+ cr_div = ETH_MACMIIAR_CR_Div26;
+ } else if (hclk < 100000000) {
+ cr_div = ETH_MACMIIAR_CR_Div42;
+ } else if (hclk < 150000000) {
+ cr_div = ETH_MACMIIAR_CR_Div62;
+ } else {
+ cr_div = ETH_MACMIIAR_CR_Div102;
+ }
+ ETH->MACMIIAR = cr_div;
+
+ // Reset the PHY
+ eth_phy_write(PHY_BCR, PHY_BCR_SOFT_RESET);
+ mp_hal_delay_ms(50);
+
+ // Wait for the PHY link to be established
+ int phy_state = 0;
+ t0 = mp_hal_ticks_ms();
+ while (phy_state != 3) {
+ if (mp_hal_ticks_ms() - t0 > PHY_INIT_TIMEOUT_MS) {
+ eth_mac_deinit(self);
+ return -MP_ETIMEDOUT;
+ }
+ uint16_t bcr = eth_phy_read(0);
+ uint16_t bsr = eth_phy_read(1);
+ switch (phy_state) {
+ case 0:
+ if (!(bcr & PHY_BCR_SOFT_RESET)) {
+ phy_state = 1;
+ }
+ break;
+ case 1:
+ if (bsr & PHY_BSR_LINK_STATUS) {
+ eth_phy_write(PHY_BCR, PHY_BCR_AUTONEG_EN);
+ phy_state = 2;
+ }
+ break;
+ case 2:
+ if ((bsr & (PHY_BSR_AUTONEG_DONE | PHY_BSR_LINK_STATUS))
+ == (PHY_BSR_AUTONEG_DONE | PHY_BSR_LINK_STATUS)) {
+ phy_state = 3;
+ }
+ break;
+ }
+ mp_hal_delay_ms(2);
+ }
+
+ // Get register with link status
+ uint16_t phy_scsr = eth_phy_read(PHY_SCSR);
+
+ // Burst mode configuration
+ ETH->DMABMR = 0;
+ mp_hal_delay_ms(2);
+
+ // Select DMA interrupts
+ ETH->DMAIER =
+ ETH_DMAIER_NISE // enable normal interrupts
+ | ETH_DMAIER_RIE // enable RX interrupt
+ ;
+
+ // Configure RX descriptor lists
+ for (size_t i = 0; i < RX_BUF_NUM; ++i) {
+ eth_dma.rx_descr[i].rdes0 = 1 << RX_DESCR_0_OWN_Pos;
+ eth_dma.rx_descr[i].rdes1 =
+ 1 << RX_DESCR_1_RCH_Pos // chained
+ | RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos
+ ;
+ eth_dma.rx_descr[i].rdes2 = (uint32_t)&eth_dma.rx_buf[i * RX_BUF_SIZE];
+ eth_dma.rx_descr[i].rdes3 = (uint32_t)&eth_dma.rx_descr[(i + 1) % RX_BUF_NUM];
+ }
+ ETH->DMARDLAR = (uint32_t)&eth_dma.rx_descr[0];
+ eth_dma.rx_descr_idx = 0;
+
+ // Configure TX descriptor lists
+ for (size_t i = 0; i < TX_BUF_NUM; ++i) {
+ eth_dma.tx_descr[i].tdes0 = 1 << TX_DESCR_0_TCH_Pos;
+ eth_dma.tx_descr[i].tdes1 = 0;
+ eth_dma.tx_descr[i].tdes2 = 0;
+ eth_dma.tx_descr[i].tdes3 = (uint32_t)&eth_dma.tx_descr[(i + 1) % TX_BUF_NUM];
+ }
+ ETH->DMATDLAR = (uint32_t)&eth_dma.tx_descr[0];
+ eth_dma.tx_descr_idx = 0;
+
+ // Configure DMA
+ ETH->DMAOMR =
+ ETH_DMAOMR_RSF // read from RX FIFO after a full frame is written
+ | ETH_DMAOMR_TSF // transmit when a full frame is in TX FIFO (needed by errata)
+ ;
+ mp_hal_delay_ms(2);
+
+ // Select MAC filtering options
+ ETH->MACFFR =
+ ETH_MACFFR_RA // pass all frames up
+ ;
+ mp_hal_delay_ms(2);
+
+ // Set MAC address
+ u8_t *mac = &self->netif.hwaddr[0];
+ ETH->MACA0HR = mac[5] << 8 | mac[4];
+ mp_hal_delay_ms(2);
+ ETH->MACA0LR = mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0];
+ mp_hal_delay_ms(2);
+
+ // Set main MAC control register
+ ETH->MACCR =
+ (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_10FULL ? ETH_MACCR_DM
+ : (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_100HALF ? ETH_MACCR_FES
+ : (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_100FULL ? (ETH_MACCR_FES | ETH_MACCR_DM)
+ : 0
+ ;
+ mp_hal_delay_ms(2);
+
+ // Start MAC layer
+ ETH->MACCR |=
+ ETH_MACCR_TE // enable TX
+ | ETH_MACCR_RE // enable RX
+ ;
+ mp_hal_delay_ms(2);
+
+ // Start DMA layer
+ ETH->DMAOMR |=
+ ETH_DMAOMR_ST // start TX
+ | ETH_DMAOMR_SR // start RX
+ ;
+ mp_hal_delay_ms(2);
+
+ // Enable interrupts
+ NVIC_SetPriority(ETH_IRQn, IRQ_PRI_PENDSV);
+ HAL_NVIC_EnableIRQ(ETH_IRQn);
+
+ return 0;
+}
+
+STATIC void eth_mac_deinit(eth_t *self) {
+ (void)self;
+ HAL_NVIC_DisableIRQ(ETH_IRQn);
+ __HAL_RCC_ETHMAC_FORCE_RESET();
+ __HAL_RCC_ETHMAC_RELEASE_RESET();
+ __HAL_RCC_ETH_CLK_DISABLE();
+}
+
+STATIC int eth_tx_buf_get(size_t len, uint8_t **buf) {
+ if (len > TX_BUF_SIZE) {
+ return -MP_EINVAL;
+ }
+
+ // Wait for DMA to release the current TX descriptor (if it has it)
+ eth_dma_tx_descr_t *tx_descr = &eth_dma.tx_descr[eth_dma.tx_descr_idx];
+ uint32_t t0 = mp_hal_ticks_ms();
+ for (;;) {
+ if (!(tx_descr->tdes0 & (1 << TX_DESCR_0_OWN_Pos))) {
+ break;
+ }
+ if (mp_hal_ticks_ms() - t0 > 1000) {
+ return -MP_ETIMEDOUT;
+ }
+ }
+
+ // Update TX descriptor with length, buffer pointer and linked list pointer
+ *buf = &eth_dma.tx_buf[eth_dma.tx_descr_idx * TX_BUF_SIZE];
+ tx_descr->tdes1 = len << TX_DESCR_1_TBS1_Pos;
+ tx_descr->tdes2 = (uint32_t)*buf;
+ tx_descr->tdes3 = (uint32_t)&eth_dma.tx_descr[(eth_dma.tx_descr_idx + 1) % TX_BUF_NUM];
+
+ return 0;
+}
+
+STATIC int eth_tx_buf_send(void) {
+ // Get TX descriptor and move to next one
+ eth_dma_tx_descr_t *tx_descr = &eth_dma.tx_descr[eth_dma.tx_descr_idx];
+ eth_dma.tx_descr_idx = (eth_dma.tx_descr_idx + 1) % TX_BUF_NUM;
+
+ // Schedule to send next outgoing frame
+ tx_descr->tdes0 =
+ 1 << TX_DESCR_0_OWN_Pos // owned by DMA
+ | 1 << TX_DESCR_0_LS_Pos // last segment
+ | 1 << TX_DESCR_0_FS_Pos // first segment
+ | 3 << TX_DESCR_0_CIC_Pos // enable all checksums inserted by hardware
+ | 1 << TX_DESCR_0_TCH_Pos // TX descriptor is chained
+ ;
+
+ // Notify ETH DMA that there is a new TX descriptor for sending
+ __DMB();
+ if (ETH->DMASR & ETH_DMASR_TBUS) {
+ ETH->DMASR = ETH_DMASR_TBUS;
+ ETH->DMATPDR = 0;
+ }
+
+ return 0;
+}
+
+STATIC void eth_dma_rx_free(void) {
+ // Get RX descriptor, RX buffer and move to next one
+ eth_dma_rx_descr_t *rx_descr = &eth_dma.rx_descr[eth_dma.rx_descr_idx];
+ uint8_t *buf = &eth_dma.rx_buf[eth_dma.rx_descr_idx * RX_BUF_SIZE];
+ eth_dma.rx_descr_idx = (eth_dma.rx_descr_idx + 1) % RX_BUF_NUM;
+
+ // Schedule to get next incoming frame
+ rx_descr->rdes1 =
+ 1 << RX_DESCR_1_RCH_Pos // RX descriptor is chained
+ | RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos // maximum buffer length
+ ;
+ rx_descr->rdes2 = (uint32_t)buf;
+ rx_descr->rdes3 = (uint32_t)&eth_dma.rx_descr[eth_dma.rx_descr_idx];
+ rx_descr->rdes0 = 1 << RX_DESCR_0_OWN_Pos; // owned by DMA
+
+ // Notify ETH DMA that there is a new RX descriptor available
+ __DMB();
+ ETH->DMARPDR = 0;
+}
+
+void ETH_IRQHandler(void) {
+ uint32_t sr = ETH->DMASR;
+ ETH->DMASR = ETH_DMASR_NIS;
+ if (sr & ETH_DMASR_RS) {
+ ETH->DMASR = ETH_DMASR_RS;
+ for (;;) {
+ eth_dma_rx_descr_t *rx_descr = &eth_dma.rx_descr[eth_dma.rx_descr_idx];
+ if (rx_descr->rdes0 & (1 << RX_DESCR_0_OWN_Pos)) {
+ // No more RX descriptors ready to read
+ break;
+ }
+
+ // Get RX buffer containing new frame
+ size_t len = (rx_descr->rdes0 & RX_DESCR_0_FL_Msk) >> RX_DESCR_0_FL_Pos;
+ len -= 4; // discard CRC at end
+ uint8_t *buf = (uint8_t*)rx_descr->rdes2;
+
+ // Process frame
+ eth_process_frame(&eth_instance, len, buf);
+ eth_dma_rx_free();
+ }
+ }
+}
+
+/*******************************************************************************/
+// ETH-LwIP bindings
+
+#define TRACE_ASYNC_EV (0x0001)
+#define TRACE_ETH_TX (0x0002)
+#define TRACE_ETH_RX (0x0004)
+#define TRACE_ETH_FULL (0x0008)
+
+STATIC void eth_trace(eth_t *self, size_t len, const void *data, unsigned int flags) {
+ if (((flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_TX))
+ || (!(flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_RX))) {
+ const uint8_t *buf;
+ if (len == (size_t)-1) {
+ // data is a pbuf
+ const struct pbuf *pbuf = data;
+ buf = pbuf->payload;
+ len = pbuf->len; // restricted to print only the first chunk of the pbuf
+ } else {
+ // data is actual data buffer
+ buf = data;
+ }
+ if (self->trace_flags & TRACE_ETH_FULL) {
+ flags |= NETUTILS_TRACE_PAYLOAD;
+ }
+ netutils_ethernet_trace(MP_PYTHON_PRINTER, len, buf, flags);
+ }
+}
+
+STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) {
+ // This function should always be called from a context where PendSV-level IRQs are disabled
+
+ LINK_STATS_INC(link.xmit);
+ eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE);
+
+ uint8_t *buf;
+ int ret = eth_tx_buf_get(p->tot_len, &buf);
+ if (ret == 0) {
+ pbuf_copy_partial(p, buf, p->tot_len, 0);
+ ret = eth_tx_buf_send();
+ }
+
+ return ret ? ERR_BUF : ERR_OK;
+}
+
+STATIC err_t eth_netif_init(struct netif *netif) {
+ netif->linkoutput = eth_netif_output;
+ netif->output = etharp_output;
+ netif->mtu = 1500;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP;
+ // Checksums only need to be checked on incoming frames, not computed on outgoing frames
+ NETIF_SET_CHECKSUM_CTRL(netif,
+ NETIF_CHECKSUM_CHECK_IP
+ | NETIF_CHECKSUM_CHECK_UDP
+ | NETIF_CHECKSUM_CHECK_TCP
+ | NETIF_CHECKSUM_CHECK_ICMP
+ | NETIF_CHECKSUM_CHECK_ICMP6);
+ return ERR_OK;
+}
+
+STATIC void eth_lwip_init(eth_t *self) {
+ ip_addr_t ipconfig[4];
+ IP4_ADDR(&ipconfig[0], 0, 0, 0, 0);
+ IP4_ADDR(&ipconfig[2], 192, 168, 0, 1);
+ IP4_ADDR(&ipconfig[1], 255, 255, 255, 0);
+ IP4_ADDR(&ipconfig[3], 8, 8, 8, 8);
+
+ MICROPY_PY_LWIP_ENTER
+
+ struct netif *n = &self->netif;
+ n->name[0] = 'e';
+ n->name[1] = '0';
+ netif_add(n, &ipconfig[0], &ipconfig[1], &ipconfig[2], self, eth_netif_init, ethernet_input);
+ netif_set_hostname(n, "MPY");
+ netif_set_default(n);
+ netif_set_up(n);
+
+ dns_setserver(0, &ipconfig[3]);
+ dhcp_set_struct(n, &self->dhcp_struct);
+ dhcp_start(n);
+
+ netif_set_link_up(n);
+
+ MICROPY_PY_LWIP_EXIT
+}
+
+STATIC void eth_lwip_deinit(eth_t *self) {
+ MICROPY_PY_LWIP_ENTER
+ for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif == &self->netif) {
+ netif_remove(netif);
+ netif->ip_addr.addr = 0;
+ netif->flags = 0;
+ }
+ }
+ MICROPY_PY_LWIP_EXIT
+}
+
+STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf) {
+ eth_trace(self, len, buf, NETUTILS_TRACE_NEWLINE);
+
+ struct netif *netif = &self->netif;
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+ if (p != NULL) {
+ pbuf_take(p, buf, len);
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ }
+ }
+}
+
+struct netif *eth_netif(eth_t *self) {
+ return &self->netif;
+}
+
+int eth_link_status(eth_t *self) {
+ struct netif *netif = &self->netif;
+ if ((netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP))
+ == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) {
+ if (netif->ip_addr.addr != 0) {
+ return 3; // link up
+ } else {
+ return 2; // link no-ip;
+ }
+ } else {
+ int s = eth_phy_read(0) | eth_phy_read(0x10) << 16;
+ if (s == 0) {
+ return 0; // link down
+ } else {
+ return 1; // link join
+ }
+ }
+}
+
+int eth_start(eth_t *self) {
+ eth_lwip_deinit(self);
+ int ret = eth_mac_init(self);
+ if (ret < 0) {
+ return ret;
+ }
+ eth_lwip_init(self);
+ return 0;
+}
+
+int eth_stop(eth_t *self) {
+ eth_lwip_deinit(self);
+ eth_mac_deinit(self);
+ return 0;
+}
+
+#endif // defined(MICROPY_HW_ETH_MDC)
diff --git a/ports/stm32/eth.h b/ports/stm32/eth.h
new file mode 100644
index 000000000..fd46c7fa7
--- /dev/null
+++ b/ports/stm32/eth.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_STM32_ETH_H
+#define MICROPY_INCLUDED_STM32_ETH_H
+
+typedef struct _eth_t eth_t;
+extern eth_t eth_instance;
+
+void eth_init(eth_t *self, int mac_idx);
+void eth_set_trace(eth_t *self, uint32_t value);
+struct netif *eth_netif(eth_t *self);
+int eth_link_status(eth_t *self);
+int eth_start(eth_t *self);
+int eth_stop(eth_t *self);
+
+#endif // MICROPY_INCLUDED_STM32_ETH_H
diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h
index b8ab8a2ab..64ff104f7 100644
--- a/ports/stm32/lwip_inc/lwipopts.h
+++ b/ports/stm32/lwip_inc/lwipopts.h
@@ -8,6 +8,7 @@
#define MEM_ALIGNMENT 4
#define LWIP_CHKSUM_ALGORITHM 3
+#define LWIP_CHECKSUM_CTRL_PER_NETIF 1
#define LWIP_ARP 1
#define LWIP_ETHERNET 1