mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
c81138367b
The situation where the firmware `lwIP` packet buffer is exhausted is an important indication that the traffic sent to and sent from the esp8266 is more than the esp8266 is able to handle. Therefore, it should be an error message.
700 lines
21 KiB
C
700 lines
21 KiB
C
/*
|
|
* 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_esp_wifi
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Network device driver for the ESP8266 WiFi interface
|
|
*
|
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
*/
|
|
|
|
#include "log.h"
|
|
#include "tools.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
|
|
#include "net/ethernet.h"
|
|
#include "net/ipv4/addr.h"
|
|
#include "net/gnrc/netif/ethernet.h"
|
|
#include "net/netdev/eth.h"
|
|
#include "od.h"
|
|
#include "xtimer.h"
|
|
|
|
#include "common.h"
|
|
#include "espressif/c_types.h"
|
|
#include "espnow.h"
|
|
#include "esp/common_macros.h"
|
|
#include "irq_arch.h"
|
|
#include "sdk/sdk.h"
|
|
|
|
#include "lwip/igmp.h"
|
|
#include "lwip/udp.h"
|
|
|
|
#include "esp_wifi_params.h"
|
|
#include "esp_wifi_netdev.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
#define ESP_WIFI_DEBUG(f, ...) \
|
|
DEBUG("[esp_wifi] %s: " f "\n", __func__, ## __VA_ARGS__)
|
|
|
|
#define ESP_WIFI_LOG_INFO(f, ...) \
|
|
LOG_INFO("[esp_wifi] " f "\n", ## __VA_ARGS__)
|
|
|
|
#define ESP_WIFI_LOG_ERROR(f, ...) \
|
|
LOG_ERROR("[esp_wifi] " f "\n", ## __VA_ARGS__)
|
|
|
|
#define ESP_WIFI_STATION_MODE (STATION_MODE)
|
|
#define ESP_WIFI_AP_MODE (SOFTAP_MODE)
|
|
#define ESP_WIFI_STATION_AP_MODE (STATIONAP_MODE)
|
|
#define ESP_WIFI_MODE (STATIONAP_MODE)
|
|
|
|
#define ESP_WIFI_STATION_IF (STATION_IF)
|
|
#define ESP_WIFI_SOFTAP_IF (SOFTAP_IF)
|
|
|
|
#define ESP_WIFI_RECONNECT_TIME (20 * US_PER_SEC)
|
|
|
|
#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]
|
|
|
|
#define PBUF_IEEE80211_HLEN (36)
|
|
|
|
/** Timer used to reconnect automatically after 20 seconds if not connected */
|
|
static xtimer_t _esp_wifi_reconnect_timer;
|
|
|
|
/**
|
|
* There is only one ESP WIFI device. We define it as static device variable
|
|
* to have accesss 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.
|
|
*/
|
|
static esp_wifi_netdev_t _esp_wifi_dev;
|
|
|
|
/** forward declaration of the driver functions structure */
|
|
static const netdev_driver_t _esp_wifi_driver;
|
|
|
|
/** Stack for the netif thread */
|
|
static char _esp_wifi_stack[ESP_WIFI_STACKSIZE];
|
|
|
|
/** Static station configuration used for the WiFi interface */
|
|
static const struct station_config station_cfg = {
|
|
.bssid_set = 0, /* no check of MAC address of AP */
|
|
.ssid = ESP_WIFI_SSID,
|
|
.password = ESP_WIFI_PASS,
|
|
};
|
|
|
|
#ifndef MODULE_ESP_NOW
|
|
/**
|
|
* Static const configuration for the SoftAP which is used to configure the
|
|
* SoftAP interface if ESP-NOW is not enabled.
|
|
*
|
|
* Since we need to use the WiFi interface in SoftAP + Station mode for
|
|
* stability reasons, although in fact only the station interface is required,
|
|
* we make the SoftAP interface 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 const struct softap_config softap_cfg = {
|
|
.ssid = ESP_WIFI_SSID,
|
|
.ssid_len = sizeof(ESP_WIFI_SSID) / sizeof(ESP_WIFI_SSID[0]),
|
|
.ssid_hidden = 1, /* don't make the AP visible */
|
|
.password = ESP_WIFI_PASS,
|
|
.authmode = AUTH_WPA2_PSK,
|
|
.max_connection = 0, /* don't allow connections */
|
|
.beacon_interval = 60000, /* send beacon only every 60 s */
|
|
};
|
|
#endif
|
|
|
|
extern struct netif * eagle_lwip_getif(uint8 index);
|
|
|
|
/** guard variable to avoid reentrance to _send */
|
|
static bool _in_send = false;
|
|
|
|
/** guard variable to avoid reentrance to _esp_wifi_recv_cb */
|
|
static bool _in_esp_wifi_recv_cb = false;
|
|
|
|
/**
|
|
* @brief Reconnect function called back by the reconnect timer
|
|
*/
|
|
static void IRAM _esp_wifi_reconnect_timer_cb(void* arg)
|
|
{
|
|
DEBUG("%s\n", __func__);
|
|
|
|
esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)arg;
|
|
|
|
if (dev->state == ESP_WIFI_DISCONNECTED ||
|
|
dev->state == ESP_WIFI_CONNECTING) {
|
|
ESP_WIFI_LOG_INFO("trying to reconnect to ssid " ESP_WIFI_SSID);
|
|
|
|
wifi_station_disconnect();
|
|
wifi_station_connect();
|
|
dev->state = ESP_WIFI_CONNECTING;
|
|
}
|
|
|
|
/* set the time for next connection check */
|
|
xtimer_set(&_esp_wifi_reconnect_timer, ESP_WIFI_RECONNECT_TIME);
|
|
}
|
|
|
|
/**
|
|
* @brief Callback when ethernet frame is received. Has to run in IRAM.
|
|
*/
|
|
void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif)
|
|
{
|
|
assert(pb != NULL);
|
|
assert(netif != NULL);
|
|
|
|
/*
|
|
* 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`.
|
|
*
|
|
* It should be therefore not possible to reenter function
|
|
* `esp_wifi_recv_cb`. If it does occur inspite of that, we use a
|
|
* guard 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;
|
|
|
|
critical_enter();
|
|
|
|
/* first, check packet buffer for the minimum packet size */
|
|
if (pb->tot_len < sizeof(ethernet_hdr_t)) {
|
|
ESP_WIFI_DEBUG("frame length is less than the size of an Ethernet"
|
|
"header (%u < %u)", pb->tot_len, sizeof(ethernet_hdr_t));
|
|
pbuf_free(pb);
|
|
_in_esp_wifi_recv_cb = false;
|
|
critical_exit();
|
|
return;
|
|
}
|
|
|
|
/* 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);
|
|
pbuf_free(pb);
|
|
_in_esp_wifi_recv_cb = false;
|
|
critical_exit();
|
|
return;
|
|
}
|
|
|
|
/* check whether packet buffer fits into receive buffer */
|
|
if (pb->tot_len > ETHERNET_MAX_LEN) {
|
|
ESP_WIFI_DEBUG("frame length is greater than the maximum size of an "
|
|
"Ethernet frame (%u > %u)", pb->tot_len, ETHERNET_MAX_LEN);
|
|
pbuf_free(pb);
|
|
_in_esp_wifi_recv_cb = false;
|
|
critical_exit();
|
|
return;
|
|
}
|
|
|
|
/* 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);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
if (_esp_wifi_dev.netdev.event_callback) {
|
|
_esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev,
|
|
NETDEV_EVENT_RX_COMPLETE);
|
|
}
|
|
|
|
_in_esp_wifi_recv_cb = false;
|
|
critical_exit();
|
|
}
|
|
|
|
#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.
|
|
*/
|
|
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:
|
|
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, reason_str);
|
|
_esp_wifi_dev.state = ESP_WIFI_DISCONNECTED;
|
|
_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:
|
|
ESP_WIFI_LOG_INFO("station " MACSTR " join, aid %d",
|
|
MAC2STR(evt->event_info.sta_connected.mac),
|
|
evt->event_info.sta_connected.aid);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int _init(netdev_t *netdev)
|
|
{
|
|
ESP_WIFI_DEBUG("%p", netdev);
|
|
|
|
#ifdef MODULE_NETSTATS_L2
|
|
memset(&netdev->stats, 0x00, sizeof(netstats_t));
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if ENABLE_DEBUG
|
|
/** buffer for sent packet dump */
|
|
uint8_t _send_pkt_buf[ETHERNET_MAX_LEN];
|
|
#endif
|
|
|
|
/** function used to send an ethernet frame over WiFi */
|
|
extern err_t ieee80211_output_pbuf(struct netif *netif, struct pbuf *p);
|
|
|
|
static int IRAM _send(netdev_t *netdev, const iolist_t *iolist)
|
|
{
|
|
ESP_WIFI_DEBUG("%p %p", netdev, iolist);
|
|
|
|
assert(netdev != NULL);
|
|
assert(iolist != NULL);
|
|
|
|
if (_in_send) {
|
|
return 0;
|
|
}
|
|
_in_send = true;
|
|
|
|
esp_wifi_netdev_t *dev = (esp_wifi_netdev_t*)netdev;
|
|
|
|
critical_enter();
|
|
if (dev->state != ESP_WIFI_CONNECTED) {
|
|
ESP_WIFI_DEBUG("WiFi is still not connected to AP, cannot send");
|
|
_in_send = false;
|
|
critical_exit();
|
|
return -EIO;
|
|
}
|
|
|
|
if (wifi_get_opmode() != ESP_WIFI_MODE) {
|
|
ESP_WIFI_DEBUG("WiFi is not in correct mode, cannot send");
|
|
_in_send = false;
|
|
critical_exit();
|
|
return -EIO;
|
|
}
|
|
|
|
const iolist_t *iol = iolist;
|
|
size_t iol_len = 0;
|
|
|
|
/* determine the frame size */
|
|
while (iol) {
|
|
iol_len += iol->iol_len;
|
|
iol = iol->iol_next;
|
|
}
|
|
|
|
/* limit checks */
|
|
if (iol_len > ETHERNET_MAX_LEN) {
|
|
ESP_WIFI_DEBUG("frame length exceeds the maximum (%u > %u)",
|
|
iol_len, ETHERNET_MAX_LEN);
|
|
_in_send = false;
|
|
critical_exit();
|
|
return -EBADMSG;
|
|
}
|
|
|
|
if (iol_len < sizeof(ethernet_hdr_t)) {
|
|
ESP_WIFI_DEBUG("frame length is less than the size of an Ethernet"
|
|
"header (%u < %u)", iol_len, sizeof(ethernet_hdr_t));
|
|
_in_send = false;
|
|
critical_exit();
|
|
return -EBADMSG;
|
|
}
|
|
|
|
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_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. We slow down sending a bit.
|
|
*/
|
|
critical_exit();
|
|
/* wait 20 ms */
|
|
xtimer_usleep(20 * US_PER_MS);
|
|
|
|
_in_send = false;
|
|
return -EIO;
|
|
}
|
|
|
|
struct pbuf *pbi = pb;
|
|
uint8_t *pbi_payload = pb->payload;
|
|
size_t pbi_pos = 0;
|
|
|
|
/* prepare lwIP packet buffer direct from iolist without any buffer */
|
|
for (const iolist_t *iol = iolist; iol && pbi; iol = iol->iol_next) {
|
|
uint8_t *iol_base = iol->iol_base;
|
|
for (unsigned i = 0; i < iol->iol_len && pbi; i++) {
|
|
pbi_payload[pbi_pos++] = iol_base[i];
|
|
if (pbi_pos >= pbi->len) {
|
|
pbi = pbi->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if ENABLE_DEBUG
|
|
pbi = pb;
|
|
pbi_pos = 0;
|
|
|
|
for (; pbi; pbi = pbi->next) {
|
|
memcpy(_send_pkt_buf + pbi_pos, pbi->payload, pbi->len);
|
|
pbi_pos += pbi->len;
|
|
}
|
|
|
|
const ethernet_hdr_t* hdr = (const ethernet_hdr_t *)_send_pkt_buf;
|
|
|
|
ESP_WIFI_DEBUG("send %u byte to " MAC_STR,
|
|
(unsigned)iol_len, MAC_STR_ARG(hdr->dst));
|
|
#if MODULE_OD
|
|
od_hex_dump(_send_pkt_buf, iol_len, OD_WIDTH_DEFAULT);
|
|
#endif /* MODULE_OD */
|
|
#endif /* ENABLE_DEBUG */
|
|
|
|
critical_exit();
|
|
err_t res = sta_netif->linkoutput(sta_netif, pb);
|
|
pbuf_free(pb);
|
|
|
|
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;
|
|
return iol_len;
|
|
}
|
|
else {
|
|
/* There was either a ieee80211_output_pbuf error or send timed out. */
|
|
#ifdef MODULE_NETSTATS_L2
|
|
netdev->stats.tx_failed++;
|
|
#endif
|
|
_in_send = false;
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
static int _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 = (esp_wifi_netdev_t*)netdev;
|
|
|
|
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 */
|
|
dev->rx_len = 0;
|
|
}
|
|
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 */
|
|
dev->rx_len = 0;
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
/* copy the buffer and free */
|
|
memcpy(buf, dev->rx_buf, dev->rx_len);
|
|
dev->rx_len = 0;
|
|
|
|
#if 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 MODULE_OD
|
|
od_hex_dump(buf, size, OD_WIDTH_DEFAULT);
|
|
#endif /* MODULE_OD */
|
|
#endif /* ENABLE_DEBUG */
|
|
|
|
#if MODULE_NETSTATS_L2
|
|
netdev->stats.rx_count++;
|
|
netdev->stats.rx_bytes += size;
|
|
#endif
|
|
|
|
return size;
|
|
}
|
|
|
|
static int _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);
|
|
|
|
esp_wifi_netdev_t *dev = (esp_wifi_netdev_t*)netdev;
|
|
|
|
switch (opt) {
|
|
|
|
case NETOPT_IS_WIRED:
|
|
return -ENOTSUP;
|
|
|
|
case NETOPT_LINK_CONNECTED:
|
|
assert(max_len == 1);
|
|
if (dev->state == ESP_WIFI_CONNECTED) {
|
|
*((netopt_enable_t *)val) = NETOPT_ENABLE;
|
|
}
|
|
else {
|
|
*((netopt_enable_t *)val) = NETOPT_DISABLE;
|
|
}
|
|
return 1;
|
|
|
|
case NETOPT_ADDRESS:
|
|
assert(max_len >= sizeof(dev->mac));
|
|
memcpy(val, dev->mac, sizeof(dev->mac));
|
|
return sizeof(dev->mac);
|
|
|
|
default:
|
|
return netdev_eth_get(netdev, opt, val, max_len);
|
|
|
|
}
|
|
}
|
|
|
|
static int _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);
|
|
|
|
esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *) netdev;
|
|
|
|
switch (opt) {
|
|
|
|
case NETOPT_ADDRESS:
|
|
assert(max_len >= sizeof(dev->mac));
|
|
memcpy(dev->mac, val, sizeof(dev->mac));
|
|
return sizeof(dev->mac);
|
|
|
|
default:
|
|
return netdev_eth_set(netdev, opt, val, max_len);
|
|
}
|
|
}
|
|
|
|
static void _isr(netdev_t *netdev)
|
|
{
|
|
ESP_WIFI_DEBUG("%p", netdev);
|
|
|
|
assert(netdev != NULL);
|
|
|
|
esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *) netdev;
|
|
|
|
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 */
|
|
extern err_t __real_ethernet_input(struct pbuf *pb, struct netif* netif);
|
|
|
|
err_t __wrap_ethernet_input(struct pbuf *pb, struct netif* netif)
|
|
{
|
|
ESP_WIFI_DEBUG("%p %p", pb, netif);
|
|
if (_esp_wifi_dev.state == ESP_WIFI_CONNECTED) {
|
|
_esp_wifi_recv_cb(pb, netif);
|
|
}
|
|
else {
|
|
__real_ethernet_input(pb, netif);
|
|
}
|
|
return ERR_OK;
|
|
}
|
|
|
|
static const netdev_driver_t _esp_wifi_driver = {
|
|
.send = _send,
|
|
.recv = _recv,
|
|
.init = _init,
|
|
.isr = _isr,
|
|
.get = _get,
|
|
.set = _set,
|
|
};
|
|
|
|
static void _esp_wifi_setup(void)
|
|
{
|
|
esp_wifi_netdev_t* dev = &_esp_wifi_dev;
|
|
|
|
ESP_WIFI_DEBUG("%p", dev);
|
|
|
|
if (dev->netdev.driver) {
|
|
ESP_WIFI_DEBUG("early returning previously initialized device");
|
|
return;
|
|
}
|
|
|
|
/* initialize netdev data structure */
|
|
dev->rx_len = 0;
|
|
dev->state = ESP_WIFI_DISCONNECTED;
|
|
dev->event = EVENT_MAX;
|
|
|
|
/* set the netdev driver */
|
|
dev->netdev.driver = &_esp_wifi_driver;
|
|
|
|
#ifndef MODULE_ESP_NOW
|
|
/* set the WiFi interface mode */
|
|
if (!wifi_set_opmode_current(ESP_WIFI_MODE)) {
|
|
ESP_WIFI_LOG_ERROR("could not set WiFi working mode");
|
|
return;
|
|
}
|
|
|
|
/* set the WiFi SoftAP configuration */
|
|
if (!wifi_softap_set_config_current((struct softap_config *)&softap_cfg)) {
|
|
ESP_WIFI_LOG_ERROR("could not set WiFi configuration");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* set the WiFi station configuration */
|
|
if (!wifi_station_set_config_current((struct station_config *)&station_cfg)) {
|
|
ESP_WIFI_LOG_ERROR("could not set WiFi configuration");
|
|
return;
|
|
}
|
|
|
|
/* get station mac address and store it in device address */
|
|
if (!wifi_get_macaddr(ESP_WIFI_STATION_IF, dev->mac)) {
|
|
ESP_WIFI_LOG_ERROR("could not get MAC address of WiFi interface");
|
|
return;
|
|
}
|
|
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);
|
|
|
|
/* reconnect timer initialization */
|
|
_esp_wifi_reconnect_timer.callback = &_esp_wifi_reconnect_timer_cb;
|
|
_esp_wifi_reconnect_timer.arg = dev;
|
|
|
|
/* set the the reconnect timer */
|
|
xtimer_set(&_esp_wifi_reconnect_timer, ESP_WIFI_RECONNECT_TIME);
|
|
|
|
/* connect */
|
|
wifi_station_connect();
|
|
_esp_wifi_dev.state = ESP_WIFI_CONNECTING;
|
|
|
|
return;
|
|
}
|
|
|
|
void auto_init_esp_wifi(void)
|
|
{
|
|
ESP_WIFI_DEBUG("auto initializing netdev\n");
|
|
|
|
/* setup netdev device */
|
|
_esp_wifi_setup();
|
|
|
|
/* create netif */
|
|
gnrc_netif_ethernet_create(_esp_wifi_stack, ESP_WIFI_STACKSIZE,
|
|
ESP_WIFI_PRIO, "esp_wifi",
|
|
(netdev_t *)&_esp_wifi_dev);
|
|
}
|
|
|
|
/** @} */
|