mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
49f06efd38
When the size of a received frame is checked, always the total length should be used instead of the length of the first lwIP pbuf in the pbuf chain. Otherwise, the check that the length does not exceed ETHERNET_MAX_LEN will always be true since the maximum size of one lwIP pbuf in a pbuf chain is 512 bytes.
691 lines
20 KiB
C
691 lines
20 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 ESP_WIFI_SEND_TIMEOUT (MS_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);
|
|
|
|
/*
|
|
* 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
|
|
* 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;
|
|
|
|
/* 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 */
|
|
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();
|
|
mutex_unlock(&_esp_wifi_dev.dev_lock);
|
|
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();
|
|
mutex_unlock(&_esp_wifi_dev.dev_lock);
|
|
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();
|
|
mutex_unlock(&_esp_wifi_dev.dev_lock);
|
|
return;
|
|
}
|
|
|
|
/* 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_ISR);
|
|
}
|
|
|
|
/* reset IRQ nesting counter */
|
|
irq_interrupt_nesting--;
|
|
|
|
_in_esp_wifi_recv_cb = false;
|
|
critical_exit();
|
|
mutex_unlock(&_esp_wifi_dev.dev_lock);
|
|
}
|
|
|
|
/**
|
|
* @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);
|
|
|
|
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;
|
|
break;
|
|
|
|
case EVENT_STAMODE_DISCONNECTED:
|
|
ESP_WIFI_LOG_INFO("disconnected from ssid %s, reason %d",
|
|
evt->event_info.disconnected.ssid,
|
|
evt->event_info.disconnected.reason);
|
|
_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;
|
|
|
|
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_DEBUG("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.
|
|
*/
|
|
critical_exit();
|
|
/* disconnect from AP */
|
|
wifi_station_disconnect();
|
|
/* 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 */
|
|
|
|
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);
|
|
}
|
|
pbuf_free(pb);
|
|
|
|
if (res == ERR_OK && _timeout) {
|
|
/* 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 {
|
|
/* There was either a ieee80211_output_pbuf error or send timed out. */
|
|
#ifdef MODULE_NETSTATS_L2
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
/* 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 */
|
|
dev->rx_len = 0;
|
|
}
|
|
mutex_unlock(&dev->dev_lock);
|
|
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;
|
|
mutex_unlock(&dev->dev_lock);
|
|
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
|
|
|
|
mutex_unlock(&dev->dev_lock);
|
|
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;
|
|
|
|
if (dev->rx_len) {
|
|
dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
|
|
}
|
|
}
|
|
|
|
/** 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;
|
|
|
|
mutex_init(&dev->dev_lock);
|
|
|
|
/* 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));
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/** @} */
|