You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2021/03/08 16:36:16 UTC

[GitHub] [incubator-nuttx] btashton commented on a change in pull request #2991: risc-v/bl602: Add wifi and ble support

btashton commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r589506991



##########
File path: arch/risc-v/src/bl602/bl602_hbn.c
##########
@@ -58,3 +58,12 @@ void bl602_set_uart_clk_sel(int clk_sel)
       modifyreg32(BL602_HBN_GLB, HBN_GLB_HBN_UART_CLK_SEL, 0);
     }
 }
+
+void bl602_aon_pad_iesmt_cfg(uint8_t pad_cfg)
+{
+    uint32_t tmp_val;
+
+    tmp_val = getreg32(BL602_HBN_IRQ_MODE);

Review comment:
       Modify32

##########
File path: arch/risc-v/src/bl602/bl602_glb.c
##########
@@ -95,3 +95,26 @@ uint8_t bl602_glb_get_bclk_div(void)
 
   return (uint8_t)tmp_val;
 }
+
+/****************************************************************************
+ * Name: bl602_set_em_sel
+ *
+ * Description:
+ *   Set how much wifi ram is allocated to ble.
+ *
+ * Input Parameters:
+ *   em_type: memory size type
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void bl602_set_em_sel(int em_type)
+{
+  uint32_t tmp_val = 0;
+
+  tmp_val = getreg32(BL602_SEAM_MISC);

Review comment:
       This should be modify not get put. 

##########
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");

Review comment:
       No \r

##########
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");

Review comment:
       No \r

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);

Review comment:
       Debug assert

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);

Review comment:
       Debug assert

##########
File path: arch/risc-v/src/bl602/bl602_netdev.h
##########
@@ -0,0 +1,114 @@
+/****************************************************************************
+ * arch/risc-v/src/bl602/bl602_netdev.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef _BL602_NETDEV_H__
+#define _BL602_NETDEV_H__
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define PRESERVE_80211_HEADER_LEN 128
+
+#define BL602_NET_EVT_TX_DONE (0x1 << 0x0)
+#define BL602_NET_EVT_RX      (0x1 << 0x1)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: <Inline function name>

Review comment:
       Missing function information

##########
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);

Review comment:
       \r is not need

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);
+
+          wifi_mgmr_sta_psk_set(passphrase);
+          kmm_free(passphrase);
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWFREQ: /* Set channel/frequency (Hz) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          if (req->u.freq.e != 0)
+            {
+              return -EINVAL;
+            }
+
+          priv->channel = req->u.freq.m;
+
+          ninfo("set channel to %d\r\n", priv->channel);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWFREQ: /* Get channel/frequency (Hz) */
+      wlwarn("WARNING: SIOCGIWFREQ not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWMODE: /* Set operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (req->u.mode == priv->current_mode)
+            {
+              syslog(LOG_INFO, "mode not change\r\n");
+              return OK;
+            }
+
+          if (req->u.mode == IW_MODE_INFRA)
+            {
+              /* station */
+
+              priv->wlan = wifi_mgmr_sta_enable();
+
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in station mode \r\n");
+              priv->current_mode = IW_MODE_INFRA;
+              return OK;
+            }
+          else if (req->u.mode == IW_MODE_MASTER)
+            {
+              /* AP Mode */
+
+              priv->wlan = wifi_mgmr_ap_enable();
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in ap state\r\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\r\n", req->u.mode);
+              return -ENOSYS;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWMODE: /* Get operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          req->u.mode       = priv->current_mode;
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWAP: /* Set access point MAC addresses */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (priv->current_mode == IW_MODE_INFRA)
+            {
+              /* Set bssid */
+
+              memcpy(
+                priv->bssid, req->u.ap_addr.sa_data, sizeof(priv->bssid));
+              ninfo("ap bssid:%s\r\n", priv->bssid);
+            }
+          else if (priv->current_mode == IW_MODE_MASTER)
+            {
+              bl_wifi_ap_mac_addr_set((uint8_t *)req->u.ap_addr.sa_data);
+            }
+          else
+            {
+              return -ENOSYS;
+            }
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWAP: /* Get access point MAC addresses */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (priv->current_mode == IW_MODE_INFRA)
+            {
+              /* Get bssid */
+
+              memcpy(req->u.ap_addr.sa_data,
+                     priv->bssid,
+                     sizeof(req->u.ap_addr.sa_data));
+            }
+          else if (priv->current_mode == IW_MODE_MASTER)
+            {
+              bl_wifi_ap_mac_addr_get((uint8_t *)req->u.ap_addr.sa_data);
+            }
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWESSID: /* Set ESSID (network name) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          wifi_mgmr_sta_ssid_set(req->u.essid.pointer);
+
+          ninfo("essid: %s\r\n", (char *)req->u.essid.pointer);
+
+          if (req->u.essid.flags == 0)
+            {
+              return bl602_ioctl_wifi_stop(priv, arg);
+            }
+          else if (req->u.essid.flags == 1)
+            {
+              priv->prev_connectd = 0;
+              return bl602_ioctl_wifi_start(priv, arg);
+            }
+          else
+            {
+              nwarn("unknow essid action: %d\r\n", req->u.essid.flags);
+              return -ENOSYS;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWESSID: /* Get ESSID */
+      do
+        {
+          struct iwreq *req  = (struct iwreq *)arg;
+          wifi_mgmr_t * mgmr = bl602_netdev_get_wifi_mgmr(priv);
+          int           length;
+
+          assert(mgmr != NULL);
+
+          length = strlen(mgmr->wifi_mgmr_stat_info.ssid) + 1;
+          length =
+            length > req->u.essid.length ? req->u.essid.length : length;
+
+          memcpy(
+            req->u.essid.pointer, mgmr->wifi_mgmr_stat_info.ssid, length);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWRATE: /* Set default bit rate (bps) */
+      wlwarn("WARNING: SIOCSIWRATE not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCGIWRATE: /* Get default bit rate (bps) */
+      wlwarn("WARNING: SIOCGIWRATE not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWTXPOW: /* Set transmit power (dBm) */
+      wlwarn("WARNING: SIOCSIWTXPOW not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCGIWTXPOW: /* Get transmit power (dBm) */
+      wlwarn("WARNING: SIOCGIWTXPOW not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCGIWENCODEEXT: /* Get encoding token mode */
+      do
+        {
+          struct iwreq *        req = (struct iwreq *)arg;
+          struct iw_encode_ext *ext;
+          wifi_mgmr_t *         mgmr = bl602_netdev_get_wifi_mgmr(priv);
+          int                   length;
+
+          assert(mgmr != NULL);
+
+          ext      = (struct iw_encode_ext *)req->u.encoding.pointer;
+          length   = req->u.encoding.length - sizeof(struct iw_encode_ext);
+          ext->alg = IW_ENCODE_ALG_NONE;
+          ext->key_len = strlen(mgmr->wifi_mgmr_stat_info.psk);
+          if (ext->key_len > length)
+            {
+              return -E2BIG;
+            }
+
+          memcpy(ext->key, mgmr->wifi_mgmr_stat_info.psk, ext->key_len);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    default:
+      nerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+extern void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+
+uint8_t *bl602_netdev_alloc_txbuf(void)
+{
+  irqstate_t irq = enter_critical_section();
+  int        idx = BIT_FFS(TX_BUFF_BIT_SIZE, &g_tx_buf_indicator);
+  if (idx == 0)
+    {
+      leave_critical_section(irq);
+      nwarn("tx buff alloc failed!\r\n");
+      return NULL;
+    }
+
+  ninfo("tx buff alloc:%d\r\n", idx - 1);
+  BIT_CLR(TX_BUFF_BIT_SIZE, idx - 1, &g_tx_buf_indicator);
+  leave_critical_section(irq);
+
+  return g_tx_buff[idx - 1];
+}
+
+/* NOTIC: The release path of tx buffer DO NOT acquire net lock */
+
+void bl602_netdev_free_txbuf(uint8_t *buf)
+{
+  irqstate_t irq;
+  int        idx;
+
+  idx = (tx_buff_t)buf - g_tx_buff;
+
+  assert(idx < BL602_NET_TXBUFF_NUM && idx >= 0);
+  assert(g_tx_buff[idx] == buf);
+
+  ninfo("tx buff free %d\r\n", idx);
+
+  irq = enter_critical_section();
+  assert(!BIT_ISSET(TX_BUFF_BIT_SIZE, idx, &g_tx_buf_indicator));
+  BIT_SET(TX_BUFF_BIT_SIZE, idx, &g_tx_buf_indicator);
+  leave_critical_section(irq);
+}
+
+/****************************************************************************
+ * Name: bl602_net_initialize
+ *
+ * Description:
+ *   Initialize the Ethernet controller and driver
+ *
+ * Input Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called early in initialization before multi-tasking is initiated.
+ *
+ ****************************************************************************/
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+#if 0
+  pthread_attr_t wifi_main_attr;
+  pthread_attr_t wifi_mgmr_attr;
+  struct sched_param s = {
+    0
+  };
+
+  pthread_t wifi_main_p;
+  pthread_t wifi_mgmr_p;
+  int ret;
+
+  pthread_attr_init(&wifi_main_attr);
+  pthread_attr_init(&wifi_mgmr_attr);
+
+  pthread_attr_setstacksize(&wifi_main_attr, 1024 * 4);
+  pthread_attr_setstacksize(&wifi_mgmr_attr, 1024 * 4);
+
+  pthread_attr_setdetachstate(&wifi_main_attr, PTHREAD_CREATE_JOINABLE);
+  pthread_attr_setdetachstate(&wifi_mgmr_attr, PTHREAD_CREATE_JOINABLE);
+
+  s.sched_priority = 45;
+  pthread_attr_setschedparam(&wifi_main_attr, &s);
+  s.sched_priority = 44;
+  pthread_attr_setschedparam(&wifi_mgmr_attr, &s);
+
+  ret = pthread_create(&wifi_main_p, &wifi_main_attr, wifi_main, NULL);
+  assert(ret == 0);
+#else
+  wifi_main_init();
+  ipc_emb_notify();
+#endif
+
+#if 0
+  wifi_mgmr_drv_init(&g_conf);
+  ret = pthread_create(&wifi_mgmr_p, &wifi_mgmr_attr, wifi_mgmr_start, NULL);
+  assert(ret == 0);
+#else
+  wifi_mgmr_drv_init(&g_conf);
+  wifi_mgmr_tsk_init();
+#endif
+
+#if 0
+  /* sleep forever */
+
+  pthread_join(wifi_main_p, NULL);
+  pthread_join(wifi_mgmr_p, NULL);
+
+  assert(0);
+#endif
+  return 0;
+}
+
+void bl602_net_event(int evt, int val)
+{
+  /* TODO distinguish which driver */
+
+  FAR struct bl602_net_driver_s *priv = &g_bl602_net[0];
+  net_lock();
+
+  switch (evt)
+    {
+    case CODE_WIFI_ON_CONNECTED:
+      do
+        {
+          ninfo("carrier on\r\n");
+          netdev_carrier_on(&priv->net_dev);
+          priv->prev_connectd = 1;
+
+          sem_post(&g_wifi_connect_sem);
+        }
+      while (0);
+      break;
+
+    case CODE_WIFI_ON_DISCONNECT:
+      do
+        {
+          ninfo("carrier off\r\n");
+          netdev_carrier_off(&priv->net_dev);
+        }
+      while (0);
+      break;
+
+    case CODE_WIFI_CMD_RECONNECT:
+      do
+        {
+          static int retry_cnt = 0;
+          ninfo("retry connect : %d\r\n", retry_cnt);
+          if (!priv->prev_connectd)
+            {
+              if (retry_cnt++ > 3)
+                {
+                  retry_cnt = 0;
+                  wifi_mgmr_sta_autoconnect_disable();
+                  wifi_mgmr_api_idle();
+
+                  sem_post(&g_wifi_connect_sem);
+                }
+            }
+        }
+      while (0);
+      break;
+
+    default:
+      ninfo("unhandled msg:%d\r\n", evt);
+      break;
+    }
+
+  net_unlock();
+}
+
+int bl602_net_initialize(int intf)
+{
+  FAR struct bl602_net_driver_s *priv;
+  int                            tmp;
+  uint8_t                        mac[6];
+
+  /* Get the interface structure associated with this interface number. */
+
+  DEBUGASSERT(intf < CONFIG_BL602_NET_NINTERFACES);
+  priv = &g_bl602_net[intf];
+
+  /* Initialize the driver structure */
+
+  memset(priv, 0, sizeof(struct bl602_net_driver_s));
+  priv->net_dev.d_ifup =
+    bl602_net_ifup; /* I/F up (new IP address) callback */
+
+  priv->net_dev.d_ifdown  = bl602_net_ifdown;  /* I/F down callback */
+  priv->net_dev.d_txavail = bl602_net_txavail; /* New TX data callback */
+#ifdef CONFIG_NET_MCASTGROUP
+  priv->net_dev.d_addmac = bl602_net_addmac; /* Add multicast MAC address */
+  priv->net_dev.d_rmmac  = bl602_net_rmmac;  /* Remove multicast MAC address */
+#endif
+#ifdef CONFIG_NETDEV_IOCTL
+  priv->net_dev.d_ioctl = bl602_net_ioctl; /* Handle network IOCTL commands */
+#endif
+  priv->net_dev.d_private = priv; /* Used to recover private state from dev */
+  priv->net_dev.d_pktsize =
+    BL602_NET_TXBUFF_SIZE - PRESERVE_80211_HEADER_LEN;
+
+  priv->current_mode    = IW_MODE_AUTO;
+  priv->scan_result_len = 0;
+
+  /* Initialize scan semaphore */
+
+  tmp = sem_init(&g_wifi_scan_sem, 0, 1);
+  if (tmp < 0)
+    {
+      return tmp;
+    }
+
+  tmp = sem_init(&g_wifi_connect_sem, 0, 0);
+  if (tmp < 0)
+    {
+      return tmp;
+    }
+
+  list_initialize(&g_rx_pending);
+
+  /* Start wifi process */
+#if 0

Review comment:
       Why is this disabled?

##########
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");

Review comment:
       No \r

##########
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);

Review comment:
       Debug assert

##########
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");

Review comment:
       No \r

##########
File path: arch/risc-v/src/bl602/bl602_glb.c
##########
@@ -95,3 +95,26 @@ uint8_t bl602_glb_get_bclk_div(void)
 
   return (uint8_t)tmp_val;
 }
+
+/****************************************************************************
+ * Name: bl602_set_em_sel
+ *
+ * Description:
+ *   Set how much wifi ram is allocated to ble.
+ *
+ * Input Parameters:
+ *   em_type: memory size type
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void bl602_set_em_sel(int em_type)
+{
+  uint32_t tmp_val = 0;
+
+  tmp_val = getreg32(BL602_SEAM_MISC);

Review comment:
       Modify32 instead of get put

##########
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);

Review comment:
       Normally we use debug assert macros since this is a configuration bug. 

##########
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);

Review comment:
       PANIC macro

##########
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);

Review comment:
       Why does this need a lock around it?

##########
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);

Review comment:
       No \r

##########
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");

Review comment:
       No \r

##########
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);

Review comment:
       Debug assert macros instead

##########
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);

Review comment:
       Debug assert for these

##########
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);

Review comment:
       It's there something going on here that makes this not atomic?

##########
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:
       I don't this we want this assert here if we were unable to queue it would still pick up on the next poll. Should be a warning?

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is

Review comment:
       Wireless interface

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);
+
+          wifi_mgmr_sta_psk_set(passphrase);
+          kmm_free(passphrase);
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWFREQ: /* Set channel/frequency (Hz) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          if (req->u.freq.e != 0)
+            {
+              return -EINVAL;
+            }
+
+          priv->channel = req->u.freq.m;
+
+          ninfo("set channel to %d\r\n", priv->channel);

Review comment:
       wlinfo instead of ninfo also no \r

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);

Review comment:
       How would this ever fail?

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);

Review comment:
       Should use the nxsig_sleep inside of os. 

##########
File path: arch/risc-v/src/bl602/bl602_systemreset.c
##########
@@ -47,19 +53,33 @@
  *
  ****************************************************************************/
 
-static void bl602_chip_reset(uint32_t mask)
+void __attribute__((section(".tcm_code"))) bl602_chip_reset(uint32_t mask)
 {
+  /* We choose to use ROM driver */

Review comment:
       Why? The rom API does not provide proper OS locking when modifying registers which makes it less safe. This implemented the same logic. 

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);

Review comment:
       Bad user input should return error not panic. 

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);

Review comment:
       Should probably not log the passphrase. Also in the driver winfo instead of syslog

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);

Review comment:
       nxsig_sleep

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;

Review comment:
       There is a UNUSED macro for this. 

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);
+
+          wifi_mgmr_sta_psk_set(passphrase);
+          kmm_free(passphrase);
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWFREQ: /* Set channel/frequency (Hz) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          if (req->u.freq.e != 0)
+            {
+              return -EINVAL;
+            }
+
+          priv->channel = req->u.freq.m;
+
+          ninfo("set channel to %d\r\n", priv->channel);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWFREQ: /* Get channel/frequency (Hz) */
+      wlwarn("WARNING: SIOCGIWFREQ not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWMODE: /* Set operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (req->u.mode == priv->current_mode)
+            {
+              syslog(LOG_INFO, "mode not change\r\n");
+              return OK;
+            }
+
+          if (req->u.mode == IW_MODE_INFRA)
+            {
+              /* station */
+
+              priv->wlan = wifi_mgmr_sta_enable();
+
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in station mode \r\n");
+              priv->current_mode = IW_MODE_INFRA;
+              return OK;
+            }
+          else if (req->u.mode == IW_MODE_MASTER)
+            {
+              /* AP Mode */
+
+              priv->wlan = wifi_mgmr_ap_enable();
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in ap state\r\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\r\n", req->u.mode);
+              return -ENOSYS;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWMODE: /* Get operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          req->u.mode       = priv->current_mode;
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWAP: /* Set access point MAC addresses */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (priv->current_mode == IW_MODE_INFRA)
+            {
+              /* Set bssid */
+
+              memcpy(
+                priv->bssid, req->u.ap_addr.sa_data, sizeof(priv->bssid));
+              ninfo("ap bssid:%s\r\n", priv->bssid);
+            }
+          else if (priv->current_mode == IW_MODE_MASTER)
+            {
+              bl_wifi_ap_mac_addr_set((uint8_t *)req->u.ap_addr.sa_data);
+            }
+          else
+            {
+              return -ENOSYS;
+            }
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWAP: /* Get access point MAC addresses */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (priv->current_mode == IW_MODE_INFRA)
+            {
+              /* Get bssid */
+
+              memcpy(req->u.ap_addr.sa_data,
+                     priv->bssid,
+                     sizeof(req->u.ap_addr.sa_data));
+            }
+          else if (priv->current_mode == IW_MODE_MASTER)
+            {
+              bl_wifi_ap_mac_addr_get((uint8_t *)req->u.ap_addr.sa_data);
+            }
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWESSID: /* Set ESSID (network name) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          wifi_mgmr_sta_ssid_set(req->u.essid.pointer);
+
+          ninfo("essid: %s\r\n", (char *)req->u.essid.pointer);
+
+          if (req->u.essid.flags == 0)
+            {
+              return bl602_ioctl_wifi_stop(priv, arg);
+            }
+          else if (req->u.essid.flags == 1)
+            {
+              priv->prev_connectd = 0;
+              return bl602_ioctl_wifi_start(priv, arg);
+            }
+          else
+            {
+              nwarn("unknow essid action: %d\r\n", req->u.essid.flags);
+              return -ENOSYS;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWESSID: /* Get ESSID */
+      do
+        {
+          struct iwreq *req  = (struct iwreq *)arg;
+          wifi_mgmr_t * mgmr = bl602_netdev_get_wifi_mgmr(priv);
+          int           length;
+
+          assert(mgmr != NULL);
+
+          length = strlen(mgmr->wifi_mgmr_stat_info.ssid) + 1;
+          length =
+            length > req->u.essid.length ? req->u.essid.length : length;
+
+          memcpy(
+            req->u.essid.pointer, mgmr->wifi_mgmr_stat_info.ssid, length);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWRATE: /* Set default bit rate (bps) */
+      wlwarn("WARNING: SIOCSIWRATE not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCGIWRATE: /* Get default bit rate (bps) */
+      wlwarn("WARNING: SIOCGIWRATE not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWTXPOW: /* Set transmit power (dBm) */
+      wlwarn("WARNING: SIOCSIWTXPOW not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCGIWTXPOW: /* Get transmit power (dBm) */
+      wlwarn("WARNING: SIOCGIWTXPOW not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCGIWENCODEEXT: /* Get encoding token mode */
+      do
+        {
+          struct iwreq *        req = (struct iwreq *)arg;
+          struct iw_encode_ext *ext;
+          wifi_mgmr_t *         mgmr = bl602_netdev_get_wifi_mgmr(priv);
+          int                   length;
+
+          assert(mgmr != NULL);
+
+          ext      = (struct iw_encode_ext *)req->u.encoding.pointer;
+          length   = req->u.encoding.length - sizeof(struct iw_encode_ext);
+          ext->alg = IW_ENCODE_ALG_NONE;
+          ext->key_len = strlen(mgmr->wifi_mgmr_stat_info.psk);
+          if (ext->key_len > length)
+            {
+              return -E2BIG;
+            }
+
+          memcpy(ext->key, mgmr->wifi_mgmr_stat_info.psk, ext->key_len);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    default:
+      nerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+extern void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+
+uint8_t *bl602_netdev_alloc_txbuf(void)
+{
+  irqstate_t irq = enter_critical_section();
+  int        idx = BIT_FFS(TX_BUFF_BIT_SIZE, &g_tx_buf_indicator);
+  if (idx == 0)
+    {
+      leave_critical_section(irq);
+      nwarn("tx buff alloc failed!\r\n");
+      return NULL;
+    }
+
+  ninfo("tx buff alloc:%d\r\n", idx - 1);
+  BIT_CLR(TX_BUFF_BIT_SIZE, idx - 1, &g_tx_buf_indicator);
+  leave_critical_section(irq);
+
+  return g_tx_buff[idx - 1];
+}
+
+/* NOTIC: The release path of tx buffer DO NOT acquire net lock */
+
+void bl602_netdev_free_txbuf(uint8_t *buf)
+{
+  irqstate_t irq;
+  int        idx;
+
+  idx = (tx_buff_t)buf - g_tx_buff;
+
+  assert(idx < BL602_NET_TXBUFF_NUM && idx >= 0);
+  assert(g_tx_buff[idx] == buf);
+
+  ninfo("tx buff free %d\r\n", idx);
+
+  irq = enter_critical_section();
+  assert(!BIT_ISSET(TX_BUFF_BIT_SIZE, idx, &g_tx_buf_indicator));
+  BIT_SET(TX_BUFF_BIT_SIZE, idx, &g_tx_buf_indicator);
+  leave_critical_section(irq);
+}
+
+/****************************************************************************
+ * Name: bl602_net_initialize
+ *
+ * Description:
+ *   Initialize the Ethernet controller and driver
+ *
+ * Input Parameters:
+ *   intf - In the case where there are multiple EMACs, this value
+ *          identifies which EMAC is to be initialized.
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   Called early in initialization before multi-tasking is initiated.
+ *
+ ****************************************************************************/
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+#if 0

Review comment:
       Can we add a comment about why this code block is disabled

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);
+
+          wifi_mgmr_sta_psk_set(passphrase);
+          kmm_free(passphrase);
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWFREQ: /* Set channel/frequency (Hz) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          if (req->u.freq.e != 0)
+            {
+              return -EINVAL;
+            }
+
+          priv->channel = req->u.freq.m;
+
+          ninfo("set channel to %d\r\n", priv->channel);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWFREQ: /* Get channel/frequency (Hz) */
+      wlwarn("WARNING: SIOCGIWFREQ not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWMODE: /* Set operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (req->u.mode == priv->current_mode)
+            {
+              syslog(LOG_INFO, "mode not change\r\n");
+              return OK;
+            }
+
+          if (req->u.mode == IW_MODE_INFRA)
+            {
+              /* station */
+
+              priv->wlan = wifi_mgmr_sta_enable();
+
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in station mode \r\n");

Review comment:
       No syslog

##########
File path: arch/risc-v/src/bl602/bl602_netdev.h
##########
@@ -0,0 +1,114 @@
+/****************************************************************************
+ * arch/risc-v/src/bl602/bl602_netdev.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef _BL602_NETDEV_H__
+#define _BL602_NETDEV_H__
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define PRESERVE_80211_HEADER_LEN 128
+
+#define BL602_NET_EVT_TX_DONE (0x1 << 0x0)
+#define BL602_NET_EVT_RX      (0x1 << 0x1)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: <Inline function name>
+ *
+ * Description:
+ *   Description of the operation of the inline function.
+ *
+ * Input Parameters:
+ *   A list of input parameters, one-per-line, appears here along with a
+ *   description of each input parameter.
+ *
+ * Returned Value:
+ *   Description of the value returned by this function (if any),
+ *   including an enumeration of all possible error values.
+ *
+ * Assumptions/Limitations:
+ *   Anything else that one might need to know to use this function.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+uint8_t *bl602_netdev_alloc_txbuf(void);
+void     bl602_netdev_free_txbuf(uint8_t *buf);
+
+int  bl602_net_notify(uint32_t event, uint8_t *data, int len);
+void bl602_net_event(int evt, int val);
+/****************************************************************************
+ * Name: <Global function name>

Review comment:
       Missing function information

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);
+
+          wifi_mgmr_sta_psk_set(passphrase);
+          kmm_free(passphrase);
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWFREQ: /* Set channel/frequency (Hz) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          if (req->u.freq.e != 0)
+            {
+              return -EINVAL;
+            }
+
+          priv->channel = req->u.freq.m;
+
+          ninfo("set channel to %d\r\n", priv->channel);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWFREQ: /* Get channel/frequency (Hz) */
+      wlwarn("WARNING: SIOCGIWFREQ not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWMODE: /* Set operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (req->u.mode == priv->current_mode)
+            {
+              syslog(LOG_INFO, "mode not change\r\n");
+              return OK;
+            }
+
+          if (req->u.mode == IW_MODE_INFRA)
+            {
+              /* station */
+
+              priv->wlan = wifi_mgmr_sta_enable();
+
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in station mode \r\n");
+              priv->current_mode = IW_MODE_INFRA;
+              return OK;
+            }
+          else if (req->u.mode == IW_MODE_MASTER)
+            {
+              /* AP Mode */
+
+              priv->wlan = wifi_mgmr_ap_enable();
+              memcpy(priv->wlan->mac,
+                     priv->net_dev.d_mac.ether.ether_addr_octet,
+                     6);
+              syslog(LOG_INFO, "now in ap state\r\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\r\n", req->u.mode);
+              return -ENOSYS;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWMODE: /* Get operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          req->u.mode       = priv->current_mode;
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWAP: /* Set access point MAC addresses */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (priv->current_mode == IW_MODE_INFRA)
+            {
+              /* Set bssid */
+
+              memcpy(
+                priv->bssid, req->u.ap_addr.sa_data, sizeof(priv->bssid));
+              ninfo("ap bssid:%s\r\n", priv->bssid);
+            }
+          else if (priv->current_mode == IW_MODE_MASTER)
+            {
+              bl_wifi_ap_mac_addr_set((uint8_t *)req->u.ap_addr.sa_data);
+            }
+          else
+            {
+              return -ENOSYS;
+            }
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWAP: /* Get access point MAC addresses */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (priv->current_mode == IW_MODE_INFRA)
+            {
+              /* Get bssid */
+
+              memcpy(req->u.ap_addr.sa_data,
+                     priv->bssid,
+                     sizeof(req->u.ap_addr.sa_data));
+            }
+          else if (priv->current_mode == IW_MODE_MASTER)
+            {
+              bl_wifi_ap_mac_addr_get((uint8_t *)req->u.ap_addr.sa_data);
+            }
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWESSID: /* Set ESSID (network name) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          wifi_mgmr_sta_ssid_set(req->u.essid.pointer);
+
+          ninfo("essid: %s\r\n", (char *)req->u.essid.pointer);

Review comment:
       wlinfo no \r

##########
File path: arch/risc-v/src/bl602/bl602_systemreset.c
##########
@@ -47,19 +53,33 @@
  *
  ****************************************************************************/
 
-static void bl602_chip_reset(uint32_t mask)
+void __attribute__((section(".tcm_code"))) bl602_chip_reset(uint32_t mask)
 {
+  /* We choose to use ROM driver */
+
+#if 0
+  uint32_t regval;
+
+  /* Disable interrupt */
+
+  asm volatile("csrci mstatus, 8");
+
   /* Reset the root clock */
 
-  modifyreg32(BL602_HBN_GLB, HBN_GLB_HBN_ROOT_CLK_SEL_MASK, 0);

Review comment:
       Why did you remove modifyreg32. This is the atomic operation?

##########
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);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Ethernet interface when an IP address is
+ *   provided
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifup(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+#ifdef CONFIG_NET_IPv4
+  ninfo("Bringing up: %ld.%ld.%ld.%ld\n",
+        dev->d_ipaddr & 0xff,
+        (dev->d_ipaddr >> 8) & 0xff,
+        (dev->d_ipaddr >> 16) & 0xff,
+        dev->d_ipaddr >> 24);
+#endif
+#ifdef CONFIG_NET_IPv6
+  ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+        dev->d_ipv6addr[0],
+        dev->d_ipv6addr[1],
+        dev->d_ipv6addr[2],
+        dev->d_ipv6addr[3],
+        dev->d_ipv6addr[4],
+        dev->d_ipv6addr[5],
+        dev->d_ipv6addr[6],
+        dev->d_ipv6addr[7]);
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+  /* Set up IPv6 multicast address filtering */
+
+  bl602_net_ipv6multicast(priv);
+#endif
+
+  /* Set and activate a timer process */
+
+  wd_start(
+    &priv->txpoll, BL602_NET_WDDELAY, bl602_net_poll_expiry, (wdparm_t)priv);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifdown
+ *
+ * Description:
+ *   NuttX Callback: Stop the interface.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_ifdown(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  irqstate_t flags;
+
+  net_lock();
+  flags = enter_critical_section();
+
+  wd_cancel(&priv->txpoll);
+
+  leave_critical_section(flags);
+  net_unlock();
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail_work
+ *
+ * Description:
+ *   Perform an out-of-cycle poll on the worker thread.
+ *
+ * Input Parameters:
+ *   arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   Runs on a work queue thread.
+ *
+ ****************************************************************************/
+
+static void bl602_net_txavail_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);
+
+  /* Ignore the notification if the interface is not yet up */
+
+  if (!IFF_IS_UP(priv->net_dev.d_flags))
+    {
+      net_unlock();
+      return;
+    }
+
+  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 so, then poll the network for new XMIT data */
+
+  if (priv->net_dev.d_buf)
+    {
+      devif_timer(&priv->net_dev, 0, 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;
+        }
+      else
+        {
+          work_queue(
+            ETHWORK, &priv->availwork, bl602_net_txavail_work, priv, 0);
+        }
+    }
+
+  assert(priv->net_dev.d_buf == NULL);
+  net_unlock();
+}
+
+/****************************************************************************
+ * Name: bl602_net_txavail
+ *
+ * Description:
+ *   Driver callback invoked when new TX data is available.  This is a
+ *   stimulus perform an out-of-cycle poll and, thereby, reduce the TX
+ *   latency.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ *   None
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int bl602_net_txavail(FAR struct net_driver_s *dev)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  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);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_addmac
+ *
+ * Description:
+ *   NuttX Callback: Add the specified MAC address to the hardware multicast
+ *   address filtering
+ *
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be added
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#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)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_rmmac
+ *
+ * Description:
+ *   NuttX Callback: Remove the specified MAC address from the hardware
+ *   multicast address filtering
+ * Input Parameters:
+ *   dev  - Reference to the NuttX driver state structure
+ *   mac  - The MAC address to be removed
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_MCASTGROUP
+static int bl602_net_rmmac(FAR struct net_driver_s *dev,
+                           FAR const uint8_t *mac)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+
+  /* Add the MAC address to the hardware multicast routing table */
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: bl602_net_ipv6multicast
+ *
+ * Description:
+ *   Configure the IPv6 multicast MAC address.
+ *
+ * Input Parameters:
+ *   priv - A reference to the private driver state structure
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_ICMPv6
+static void bl602_net_ipv6multicast(FAR struct bl602_net_driver_s *priv)
+{
+  FAR struct net_driver_s *dev;
+  uint16_t                 tmp16;
+  uint8_t                  mac[6];
+
+  /* For ICMPv6, we need to add the IPv6 multicast address
+   *
+   * For IPv6 multicast addresses, the Ethernet MAC is derived by
+   * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00,
+   * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map
+   * to the Ethernet MAC address 33:33:00:01:00:03.
+   *
+   * NOTES:  This appears correct for the ICMPv6 Router Solicitation
+   * Message, but the ICMPv6 Neighbor Solicitation message seems to
+   * use 33:33:ff:01:00:03.
+   */
+
+  mac[0] = 0x33;
+  mac[1] = 0x33;
+
+  dev    = &priv->dev;
+  tmp16  = dev->d_ipv6addr[6];
+  mac[2] = 0xff;
+  mac[3] = tmp16 >> 8;
+
+  tmp16  = dev->d_ipv6addr[7];
+  mac[4] = tmp16 & 0xff;
+  mac[5] = tmp16 >> 8;
+
+  ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        mac[0],
+        mac[1],
+        mac[2],
+        mac[3],
+        mac[4],
+        mac[5]);
+
+  bl602_net_addmac(dev, mac);
+
+#ifdef CONFIG_NET_ICMPv6_AUTOCONF
+  /* Add the IPv6 all link-local nodes Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Advertisement
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_AUTOCONF */
+
+#ifdef CONFIG_NET_ICMPv6_ROUTER
+  /* Add the IPv6 all link-local routers Ethernet address.  This is the
+   * address that we expect to receive ICMPv6 Router Solicitation
+   * packets.
+   */
+
+  bl602_net_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet);
+
+#endif /* CONFIG_NET_ICMPv6_ROUTER */
+}
+#endif /* CONFIG_NET_ICMPv6 */
+
+static void scan_complete_indicate(void *data, void *param)
+{
+  int                        i;
+  struct scan_parse_param_s *para;
+  wifi_mgmr_scan_item_t *    scan;
+
+  para = (struct scan_parse_param_s *)data;
+  assert(para != NULL);
+  assert(para->priv != NULL);
+  para->priv->scan_result_len = 0;
+
+  for (i = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      scan = &WIFI_MGMR.scan_items[i];
+
+      if (wifi_mgmr_scan_item_is_timeout(&WIFI_MGMR,
+                                         &(WIFI_MGMR.scan_items[i])))
+        {
+          scan->is_used = 0;
+        }
+      else if (scan->is_used)
+        {
+          if (para->flags & IW_SCAN_THIS_ESSID)
+            {
+              if (strncmp(scan->ssid,
+                          (char *)para->scan_req.essid,
+                          sizeof(scan->ssid)) == 0)
+                {
+                  scan->is_used = 1;
+                  para->priv->scan_result_len++;
+                }
+              else
+                {
+                  scan->is_used = 0;
+                }
+            }
+          else
+            {
+              para->priv->scan_result_len++;
+            }
+        }
+    }
+
+  sem_post(&g_wifi_scan_sem);
+  kmm_free(data);
+  return;
+}
+
+static int rssi_compare(const void *arg1, const void *arg2)
+{
+  wifi_mgmr_scan_item_t *item1 = &WIFI_MGMR.scan_items[*(uint8_t *)arg1];
+  wifi_mgmr_scan_item_t *item2 = &WIFI_MGMR.scan_items[*(uint8_t *)arg2];
+
+  return item1->rssi - item2->rssi;
+}
+
+static int format_scan_result_to_wapi(struct iwreq *req, int result_cnt)
+{
+  int      i = 0;
+  int      j = 0;
+  int      event_buff_len = 0;
+  uint8_t *curr_pos       = NULL;
+
+  uint8_t *rssi_list = NULL; /* for sort */
+
+  if (result_cnt == 0)
+    {
+      return OK;
+    }
+
+  /* More compact arrangement */
+
+  event_buff_len = result_cnt *
+    (offsetof(struct iw_event, u) * 4 + sizeof(struct sockaddr) +
+    sizeof(struct iw_freq) + sizeof(struct iw_quality) +
+    sizeof(struct iw_point) + IW_ESSID_MAX_SIZE);
+
+  if (req->u.data.length == 0 || req->u.data.length < event_buff_len)
+    {
+      return -E2BIG;
+    }
+
+  req->u.data.length = event_buff_len;
+
+  /* alloc rssi list */
+
+  rssi_list = kmm_malloc(result_cnt * sizeof(uint8_t));
+  if (rssi_list == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* record all valid scan result items */
+
+  for (i = 0, j = 0;
+       i < sizeof(WIFI_MGMR.scan_items) / sizeof(WIFI_MGMR.scan_items[0]);
+       i++)
+    {
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[i];
+
+      if (scan->is_used == 0)
+        {
+          continue;
+        }
+
+      rssi_list[j++] = i;
+    }
+
+  assert(j == result_cnt);
+
+  /* sort the vaild list according the rssi */
+
+  qsort(rssi_list, result_cnt, sizeof(uint8_t), rssi_compare);
+
+  /* construct iw event buffer */
+
+  curr_pos = req->u.data.pointer;
+  assert(curr_pos != NULL);
+  assert(((uintptr_t)curr_pos & 0x3) == 0);
+
+  for (i = 0; i < result_cnt; i++)
+    {
+      int                    idx  = rssi_list[i];
+      wifi_mgmr_scan_item_t *scan = &WIFI_MGMR.scan_items[idx];
+
+      struct iw_event *iwe;
+
+      iwe = (struct iw_event *)curr_pos;
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct sockaddr);
+      iwe->cmd = SIOCGIWAP;
+      memcpy(iwe->u.ap_addr.sa_data, scan->bssid, sizeof(struct ether_addr));
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len      = offsetof(struct iw_event, u) + sizeof(struct iw_freq);
+      iwe->cmd      = SIOCGIWFREQ;
+      iwe->u.freq.e = 0;
+      iwe->u.freq.m = scan->channel;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_quality);
+      iwe->cmd = IWEVQUAL;
+      iwe->u.qual.level   = scan->rssi;
+      iwe->u.qual.updated = IW_QUAL_DBM;
+
+      iwe = (struct iw_event *)((uintptr_t)iwe + iwe->len);
+      assert(((uintptr_t)iwe & 0x3) == 0);
+      iwe->len = offsetof(struct iw_event, u) + sizeof(struct iw_point) +
+                 IW_ESSID_MAX_SIZE;
+      iwe->cmd            = SIOCGIWESSID;
+      iwe->u.essid.length = strlen(scan->ssid);
+      iwe->u.essid.flags  = 1;
+
+      /* refer:wapi wireless.c:272 */
+
+      iwe->u.essid.pointer = (void *)(uintptr_t)sizeof(struct iw_point);
+
+      memcpy((uint8_t *)iwe + offsetof(struct iw_event, u) +
+               sizeof(struct iw_point),
+             scan->ssid,
+             IW_ESSID_MAX_SIZE);
+
+      curr_pos = (uint8_t *)(uintptr_t)iwe + iwe->len;
+    }
+
+  kmm_free(rssi_list);
+
+  return OK;
+}
+
+static wifi_mgmr_t *
+bl602_netdev_get_wifi_mgmr(FAR struct bl602_net_driver_s *priv)
+{
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_sta);
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      return container_of(priv->wlan, wifi_mgmr_t, wlan_ap);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+#ifdef CONFIG_NETDEV_IOCTL
+static int bl602_ioctl_wifi_start(FAR struct bl602_net_driver_s *priv,
+                                  uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  assert(mgmr != NULL);
+
+  if (IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      return OK;
+    }
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      int state;
+
+      wifi_mgmr_sta_autoconnect_enable();
+      if (wifi_mgmr_api_connect(mgmr->wifi_mgmr_stat_info.ssid,
+                                mgmr->wifi_mgmr_stat_info.psk,
+                                NULL,
+                                (uint8_t *)priv->bssid,
+                                0,
+                                priv->channel) == -1)
+        {
+          return -ENOBUFS;
+        }
+
+      sem_wait(&g_wifi_connect_sem);
+
+      /* check connect state */
+
+      wifi_mgmr_state_get_internal(&state);
+      if (state != WIFI_STATE_CONNECTED_IP_GOT)
+        {
+          return -EINVAL;
+        }
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      if (wifi_mgmr_api_ap_start(mgmr->wifi_mgmr_stat_info.ssid,
+                                 mgmr->wifi_mgmr_stat_info.psk,
+                                 1,
+                                 0) < 0)
+        {
+          return -ENOBUFS;
+        }
+    }
+  else
+    {
+      return -ENOSYS;
+    }
+
+  return OK;
+}
+
+static int bl602_ioctl_wifi_stop(FAR struct bl602_net_driver_s *priv,
+                                 uintptr_t                      arg)
+{
+  (void)arg;
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      sleep(1);
+      wifi_mgmr_api_idle();
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: bl602_net_ioctl
+ *
+ * Description:
+ *   Handle network IOCTL commands directed to this device.
+ *
+ * Input Parameters:
+ *   dev - Reference to the NuttX driver state structure
+ *   cmd - The IOCTL command
+ *   arg - The argument for the IOCTL command
+ *
+ * Returned Value:
+ *   OK on success; Negated errno on failure.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+static int
+bl602_net_ioctl(FAR struct net_driver_s *dev, int cmd, unsigned long arg)
+{
+  FAR struct bl602_net_driver_s *priv =
+    (FAR struct bl602_net_driver_s *)dev->d_private;
+  int ret = -ENOSYS;
+
+  /* Decode and dispatch the driver-specific IOCTL command */
+
+  switch (cmd)
+    {
+    case SIOCSIWSCAN:
+      do
+        {
+          struct iwreq *             req  = (struct iwreq *)arg;
+          struct scan_parse_param_s *para = NULL;
+
+          para = kmm_malloc(sizeof(struct scan_parse_param_s));
+          if (para == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          para->priv = priv;
+          if (req->u.data.flags)
+            {
+              para->flags = req->u.data.flags;
+
+              assert(req->u.data.pointer != NULL);
+              para->scan_req = *(struct iw_scan_req *)req->u.data.pointer;
+            }
+          else
+            {
+              para->flags = 0;
+            }
+
+          if (sem_trywait(&g_wifi_scan_sem) == 0)
+            {
+              wifi_mgmr_scan(para, scan_complete_indicate);
+              return OK;
+            }
+          else
+            {
+              return -EBUSY;
+            }
+        }
+      while (0);
+      break;
+
+    case SIOCGIWSCAN:
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          sem_wait(&g_wifi_scan_sem);
+
+          if (priv->scan_result_len == 0)
+            {
+              req->u.data.length = 0;
+              sem_post(&g_wifi_scan_sem);
+              return OK;
+            }
+
+          ret = format_scan_result_to_wapi(req, priv->scan_result_len);
+          sem_post(&g_wifi_scan_sem);
+          return ret;
+        }
+      while (0);
+      break;
+
+    case SIOCSIFHWADDR: /* Set device MAC address */
+      return -ENOSYS;
+      break;
+
+    case SIOCSIWAUTH:
+      /* FIXME not support set auth param now, return OK to support
+       * wapi reconnect command
+       */
+
+      return OK;
+      break;
+
+    case SIOCSIWENCODEEXT: /* Set psk */
+      do
+        {
+          struct iwreq *        req        = (struct iwreq *)arg;
+          struct iw_encode_ext *ext        = req->u.encoding.pointer;
+          char *                passphrase = kmm_malloc(ext->key_len + 1);
+          if (passphrase == NULL)
+            {
+              return -ENOMEM;
+            }
+
+          strncpy(passphrase, (char *)ext->key, ext->key_len);
+          passphrase[ext->key_len] = 0;
+          syslog(LOG_INFO, "passphrase: %s\r\n", passphrase);
+
+          wifi_mgmr_sta_psk_set(passphrase);
+          kmm_free(passphrase);
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCSIWFREQ: /* Set channel/frequency (Hz) */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+
+          if (req->u.freq.e != 0)
+            {
+              return -EINVAL;
+            }
+
+          priv->channel = req->u.freq.m;
+
+          ninfo("set channel to %d\r\n", priv->channel);
+
+          return OK;
+        }
+      while (0);
+      break;
+
+    case SIOCGIWFREQ: /* Get channel/frequency (Hz) */
+      wlwarn("WARNING: SIOCGIWFREQ not implemented\n");
+      ret = -ENOSYS;
+      break;
+
+    case SIOCSIWMODE: /* Set operation mode */
+      do
+        {
+          struct iwreq *req = (struct iwreq *)arg;
+          if (req->u.mode == priv->current_mode)
+            {
+              syslog(LOG_INFO, "mode not change\r\n");

Review comment:
       No syslog




----------------------------------------------------------------
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:
users@infra.apache.org