/* * Copyright (C) 2019 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_esp_common_esp_wifi * @{ * * @file * @brief Network device driver for the ESP SoCs WiFi interface * * @author Gunar Schorcht */ #ifdef MODULE_ESP_WIFI #include #include #include #include "net/ethernet.h" #include "net/netdev/eth.h" #include "od.h" #include "esp_common.h" #include "esp_attr.h" #include "esp_event_loop.h" #ifndef MODULE_ESP_WIFI_AP #include "esp_now.h" #endif #include "esp_system.h" #include "esp_wifi.h" #include "esp_wifi_internal.h" #include "irq_arch.h" #include "tools.h" #include "nvs_flash/include/nvs_flash.h" #ifdef MODULE_ESP_WIFI_ENTERPRISE #include "esp_wpa2.h" #endif #include "esp_wifi_params.h" #include "esp_wifi_netdev.h" #include "log.h" #include "board.h" #define ESP_WIFI_DEBUG(f, ...) \ DEBUG("[esp_wifi] %s: " f "\n", __func__, ## __VA_ARGS__) #define ESP_WIFI_LOG_INFO(f, ...) \ LOG_TAG_INFO("esp_wifi", f "\n", ## __VA_ARGS__) #define ESP_WIFI_LOG_ERROR(f, ...) \ LOG_TAG_ERROR("esp_wifi", f "\n", ## __VA_ARGS__) #define ESP_WIFI_LOG_DEBUG(f, ...) \ LOG_TAG_DEBUG("esp_wifi", f "\n", ## __VA_ARGS__) #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] #ifdef MCU_ESP8266 #include "esp_socket.h" #include "net/sockio.h" #include "xtensa/xtensa_context.h" #define CONFIG_TCP_OVERSIZE_MSS 1 #define LL_ALIGN(s) (((uint32_t)s + 3) & 0xfffffffcU) /** * The SDK interface of the WiFi module uses the lwIP `pbuf` structure for * packets sent to and received from the WiFi interface. For compatibility * reasons with the binary SDK libraries we need to include the SDK lwIP * `pbuf` header here. * * To avoid compilation errors, we need to undefine all our pkg/lwIP settings * that are also defined by SDK lwIP header files. These definitions do not * affect the implementation of this module. */ #undef ETHARP_SUPPORT_STATIC_ENTRIES #undef LWIP_HAVE_LOOPIF #undef LWIP_NETIF_LOOPBACK #undef SO_REUSE #undef TCPIP_THREAD_PRIO #undef TCPIP_THREAD_STACKSIZE #include "lwip/pbuf.h" #endif /* MCU_ESP8266 */ #define ENABLE_DEBUG_HEXDUMP 0 #define ENABLE_DEBUG 0 #include "debug.h" /** * There is only one ESP WiFi device. We define it as static device variable * to have access to the device inside ESP WiFi interrupt routines which do * not provide an argument that could be used as pointer to the ESP WiFi * device which triggers the interrupt. */ esp_wifi_netdev_t _esp_wifi_dev; static const netdev_driver_t _esp_wifi_driver; /** guard variable to avoid reentrance to _esp_wifi_send function */ static bool _esp_wifi_send_is_in = false; /** guard variable to to decive when receive buffer can be overwritten */ static bool _esp_wifi_rx_in_progress = false; extern esp_err_t esp_system_event_add_handler (system_event_cb_t handler, void *arg); #ifdef MCU_ESP8266 /** * The low level WiFi driver function expects a lwIP pbuf data structure as * input. To avoid the integration of the whole lwIP package from ESP8266 RTOS * SDK, only the pbuf allocation function is realized with a very restricted * functionality. It uses malloc to allocate a packet buffer of type PBUF_RAM * for layer PBUF_RAW_TX. */ static struct pbuf *_esp_wifi_pbuf_alloc(size_t size) { /* Low level WiFi driver can only use 32-bit aligned DRAM memory */ size_t mem_size = LL_ALIGN(sizeof(struct pbuf)) + LL_ALIGN(size + PBUF_LINK_ENCAPSULATION_HLEN); struct pbuf *pb = heap_caps_malloc(mem_size, MALLOC_CAP_8BIT); if (pb == NULL) { ESP_WIFI_LOG_ERROR("no space left for packet buffer allocation"); return NULL; } memset(pb, 0, mem_size); /* initialize pbuf data structure */ pb->next = NULL; pb->payload = (void *)LL_ALIGN((uint8_t *)pb + sizeof(struct pbuf) + PBUF_LINK_ENCAPSULATION_HLEN); pb->tot_len = size; pb->len = size; pb->type = PBUF_RAM; pb->flags = 0; pb->ref = 1; ESP_WIFI_DEBUG("pb=%p size=%d", pb, size); return (struct pbuf*)pb; } /** * Free function for pbuf allocation */ static int _esp_wifi_pbuf_free(struct pbuf *pb) { assert(pb != NULL); ESP_WIFI_DEBUG("pb=%p ref=%d", pb, pb->ref); if (pb->ref > 1) { pb->ref--; } else if (pb->ref == 1) { pb->ref = 0; heap_caps_free(pb); return 1; } return 0; } /** * Socket used for interaction with low level WiFi driver, -1 if not opened. * Since we have only one WiFi interface, it has not to be a member of the * netdev data structures. We can use a static variable instead. */ static int _esp_wifi_socket = -1; /** * Function called when transmission of a packet has been finished. */ static int _esp_wifi_tx_cb(esp_aio_t* aio) { assert(aio != NULL); ESP_WIFI_DEBUG("aio=%p buf=%p", aio, aio->pbuf); struct pbuf* pbuf = aio->arg; _esp_wifi_pbuf_free(pbuf); _esp_wifi_send_is_in = false; _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_TX_COMPLETE); return 0; } /** * Function for source code compatibility with ESP-IDF for ESP32 */ int esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buf, uint16_t len) { (void)wifi_if; ESP_WIFI_DEBUG("buf=%p len=%u", buf, len); struct pbuf *pb = _esp_wifi_pbuf_alloc(len); if (pb == NULL) { return ERR_MEM; } if (len) { memcpy(pb->payload, buf, len); } esp_aio_t aio; aio.fd = _esp_wifi_socket; aio.pbuf = pb->payload; aio.len = pb->len; aio.cb = _esp_wifi_tx_cb; aio.arg = pb; aio.ret = 0; if (esp_aio_sendto(&aio, NULL, 0) != 0) { return ERR_IF; } ESP_WIFI_DEBUG("done"); return ERR_OK; } /** * Function for source code compatibility with ESP-IDF for ESP32 */ void esp_wifi_internal_free_rx_buffer(const char* buf) { assert(buf != NULL); assert(_esp_wifi_socket != -1); ESP_WIFI_DEBUG("buf=%p sock=%d", buf, _esp_wifi_socket); esp_free_pbuf(_esp_wifi_socket, (void *)buf); } /** * Type definition for source code compatibility with ESP-IDF for ESP32 */ typedef int (*wifi_rxcb_t)(struct esp_aio *aio); /** * Function for source code compatibility with ESP-IDF for ESP32 */ esp_err_t esp_wifi_internal_reg_rxcb(wifi_interface_t ifx, wifi_rxcb_t fn) { assert(ifx == ESP_IF_WIFI_STA || ifx == ESP_IF_WIFI_AP); ESP_WIFI_DEBUG("%d %p", ifx, fn); extern int8_t wifi_get_netif(uint8_t fd); /* if function is NULL, it is deregistered */ if (fn == NULL) { /* if socket is allocated, it has to be closed */ if (_esp_wifi_socket != -1 && esp_close(_esp_wifi_socket) < 0) { return ESP_FAIL; } _esp_wifi_socket = -1; return ESP_OK; } /* if socket is already allocated we have to close it to register a function */ if (_esp_wifi_socket != -1 && esp_close(_esp_wifi_socket) < 0) { return ESP_FAIL; } /* now, we have to allocate a new socket and register the function */ _esp_wifi_socket = esp_socket(AF_PACKET, SOCK_RAW, ETH_P_ALL); const char *ifx_name = "sta0"; if (ifx == ESP_IF_WIFI_AP) { ifx_name = "ap0"; } if (_esp_wifi_socket < 0) { ESP_WIFI_LOG_ERROR("create socket of (AF_PACKET, SOCK_RAW, ETH_P_ALL) error"); return ESP_FAIL; } if (esp_ioctl(_esp_wifi_socket, SIOCGIFINDEX, ifx_name) < 0) { ESP_WIFI_LOG_ERROR("bind socket %d to netcard %s error", _esp_wifi_socket, ifx_name); esp_close(_esp_wifi_socket); return ESP_FAIL; } if (esp_aio_event(_esp_wifi_socket, ESP_SOCKET_RECV_EVENT, fn, &_esp_wifi_dev) < 0) { ESP_WIFI_LOG_ERROR("socket %d register receive callback function %p error", _esp_wifi_socket, fn); esp_close(_esp_wifi_socket); return ESP_FAIL; } return ESP_OK; } #endif /* MCU_ESP8266 */ #ifdef MCU_ESP8266 /* Prolog for source code compatibility with ESP-IDF for ESP32 */ static int _esp_wifi_rx_cb(struct esp_aio *aio) { assert(aio != NULL); const char *eb = aio->pbuf; const char *buffer = aio->pbuf; uint16_t len = aio->len; #else /* MCU_ESP8266 */ esp_err_t _esp_wifi_rx_cb(void *buffer, uint16_t len, void *eb) { #endif /* MCU_ESP8266 */ /* * This callback function is not executed in interrupt context but in the * context of the low level WiFi driver thread. That is, mutex_lock or * msg_send functions could block. */ assert(buffer != NULL); assert(len <= ETHERNET_MAX_LEN); critical_enter(); ESP_WIFI_DEBUG("buf=%p len=%d eb=%p", buffer, len, eb); /* * The ring buffer uses two bytes for the pkt length, followed by the * actual packet data. */ if (ringbuffer_get_free(&_esp_wifi_dev.rx_buf) < len + sizeof(uint16_t)) { ESP_WIFI_DEBUG("buffer full, dropping incoming packet of %d bytes", len); /* free the receive buffer */ if (eb) { esp_wifi_internal_free_rx_buffer(eb); } /* * we must not return a failure code in this case, otherwise, * the WiFi driver hangs up */ critical_exit(); return ESP_OK; } /* store length information as first two bytes */ ringbuffer_add(&_esp_wifi_dev.rx_buf, (char *)&len, sizeof(uint16_t)); /* copy the buffer and free WiFi driver buffer */ ringbuffer_add(&_esp_wifi_dev.rx_buf, (char *)buffer, len); if (eb) { esp_wifi_internal_free_rx_buffer(eb); } /* * 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 */ _esp_wifi_dev.event_recv++; netdev_trigger_event_isr(&_esp_wifi_dev.netdev); /* reset IRQ nesting counter */ irq_interrupt_nesting--; critical_exit(); return ESP_OK; } #ifndef MODULE_ESP_WIFI_AP #define REASON_BEACON_TIMEOUT (200) #define REASON_HANDSHAKE_TIMEOUT (204) #define INDEX_BEACON_TIMEOUT (REASON_BEACON_TIMEOUT - 24) 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 */ }; #endif /* MODULE_ESP_WIFI_AP */ /* indicator whether the WiFi interface is started */ static unsigned _esp_wifi_started = 0; /* current channel used by the WiFi interface */ static unsigned _esp_wifi_channel = 0; /* * Event handler for esp system events. */ static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t *event) { assert(event != NULL); #ifndef MODULE_ESP_WIFI_AP esp_err_t result; uint8_t reason; const char* reason_str = "UNKNOWN"; #endif /* MODULE_ESP_WIFI_AP */ switch (event->event_id) { #ifdef MODULE_ESP_WIFI_AP case SYSTEM_EVENT_AP_START: _esp_wifi_started = 1; esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, _esp_wifi_rx_cb); ESP_WIFI_DEBUG("WiFi started"); break; case SYSTEM_EVENT_AP_STOP: _esp_wifi_started = 0; esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_AP, NULL); ESP_WIFI_DEBUG("WiFi stopped"); break; case SYSTEM_EVENT_AP_STACONNECTED: _esp_wifi_dev.sta_connected += 1; ESP_WIFI_LOG_INFO("Station "MAC_STR" join (AID=%d)", MAC_STR_ARG(event->event_info.sta_connected.mac), event->event_info.sta_connected.aid); break; case SYSTEM_EVENT_AP_STADISCONNECTED: _esp_wifi_dev.sta_connected -= 1; ESP_WIFI_LOG_INFO("Station "MAC_STR" leave (AID=%d)", MAC_STR_ARG(event->event_info.sta_disconnected.mac), event->event_info.sta_disconnected.aid); break; case SYSTEM_EVENT_AP_PROBEREQRECVED: ESP_WIFI_LOG_DEBUG("Station "MAC_STR" probed (rssi=%d)", MAC_STR_ARG(event->event_info.ap_probereqrecved.mac), event->event_info.ap_probereqrecved.rssi); break; #else /* MODULE_ESP_WIFI_AP */ case SYSTEM_EVENT_STA_START: _esp_wifi_started = 1; ESP_WIFI_DEBUG("WiFi started"); result = esp_wifi_connect(); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_connect failed with return " "value %d", result); } break; case SYSTEM_EVENT_STA_STOP: _esp_wifi_started = 0; ESP_WIFI_DEBUG("WiFi stopped"); break; case SYSTEM_EVENT_SCAN_DONE: ESP_WIFI_DEBUG("WiFi scan done"); break; case SYSTEM_EVENT_STA_CONNECTED: ESP_WIFI_LOG_INFO("WiFi connected to ssid %s, channel %d", event->event_info.connected.ssid, event->event_info.connected.channel); _esp_wifi_channel = event->event_info.connected.channel; #ifdef MODULE_ESP_NOW extern void esp_now_set_channel(uint8_t channel); esp_now_set_channel(_esp_wifi_channel); #endif /* register RX callback function */ esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, _esp_wifi_rx_cb); _esp_wifi_dev.connected = true; _esp_wifi_dev.event_conn++; netdev_trigger_event_isr(&_esp_wifi_dev.netdev); break; case SYSTEM_EVENT_STA_DISCONNECTED: reason = event->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 - INDEX_BEACON_TIMEOUT]; } ESP_WIFI_LOG_INFO("WiFi disconnected from ssid %s, reason %d (%s)", event->event_info.disconnected.ssid, event->event_info.disconnected.reason, reason_str); /* unregister RX callback function */ esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL); _esp_wifi_dev.connected = false; _esp_wifi_dev.event_disc++; netdev_trigger_event_isr(&_esp_wifi_dev.netdev); if (reason != WIFI_REASON_ASSOC_LEAVE) { /* call disconnect to reset internal state */ result = esp_wifi_disconnect(); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_disconnect failed with " "return value %d", result); return result; } /* try to reconnect */ if (_esp_wifi_started && ((result = esp_wifi_connect()) != ESP_OK)) { ESP_WIFI_LOG_ERROR("esp_wifi_connect failed with " "return value %d", result); } } break; #endif /* MODULE_ESP_WIFI_AP */ default: ESP_WIFI_DEBUG("event %d", event->event_id); break; } return ESP_OK; } static int _esp_wifi_send(netdev_t *netdev, const iolist_t *iolist) { ESP_WIFI_DEBUG("netdev=%p iolist=%p", netdev, iolist); assert(netdev != NULL); assert(iolist != NULL); if (_esp_wifi_send_is_in) { return 0; } _esp_wifi_send_is_in = true; esp_wifi_netdev_t* dev = container_of(netdev, esp_wifi_netdev_t, netdev); #ifdef MODULE_ESP_WIFI_AP if (_esp_wifi_dev.sta_connected == 0) { ESP_WIFI_DEBUG("No STAs are connected to SoftAP, cannot send"); _esp_wifi_send_is_in = false; return -ENODEV; } #else /* MODULE_ESP_WIFI_AP */ if (!_esp_wifi_dev.connected) { ESP_WIFI_DEBUG("WiFi is still not connected to AP, cannot send"); _esp_wifi_send_is_in = false; return -ENODEV; } #endif /* MODULE_ESP_WIFI_AP */ critical_enter(); dev->tx_len = 0; /* load packet data into TX buffer */ for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { if (dev->tx_len + iol->iol_len > ETHERNET_MAX_LEN) { _esp_wifi_send_is_in = false; critical_exit(); return -EOVERFLOW; } if (iol->iol_len) { memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len); dev->tx_len += iol->iol_len; } } if (IS_ACTIVE(ENABLE_DEBUG)) { ESP_WIFI_DEBUG("send %d byte", dev->tx_len); if (IS_ACTIVE(ENABLE_DEBUG_HEXDUMP) && IS_USED(MODULE_OD)) { od_hex_dump(dev->tx_buf, dev->tx_len, OD_WIDTH_DEFAULT); } } critical_exit(); #ifdef MODULE_ESP_WIFI_AP if (esp_wifi_internal_tx(ESP_IF_WIFI_AP, dev->tx_buf, dev->tx_len) == ESP_OK) { #else /* MODULE_ESP_WIFI_AP */ /* send the the packet to the peer(s) mac address */ if (esp_wifi_internal_tx(ESP_IF_WIFI_STA, dev->tx_buf, dev->tx_len) == ESP_OK) { #endif #ifdef MCU_ESP32 /* for ESP8266 it is done in _esp_wifi_tx_cb */ _esp_wifi_send_is_in = false; netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); #endif return dev->tx_len; } else { _esp_wifi_send_is_in = false; ESP_WIFI_DEBUG("sending WiFi packet failed"); return -EIO; } } static int _esp_wifi_recv(netdev_t *netdev, void *buf, size_t len, void *info) { ESP_WIFI_DEBUG("%p %p %u %p", netdev, buf, len, info); assert(netdev != NULL); esp_wifi_netdev_t* dev = container_of(netdev, esp_wifi_netdev_t, netdev); uint16_t size; critical_enter(); _esp_wifi_rx_in_progress = true; if (ringbuffer_peek(&dev->rx_buf, (char *)&size, sizeof(uint16_t)) < sizeof(uint16_t)) { critical_exit(); return 0; } if (!buf) { /* get the size of the frame */ if (len > 0 && size) { /* if len > 0, drop the frame */ ringbuffer_remove(&dev->rx_buf, sizeof(uint16_t) + size); _esp_wifi_rx_in_progress = false; } critical_exit(); return size; } if (len < size) { /* 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 */ ringbuffer_remove(&dev->rx_buf, sizeof(uint16_t) + size); _esp_wifi_rx_in_progress = false; critical_exit(); return -ENOBUFS; } /* remove length bytes, copy the buffer to the ringbuffer and free it */ ringbuffer_remove(&dev->rx_buf, sizeof(uint16_t)); ringbuffer_get(&dev->rx_buf, buf, size); if (IS_ACTIVE(ENABLE_DEBUG)) { ethernet_hdr_t *hdr = (ethernet_hdr_t *)buf; ESP_WIFI_DEBUG("received %u byte from addr " MAC_STR, size, MAC_STR_ARG(hdr->src)); if (IS_ACTIVE(ENABLE_DEBUG_HEXDUMP) && IS_USED(MODULE_OD)) { od_hex_dump(buf, size, OD_WIDTH_DEFAULT); } } critical_exit(); return size; } static int _esp_wifi_get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) { ESP_WIFI_DEBUG("%s %p %p %u", netopt2str(opt), netdev, val, max_len); assert(netdev != NULL); assert(val != NULL); #ifndef MODULE_ESP_WIFI_AP esp_wifi_netdev_t* dev = container_of(netdev, esp_wifi_netdev_t, netdev); #endif /* MODULE_ESP_WIFI_AP */ switch (opt) { case NETOPT_IS_WIRED: return -ENOTSUP; case NETOPT_CHANNEL: CHECK_PARAM_RET(max_len >= sizeof(uint16_t), -EOVERFLOW); *((uint16_t *)val) = _esp_wifi_channel; return sizeof(uint16_t); case NETOPT_ADDRESS: assert(max_len >= ETHERNET_ADDR_LEN); #ifdef MODULE_ESP_WIFI_AP esp_wifi_get_mac(ESP_MAC_WIFI_SOFTAP, (uint8_t *)val); #else /* MODULE_ESP_WIFI_AP */ esp_wifi_get_mac(ESP_MAC_WIFI_STA, (uint8_t *)val); #endif /* MODULE_ESP_WIFI_AP */ return ETHERNET_ADDR_LEN; case NETOPT_LINK: assert(max_len == sizeof(netopt_enable_t)); #ifdef MODULE_ESP_WIFI_AP *((netopt_enable_t *)val) = (_esp_wifi_started) ? NETOPT_ENABLE : NETOPT_DISABLE; #else /* MOUDLE_ESP_WIFI_AP */ *((netopt_enable_t *)val) = (dev->connected) ? NETOPT_ENABLE : NETOPT_DISABLE; #endif /* MODULE_ESP_WIFI_AP */ return sizeof(netopt_enable_t); default: return netdev_eth_get(netdev, opt, val, max_len); } } static int _esp_wifi_set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len) { ESP_WIFI_DEBUG("%s %p %p %u", netopt2str(opt), netdev, val, max_len); assert(netdev != NULL); assert(val != NULL); switch (opt) { case NETOPT_ADDRESS: assert(max_len == ETHERNET_ADDR_LEN); #ifdef MODULE_ESP_WIFI_AP esp_wifi_set_mac(ESP_MAC_WIFI_SOFTAP, (uint8_t *)val); #else /* MODULE_ESP_WIFI_AP */ esp_wifi_set_mac(ESP_MAC_WIFI_STA, (uint8_t *)val); #endif /* MODULE_ESP_WIFI_AP */ return ETHERNET_ADDR_LEN; default: return netdev_eth_set(netdev, opt, val, max_len); } } static void _esp_wifi_isr(netdev_t *netdev) { ESP_WIFI_DEBUG("%p", netdev); assert(netdev != NULL); esp_wifi_netdev_t* dev = container_of(netdev, esp_wifi_netdev_t, netdev); while (dev->event_recv) { dev->event_recv--; dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); } #ifndef MODULE_ESP_WIFI_AP if (dev->event_conn) { dev->event_conn--; dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP); } else if (dev->event_disc) { dev->event_disc--; dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN); } #endif return; } static int _esp_wifi_init(netdev_t *netdev) { ESP_WIFI_DEBUG("%p", netdev); return 0; } static const netdev_driver_t _esp_wifi_driver = { .send = _esp_wifi_send, .recv = _esp_wifi_recv, .init = _esp_wifi_init, .isr = _esp_wifi_isr, .get = _esp_wifi_get, .set = _esp_wifi_set, }; #ifndef MODULE_ESP_WIFI_AP /* * Static configuration for the Station interface */ static wifi_config_t wifi_config_sta = { .sta = { .ssid = ESP_WIFI_SSID, #if !defined(MODULE_ESP_WIFI_ENTERPRISE) && defined(ESP_WIFI_PASS) .password = ESP_WIFI_PASS, #endif .channel = 0, .scan_method = WIFI_ALL_CHANNEL_SCAN, .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, .threshold.rssi = -127, #if defined(MODULE_ESP_WIFI_ENTERPRISE) .threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE #elif defined(ESP_WIFI_PASS) .threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK #else .threshold.authmode = WIFI_AUTH_OPEN #endif } }; #endif /* MODULE_ESP_WIFI_AP */ #if (defined(MCU_ESP8266) && !defined(MODULE_ESP_NOW)) || defined(MODULE_ESP_WIFI_AP) /** * Static configuration for the SoftAP interface if ESP-NOW is not enabled. * * Although only the Station interface is needed, the SoftAP interface must * also be enabled for stability reasons to prevent the Station interface * from being shut down by power management in the event of silence. * Otherwise, the WiFi module and the WiFi task will hang sporadically. * * Since the SoftAP interface is not required, we make it invisible and * unusable. This configuration * * - uses the same hidden SSID that the Station interface uses to * connect to the AP, * - uses the same channel that the Station interface uses to connect to the AP, * - defines a very long beacon interval * - doesn't allow any connection. */ static wifi_config_t wifi_config_ap = { .ap = { #ifdef ESP_WIFI_SSID .ssid = ESP_WIFI_SSID, .ssid_len = ARRAY_SIZE(ESP_WIFI_SSID) - 1, #endif #ifdef ESP_WIFI_PASS .password = ESP_WIFI_PASS, .authmode = WIFI_AUTH_WPA2_PSK, #else .authmode = WIFI_AUTH_OPEN, #endif #ifdef MODULE_ESP_WIFI_AP .ssid_hidden = ESP_WIFI_SSID_HIDDEN, /* don't make the AP visible */ .max_connection = ESP_WIFI_MAX_CONN, /* maximum number of connections */ .beacon_interval = ESP_WIFI_BEACON_INTERVAL, #else .ssid_hidden = 1, .max_connection = 0, /* don't allow connections */ .beacon_interval = 60000, /* send beacon only every 60 s */ #endif } }; #endif /* (defined(MCU_ESP8266) && !defined(MODULE_ESP_NOW)) || defined(MODULE_ESP_WIFI_AP) */ void esp_wifi_setup (esp_wifi_netdev_t* dev) { ESP_WIFI_DEBUG("dev=%p", dev); /* initialize buffer */ ringbuffer_init(&dev->rx_buf, (char*)dev->rx_mem, sizeof(dev->rx_mem)); /* set the event handler */ esp_system_event_add_handler(_esp_system_event_handler, NULL); /* * Init the WiFi driver. TODO It is not only required before ESP_WIFI is * initialized but also before other WiFi functions are used. Once other * WiFi functions are realized it has to be moved to a more common place. */ esp_err_t result; #ifndef MODULE_ESP_NOW /* if module esp_now is used, the following part is already done */ #ifdef MCU_ESP32 extern portMUX_TYPE g_intr_lock_mux; mutex_init(&g_intr_lock_mux); #endif #if CONFIG_ESP32_WIFI_NVS_ENABLED result = nvs_flash_init(); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("nfs_flash_init failed with return value %d", result); return; } #endif /* CONFIG_ESP32_WIFI_NVS_ENABLED */ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); result = esp_wifi_init(&cfg); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_init failed with return value %d", result); return; } #ifdef CONFIG_WIFI_COUNTRY /* TODO */ #endif #ifdef MODULE_ESP_WIFI_AP /* Activate the SoftAP interface */ result = esp_wifi_set_mode(WIFI_MODE_AP); #elif defined(MCU_ESP8266) /* * Although only the Station interface is needed, the SoftAP interface must * also be enabled on ESP8266 for stability reasons to prevent the Station * interface from being shut down by power management in the event of * silence. Otherwise, the WiFi module and the WiFi task will hang * sporadically. */ /* activate the Station and the SoftAP interface */ result = esp_wifi_set_mode(WIFI_MODE_APSTA); #else /* defined(MCU_ESP8266) */ /* activate only the Station interface */ result = esp_wifi_set_mode(WIFI_MODE_STA); #endif /* defined(MCU_ESP8266) */ if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_set_mode failed with return value %d", result); return; } #if defined(MCU_ESP8266) || defined(MODULE_ESP_WIFI_AP) #if IS_ACTIVE(ESP_WIFI_SSID_DYNAMIC) uint8_t mac[ETHERNET_ADDR_LEN]; esp_wifi_get_mac(ESP_MAC_WIFI_SOFTAP, mac); sprintf((char*)wifi_config_ap.ap.ssid, "%s_%02x%02x%02x%02x%02x%02x", ESP_WIFI_SSID, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); wifi_config_ap.ap.ssid_len = strlen((char*)wifi_config_ap.ap.ssid); #endif /* IS_ACTIVE(ESP_WIFI_SSID_DYNAMIC) */ /* set the SoftAP configuration */ result = esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config_ap); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_set_config softap failed with return value %d", result); return; } #endif /* defined(MCU_ESP8266) || defined(MODULE_ESP_WIFI_AP) */ #endif /* MODULE_ESP_NOW */ #ifndef MODULE_ESP_WIFI_AP /* set the Station configuration */ result = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config_sta); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_set_config station failed with return value %d", result); return; } #endif /* MODULE_ESP_WIFI_AP */ #if defined(MODULE_ESP_WIFI_ENTERPRISE) && !defined(MODULE_ESP_WIFI_AP) esp_wpa2_config_t wifi_config_wpa2 = WPA2_CONFIG_INIT_DEFAULT(); #ifdef ESP_WIFI_EAP_ID esp_wifi_sta_wpa2_ent_set_identity((const unsigned char *)ESP_WIFI_EAP_ID, strlen(ESP_WIFI_EAP_ID)); #endif /* ESP_WIFI_EAP_ID */ #if defined(ESP_WIFI_EAP_USER) && defined(ESP_WIFI_EAP_PASS) ESP_WIFI_DEBUG("eap_user=%s eap_pass=%s\n", ESP_WIFI_EAP_USER, ESP_WIFI_EAP_PASS); esp_wifi_sta_wpa2_ent_set_username((const unsigned char *)ESP_WIFI_EAP_USER, strlen(ESP_WIFI_EAP_USER)); esp_wifi_sta_wpa2_ent_set_password((const unsigned char *)ESP_WIFI_EAP_PASS, strlen(ESP_WIFI_EAP_PASS)); #else /* defined(ESP_WIFI_EAP_USER) && defined(ESP_WIFI_EAP_PASS) */ #error ESP_WIFI_EAP_USER and ESP_WIFI_EAP_PASS have to define the user name \ and the password for EAP phase 2 authentication in esp_wifi_enterprise #endif /* defined(ESP_WIFI_EAP_USER) && defined(ESP_WIFI_EAP_PASS) */ esp_wifi_sta_wpa2_ent_enable(&wifi_config_wpa2); #endif /* defined(MODULE_ESP_WIFI_ENTERPRISE) && !defined(MODULE_ESP_WIFI_AP) */ /* start the WiFi driver */ result = esp_wifi_start(); if (result != ESP_OK) { ESP_WIFI_LOG_ERROR("esp_wifi_start failed with return value %d", result); return; } /* set the netdev driver */ dev->netdev.driver = &_esp_wifi_driver; /* initialize netdev data structure */ dev->event_recv = 0; #ifdef MODULE_ESP_WIFI_AP dev->sta_connected = 0; #else /* MODULE_ESP_WIFI_AP */ dev->event_conn = 0; dev->event_disc = 0; dev->connected = false; #endif /* MODULE_ESP_WIFI_AP */ } #endif /* MODULE_ESP_WIFI */ /**@}*/