diff --git a/cpu/esp8266/Makefile.dep b/cpu/esp8266/Makefile.dep index f5527b8efbf2bb62f5dadad84934c4e07af778d3..cc2422d6f6a8b0c410e4c20cf3a5a3b54a70aec2 100644 --- a/cpu/esp8266/Makefile.dep +++ b/cpu/esp8266/Makefile.dep @@ -2,6 +2,7 @@ ifneq (, $(filter esp_sdk, $(USEMODULE))) USEMODULE += core_thread_flags + INCLUDES += -I$(ESP8266_SDK_DIR)/third_party/include LINKFLAGS += -Wl,-wrap=malloc LINKFLAGS += -Wl,-wrap=free LINKFLAGS += -Wl,-wrap=calloc @@ -20,7 +21,6 @@ endif ifneq (, $(filter esp_wifi, $(USEMODULE))) CFLAGS += -DLWIP_OPEN_SRC - INCLUDES += -I$(ESP8266_SDK_DIR)/third_party/include LINKFLAGS += -Wl,-wrap=ethernet_input USEMODULE += netdev_eth endif diff --git a/cpu/esp8266/esp-wifi/esp_wifi_netdev.c b/cpu/esp8266/esp-wifi/esp_wifi_netdev.c index 0219396857beb8ace35646b53849c62413484a17..7463d32c2dc7917ca71125b4cd1b5fd458838389 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_netdev.c +++ b/cpu/esp8266/esp-wifi/esp_wifi_netdev.c @@ -65,7 +65,7 @@ #define ESP_WIFI_SOFTAP_IF (SOFTAP_IF) #define ESP_WIFI_RECONNECT_TIME (20 * US_PER_SEC) -#define ESP_WIFI_SEND_TIMEOUT (MS_PER_SEC) +#define ESP_WIFI_HEAP_MARGIN (2 * ETHERNET_MAX_LEN) #define MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define MAC_STR_ARG(m) m[0], m[1], m[2], m[3], m[4], m[5] @@ -161,9 +161,9 @@ void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) assert(netif != NULL); /* - * The function `esp_wifi_recv_cb` is executed in the context of the `ets` - * thread. The ISRs handling the hardware interrupts from the WiFi - * interface pass events to a message queue of the `ets` thread which is + * Function `esp_wifi_recv_cb` is executed in the context of the `ets` + * thread. ISRs which handle hardware interrupts from the WiFi interface + * simply pass events to a message queue of the `ets` thread which are then * sequentially processed by the `ets` thread to asynchronously execute * callback functions such as `esp_wifi_recv_cb`. * @@ -179,9 +179,6 @@ void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) } _in_esp_wifi_recv_cb = true; - /* avoid concurrent access to the receive buffer */ - mutex_lock(&_esp_wifi_dev.dev_lock); - critical_enter(); /* first, check packet buffer for the minimum packet size */ @@ -191,7 +188,6 @@ void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) pbuf_free(pb); _in_esp_wifi_recv_cb = false; critical_exit(); - mutex_unlock(&_esp_wifi_dev.dev_lock); return; } @@ -202,7 +198,6 @@ void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) pbuf_free(pb); _in_esp_wifi_recv_cb = false; critical_exit(); - mutex_unlock(&_esp_wifi_dev.dev_lock); return; } @@ -213,39 +208,67 @@ void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) pbuf_free(pb); _in_esp_wifi_recv_cb = false; critical_exit(); - mutex_unlock(&_esp_wifi_dev.dev_lock); return; } - /* store the frame in the buffer and free lwIP pbuf */ + /* we have to store the frame in the buffer and free lwIP pbuf immediatly */ _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. + * Since _esp_wifi_recv_cb is not executed in interrupt context but in + * the context of the `ets` thread, it is not necessary to pass the + * `NETDEV_EVENT_ISR` event first. Instead, the receive function can be + * called directly which result in much faster handling, a less frame lost + * rate and more robustness. There is no need for a mutex anymore to + * synchronize the access to the receive buffer between _esp_wifi_recv_cb + * and _recv function. */ - 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_ISR); + NETDEV_EVENT_RX_COMPLETE); } - /* reset IRQ nesting counter */ - irq_interrupt_nesting--; - _in_esp_wifi_recv_cb = false; critical_exit(); - mutex_unlock(&_esp_wifi_dev.dev_lock); } +#define BEACON_TIMEOUT (200) +#define HANDSHAKE_TIMEOUT (204) + +static const char *_esp_wifi_disc_reasons [] = { + "INVALID", /* 0 */ + "UNSPECIFIED", /* 1 */ + "AUTH_EXPIRE", /* 2 */ + "AUTH_LEAVE", /* 3 */ + "ASSOC_EXPIRE", /* 4 */ + "ASSOC_TOOMANY", /* 5 */ + "NOT_AUTHED", /* 6 */ + "NOT_ASSOCED", /* 7 */ + "ASSOC_LEAVE", /* 8 */ + "ASSOC_NOT_AUTHED", /* 9 */ + "DISASSOC_PWRCAP_BAD", /* 10 (11h) */ + "DISASSOC_SUPCHAN_BAD", /* 11 (11h) */ + "IE_INVALID", /* 13 (11i) */ + "MIC_FAILURE", /* 14 (11i) */ + "4WAY_HANDSHAKE_TIMEOUT", /* 15 (11i) */ + "GROUP_KEY_UPDATE_TIMEOUT", /* 16 (11i) */ + "IE_IN_4WAY_DIFFERS", /* 17 (11i) */ + "GROUP_CIPHER_INVALID", /* 18 (11i) */ + "PAIRWISE_CIPHER_INVALID", /* 19 (11i) */ + "AKMP_INVALID", /* 20 (11i) */ + "UNSUPP_RSN_IE_VERSION", /* 21 (11i) */ + "INVALID_RSN_IE_CAP", /* 22 (11i) */ + "802_1X_AUTH_FAILED", /* 23 (11i) */ + "CIPHER_SUITE_REJECTED", /* 24 (11i) */ + "BEACON_TIMEOUT", /* 200 */ + "NO_AP_FOUND", /* 201 */ + "AUTH_FAIL", /* 202 */ + "ASSOC_FAIL", /* 203 */ + "HANDSHAKE_TIMEOUT" /* 204 */ +}; + /** * @brief Event handler for esp system events. */ @@ -253,29 +276,33 @@ static void _esp_wifi_handle_event_cb(System_Event_t *evt) { ESP_WIFI_DEBUG("event %d", evt->event); + uint8_t reason; + const char* reason_str = "UNKNOWN"; + switch (evt->event) { case EVENT_STAMODE_CONNECTED: ESP_WIFI_LOG_INFO("connected to ssid %s, channel %d", evt->event_info.connected.ssid, evt->event_info.connected.channel); _esp_wifi_dev.state = ESP_WIFI_CONNECTED; + _esp_wifi_dev.event = EVENT_STAMODE_CONNECTED; + _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); break; case EVENT_STAMODE_DISCONNECTED: - ESP_WIFI_LOG_INFO("disconnected from ssid %s, reason %d", + reason = evt->event_info.disconnected.reason; + if (reason < REASON_BEACON_TIMEOUT) { + reason_str = _esp_wifi_disc_reasons[reason]; + } + else if (reason <= REASON_HANDSHAKE_TIMEOUT) { + reason_str = _esp_wifi_disc_reasons[reason - REASON_BEACON_TIMEOUT]; + } + ESP_WIFI_LOG_INFO("disconnected from ssid %s, reason %d (%s)", evt->event_info.disconnected.ssid, - evt->event_info.disconnected.reason); + evt->event_info.disconnected.reason, reason_str); _esp_wifi_dev.state = ESP_WIFI_DISCONNECTED; - - /* call disconnect to reset internal state */ - if (evt->event_info.disconnected.reason != REASON_ASSOC_LEAVE) { - wifi_station_disconnect(); - } - - /* try to reconnect */ - wifi_station_connect(); - _esp_wifi_dev.state = ESP_WIFI_CONNECTING; - + _esp_wifi_dev.event = EVENT_STAMODE_DISCONNECTED; + _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); break; case EVENT_SOFTAPMODE_STACONNECTED: @@ -308,6 +335,9 @@ uint8_t _send_pkt_buf[ETHERNET_MAX_LEN]; /** function used to send an ethernet frame over WiFi */ extern err_t ieee80211_output_pbuf(struct netif *netif, struct pbuf *p); +/** function to get free heap */ +unsigned int IRAM get_free_heap_size (void); + static int IRAM _send(netdev_t *netdev, const iolist_t *iolist) { ESP_WIFI_DEBUG("%p %p", netdev, iolist); @@ -366,19 +396,18 @@ static int IRAM _send(netdev_t *netdev, const iolist_t *iolist) struct netif *sta_netif = (struct netif *)eagle_lwip_getif(ESP_WIFI_STATION_IF); netif_set_default(sta_netif); - struct pbuf *pb = pbuf_alloc(PBUF_LINK, iol_len + PBUF_IEEE80211_HLEN, PBUF_RAM); - if (pb == NULL || pb->tot_len < iol_len) { - ESP_WIFI_DEBUG("could not allocate buffer to send %d bytes ", iol_len); + struct pbuf *pb; + + if (get_free_heap_size() < ESP_WIFI_HEAP_MARGIN || + (pb = pbuf_alloc(PBUF_LINK, iol_len, PBUF_RAM)) == NULL || + (pb->tot_len < iol_len)) { + ESP_WIFI_LOG_ERROR("could not allocate buffer to send %d bytes ", iol_len); /* * The memory of EPS8266 is quite small. Therefore, it may happen on * heavy network load that we run into out of memory and we have - * to wait until lwIP pbuf has been flushed. For that purpose, we - * have to disconnect from AP and slow down sending. The node will - * then reconnect to AP automatically. + * to wait until lwIP pbuf has been flushed. We slow down sending a bit. */ critical_exit(); - /* disconnect from AP */ - wifi_station_disconnect(); /* wait 20 ms */ xtimer_usleep(20 * US_PER_MS); @@ -419,29 +448,18 @@ static int IRAM _send(netdev_t *netdev, const iolist_t *iolist) #endif /* MODULE_OD */ #endif /* ENABLE_DEBUG */ - int res = ieee80211_output_pbuf(sta_netif, pb); - - /* - * Attempting to send the next frame before completing the transmission - * of the previous frame may result in a complete blockage of the send - * function. To avoid this blockage, we have to wait here until the frame - * has been sent. The frame has been sent when pb->ref becomes 1. - * We wait for a maximum time of ESP_WIFI_SEND_TIMEOUT milliseconds. - */ - unsigned _timeout = ESP_WIFI_SEND_TIMEOUT; - while (pb->ref > 1 && --_timeout && dev->state == ESP_WIFI_CONNECTED) { - xtimer_usleep(US_PER_MS); - } + critical_exit(); + /* sta_netif->linkoutput = ieee80211_output_pbuf */ + err_t res = sta_netif->linkoutput(sta_netif, pb); pbuf_free(pb); - if (res == ERR_OK && _timeout) { + if (res == ERR_OK) { /* There was no ieee80211_output_pbuf error and no send timeout. */ #ifdef MODULE_NETSTATS_L2 netdev->stats.tx_bytes += iol_len; netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); #endif _in_send = false; - critical_exit(); return iol_len; } else { @@ -450,14 +468,6 @@ static int IRAM _send(netdev_t *netdev, const iolist_t *iolist) netdev->stats.tx_failed++; #endif _in_send = false; - critical_exit(); - /* - * ieee80211_output_pbuf usually happens because we run into out of - * memory. We have to wait until lwIP pbuf has been flushed. For that - * purpose, we have to disconnect from AP and wait for a short time. - * The node will then reconnect to AP automatically. - */ - wifi_station_disconnect(); return -EIO; } } @@ -470,9 +480,6 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev; - /* avoid concurrent access to the receive buffer */ - mutex_lock(&dev->dev_lock); - uint16_t size = dev->rx_len ? dev->rx_len : 0; if (!buf) { @@ -481,7 +488,6 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) /* if len > 0, drop the frame */ dev->rx_len = 0; } - mutex_unlock(&dev->dev_lock); return size; } @@ -490,7 +496,6 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) ESP_WIFI_DEBUG("not enough space in receive buffer"); /* newest API requires to drop the frame in that case */ dev->rx_len = 0; - mutex_unlock(&dev->dev_lock); return -ENOBUFS; } @@ -513,7 +518,6 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) netdev->stats.rx_bytes += size; #endif - mutex_unlock(&dev->dev_lock); return size; } @@ -579,11 +583,21 @@ static void _isr(netdev_t *netdev) assert(netdev != NULL); - esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *)netdev; + esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *) netdev; - if (dev->rx_len) { - dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + switch (dev->event) { + case EVENT_STAMODE_CONNECTED: + dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP); + break; + case EVENT_STAMODE_DISCONNECTED: + dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN); + break; + default: + break; } + _esp_wifi_dev.event = EVENT_MAX; /* no event */ + + return; } /** override lwIP ethernet_intput to get ethernet frames */ @@ -624,8 +638,7 @@ static void _esp_wifi_setup(void) /* initialize netdev data structure */ dev->rx_len = 0; dev->state = ESP_WIFI_DISCONNECTED; - - mutex_init(&dev->dev_lock); + dev->event = EVENT_MAX; /* set the netdev driver */ dev->netdev.driver = &_esp_wifi_driver; @@ -657,6 +670,10 @@ static void _esp_wifi_setup(void) } ESP_WIFI_DEBUG("own MAC addr is " MAC_STR, MAC_STR_ARG(dev->mac)); + /* set auto reconnect policy */ + wifi_station_set_reconnect_policy(true); + wifi_station_set_auto_connect(true); + /* register callbacks */ wifi_set_event_handler_cb(_esp_wifi_handle_event_cb); @@ -667,6 +684,9 @@ static void _esp_wifi_setup(void) /* set the the reconnect timer */ xtimer_set(&_esp_wifi_reconnect_timer, ESP_WIFI_RECONNECT_TIME); + /* avoid the WiFi modem going into sleep mode */ + wifi_set_sleep_type(NONE_SLEEP_T); + /* connect */ wifi_station_connect(); _esp_wifi_dev.state = ESP_WIFI_CONNECTING; diff --git a/cpu/esp8266/esp-wifi/esp_wifi_netdev.h b/cpu/esp8266/esp-wifi/esp_wifi_netdev.h index 8cb331eb7aaca8ae3067c0c25d2f87de2bdbb737..05d317d70cff74d81184f9dda3f11a93f1614d2a 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_netdev.h +++ b/cpu/esp8266/esp-wifi/esp_wifi_netdev.h @@ -48,9 +48,8 @@ typedef struct uint16_t rx_len; /**< number of bytes received from lwIP */ esp_wifi_state_t state; /**< indicates the interface state */ + uint32_t event; /**< received event */ - mutex_t dev_lock; /**< for exclusive access to buffer in - receive functions */ } esp_wifi_netdev_t; #ifdef __cplusplus diff --git a/cpu/esp8266/sdk/lwip.c b/cpu/esp8266/sdk/lwip.c new file mode 100644 index 0000000000000000000000000000000000000000..8c851ba7fc9997c7c19eac2779844dac97633672 --- /dev/null +++ b/cpu/esp8266/sdk/lwip.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * 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 cpu_esp8266 + * @{ + * + * @file + * @brief lwIP functions required as symbols by the SDK + * + * This file defines a number of lwIP functions that are required as symbols by + * Espressif's SDK libraries. Since RIOT doesn't need lwIP, these functions are + * only dummies without real functionality. Using these functions instead of + * the lwIP functions as provided with the SDK saves arround 4 kBytes of RAM. + * + * @author Gunar Schorcht <gunar@schorcht.net> + * @} + */ + +#ifdef MODULE_ESP_SDK + +#include "lwip/err.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/pbuf.h" +#include "lwip/tcp_impl.h" +#include "netif/etharp.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "log.h" + +#ifndef ERR_OK +#define ERR_OK 0 +#endif + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; + +err_t ethernet_input(struct pbuf *pb, struct netif* netif) +{ + DEBUG("%s\n", __func__); + (void)pb; + (void)netif; + return ERR_OK; +} + +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr) +{ + DEBUG("%s\n", __func__); + (void)netif; + (void)q; + (void)ipaddr; + return ERR_OK; +} + +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr) +{ + DEBUG("%s\n", __func__); + (void)netif; + (void)ipaddr; + return ERR_OK; +} + +void etharp_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +void etharp_cleanup_netif(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; +} + +void dhcp_cleanup(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; +} + +err_t dhcp_start(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; + return ERR_OK; +} + +err_t dhcp_renew(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; + return ERR_OK; +} + +err_t dhcp_release(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; + return ERR_OK; +} + +void dhcp_stop(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; +} + +void dhcp_network_changed(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; +} + +void dhcp_coarse_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +void dhcp_fine_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +void dhcps_start(struct ip_info *info) +{ + DEBUG("%s\n", __func__); + (void)info; +} + +void dhcps_stop(void) +{ + DEBUG("%s\n", __func__); +} + +void dhcps_coarse_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +union tcp_listen_pcbs_t tcp_listen_pcbs; +struct tcp_pcb *tcp_active_pcbs; +struct tcp_pcb *tcp_tw_pcbs; + +void tcp_seg_free(struct tcp_seg *seg) +{ + DEBUG("%s\n", __func__); + (void)seg; +} + +void tcp_abort (struct tcp_pcb *pcb) +{ + DEBUG("%s\n", __func__); + (void)pcb; +} + +void tcp_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +void igmp_init(void) +{ + DEBUG("%s\n", __func__); +} + +err_t igmp_start(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; + return ERR_OK; +} + +err_t igmp_stop(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; + return ERR_OK; +} + +void igmp_report_groups(struct netif *netif) +{ + DEBUG("%s\n", __func__); + (void)netif; +} + +void igmp_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +void dns_init(void) +{ + DEBUG("%s\n", __func__); +} + +void dns_tmr(void) +{ + DEBUG("%s\n", __func__); +} + +uint32_t espconn_init(uint32 arg) +{ + DEBUG("%s\n", __func__); + (void)arg; + return 1; +} + +#endif /* MODULE_ESP_SDK */