Virus-V commented on a change in pull request #2991: URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r589867150
########## File path: arch/risc-v/src/bl602/bl602_netdev.c ########## @@ -0,0 +1,2113 @@ +/**************************************************************************** + * arch/risc-v/src/bl602/bl602_netdev.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 <stdint.h> +#include <stdbool.h> +#include <time.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <debug.h> +#include <sched.h> +#include <semaphore.h> +#include <nuttx/nuttx.h> +#include <nuttx/kmalloc.h> +#include <nuttx/list.h> + +#include <sys/types.h> + +#include <arpa/inet.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/wdog.h> +#include <nuttx/wqueue.h> +#include <nuttx/net/arp.h> +#include <nuttx/net/net.h> +#include <nuttx/net/netdev.h> +#include <nuttx/wireless/wireless.h> + +#ifdef CONFIG_NET_PKT +#include <nuttx/net/pkt.h> +#endif + +#include "wifi_manager/include/bitset.h" +#include "wifi_manager/wifi_mgmr.h" +#include "wifi_manager/wifi_mgmr_api.h" +#include "wifi_manager/bl_wifi.h" +#include "wifi_manager/include/wifi_mgmr_ext.h" +#include "wifi_driver/os_hal.h" +#include "bl602_netdev.h" + +#ifdef CONFIG_BL602_WIRELESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Work queue support is required. */ + +#if !defined(CONFIG_SCHED_WORKQUEUE) +#error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE) +#endif + +/* The low priority work queue is preferred. If it is not enabled, LPWORK + * will be the same as HPWORK. + * + * NOTE: However, the network should NEVER run on the high priority work + * queue! That queue is intended only to service short back end interrupt + * processing that never suspends. Suspending the high priority work queue + * may bring the system to its knees! + */ + +/* FIXME According to some network IO throughput tests, using HPWORK will + * get higher throughput. + */ + +#define ETHWORK HPWORK + +/* CONFIG_BL602_NET_NINTERFACES determines the number of physical interfaces + * that will be supported. + */ + +#ifndef CONFIG_BL602_NET_NINTERFACES +#define CONFIG_BL602_NET_NINTERFACES 1 +#endif + +/* TX poll delay = 1 seconds. + * CLK_TCK is the number of clock ticks per second + */ + +#define BL602_NET_WDDELAY (1 * CLK_TCK) + +#define BL602_NET_TXBUFF_NUM 6 +#define BL602_NET_TXBUFF_SIZE (1650) + +#define BL602_TXDESC_THRESHOLD 3 + +#define WIFI_MTU_SIZE 1514 + +#if BL602_NET_TXBUFF_SIZE & 0x3 != 0 +#error "BL602_NET_TXBUFF_SIZE must be aligned to 4 bytes" +#endif + +#if !(BL602_TXDESC_THRESHOLD > 0) +#error "BL602_TXDESC_THRESHOLD invalid." +#endif + +#define TX_BUFF_BIT_SIZE BL602_NET_TXBUFF_NUM + +/* This is a helper pointer for accessing the contents of Ethernet header */ + +#define BUF ((struct eth_hdr_s *)priv->net_dev.d_buf) + +#define WIFI_MGMR wifiMgmr + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* The bl602_net_driver_s encapsulates all state information for a single + * hardware interface + */ + +struct bl602_net_driver_s +{ + struct wdog_s txpoll; /* TX poll timer */ + struct work_s pollwork; /* For deferring poll work to the work queue */ + struct work_s availwork; + + /* wifi manager */ + + struct wlan_netif *wlan; + + /* there is impossble to concurrency access these fields, so + * we use bit-field to save some little space :) + */ + + unsigned int current_mode : 2; /* current mode */ + unsigned int scan_result_len : 6; /* max 64 */ + unsigned int push_cnt : 4; /* max 16 */ + unsigned int prev_connectd : 1; /* mark of prev connection status */ + + uint16_t channel; /* FIXME freq number. eg. 3, 0 is not set */ + + char bssid[18]; /* AP mac address */ + + /* This holds the information visible to the NuttX network */ + + struct net_driver_s net_dev; /* Interface understood by the network */ +}; + +struct scan_parse_param_s +{ + FAR struct bl602_net_driver_s *priv; + + int flags; + struct iw_scan_req scan_req; +}; + +BITSET_DEFINE(tx_buf_ind_s, TX_BUFF_BIT_SIZE); + +typedef uint8_t (*tx_buff_t)[BL602_NET_TXBUFF_SIZE]; + +/* When a data packet is received, the NuttX protocol stack may use this + * RX buffer to construct a response data packet. However, the WiFi Firmware + * does not support using the RX buffer as the TX buffer directly, so it is + * necessary to allocate a TX buffer to make a copy. + * If there is no TX buffer, the response packet will be discarded. + * Therefore, when a data packet is received, it will check whether there is + * an available TX buffer. If not, it will hang the RX packet in this linked + * list, and wait until TX buffer is available before submitting it to the + * NuttX protocol stack. + */ + +struct rx_pending_item_s +{ + struct list_node node; + uint8_t * data; + int len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Driver state structure */ + +struct bl602_net_driver_s g_bl602_net[CONFIG_BL602_NET_NINTERFACES]; + +static struct tx_buf_ind_s g_tx_buf_indicator = + BITSET_T_INITIALIZER((1 << BL602_NET_TXBUFF_NUM) - 1); + +static uint8_t __attribute__((section(".wifi_ram.txbuff"))) +g_tx_buff[BL602_NET_TXBUFF_NUM][BL602_NET_TXBUFF_SIZE]; + +static sem_t g_wifi_scan_sem; /* wifi scan complete semaphore */ +static sem_t g_wifi_connect_sem; + +/* Rx Pending List */ + +static struct list_node g_rx_pending; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +extern int bl_output(struct bl_hw *bl_hw, char *p, int tot_len, int is_sta); +extern void bl_free_rx_buffer(void *p); +extern void bl_irq_handler(void); +extern void wifi_main_init(void); +extern void ipc_emb_notify(void); +extern void wifi_mgmr_tsk_init(void); +extern int bl602_ef_ctrl_read_mac_address(uint8_t mac[6]); +extern struct net_device bl606a0_sta; + +/* Common TX logic */ + +static int bl602_net_transmit(FAR struct bl602_net_driver_s *priv); +static int bl602_net_txpoll(FAR struct net_driver_s *dev); + +/* Interrupt handling */ + +static void bl602_net_reply(struct bl602_net_driver_s *priv); +static void bl602_net_receive(FAR struct bl602_net_driver_s *priv); + +/* Watchdog timer expirations */ + +static void bl602_net_poll_work(FAR void *arg); +static void bl602_net_poll_expiry(wdparm_t arg); + +/* NuttX callback functions */ + +static int bl602_net_ifup(FAR struct net_driver_s *dev); +static int bl602_net_ifdown(FAR struct net_driver_s *dev); + +static void bl602_net_txavail_work(FAR void *arg); +static int bl602_net_txavail(FAR struct net_driver_s *dev); + +#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6) +static int bl602_net_addmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +# ifdef CONFIG_NET_MCASTGROUP +static int bl602_net_rmmac(FAR struct net_driver_s *dev, + FAR const uint8_t *mac); +# endif +# ifdef CONFIG_NET_ICMPv6 +static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv); +# endif +#endif + +#ifdef CONFIG_NETDEV_IOCTL +static int +bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg); +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: bl602_net_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: + * The network is locked. + * + ****************************************************************************/ + +static int bl602_net_transmit(FAR struct bl602_net_driver_s *priv) +{ + int ret = OK; + ninfo("tx pkt len:%d\r\n", priv->net_dev.d_len); + + assert(priv->net_dev.d_len <= WIFI_MTU_SIZE); + assert(priv->push_cnt < BL602_TXDESC_THRESHOLD); + + if (!IFF_IS_RUNNING(priv->net_dev.d_flags)) + { + nwarn("drop packet due to iface no carrier\r\n"); + bl602_netdev_free_txbuf(priv->net_dev.d_buf - + PRESERVE_80211_HEADER_LEN); + priv->net_dev.d_buf = NULL; + + return -EPERM; + } + + assert(priv->wlan != NULL); + + /* Increment statistics */ + + NETDEV_TXPACKETS(priv->net_dev); + + bl_output(bl606a0_sta.bl_hw, + (char *)priv->net_dev.d_buf, + priv->net_dev.d_len, + 0 == priv->wlan->mode); + + priv->push_cnt++; + + if (priv->push_cnt == BL602_TXDESC_THRESHOLD) + { + /* notify to tx now */ + + bl_irq_handler(); + priv->push_cnt = 0; + } + + priv->net_dev.d_buf = NULL; + + return ret; +} + +/**************************************************************************** + * Name: bl602_net_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 timesout 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: + * The network is locked. + * + ****************************************************************************/ + +static int bl602_net_txpoll(FAR struct net_driver_s *dev) +{ + FAR struct bl602_net_driver_s *priv = + (FAR struct bl602_net_driver_s *)dev->d_private; + + if (priv->net_dev.d_len > 0) + { + assert(priv->net_dev.d_buf); + + /* 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->net_dev.d_flags)) +#endif + { + arp_out(&priv->net_dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + neighbor_out(&priv->net_dev); + } +#endif /* CONFIG_NET_IPv6 */ + + /* Check if the network is sending this packet to the IP address of + * this device. If so, just loop the packet back into the network but + * don't attempt to put it on the wire. + */ + + if (!devif_loopback(&priv->net_dev)) + { + /* Send the packet */ + + bl602_net_transmit(priv); + + /* Check if there is room in the device to hold another packet. + * If not, return a non-zero value to terminate the poll. + */ + + priv->net_dev.d_buf = bl602_netdev_alloc_txbuf(); + if (priv->net_dev.d_buf) + { + priv->net_dev.d_buf += PRESERVE_80211_HEADER_LEN; + priv->net_dev.d_len = 0; + } + + return priv->net_dev.d_buf == NULL; + } + } + + /* If zero is returned, the polling will continue until all connections + * have been examined. + */ + + return 0; +} + +/**************************************************************************** + * Name: bl602_net_reply + * + * Description: + * After a packet has been received and dispatched to the network, it + * may return return with an outgoing packet. This function checks for + * that case and performs the transmission if necessary. + * + * Input Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void bl602_net_reply(struct bl602_net_driver_s *priv) +{ + uint8_t *tx_p = NULL; + + /* If the packet dispatch resulted in data that should be sent out on the + * network, the field d_len will set to a value > 0. + */ + + if (priv->net_dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* Check for an outgoing IPv4 packet */ + + if (IFF_IS_IPv4(priv->net_dev.d_flags)) +#endif + { + arp_out(&priv->net_dev); + } +#endif + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + /* Otherwise, it must be an outgoing IPv6 packet */ + + else +#endif + { + neighbor_out(&bl602_net->net_dev); + } +#endif + + /* alloc tx buffer and copy to it */ + + tx_p = bl602_netdev_alloc_txbuf(); + if (tx_p) + { + tx_p += PRESERVE_80211_HEADER_LEN; + assert(priv->net_dev.d_len <= + BL602_NET_TXBUFF_SIZE - PRESERVE_80211_HEADER_LEN); + + /* we copy it, and release rx buffer */ + + memcpy(tx_p, priv->net_dev.d_buf, priv->net_dev.d_len); + + bl_free_rx_buffer(priv->net_dev.d_buf); + + priv->net_dev.d_buf = tx_p; + + bl602_net_transmit(priv); + +#if BL602_TXDESC_THRESHOLD > 1 + /* notify to tx now */ + + bl_irq_handler(); + priv->push_cnt = 0; +#endif + + return; + } + else + { + /* NOTIC: The release path of tx buffer cannot acquire net lock */ + + nwarn("can not replay due to no tx buffer! \r\n"); + assert(0); + } + } + + /* we not have tx buffer, so we lost this packet */ + + bl_free_rx_buffer(priv->net_dev.d_buf); + priv->net_dev.d_buf = NULL; +} + +/**************************************************************************** + * Name: bl602_net_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: + * The network is locked. + * + ****************************************************************************/ + +static void bl602_net_receive(FAR struct bl602_net_driver_s *priv) +{ +#ifdef CONFIG_NET_PKT + /* When packet sockets are enabled, feed the frame into the tap */ + + pkt_input(&priv->net_dev); +#endif + +#ifdef CONFIG_NET_IPv4 + /* Check for an IPv4 packet */ + + if (BUF->type == HTONS(ETHTYPE_IP)) + { + ninfo("IPv4 frame\n"); + NETDEV_RXIPV4(&priv->net_dev); + + /* Handle ARP on input, then dispatch IPv4 packet to the network + * layer. + */ + + arp_ipin(&priv->net_dev); + ipv4_input(&priv->net_dev); + + /* Check for a reply to the IPv4 packet */ + + bl602_net_reply(priv); + } + else +#endif +#ifdef CONFIG_NET_IPv6 + /* Check for an IPv6 packet */ + + if (BUF->type == HTONS(ETHTYPE_IP6)) + { + ninfo("IPv6 frame\n"); + NETDEV_RXIPV6(&priv->net_dev); + + /* Dispatch IPv6 packet to the network layer */ + + ipv6_input(&priv->net_dev); + + /* Check for a reply to the IPv6 packet */ + + bl602_net_reply(priv); + } + else +#endif +#ifdef CONFIG_NET_ARP + /* Check for an ARP packet */ + + if (BUF->type == htons(ETHTYPE_ARP)) + { + /* Dispatch ARP packet to the network layer */ + + arp_arpin(&priv->net_dev); + NETDEV_RXARP(&priv->net_dev); + + if (priv->net_dev.d_len > 0) + { + uint8_t *tx_p = bl602_netdev_alloc_txbuf(); + if (tx_p != NULL) + { + assert(priv->net_dev.d_len <= + BL602_NET_TXBUFF_SIZE - PRESERVE_80211_HEADER_LEN); + + tx_p += PRESERVE_80211_HEADER_LEN; + + /* we copy it, and release rx buffer */ + + memcpy(tx_p, priv->net_dev.d_buf, priv->net_dev.d_len); + + bl_free_rx_buffer(priv->net_dev.d_buf); + + priv->net_dev.d_buf = tx_p; + bl602_net_transmit(priv); + +#if BL602_TXDESC_THRESHOLD > 1 + /* notify to tx now */ + + bl_irq_handler(); + priv->push_cnt = 0; +#endif + return; + } + else + { + nwarn("can not replay due to no tx buffer!\r\n"); + assert(0); + } + } + + /* we not have tx buffer, so we lost this packet */ + + bl_free_rx_buffer(priv->net_dev.d_buf); + priv->net_dev.d_buf = NULL; + } + else +#endif + { + NETDEV_RXDROPPED(&priv->net_dev); + bl_free_rx_buffer(priv->net_dev.d_buf); + priv->net_dev.d_buf = NULL; + } +} + +static int bl602_launch_pending_rx(FAR struct bl602_net_driver_s *priv) +{ + struct rx_pending_item_s *item; + irqstate_t irqstate; + int tx_buf_empty; + + while (1) + { + net_lock(); + + irqstate = enter_critical_section(); + tx_buf_empty = BIT_EMPTY(TX_BUFF_BIT_SIZE, &g_tx_buf_indicator); + leave_critical_section(irqstate); + + if (tx_buf_empty) + { + /* we dont have tx buffer, so we cant go ahead, abort.. */ + + nwarn("tx buf empty!\r\n"); + + net_unlock(); + return -ENOMEM; + } + + irqstate = enter_critical_section(); + item = + list_remove_head_type(&g_rx_pending, struct rx_pending_item_s, node); + leave_critical_section(irqstate); + + if (item == NULL) + { + /* we have free tx buffer, but we don't have pending rx data to + * input, we start poll the normal tx routine. + */ + + net_unlock(); + + return OK; + } + + ninfo("input stack rx data :%p %d\r\n", item->data, item->len); + + /* now we have avaliable tx buffer and pending rx data, launch it */ + + assert(priv->net_dev.d_buf == NULL); + assert(item->data != NULL && item->len > 0); + + priv->net_dev.d_buf = item->data; + priv->net_dev.d_len = item->len; + bl602_net_receive(priv); + + assert(priv->net_dev.d_buf == NULL); + net_unlock(); + + kmm_free(item); + } +} + +/**************************************************************************** + * Name: bl602_net_notify + * + * Description: + * Notify 602 net driver to handle + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info (architecture-specific) + * + * Returned Value: + * OK on success + * + * Assumptions: + * Runs in the context of a the Ethernet interrupt handler. Local + * interrupts are disabled by the interrupt logic. + * + ****************************************************************************/ + +int bl602_net_notify(uint32_t event, uint8_t *data, int len) +{ + /* TODO distinguish which driver */ + + FAR struct bl602_net_driver_s *priv = &g_bl602_net[0]; + int ret; + + if (event & BL602_NET_EVT_TX_DONE) + { + /* if we have tx buffer, we put pending input packet first */ + + ret = bl602_launch_pending_rx(priv); + if (ret != OK) + { + /* There is no tx buffer, we needn't to poll.. */ + + return -ENOMEM; + } + + if (work_available(&priv->availwork)) + { + /* Schedule to serialize the poll on the worker thread. */ + + work_queue( + ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0); + } + } + + if (event & BL602_NET_EVT_RX) + { + int tx_buf_empty = 0; + + irqstate_t irqstate = enter_critical_section(); + tx_buf_empty = BIT_EMPTY(TX_BUFF_BIT_SIZE, &g_tx_buf_indicator); + leave_critical_section(irqstate); + + if (tx_buf_empty) + { + /* pending this packet to list */ + + struct rx_pending_item_s *item = + kmm_malloc(sizeof(struct rx_pending_item_s)); + if (item == NULL) + { + nwarn("failed to alloc rx pending item, drop rx packet!\r\n"); + bl_free_rx_buffer(data); + + return -ENOMEM; + } + + list_initialize(&item->node); + + item->data = data; + item->len = len; + + ninfo("pending rx data :%p %d\r\n", item->data, item->len); + + irqstate = enter_critical_section(); + list_add_tail(&g_rx_pending, &item->node); + leave_critical_section(irqstate); + } + else + { + /* Thanks god, we have tx buffer now, put this packet to stack */ + + ninfo("input stack direct:%p %d\r\n", data, len); + + net_lock(); + assert(priv->net_dev.d_buf == NULL); + + priv->net_dev.d_buf = data; + priv->net_dev.d_len = len; + bl602_net_receive(priv); + + assert(priv->net_dev.d_buf == NULL); + net_unlock(); + } + } + + return OK; +} + +/**************************************************************************** + * Name: bl602_net_poll_work + * + * Description: + * Perform periodic polling from the worker thread + * + * Input Parameters: + * arg - The argument passed when work_queue() as called. + * + * Returned Value: + * OK on success + * + * Assumptions: + * Run on a work queue thread. + * + ****************************************************************************/ + +static void bl602_net_poll_work(FAR void *arg) +{ + FAR struct bl602_net_driver_s *priv = (FAR struct bl602_net_driver_s *)arg; + + net_lock(); + assert(priv->net_dev.d_buf == NULL); + assert(priv->push_cnt == 0); + + priv->net_dev.d_buf = bl602_netdev_alloc_txbuf(); + if (priv->net_dev.d_buf) + { + priv->net_dev.d_buf += PRESERVE_80211_HEADER_LEN; + priv->net_dev.d_len = 0; + } + + if (priv->net_dev.d_buf) + { + devif_timer(&priv->net_dev, BL602_NET_WDDELAY, bl602_net_txpoll); + + if (priv->push_cnt != 0) + { + /* notify to tx now */ + + bl_irq_handler(); + priv->push_cnt = 0; + } + + if (priv->net_dev.d_buf != NULL) + { + bl602_netdev_free_txbuf(priv->net_dev.d_buf - + PRESERVE_80211_HEADER_LEN); + priv->net_dev.d_buf = NULL; + } + } + + wd_start( + &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv); + + assert(priv->net_dev.d_buf == NULL); + net_unlock(); +} + +/**************************************************************************** + * Name: bl602_net_poll_expiry + * + * Description: + * Periodic timer handler. Called from the timer interrupt handler. + * + * Input Parameters: + * arg - The argument + * + * Returned Value: + * None + * + * Assumptions: + * Runs in the context of a the timer interrupt handler. Local + * interrupts are disabled by the interrupt logic. + * + ****************************************************************************/ + +static void bl602_net_poll_expiry(wdparm_t arg) +{ + FAR struct bl602_net_driver_s *priv = (FAR struct bl602_net_driver_s *)arg; + + /* Schedule to perform the interrupt processing on the worker thread. */ + + if (work_available(&priv->pollwork)) + { + int ret = + work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0); + assert(ret == 0); Review comment: It was added during debugging, and I will remove it. ---------------------------------------------------------------- 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