diff --git a/cpu/esp_common/esp-now/esp_now_gnrc.c b/cpu/esp_common/esp-now/esp_now_gnrc.c new file mode 100644 index 0000000000000000000000000000000000000000..2680a2316e41a12c166936420cf4435b36b7e69e --- /dev/null +++ b/cpu/esp_common/esp-now/esp_now_gnrc.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2018 Timo Rothenpieler + * + * 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_now + * @{ + * + * @file + * @brief Netif interface for the ESP-NOW WiFi P2P protocol + * + * @author Timo Rothenpieler <timo.rothenpieler@uni-bremen.de> + */ + +#include <assert.h> +#include <stdlib.h> + +#include <sys/uio.h> + +#include "net/netdev.h" +#include "net/gnrc.h" +#include "esp_now_params.h" +#include "esp_now_netdev.h" +#include "esp_now_gnrc.h" +#include "net/gnrc/netif.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) +{ + uint8_t mac[ESP_NOW_ADDR_LEN]; + esp_now_pkt_hdr_t esp_hdr; + netdev_t *dev = netif->dev; + + assert(pkt != NULL); + + if (pkt->type != GNRC_NETTYPE_NETIF) { + DEBUG("gnrc_esp_now: First header was not generic netif header\n"); + gnrc_pktbuf_release(pkt); + return -EBADMSG; + } + + gnrc_netif_hdr_t *netif_hdr = (gnrc_netif_hdr_t*)pkt->data; + gnrc_pktsnip_t *payload = pkt->next; + + if (netif_hdr->flags & (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) { + /* ESP-NOW does not support multicast, always broadcast */ + memset(mac, 0xff, ESP_NOW_ADDR_LEN); + } else if (netif_hdr->dst_l2addr_len == ESP_NOW_ADDR_LEN) { + memcpy(mac, gnrc_netif_hdr_get_dst_addr(netif_hdr), ESP_NOW_ADDR_LEN); + } else { + DEBUG("gnrc_esp_now: destination address had unexpected format" + "(flags=%d, dst_l2addr_len=%d)\n", netif_hdr->flags, netif_hdr->dst_l2addr_len); + gnrc_pktbuf_release(pkt); + return -EBADMSG; + } + + iolist_t esp_hdr_iolist = { + .iol_base = &esp_hdr, + .iol_len = sizeof(esp_hdr), + .iol_next = (iolist_t*)payload + }; + + iolist_t iolist = { + .iol_base = mac, + .iol_len = sizeof(mac), + .iol_next = &esp_hdr_iolist + }; + + switch (payload->type) { +#ifdef MODULE_GNRC_SIXLOWPAN + case GNRC_NETTYPE_SIXLOWPAN: + esp_hdr.flags = ESP_NOW_PKT_HDR_FLAG_SIXLO; + break; +#endif + default: + esp_hdr.flags = 0; + } + + DEBUG("gnrc_esp_now: sending packet to %02x:%02x:%02x:%02x:%02x:%02x with size %u\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], (unsigned)payload->size); + + int res = dev->driver->send(dev, &iolist); + + gnrc_pktbuf_release(pkt); + + return res; +} + +static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif) +{ + netdev_t *dev = netif->dev; + esp_now_netdev_t *esp_now = (esp_now_netdev_t*)dev; + + int bytes_expected = dev->driver->recv(dev, NULL, 0, NULL); + if (bytes_expected <= 0) { + DEBUG("gnrc_esp_now: failed receiving packet: %d\n", bytes_expected); + return NULL; + } + + gnrc_pktsnip_t *pkt; + pkt = gnrc_pktbuf_add(NULL, NULL, + bytes_expected, + GNRC_NETTYPE_UNDEF); + if (!pkt) { + DEBUG("gnrc_esp_now: cannot allocate pktsnip.\n"); + + /* drop the packet */ + dev->driver->recv(dev, NULL, bytes_expected, NULL); + + return NULL; + } + + int nread = dev->driver->recv(dev, pkt->data, bytes_expected, NULL); + if (nread <= 0) { + DEBUG("gnrc_esp_now: read error %d\n", nread); + goto err; + } + + if (nread < bytes_expected) { + DEBUG("gnrc_esp_now: reallocating.\n"); + gnrc_pktbuf_realloc_data(pkt, nread); + } + + gnrc_pktsnip_t *mac_hdr; + mac_hdr = gnrc_pktbuf_mark(pkt, ESP_NOW_ADDR_LEN, GNRC_NETTYPE_UNDEF); + if (!mac_hdr) { + DEBUG("gnrc_esp_now: no space left in packet buffer\n"); + goto err; + } + + gnrc_pktsnip_t *esp_hdr; + esp_hdr = gnrc_pktbuf_mark(pkt, sizeof(esp_now_pkt_hdr_t), GNRC_NETTYPE_UNDEF); + if (!esp_hdr) { + DEBUG("gnrc_esp_now: no space left in packet buffer\n"); + pkt = mac_hdr; + goto err; + } + esp_now_pkt_hdr_t *hdr = (esp_now_pkt_hdr_t*)esp_hdr->data; + +#ifdef MODULE_L2FILTER + if (!l2filter_pass(dev->filter, mac_hdr->data, ESP_NOW_ADDR_LEN)) { + DEBUG("gnrc_esp_now: incoming packet filtered by l2filter\n"); + pkt = mac_hdr; + goto err; + } +#endif + + pkt->type = GNRC_NETTYPE_UNDEF; + +#ifdef MODULE_GNRC_SIXLOWPAN + if (hdr->flags & ESP_NOW_PKT_HDR_FLAG_SIXLO) { + pkt->type = GNRC_NETTYPE_SIXLOWPAN; + } +#endif + + gnrc_pktsnip_t *netif_hdr = gnrc_netif_hdr_build(mac_hdr->data, ESP_NOW_ADDR_LEN, + esp_now->addr, ESP_NOW_ADDR_LEN); + if (!netif_hdr) { + DEBUG("gnrc_esp_now: no space left in packet buffer\n"); + pkt = mac_hdr; + goto err; + } + + ((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid = netif->pid; + + uint8_t *mac = mac_hdr->data; + DEBUG("gnrc_esp_now: received packet from %02x:%02x:%02x:%02x:%02x:%02x of length %u\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], pkt->size); + + gnrc_pktbuf_remove_snip(pkt, mac_hdr); + gnrc_pktbuf_remove_snip(pkt, esp_hdr); + LL_APPEND(pkt, netif_hdr); + + return pkt; + +err: + gnrc_pktbuf_release(pkt); + return NULL; +} + +static const gnrc_netif_ops_t _esp_now_ops = { + .send = _send, + .recv = _recv, + .get = gnrc_netif_get_from_netdev, + .set = gnrc_netif_set_from_netdev, +}; + +gnrc_netif_t *gnrc_netif_esp_now_create(char *stack, int stacksize, char priority, + char *name, netdev_t *dev) +{ + return gnrc_netif_create(stack, stacksize, priority, name, dev, &_esp_now_ops); +} + +/* device thread stack */ +static char _esp_now_stack[ESP_NOW_STACKSIZE]; + +void auto_init_esp_now(void) +{ + LOG_TAG_INFO("esp_now", "initializing ESP-NOW device\n"); + + esp_now_netdev_t *esp_now_dev = netdev_esp_now_setup(); + if (!esp_now_dev) { + LOG_ERROR("[auto_init_netif] error initializing esp_now\n"); + } else { + gnrc_netif_esp_now_create(_esp_now_stack, sizeof(_esp_now_stack), + ESP_NOW_PRIO, + "net-esp-now", + &esp_now_dev->netdev); + } +} + +/** @} */ diff --git a/cpu/esp_common/esp-now/esp_now_gnrc.h b/cpu/esp_common/esp-now/esp_now_gnrc.h new file mode 100644 index 0000000000000000000000000000000000000000..9d451d5cc1c9736eafeb06f359a25c91c3220881 --- /dev/null +++ b/cpu/esp_common/esp-now/esp_now_gnrc.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 Timo Rothenpieler + * + * 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_now + * @{ + * + * @file + * @brief ESP-NOW adaption for @ref net_gnrc_netif + * + * @author Timo Rothenpieler <timo.rothenpieler@uni-bremen.de> + */ +#ifndef ESP_NOW_GNRC_H +#define ESP_NOW_GNRC_H + +#include "net/gnrc/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Creates the ESP-NOW network interface + * @see gnrc_netif_create + * @param [in] stack The stack for the network interface's thread. + * @param [in] stacksize Size of stack. + * @param [in] priority Priority for the network interface's thread. + * @param [in] name Name for the network interface. May be NULL. + * @param [in] dev Device for the interface. + */ +gnrc_netif_t *gnrc_netif_esp_now_create(char *stack, int stacksize, char priority, + char *name, netdev_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* ESP_NOW_GNRC_H */ +/** @} */ diff --git a/cpu/esp_common/esp-now/esp_now_netdev.c b/cpu/esp_common/esp-now/esp_now_netdev.c index 67cfad24486c5e5f47de1e43d3c89ba83dae8071..5b48dbe34240e455added3e7680d1b177d4d5906 100644 --- a/cpu/esp_common/esp-now/esp_now_netdev.c +++ b/cpu/esp_common/esp-now/esp_now_netdev.c @@ -14,10 +14,9 @@ * @brief Netdev interface for the ESP-NOW WiFi P2P protocol * * @author Gunar Schorcht <gunar@schorcht.net> + * @author Timo Rothenpieler <timo.rothenpieler@uni-bremen.de> */ -#define ENABLE_DEBUG (0) -#include "debug.h" #include "log.h" #include "tools.h" @@ -25,7 +24,6 @@ #include <assert.h> #include <errno.h> -#include "net/gnrc/netif/raw.h" #include "net/gnrc.h" #include "xtimer.h" @@ -36,19 +34,20 @@ #include "esp_system.h" #include "esp_wifi.h" #include "irq_arch.h" +#include "od.h" #include "nvs_flash/include/nvs_flash.h" #include "esp_now_params.h" #include "esp_now_netdev.h" -#include "net/ipv6/hdr.h" -#include "net/gnrc/ipv6/nib.h" +#define ENABLE_DEBUG (0) +#include "debug.h" -#define ESP_NOW_UNICAST 1 +#define ESP_NOW_UNICAST (1) -#define ESP_NOW_WIFI_STA 1 -#define ESP_NOW_WIFI_SOFTAP 2 +#define ESP_NOW_WIFI_STA (1) +#define ESP_NOW_WIFI_SOFTAP (2) #define ESP_NOW_WIFI_STA_SOFTAP (ESP_NOW_WIFI_STA + ESP_NOW_WIFI_SOFTAP) #define ESP_NOW_AP_PREFIX "RIOT_ESP_" @@ -60,34 +59,10 @@ * not provide an argument that could be used as pointer to the ESP-NOW * device which triggers the interrupt. */ -static esp_now_netdev_t _esp_now_dev; +static esp_now_netdev_t _esp_now_dev = { 0 }; static const netdev_driver_t _esp_now_driver; -/* device thread stack */ -static char _esp_now_stack[ESP_NOW_STACKSIZE]; - -static inline int _get_mac_from_iid(uint8_t *iid, uint8_t *mac) -{ - CHECK_PARAM_RET (iid != NULL, -EINVAL); - CHECK_PARAM_RET (mac != NULL, -EINVAL); - - /* interface id according to */ - /* https://tools.ietf.org/html/rfc4291#section-2.5.1 */ - mac[0] = iid[0] ^ 0x02; /* invert bit1 */ - mac[1] = iid[1]; - mac[2] = iid[2]; - mac[3] = iid[5]; - mac[4] = iid[6]; - mac[5] = iid[7]; - - return 0; -} - -#if ESP_NOW_UNICAST -static xtimer_t _esp_now_scan_peers_timer; -static bool _esp_now_scan_peers_done = false; - -static bool _esp_now_add_peer(uint8_t* bssid, uint8_t channel, uint8_t* key) +static bool _esp_now_add_peer(const uint8_t* bssid, uint8_t channel, uint8_t* key) { if (esp_now_is_peer_exist(bssid)) { return false; @@ -111,11 +86,26 @@ static bool _esp_now_add_peer(uint8_t* bssid, uint8_t channel, uint8_t* key) return (ret == ESP_OK); } +#if ESP_NOW_UNICAST + +static xtimer_t _esp_now_scan_peers_timer; +static bool _esp_now_scan_peers_done = false; + #define ESP_NOW_APS_BLOCK_SIZE 8 /* has to be power of two */ static wifi_ap_record_t* aps = NULL; static uint32_t aps_size = 0; +static const wifi_scan_config_t scan_cfg = { + .ssid = NULL, + .bssid = NULL, + .channel = ESP_NOW_CHANNEL, + .show_hidden = true, + .scan_type = WIFI_SCAN_TYPE_ACTIVE, + .scan_time.active.min = 0, + .scan_time.active.max = 120 /* TODO tune value */ +}; + static void IRAM_ATTR esp_now_scan_peers_done(void) { mutex_lock(&_esp_now_dev.dev_lock); @@ -152,9 +142,8 @@ static void IRAM_ATTR esp_now_scan_peers_done(void) /* iterate over APs records */ for (uint16_t i = 0; i < ap_num; i++) { - /* check whether the AP is an ESP_NOW node which is not already a peer */ - if (strncmp((char*)aps[i].ssid, ESP_NOW_AP_PREFIX, ESP_NOW_AP_PREFIX_LEN) == 0 && - !esp_now_is_peer_exist(aps[i].bssid)) { + /* check whether the AP is an ESP_NOW node */ + if (strncmp((char*)aps[i].ssid, ESP_NOW_AP_PREFIX, ESP_NOW_AP_PREFIX_LEN) == 0) { /* add the AP as peer */ _esp_now_add_peer(aps[i].bssid, aps[i].primary, esp_now_params.key); } @@ -181,44 +170,19 @@ static void esp_now_scan_peers_start(void) { DEBUG("%s\n", __func__); - wifi_scan_config_t scan_cfg = { - .ssid = NULL, - .bssid = NULL, - .channel = esp_now_params.channel, - .show_hidden = true, - .scan_type = WIFI_SCAN_TYPE_ACTIVE, - .scan_time.active.min = 0, - .scan_time.active.max = 120 /* TODO tune value */ - }; - esp_wifi_scan_start(&scan_cfg, false); } -#define ESP_NOW_EVENT_SCAN_PEERS 1 - -static kernel_pid_t esp_now_event_handler_pid; -char esp_now_event_handler_stack [THREAD_STACKSIZE_DEFAULT]; - -static void *esp_now_event_handler(void *arg) -{ - msg_t event; - while (1) { - msg_receive(&event); - switch (event.content.value) { - case ESP_NOW_EVENT_SCAN_PEERS: - esp_now_scan_peers_start(); - break; - } - } - return NULL; -} - static void IRAM_ATTR esp_now_scan_peers_timer_cb(void* arg) { DEBUG("%s\n", __func__); - static msg_t event = { .content = { .value = ESP_NOW_EVENT_SCAN_PEERS } }; - msg_send(&event, esp_now_event_handler_pid); + esp_now_netdev_t* dev = (esp_now_netdev_t*)arg; + + if (dev->netdev.event_callback) { + dev->scan_event = true; + dev->netdev.event_callback((netdev_t*)dev, NETDEV_EVENT_ISR); + } } #else @@ -236,33 +200,43 @@ static IRAM_ATTR void esp_now_recv_cb(const uint8_t *mac, const uint8_t *data, i } #endif - if (_esp_now_dev.rx_len) { - /* there is already a packet in receive buffer, we drop the new one */ + mutex_lock(&_esp_now_dev.rx_lock); + critical_enter(); + + /* + * The ring buffer uses a single byte for the pkt length, followed by the mac address, + * followed by the actual packet data. The MTU for ESP-NOW is 250 bytes, so len will never + * exceed the limits of a byte as the mac address length is not included. + */ + if ((int)ringbuffer_get_free(&_esp_now_dev.rx_buf) < 1 + ESP_NOW_ADDR_LEN + len) { + critical_exit(); + mutex_unlock(&_esp_now_dev.rx_lock); + DEBUG("%s: buffer full, dropping incoming packet of %d bytes\n", __func__, len); return; } - critical_enter(); - #if 0 /* don't printf anything in ISR */ - printf ("%s\n", __func__); - printf ("%s: received %d byte from %02x:%02x:%02x:%02x:%02x:%02x\n", - __func__, len, - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - esp_hexdump (data, len, 'b', 16); + printf("%s\n", __func__); + printf("%s: received %d byte from %02x:%02x:%02x:%02x:%02x:%02x\n", + __func__, len, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + od_hex_dump(data, len, OD_WIDTH_DEFAULT); #endif - _esp_now_dev.rx_len = len; - memcpy(_esp_now_dev.rx_buf, data, len); - memcpy(_esp_now_dev.rx_mac, mac, ESP_NOW_ADDR_LEN); + ringbuffer_add_one(&_esp_now_dev.rx_buf, len); + ringbuffer_add(&_esp_now_dev.rx_buf, (char*)mac, ESP_NOW_ADDR_LEN); + ringbuffer_add(&_esp_now_dev.rx_buf, (char*)data, len); if (_esp_now_dev.netdev.event_callback) { + _esp_now_dev.recv_event = true; _esp_now_dev.netdev.event_callback((netdev_t*)&_esp_now_dev, NETDEV_EVENT_ISR); } critical_exit(); + mutex_unlock(&_esp_now_dev.rx_lock); } -static int _esp_now_sending = 0; +static volatile int _esp_now_sending = 0; static void IRAM_ATTR esp_now_send_cb(const uint8_t *mac, esp_now_send_status_t status) { @@ -286,7 +260,9 @@ static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t * break; case SYSTEM_EVENT_SCAN_DONE: DEBUG("%s WiFi scan done\n", __func__); +#if ESP_NOW_UNICAST esp_now_scan_peers_done(); +#endif break; default: break; @@ -322,10 +298,17 @@ static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t * extern esp_err_t esp_system_event_add_handler(system_event_cb_t handler, void *arg); -static void esp_now_setup(esp_now_netdev_t* dev) +esp_now_netdev_t *netdev_esp_now_setup(void) { + esp_now_netdev_t* dev = &_esp_now_dev; + DEBUG("%s: %p\n", __func__, dev); + if (dev->netdev.driver) { + DEBUG("%s: early returning previously initialized device\n", __func__); + return dev; + } + /* * Init the WiFi driver. TODO It is not only required before ESP_NOW is * initialized but also before other WiFi functions are used. Once other @@ -334,6 +317,8 @@ static void esp_now_setup(esp_now_netdev_t* dev) extern portMUX_TYPE g_intr_lock_mux; mutex_init(&g_intr_lock_mux); + ringbuffer_init(&dev->rx_buf, (char*)dev->rx_mem, sizeof(dev->rx_mem)); + esp_system_event_add_handler(_esp_system_event_handler, NULL); esp_err_t result; @@ -342,7 +327,7 @@ static void esp_now_setup(esp_now_netdev_t* dev) if (result != ESP_OK) { LOG_TAG_ERROR("esp_now", "nfs_flash_init failed with return value %d\n", result); - return; + return NULL; } #endif @@ -351,7 +336,7 @@ static void esp_now_setup(esp_now_netdev_t* dev) if (result != ESP_OK) { LOG_TAG_ERROR("esp_now", "esp_wifi_init failed with return value %d\n", result); - return; + return NULL; } #ifdef CONFIG_WIFI_COUNTRY @@ -396,7 +381,7 @@ static void esp_now_setup(esp_now_netdev_t* dev) LOG_TAG_ERROR("esp_now", "esp_wifi_set_mode failed with return value %d\n", result); - return; + return NULL; } /* set the Station and SoftAP configuration */ @@ -404,14 +389,14 @@ static void esp_now_setup(esp_now_netdev_t* dev) if (result != ESP_OK) { LOG_TAG_ERROR("esp_now", "esp_wifi_set_config station failed with " "return value %d\n", result); - return; + return NULL; } result = esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config_ap); if (result != ESP_OK) { LOG_TAG_ERROR("esp_now", "esp_wifi_set_mode softap failed with return value %d\n", result); - return; + return NULL; } /* start the WiFi driver */ @@ -419,57 +404,50 @@ static void esp_now_setup(esp_now_netdev_t* dev) if (result != ESP_OK) { LOG_TAG_ERROR("esp_now", "esp_wifi_start failed with return value %d\n", result); - return; + return NULL; } -#if ESP_NOW_UNICAST==0 /* TODO */ +#if !ESP_NOW_UNICAST /* all ESP-NOW nodes get the shared mac address on their station interface */ - wifi_set_macaddr(STATION_IF, (uint8_t*)_esp_now_mac); + esp_wifi_set_mac(ESP_IF_WIFI_STA, (uint8_t*)_esp_now_mac); #endif /* set the netdev driver */ dev->netdev.driver = &_esp_now_driver; /* initialize netdev data structure */ - dev->peers_all = 0; - dev->peers_enc = 0; + dev->recv_event = false; + dev->scan_event = false; + mutex_init(&dev->dev_lock); + mutex_init(&dev->rx_lock); /* initialize ESP-NOW and register callback functions */ result = esp_now_init(); if (result != ESP_OK) { LOG_TAG_ERROR("esp_now", "esp_now_init failed with return value %d\n", result); - return; + return NULL; } esp_now_register_send_cb(esp_now_send_cb); esp_now_register_recv_cb(esp_now_recv_cb); #if ESP_NOW_UNICAST - /* create the ESP_NOW event handler thread */ - esp_now_event_handler_pid = thread_create(esp_now_event_handler_stack, - sizeof(esp_now_event_handler_stack), - ESP_NOW_PRIO + 1, - THREAD_CREATE_WOUT_YIELD | - THREAD_CREATE_STACKTEST, - esp_now_event_handler, - NULL, "net-esp-now-event"); - /* timer for peer scan initialization */ _esp_now_scan_peers_done = false; _esp_now_scan_peers_timer.callback = &esp_now_scan_peers_timer_cb; _esp_now_scan_peers_timer.arg = dev; /* execute the first scan */ - esp_now_scan_peers_done(); + esp_now_scan_peers_start(); #else /* ESP_NOW_UNICAST */ -#if 0 - int res = esp_now_add_peer((uint8_t*)_esp_now_mac, ESP_NOW_ROLE_COMBO, - esp_now_params.channel, NULL, 0); - DEBUG("%s: multicast node added %d\n", __func__, res); -#endif + bool res = _esp_now_add_peer(_esp_now_mac, esp_now_params.channel, + esp_now_params.key); + DEBUG("%s: multicast node add %s\n", __func__, res ? "success" : "error"); #endif /* ESP_NOW_UNICAST */ + + return dev; } static int _init(netdev_t *netdev) @@ -494,162 +472,75 @@ static int _send(netdev_t *netdev, const iolist_t *iolist) DEBUG("%s: %p %p\n", __func__, netdev, iolist); CHECK_PARAM_RET(netdev != NULL, -ENODEV); - CHECK_PARAM_RET(iolist != NULL, -EINVAL); + CHECK_PARAM_RET(iolist != NULL && iolist->iol_len == ESP_NOW_ADDR_LEN, -EINVAL); + CHECK_PARAM_RET(iolist->iol_next != NULL, -EINVAL); - esp_now_netdev_t* dev = (esp_now_netdev_t*)netdev; + esp_now_netdev_t *dev = (esp_now_netdev_t*)netdev; mutex_lock(&dev->dev_lock); - 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 > ESP_NOW_MAX_SIZE) { - mutex_unlock(&dev->dev_lock); - return -EOVERFLOW; +#if ESP_NOW_UNICAST + uint8_t* _esp_now_dst = NULL; + + for (uint8_t i = 0; i < ESP_NOW_ADDR_LEN; i++) { + if (((uint8_t*)iolist->iol_base)[i] != 0xff) { + _esp_now_dst = iolist->iol_base; + break; } - memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len); - dev->tx_len += iol->iol_len; } - -#if ENABLE_DEBUG - printf("%s: send %d byte\n", __func__, dev->tx_len); - /* esp_hexdump(dev->tx_buf, dev->tx_len, 'b', 16); */ +#else + const uint8_t* _esp_now_dst = _esp_now_mac; #endif + iolist = iolist->iol_next; - _esp_now_sending = 1; - - uint8_t* _esp_now_dst = 0; - - #if ESP_NOW_UNICAST - ipv6_hdr_t* ipv6_hdr = (ipv6_hdr_t*)dev->tx_buf; - uint8_t _esp_now_dst_from_iid[6]; + uint8_t *data_pos = dev->tx_mem; + uint8_t data_len = 0; - if (ipv6_hdr->dst.u8[0] == 0xff) { - /* packets to multicast prefix ff::/8 are sent to all peers */ - DEBUG("multicast to all peers\n"); - _esp_now_dst = 0; - _esp_now_sending = dev->peers_all; - - #ifdef MODULE_NETSTATS_L2 - netdev->stats.tx_mcast_count++; - #endif - } - - else if ((byteorder_ntohs(ipv6_hdr->dst.u16[0]) & 0xffc0) == 0xfe80) { - /* for link local addresses fe80::/10, the MAC address is derived from dst address */ - _get_mac_from_iid(&ipv6_hdr->dst.u8[8], _esp_now_dst_from_iid); - DEBUG("link local to %02x:%02x:%02x:%02x:%02x:%02x\n", - _esp_now_dst_from_iid[0], _esp_now_dst_from_iid[1], - _esp_now_dst_from_iid[2], _esp_now_dst_from_iid[3], - _esp_now_dst_from_iid[4], _esp_now_dst_from_iid[5]); - _esp_now_dst = _esp_now_dst_from_iid; - _esp_now_sending = 1; - } - - else { - #ifdef MODULE_GNRC_IPV6_NIB - /* for other addresses, try to find an entry in NIB cache */ - gnrc_ipv6_nib_nc_t nce; - int ret = gnrc_ipv6_nib_get_next_hop_l2addr (&ipv6_hdr->dst, dev->netif, - NULL, &nce); - if (ret == 0) { - /* entry was found in NIB, use MAC adress from the NIB cache entry */ - DEBUG("global, next hop to neighbor %02x:%02x:%02x:%02x:%02x:%02x\n", - nce.l2addr[0], nce.l2addr[1], nce.l2addr[2], - nce.l2addr[3], nce.l2addr[4], nce.l2addr[5]); - _esp_now_dst = nce.l2addr; - _esp_now_sending = 1; - } - else { - #endif - /* entry was not found in NIB, send to all peers */ - DEBUG("global, no neibhbor found, multicast to all peers\n"); - _esp_now_dst = 0; - _esp_now_sending = dev->peers_all; - - #ifdef MODULE_NETSTATS_L2 - netdev->stats.tx_mcast_count++; - #endif - - #ifdef MODULE_GNRC_IPV6_NIB + while (iolist) { + if (((int)data_len + iolist->iol_len) > ESP_NOW_MAX_SIZE_RAW) { + DEBUG("%s: payload length exceeds maximum(%u>%u)\n", __func__, + data_len + iolist->iol_len, ESP_NOW_MAX_SIZE_RAW); + return -EBADMSG; } - #endif - } - - #else /* ESP_NOW_UNICAST */ - ipv6_hdr_t* ipv6_hdr = (ipv6_hdr_t*)dev->tx_buf; - uint8_t _esp_now_dst_from_iid[6]; + memcpy(data_pos, iolist->iol_base, iolist->iol_len); + data_pos += iolist->iol_len; + data_len += iolist->iol_len; - _esp_now_dst = (uint8_t*)_esp_now_mac; - _esp_now_sending = 1; - - if (ipv6_hdr->dst.u8[0] == 0xff) { - /* packets to multicast prefix ff::/8 are sent to all peers */ - DEBUG("multicast to all peers\n"); - - #ifdef MODULE_NETSTATS_L2 - netdev->stats.tx_mcast_count++; - #endif + iolist = iolist->iol_next; } - else if ((byteorder_ntohs(ipv6_hdr->dst.u16[0]) & 0xffc0) == 0xfe80) { - /* for link local addresses fe80::/10, the MAC address is derived from dst address */ - _get_mac_from_iid(&ipv6_hdr->dst.u8[8], _esp_now_dst_from_iid); - DEBUG("link local to %02x:%02x:%02x:%02x:%02x:%02x\n", - _esp_now_dst_from_iid[0], _esp_now_dst_from_iid[1], - _esp_now_dst_from_iid[2], _esp_now_dst_from_iid[3], - _esp_now_dst_from_iid[4], _esp_now_dst_from_iid[5]); - if (esp_now_is_peer_exist(_esp_now_dst_from_iid) > 0) { - _esp_now_dst = _esp_now_dst_from_iid; - } - } - - else - { - /* for other addresses, try to find an entry in NIB cache */ - gnrc_ipv6_nib_nc_t nce; - int ret = gnrc_ipv6_nib_get_next_hop_l2addr (&ipv6_hdr->dst, dev->netif, - NULL, &nce); - if (ret == 0 && esp_now_is_peer_exist(nce.l2addr) > 0) { - /* entry was found in NIB, use MAC adress from the NIB cache entry */ - DEBUG("global, next hop to neighbor %02x:%02x:%02x:%02x:%02x:%02x\n", - nce.l2addr[0], nce.l2addr[1], nce.l2addr[2], - nce.l2addr[3], nce.l2addr[4], nce.l2addr[5]); - _esp_now_dst = nce.l2addr; - } - else { - /* entry was not found in NIB, send to all peers */ - DEBUG("global, no neibhbor found, multicast to all peers\n"); - - #ifdef MODULE_NETSTATS_L2 - netdev->stats.tx_mcast_count++; - #endif - } - } + DEBUG("%s: send %u byte\n", __func__, (unsigned)data_len); +#if defined(MODULE_OD) && ENABLE_DEBUG + od_hex_dump(dev->tx_mem, data_len, OD_WIDTH_DEFAULT); +#endif - #endif /* ESP_NOW_UNICAST */ if (_esp_now_dst) { DEBUG("%s: send to esp_now addr %02x:%02x:%02x:%02x:%02x:%02x\n", __func__, _esp_now_dst[0], _esp_now_dst[1], _esp_now_dst[2], _esp_now_dst[3], _esp_now_dst[4], _esp_now_dst[5]); + } else { + DEBUG("%s: send esp_now broadcast\n", __func__); } - /* send the the packet to the peer(s) mac address */ - if (esp_now_send (_esp_now_dst, dev->tx_buf, dev->tx_len) == 0) { + _esp_now_sending = 1; + + /* send the packet to the peer(s) mac address */ + if (esp_now_send(_esp_now_dst, dev->tx_mem, data_len) == ESP_OK) { while (_esp_now_sending > 0) { thread_yield_higher(); } #ifdef MODULE_NETSTATS_L2 - netdev->stats.tx_bytes += dev->tx_len; + netdev->stats.tx_bytes += data_len; netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); #endif mutex_unlock(&dev->dev_lock); - return dev->tx_len; - } - else { + return data_len; + } else { + _esp_now_sending = 0; + #ifdef MODULE_NETSTATS_L2 netdev->stats.tx_failed++; #endif @@ -667,55 +558,74 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) esp_now_netdev_t* dev = (esp_now_netdev_t*)netdev; - mutex_lock(&dev->dev_lock); + mutex_lock(&dev->rx_lock); + + uint16_t size = ringbuffer_empty(&dev->rx_buf) + ? 0 + : (ringbuffer_peek_one(&dev->rx_buf) + ESP_NOW_ADDR_LEN); - uint8_t size = dev->rx_len; + if (size && dev->rx_buf.avail < size) { + /* this should never happen unless this very function messes up */ + mutex_unlock(&dev->rx_lock); + return -EIO; + } if (!buf && !len) { /* return the size without dropping received data */ - mutex_unlock(&dev->dev_lock); + mutex_unlock(&dev->rx_lock); return size; } if (!buf && len) { /* return the size and drop received data */ - mutex_unlock(&dev->dev_lock); - dev->rx_len = 0; + if (size) { + ringbuffer_remove(&dev->rx_buf, 1 + size); + } + mutex_unlock(&dev->rx_lock); return size; } - if (buf && len && dev->rx_len) { - if (dev->rx_len > len) { + if (buf && len && !size) { + mutex_unlock(&dev->rx_lock); + return 0; + } + + if (buf && len && size) { + if (size > len) { DEBUG("[esp_now] No space in receive buffers\n"); - mutex_unlock(&dev->dev_lock); + mutex_unlock(&dev->rx_lock); return -ENOBUFS; } -#if ENABLE_DEBUG - printf ("%s: received %d byte from %02x:%02x:%02x:%02x:%02x:%02x\n", - __func__, dev->rx_len, - dev->rx_mac[0], dev->rx_mac[1], dev->rx_mac[2], - dev->rx_mac[3], dev->rx_mac[4], dev->rx_mac[5]); - /* esp_hexdump (dev->rx_buf, dev->rx_len, 'b', 16); */ + /* remove already peeked size byte */ + ringbuffer_remove(&dev->rx_buf, 1); + ringbuffer_get(&dev->rx_buf, buf, size); + + uint8_t *mac = buf; + + DEBUG("%s: received %d byte from %02x:%02x:%02x:%02x:%02x:%02x\n", + __func__, size - ESP_NOW_ADDR_LEN, + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +#if defined(MODULE_OD) && ENABLE_DEBUG + od_hex_dump(buf + ESP_NOW_ADDR_LEN, size - ESP_NOW_ADDR_LEN, OD_WIDTH_DEFAULT); #endif - if (esp_now_is_peer_exist(dev->rx_mac) <= 0) { - _esp_now_add_peer(dev->rx_mac, esp_now_params.channel, esp_now_params.key); +#if ESP_NOW_UNICAST + if (esp_now_is_peer_exist(mac) <= 0) { + _esp_now_add_peer(mac, esp_now_params.channel, esp_now_params.key); } - - memcpy(buf, dev->rx_buf, dev->rx_len); - dev->rx_len = 0; +#endif #ifdef MODULE_NETSTATS_L2 netdev->stats.rx_count++; netdev->stats.rx_bytes += size; #endif - mutex_unlock(&dev->dev_lock); + mutex_unlock(&dev->rx_lock); return size; } - mutex_unlock(&dev->dev_lock); + mutex_unlock(&dev->rx_lock); return -EINVAL; } @@ -841,7 +751,15 @@ static void _isr(netdev_t *netdev) CHECK_PARAM(netdev != NULL); esp_now_netdev_t *dev = (esp_now_netdev_t*)netdev; - dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + + if (dev->recv_event) { + dev->recv_event = false; + dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + } + else if (dev->scan_event) { + dev->scan_event = false; + esp_now_scan_peers_start(); + } return; } @@ -855,15 +773,4 @@ static const netdev_driver_t _esp_now_driver = .set = _set, }; -void auto_init_esp_now (void) -{ - LOG_TAG_INFO("esp_now", "initializing ESP-NOW device\n"); - - esp_now_setup(&_esp_now_dev); - _esp_now_dev.netif = gnrc_netif_raw_create(_esp_now_stack, - ESP_NOW_STACKSIZE, ESP_NOW_PRIO, - "net-esp-now", - (netdev_t *)&_esp_now_dev); -} - /** @} */ diff --git a/cpu/esp_common/esp-now/esp_now_netdev.h b/cpu/esp_common/esp-now/esp_now_netdev.h index abeca8b33058aa678840728add6fcf5257a5e61f..2e0a41884bc1a539d4b401cf6c5fd7e50359bb13 100644 --- a/cpu/esp_common/esp-now/esp_now_netdev.h +++ b/cpu/esp_common/esp-now/esp_now_netdev.h @@ -14,32 +14,67 @@ * @brief Netdev interface for the ESP-NOW WiFi P2P protocol * * @author Gunar Schorcht <gunar@schorcht.net> + * @author Timo Rothenpieler <timo.rothenpieler@uni-bremen.de> */ #ifndef ESP_NOW_NETDEV_H #define ESP_NOW_NETDEV_H #include "net/netdev.h" +#include "ringbuffer.h" #ifdef __cplusplus extern "C" { #endif /** - * @brief Maximum packet size that can be used with ESP-NOW + * @brief Maximum raw packet size that can be used with ESP-NOW (including headers) */ -#define ESP_NOW_MAX_SIZE (250) +#define ESP_NOW_MAX_SIZE_RAW (250) /** * @brief Length of ESP-NOW addresses */ #define ESP_NOW_ADDR_LEN ETHERNET_ADDR_LEN +/** + * @brief Size of non-data header elements in ESP-NOW packet + */ +#define ESP_NOW_HEADER_LENGTH (sizeof(esp_now_pkt_hdr_t)) + +/** + * @brief Maximum packet size that can be used with ESP-NOW + */ +#define ESP_NOW_MAX_SIZE (ESP_NOW_MAX_SIZE_RAW - ESP_NOW_HEADER_LENGTH) + +/** + * @brief buffer size used for RX buffering + * + * Reduce this value if your expected traffic does not include full IPv6 MTU + * sized packets. + */ +#ifndef ESP_NOW_BUFSIZE +#define ESP_NOW_BUFSIZE (1500) +#endif + /** * @brief Reference to the netdev device driver struct */ extern const netdev_driver_t esp_now_driver; +/** + * @brief Header with neccesary flags for ESP-NOW packets + */ +typedef struct __attribute__((packed)) +{ + uint8_t flags; /**< Flags */ +} esp_now_pkt_hdr_t; + +/** + * @brief Packet is carrying 6Lo data + */ +#define ESP_NOW_PKT_HDR_FLAG_SIXLO (1) + /** * @brief Device descriptor for ESP-NOW devices */ @@ -47,28 +82,32 @@ typedef struct { netdev_t netdev; /**< netdev parent struct */ - uint8_t addr[ESP_NOW_ADDR_LEN]; /**< device addr (MAC address) */ - - uint8_t rx_len; /**< number of bytes received */ - uint8_t rx_buf[ESP_NOW_MAX_SIZE]; /**< receive buffer */ - uint8_t rx_mac[ESP_NOW_ADDR_LEN]; /**< source address */ + uint8_t addr[ESP_NOW_ADDR_LEN]; /**< device addr (MAC address) */ - uint8_t tx_len; /**< number of bytes in transmit buffer */ - uint8_t tx_buf[ESP_NOW_MAX_SIZE]; /**< transmit buffer */ + uint8_t rx_mem[ESP_NOW_BUFSIZE]; /**< memory holding incoming packages */ + ringbuffer_t rx_buf; /**< ringbuffer for incoming packages */ - gnrc_netif_t* netif; /**< reference to the corresponding netif */ + uint8_t tx_mem[ESP_NOW_MAX_SIZE_RAW]; /**< memory holding outgoing package */ #ifdef MODULE_GNRC gnrc_nettype_t proto; /**< protocol for upper layer */ #endif - uint8_t peers_all; /**< number of peers reachable */ - uint8_t peers_enc; /**< number of encrypted peers */ - mutex_t dev_lock; /**< device is already in use */ + mutex_t rx_lock; /**< rx_buf handling in progress */ + + bool recv_event; /**< ESP-NOW frame received */ + bool scan_event; /**< ESP-NOW peers have to be scannged */ } esp_now_netdev_t; +/** + * @brief netdev <-> esp_npw glue code initialization function + * + * @return NULL on error, pointer to esp_now_netdev on success + */ +esp_now_netdev_t *netdev_esp_now_setup(void); + #ifdef __cplusplus } #endif