diff --git a/cpu/esp8266/esp-wifi/esp_wifi_netdev.c b/cpu/esp8266/esp-wifi/esp_wifi_netdev.c index 40733a2a16a7f9765104292b80f78a82dad30c96..cd89bde76fa8521a6e889c7ced99904adb97ec7e 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_netdev.c +++ b/cpu/esp8266/esp-wifi/esp_wifi_netdev.c @@ -102,50 +102,71 @@ void _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) * callback functions such as `esp_wifi_recv_cb`. * * It should be therefore not possible to reenter function - * `esp_wifi_recv_cb`. To avoid inconsistencies this is checked by an - * additional boolean variable . This can not be realized by a mutex - * because `esp_wifi_recv_cb` would be reentered from same thread context. + * `esp_wifi_recv_cb`. If it does occur inspite of that, we use a + * protection variable to avoid inconsistencies. This can not be realized + * by a mutex because `esp_wifi_recv_cb` would be reentered from same + * thread context. */ - if (_in_esp_wifi_recv_cb) { + pbuf_free(pb); return; } _in_esp_wifi_recv_cb = true; - /* - * Since it is not possible to reenter the function `esp_wifi_recv_cb`, and - * the functions netif::_ recv and esp_wifi_netdev::_ recv are called - * directly in the same thread context, neither a mutual exclusion has to - * be realized nor have the interrupts to be deactivated. - * Therefore we can read directly from the `data` and don't need a receive - * buffer. - */ + /* avoid concurrent access to the receive buffer */ + mutex_lock(&_esp_wifi_dev.dev_lock); + + critical_enter(); /* check the first packet buffer for the minimum packet size */ if (pb->len < sizeof(ethernet_hdr_t)) { ESP_WIFI_DEBUG("frame length is less than the size of an Ethernet" "header (%u < %u)", pb->len, sizeof(ethernet_hdr_t)); - _in_esp_wifi_recv_cb = false; pbuf_free(pb); + _in_esp_wifi_recv_cb = false; + critical_exit(); + mutex_unlock(&_esp_wifi_dev.dev_lock); return; } - if (_esp_wifi_dev.rx_pbuf) { + /* check whether the receive buffer is already holding a frame */ + if (_esp_wifi_dev.rx_len) { ESP_WIFI_DEBUG("buffer used, dropping incoming frame of %d bytes", pb->tot_len); - _in_esp_wifi_recv_cb = false; pbuf_free(pb); + _in_esp_wifi_recv_cb = false; + critical_exit(); + mutex_unlock(&_esp_wifi_dev.dev_lock); return; } - _esp_wifi_dev.rx_pbuf = pb; + /* store the frame in the buffer and free lwIP pbuf */ + _esp_wifi_dev.rx_len = pb->tot_len; + pbuf_copy_partial(pb, _esp_wifi_dev.rx_buf, _esp_wifi_dev.rx_len, 0); + pbuf_free(pb); + /* + * Because this function is not executed in interrupt context but in thread + * context, following msg_send could block on heavy network load, if frames + * are coming in faster than the ISR events can be handled. To avoid + * blocking during msg_send, we pretend we are in an ISR by incrementing + * the IRQ nesting counter. If IRQ nesting counter is greater 0, function + * irq_is_in returns true and the non-blocking version of msg_send is used. + */ + irq_interrupt_nesting++; + + /* trigger netdev event to read the data */ if (_esp_wifi_dev.netdev.event_callback) { _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, - NETDEV_EVENT_RX_COMPLETE); + NETDEV_EVENT_ISR); } + /* reset IRQ nesting counter */ + irq_interrupt_nesting--; + _in_esp_wifi_recv_cb = false; + critical_exit(); + mutex_unlock(&_esp_wifi_dev.dev_lock); } /** @@ -318,16 +339,18 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev; - /* we store received data in `buf` */ - uint16_t size = dev->rx_pbuf->tot_len ? dev->rx_pbuf->tot_len : 0; + /* avoid concurrent access to the receive buffer */ + mutex_lock(&dev->dev_lock); + + uint16_t size = dev->rx_len ? dev->rx_len : 0; if (!buf) { /* get the size of the frame */ if (len > 0 && size) { /* if len > 0, drop the frame */ - pbuf_free(dev->rx_pbuf); - dev->rx_pbuf = NULL; + dev->rx_len = 0; } + mutex_unlock(&dev->dev_lock); return size; } @@ -335,15 +358,14 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) /* buffer is smaller than the number of received bytes */ ESP_WIFI_DEBUG("not enough space in receive buffer"); /* newest API requires to drop the frame in that case */ - pbuf_free(dev->rx_pbuf); - dev->rx_pbuf = NULL; + dev->rx_len = 0; + mutex_unlock(&dev->dev_lock); return -ENOBUFS; } /* copy the buffer and free */ - pbuf_copy_partial(dev->rx_pbuf, buf, dev->rx_pbuf->tot_len, 0); - pbuf_free(dev->rx_pbuf); - dev->rx_pbuf = NULL; + memcpy(buf, dev->rx_buf, dev->rx_len); + dev->rx_len = 0; #if ENABLE_DEBUG ethernet_hdr_t *hdr = (ethernet_hdr_t *)buf; @@ -360,7 +382,8 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) netdev->stats.rx_bytes += size; #endif - return size; + mutex_unlock(&dev->dev_lock); + return size; } static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) @@ -424,6 +447,12 @@ static void _isr(netdev_t *netdev) ESP_WIFI_DEBUG("%p", netdev); assert(netdev != NULL); + + esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *)netdev; + + if (dev->rx_len) { + dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + } } /** override lwIP ethernet_intput to get ethernet frames */ @@ -457,9 +486,11 @@ static void _esp_wifi_setup(void) } /* initialize netdev data structure */ - dev->rx_pbuf = NULL; + dev->rx_len = 0; dev->connected = false; + mutex_init(&dev->dev_lock); + /* set the netdev driver */ dev->netdev.driver = &_esp_wifi_driver; diff --git a/cpu/esp8266/esp-wifi/esp_wifi_netdev.h b/cpu/esp8266/esp-wifi/esp_wifi_netdev.h index b6ab42a310eb2e88eb10a4f22615623dca39092e..e25c681a7cd9a7db6799c25514e3c5ff866f36d7 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_netdev.h +++ b/cpu/esp8266/esp-wifi/esp_wifi_netdev.h @@ -36,10 +36,12 @@ typedef struct uint8_t mac[ETHERNET_ADDR_LEN]; /**< MAC address of the device */ ip_addr_t ip; /**< IPv4 address of the device */ - struct pbuf *rx_pbuf; /**< lwIP receive buffer reference */ + uint8_t rx_buf[ETHERNET_DATA_LEN];/**< receive buffer */ uint16_t rx_len; /**< number of bytes received from lwIP */ - bool connected; /**< indicates the connection state to the AP */ + bool connected; /**< indicates the connection state to the AP */ + + mutex_t dev_lock; /**< for exclusive access to buffer in receive functions */ } esp_wifi_netdev_t;