ghn-certi commented on a change in pull request #1893: URL: https://github.com/apache/incubator-nuttx/pull/1893#discussion_r501662502
########## File path: arch/xtensa/src/esp32/esp32_wlan.c ########## @@ -0,0 +1,1589 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_wlan.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <debug.h> +#include <queue.h> +#include <errno.h> +#include <arpa/inet.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/wdog.h> +#include <nuttx/wqueue.h> +#include <nuttx/net/mii.h> +#include <nuttx/net/arp.h> +#include <nuttx/net/netdev.h> +#include <crc64.h> + +#if defined(CONFIG_NET_PKT) +# include <nuttx/net/pkt.h> +#endif + +#include <nuttx/net/net.h> +#include <nuttx/kmalloc.h> +#include <debug.h> + +#include "esp32_wifi_adapter.h" + +#include <arch/board/board.h> + +#ifdef CONFIG_ESP32_WIRELESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Only one ESP32 */ + +#ifndef CONFIG_ESP32_NINTERFACES +# define CONFIG_ESP32_NINTERFACES 1 +#endif +#define STA_DEVNO 0 + +/* TX poll delay = 1 seconds. + * CLK_TCK is the number of clock ticks per second + */ + +#define ESP_WDDELAY (1*CLK_TCK) +#define ESPWORK LPWORK + +/* TX timeout = 1 minute */ + +#define ESP_TXTIMEOUT (60*CLK_TCK) +#define DEFAULT_SCAN_LIST_SIZE 2 + +/* Add 4 to the configured buffer size to account for the 2 byte checksum + * memory needed at the end of the maximum size packet. Buffer sizes must + * be an even multiple of 4, 8, or 16 bytes (depending on buswidth). We + * will use the 16-byte alignment in all cases. + */ + +#define OPTIMAL_ETH_BUFSIZE ((CONFIG_NET_ETH_PKTSIZE + 4 + 15) & ~15) + +#ifndef CONFIG_ESP_ETH_BUFSIZE +# define CONFIG_ESP_ETH_BUFSIZE OPTIMAL_ETH_BUFSIZE +#endif + +#ifndef CONFIG_ESP_ETH_NTXDESC +# define CONFIG_ESP_ETH_NTXDESC 4 +#endif + +/* We need at least one more free buffer than transmit buffers */ + +#define ESP_ETH_NFREEBUFFERS (CONFIG_ESP_ETH_NTXDESC+1) +#define ETH_MAX_LEN 1518 + +/* This is a helper pointer for accessing the contents of wlan header */ + +#define BUF ((struct eth_hdr_s *)priv->esp_dev.d_buf) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The esp_dev_s encapsulates all state information for a single + * hardware interface + */ + +struct esp_dev_s +{ + bool ifup; /* true:ifup false:ifdown */ + struct wdog_s esp_txpoll; /* TX poll timer */ + struct wdog_s esp_txtimeout; /* TX timeout timer */ + struct work_s esp_irqwork; /* For deferring interrupt work to the work queue */ + struct work_s esp_pollwork; /* For deferring poll work to the work queue */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s esp_dev; + sq_queue_t freeb; /* The free buffer list */ + sem_t waitsem; /* Implements event waiting */ + + /* Buffer allocations */ + + uint8_t alloc[CONFIG_ESP_ETH_NTXDESC*CONFIG_ESP_ETH_BUFSIZE]; + uint8_t rxbuf[ETH_MAX_LEN]; + uint32_t rx_len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct esp_dev_s g_esp32[CONFIG_ESP32_NINTERFACES]; +static bool g_tx_ready = false; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void esp_takesem(struct esp_dev_s *priv); +#define esp_givesem(priv) (nxsem_post(&priv->waitsem)) + +/* Free buffer management */ + +static void esp_initbuffer(FAR struct esp_dev_s *priv); +static inline uint8_t *esp_allocbuffer(FAR struct esp_dev_s *priv); +static inline void esp_freebuffer(FAR struct esp_dev_s *priv, + uint8_t *buffer); +static inline bool esp_isfreebuffer(FAR struct esp_dev_s *priv); + +/* Common TX logic */ + +static int esp_transmit(FAR struct esp_dev_s *priv); +static void esp_receive(FAR struct esp_dev_s *priv); +static int esp_txpoll(FAR struct net_driver_s *dev); +static void esp_rxpoll(FAR void *arg); +static void esp_dopoll(FAR struct esp_dev_s *priv); +static void esp_netdev_notify_rx(FAR struct esp_dev_s *priv, + void *buffer, uint16_t len); +static int esp_sta_input(void *buffer, uint16_t len, void *eb); + +/* Watchdog timer expirations */ + +static void esp_txtimeout_work(FAR void *arg); +static void esp_txtimeout_expiry(wdparm_t arg); + +static void esp_poll_work(FAR void *arg); +static void esp_poll_expiry(wdparm_t arg); + +/* NuttX callback functions */ + +static int esp_ifup(struct net_driver_s *dev); +static int esp_ifdown(struct net_driver_s *dev); + +static void esp_txavail_work(FAR void *arg); +static int esp_txavail(struct net_driver_s *dev); + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static int esp_addmac(struct net_driver_s *dev, FAR const uint8_t *mac); +#endif +#ifdef CONFIG_NET_MCASTGROUP +static int esp_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac); +#endif +#ifdef CONFIG_NETDEV_IOCTL +static int esp_ioctl(struct net_driver_s *dev, int cmd, + unsigned long arg); +#endif +static void esp_sem_initialize(FAR struct esp_dev_s *priv); +static int esp32_net_initialize(unsigned int devno); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp_takesem + * + * Description: + * Take the wait semaphore (handling false alarm wakeups due to the receipt + * of signals). + * + * Input Parameters: + * dev - Instance of the esp32 wlan driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void esp_takesem(struct esp_dev_s *priv) +{ + int ret; + + do + { + /* Take the semaphore (perhaps waiting) */ + + ret = nxsem_wait(&priv->waitsem); + + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); +} + +/**************************************************************************** + * Function: esp_initbuffer + * + * Description: + * Initialize the free buffer list. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called during early driver initialization before Ethernet interrupts + * are enabled. + * + ****************************************************************************/ + +static void esp_initbuffer(FAR struct esp_dev_s *priv) +{ + uint8_t *buffer; + int i; + + /* Initialize the head of the free buffer list */ + + sq_init(&priv->freeb); + + /* Add all of the pre-allocated buffers to the free buffer list */ + + for (i = 0, buffer = priv->alloc; i < ESP_ETH_NFREEBUFFERS; + i++, buffer += CONFIG_ESP_ETH_BUFSIZE) + { + sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb); + } +} + +/**************************************************************************** + * Function: esp_allocbuffer + * + * Description: + * Allocate one buffer from the free buffer list. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * Pointer to the allocated buffer on success; NULL on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static inline uint8_t *esp_allocbuffer(FAR struct esp_dev_s *priv) +{ + /* Allocate a buffer by returning the head of the free buffer list */ + + return (uint8_t *)sq_remfirst(&priv->freeb); +} + +/**************************************************************************** + * Function: esp_freebuffer + * + * Description: + * Return a buffer to the free buffer list. + * + * Input Parameters: + * priv - Reference to the driver state structure + * buffer - A pointer to the buffer to be freed + * + * Returned Value: + * None + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * global interrupts are disabled, either explicitly or indirectly through + * interrupt handling logic. + * + ****************************************************************************/ + +static inline void esp_freebuffer(FAR struct esp_dev_s *priv, + uint8_t *buffer) +{ + /* Free the buffer by adding it to the end of the free buffer list */ + + sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb); +} + +/**************************************************************************** + * Function: esp_isfreebuffer + * + * Description: + * Return TRUE if the free buffer list is not empty. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * True if there are one or more buffers in the free buffer list; + * false if the free buffer list is empty + * + * Assumptions: + * None. + * + ****************************************************************************/ + +static inline bool esp_isfreebuffer(FAR struct esp_dev_s *priv) +{ + /* Return TRUE if the free buffer list is not empty */ + + return !sq_empty(&priv->freeb); +} + +/**************************************************************************** + * Name: esp_transmit + * + * Description: + * Start hardware transmission. Called either from the txdone interrupt + * handling or from watchdog based polling. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * the network is locked. + * + ****************************************************************************/ + +static int esp_transmit(FAR struct esp_dev_s *priv) +{ + int ret = 0; + uint8_t *buffer; + uint32_t buffer_len; + + /* Set up all but the last TX descriptor */ + + buffer = priv->esp_dev.d_buf; + buffer_len = priv->esp_dev.d_len; + ret = esp_wifi_sta_send_data(buffer, buffer_len); + + if (ret != 0) + { + wlerr("ERROR: Failed to transmit frame\n"); + (void)wd_start(&priv->esp_txtimeout, ESP_TXTIMEOUT, + esp_txtimeout_expiry, (uint32_t)priv); + return -EIO; + } + + return OK; +} + +/**************************************************************************** + * Function: esp_recvframe + * + * Description: + * The function is called when a frame is received using the DMA receive + * interrupt. It scans the RX descriptors of the received frame. + * + * NOTE: This function will silently discard any packets containing errors. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK if a packet was successfully returned; -EAGAIN if there are no + * further packets available + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static int esp_recvframe(FAR struct esp_dev_s *priv) +{ + struct net_driver_s *dev = &priv->esp_dev; + uint8_t *buffer; + uint32_t buffer_len = 0; + buffer = dev->d_buf; + buffer_len = dev->d_len; + + /* Check if there are free buffers. We cannot receive new frames in this + * design unless there is at least one free buffer. + */ + + if (!esp_isfreebuffer(priv)) + { + wlerr("ERROR: No free buffers\n"); + return -ENOMEM; + } + + /* Check if any errors are reported in the frame */ + + if (buffer == NULL || buffer_len == 0) + { + return -EAGAIN; + } + + return OK; +} + +/**************************************************************************** + * Function: esp_receive + * + * Description: + * An interrupt was received indicating the availability of a new RX packet + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void esp_receive(FAR struct esp_dev_s *priv) +{ + struct net_driver_s *dev = &priv->esp_dev; + + /* Loop while while stm32_recvframe() successfully retrieves valid + * Ethernet frames. + */ + + while (esp_recvframe(priv) == OK) + { +#ifdef CONFIG_NET_PKT + + /* When packet sockets are enabled, + * feed the frame into the packet tap. + */ + + pkt_input(&priv->esp_dev); +#endif + + /* Check if the packet is a valid size for the network + * buffer configuration (this should not happen) + */ + + if (dev->d_len > CONFIG_NET_ETH_PKTSIZE) + { + wlwarn("WARNING: DROPPED Too big: %d\n", dev->d_len); + + /* Free dropped packet buffer */ + + if (dev->d_buf) + { + esp_freebuffer(priv, dev->d_buf); + dev->d_buf = NULL; + dev->d_len = 0; + } + + continue; + } + + /* We only accept IP packets of the configured type and ARP packets */ + +#ifdef CONFIG_NET_IPv4 + if (BUF->type == HTONS(ETHTYPE_IP)) + { + wlinfo("IPv4 frame\n"); + + /* Handle ARP on input then give the IPv4 packet to the network + * layer + */ + + arp_ipin(&priv->esp_dev); + ipv4_input(&priv->esp_dev); + + /* If the above function invocation resulted in data + * that should be sent out on the network, + * the field d_len will set to a value > 0. + */ + + if (priv->esp_dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->esp_dev.d_flags)) +#endif + { + arp_out(&priv->esp_dev); + } +#ifdef CONFIG_NET_IPv6 + else + { + neighbor_out(&priv->esp_dev); + } +#endif + + /* And send the packet */ + + esp_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_IPv6 + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + wlinfo("Iv6 frame\n"); + + /* Give the IPv6 packet to the network layer */ + + ipv6_input(&priv->esp_dev); + + /* If the above function invocation resulted in data + * that should be sent out on the network, the field + * d_len will set to a value > 0. + */ + + if (priv->esp_dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv4(priv->esp_dev.d_flags)) + { + arp_out(&priv->esp_dev); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + { + neighbor_out(&priv->esp_dev); + } +#endif + + /* And send the packet */ + + esp_transmit(priv); + } + } + else +#endif +#ifdef CONFIG_NET_ARP + if (BUF->type == htons(ETHTYPE_ARP)) + { + wlinfo("ARP frame\n"); + + /* Handle ARP packet */ + + arp_arpin(&priv->esp_dev); + + /* If the above function invocation resulted in data + * that should be sent out on the network, the field + * d_len will set to a value > 0. + */ + + if (priv->esp_dev.d_len > 0) + { + esp_transmit(priv); + } + } + else +#endif + { + wlerr("ERROR: Dropped, Unknown type: %04x\n", BUF->type); + } + + /* We are finished with the RX buffer. NOTE: If the buffer is + * re-used for transmission, the dev->d_buf field will have been + * nullified. + */ + + if (dev->d_buf) + { + /* Free the receive packet buffer */ + + esp_freebuffer(priv, dev->d_buf); + dev->d_buf = NULL; + dev->d_len = 0; + } + } +} + +/**************************************************************************** + * Name: esp_txpoll + * + * Description: + * The transmitter is available, check if the network has any outgoing + * packets ready to send. This is a callback from devif_poll(). + * devif_poll() may be called: + * + * 1. When the preceding TX packet send is complete, + * 2. When the preceding TX packet send times out and the interface is + * reset + * 3. During normal TX polling + * + * Input Parameters: + * dev - Reference to the NuttX driver state structure + * + * Returned Value: + * OK on success; a negated errno on failure + * + * Assumptions: + * May or may not be called from an interrupt handler. In either case, + * the network is locked. + * + ****************************************************************************/ + +static int esp_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct esp_dev_s *priv = (FAR struct esp_dev_s *)dev->d_private; + + DEBUGASSERT(priv->esp_dev.d_buf != NULL); + + /* If the polling resulted in data that should be sent out on the network, + * the field d_len is set to a value > 0. + */ + + if (priv->esp_dev.d_len > 0) + { + /* Look up the destination MAC address and add it to the Ethernet + * header. + */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->esp_dev.d_flags)) +#endif + { + arp_out(&priv->esp_dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + neighbor_out(&priv->esp_dev); + } +#endif /* CONFIG_NET_IPv6 */ + + if (!devif_loopback(&priv->esp_dev)) + { + /* Send the packet */ + + int ret = esp_transmit(priv); + if (ret != OK) + { + wlerr("TX failed\r\n"); + return -EBUSY; + } + } + } + + /* If zero is returned, the polling will continue until + * all connections have been examined. + */ + + return OK; +} + +/**************************************************************************** + * Name: esp_rxpoll + * + * Description: + * Process RX frames + * + * Input Parameters: + * arg - context of device to use + * + * Returned Value: + * OK on success + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void esp_rxpoll(FAR void *arg) +{ + FAR struct esp_dev_s *priv = (FAR struct esp_dev_s *)arg; + + if (priv->esp_dev.d_buf == NULL) + { + priv->esp_dev.d_buf = priv->rxbuf; + priv->esp_dev.d_len = priv->rx_len; + } + else + { + wlinfo("priv->esp_dev.d_buf != NULL"); + return; + } + + /* Lock the network and serialize driver operations if necessary. + * NOTE: Serialization is only required in the case where the driver work + * is performed on an LP worker thread and where more than one LP worker + * thread has been configured. + */ + + esp_takesem(priv); + + net_lock(); + + esp_receive(priv); + + /* Check if a packet transmission just completed. If so, call esp_txdone. + * This may disable further Tx interrupts if there are no pending + * transmissions. + */ + + net_unlock(); + if (priv->esp_dev.d_buf) + { + priv->esp_dev.d_buf = NULL; + memset(priv->rxbuf, 0x0, sizeof(priv->rxbuf)); + priv->rx_len = 0; + } + + esp_givesem(priv); +} + +/**************************************************************************** + * Function: esp_dopoll + * + * Description: + * The function is called in order to perform an out-of-sequence TX poll. + * This is done: + * + * 1. After completion of a transmission (esp32_txdone), + * 2. When new TX data is available (esp32_txavail_process), and + * 3. After a TX timeout to restart the sending process + * (stm32_txtimeout_process). Review comment: I've found that these steps are replicated through several ethernet driver implementations (STM32, SAM, tiva, rx65n...), and all of them are reference the same functions that no longer exist (e.g. <chip>_txavail_process). ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org