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/07 12:28:52 UTC

[GitHub] [incubator-nuttx] Virus-V opened a new pull request #2991: risc-v/bl602: Add wifi and ble support

Virus-V opened a new pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991


   ## Summary
   BL602 adds WiFi and BLE support
   
   ## Impact
   risc-v/bl602
   
   ## Testing
   BL602 Boards
   
   


----------------------------------------------------------------
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



[GitHub] [incubator-nuttx] Virus-V commented on pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
Virus-V commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-792623142


   > @Virus-V it's better to squach your patchset into one
   
   done


----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591185047



##########
File path: arch/risc-v/src/bl602/bl602_idle.c
##########
@@ -49,6 +49,11 @@
 
 void up_idle(void)
 {
+#if defined(CONFIG_BL602_BLE_CONTROLLER)
+  extern int ble_hci_do_rx(void);
+  ble_hci_do_rx();

Review comment:
       BLE HCI is implemented as a virtual serial port, and this function needs to be called regularly to simulate the RX of the serial port. Putting it in the idle process does not have much impact. 




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591220465



##########
File path: arch/risc-v/src/bl602/bl602_idle.c
##########
@@ -49,6 +49,11 @@
 
 void up_idle(void)
 {
+#if defined(CONFIG_BL602_BLE_CONTROLLER)
+  extern int ble_hci_do_rx(void);
+  ble_hci_do_rx();

Review comment:
       Why not drive the receive by interrupt or dedicated thread. The idle thread may schedule too late to receive the data.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
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



[GitHub] [incubator-nuttx] Virus-V commented on pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
Virus-V commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-805396938


   rebase on master


-- 
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-795005137


   > @btashton You need to execute `renew wlan0` to request an IP from the DHCP Server
   
   Something goofy happens if you do not issue ifup prior to wapi essid command.  The interface showed as up and I would see packets being received but it was not able to find the device address for routing packets including for renew
   
   ```
   nsh> renew wlan0
   netdev_ifr_ioctl: cmd: 1812
   dhcpc_open: MAC: b4:e8:42:02:a0:c2
   netdev_ifr_ioctl: cmd: 1793
   netdev_ifr_ioctl: cmd: 1794
   dhcpc_request: Broadcast DISCOVER
   psock_udp_sendto: ERROR: udp_find_raddr_device failed
   ERROR: dhcpc_request() failed
   ```
   
   When I brought it up first I had no issues and was able to use iperf just fine.


----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-792541709


   @Virus-V it's better to squach your patchset into one


----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-805488641


   > @btashton let's merge it if you don't have more concern.
   
   Yes that is fine. I can address some of my concerns later when I have some time again. The important stuff has been addressed. 


-- 
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r592169562



##########
File path: arch/risc-v/src/bl602/bl602_systemreset.c
##########
@@ -68,25 +96,34 @@ static void bl602_chip_reset(uint32_t mask)
   putreg32(1, 0x40000ffc);
   putreg32(0, 0x40000ffc);
 
+  /* Avoid glitches  */
+
+  asm volatile("nop;nop;nop;nop");
+  asm volatile("nop;nop;nop;nop");
+
   /* Trigger reset
    * NOTE: The reset seems to be rising _edge_ triggered so the reset
    *       bit should be cleared first otherwise the reset will not
    *       trigger if it has previously fired.
    */
 
-  modifyreg32(
-      BL602_SWRST_CFG2,
-      (SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET | \
-       SWRST_CFG2_CTRL_PWRON_RST),
-      0
-  );
-
-  modifyreg32(
-      BL602_SWRST_CFG2,
-      (SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET | \
-       SWRST_CFG2_CTRL_PWRON_RST),
-      mask
-  );
+  regval = getreg32(BL602_SWRST_CFG2);
+  regval &= ~(SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET |
+        SWRST_CFG2_CTRL_PWRON_RST);
+  putreg32(regval, BL602_SWRST_CFG2);
+
+  regval = getreg32(BL602_SWRST_CFG2);
+  regval &= ~(SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET |
+        SWRST_CFG2_CTRL_PWRON_RST);
+  regval |= mask;
+  putreg32(regval, BL602_SWRST_CFG2);
+#else
+  /* When perform reset before, MUST disable interrupt */
+
+  asm volatile("csrci mstatus, 8");
+
+  ((bl602_romdrv_reset_por)(*((uint32_t *)(0x210108c4))))();

Review comment:
       The rest of the resets have been changed to ROM Driver API 




----------------------------------------------------------------
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



[GitHub] [incubator-nuttx] xiaoxiang781216 merged pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991


   


-- 
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r590500508



##########
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:
       This makes sense I did not know this limitation. Thanks. 




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-805483373


   @btashton let's merge it if you don't have more concern.


-- 
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591024417



##########
File path: arch/risc-v/src/bl602/bl602_idle.c
##########
@@ -49,6 +49,11 @@
 
 void up_idle(void)
 {
+#if defined(CONFIG_BL602_BLE_CONTROLLER)
+  extern int ble_hci_do_rx(void);
+  ble_hci_do_rx();

Review comment:
       What is the calling considerations on this?  This does not seem like the right place to call this.  Also the function prototype should probably be moved to another header.

##########
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:
       I did not realize the implementation of BIT_EMPTY.  You are right this does need to be protected.

##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       Does this control allowed channels? If so do we need to expose this via an ioctl or to start with via Kconfig?

##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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

Review comment:
       Are there multiple physical interfaces that should be supported here?  I don't think we need this and if we do this should be moved to the Kconfig where we can drop this check.

##########
File path: arch/risc-v/src/bl602/bl602_systemreset.c
##########
@@ -68,25 +96,34 @@ static void bl602_chip_reset(uint32_t mask)
   putreg32(1, 0x40000ffc);
   putreg32(0, 0x40000ffc);
 
+  /* Avoid glitches  */
+
+  asm volatile("nop;nop;nop;nop");
+  asm volatile("nop;nop;nop;nop");
+
   /* Trigger reset
    * NOTE: The reset seems to be rising _edge_ triggered so the reset
    *       bit should be cleared first otherwise the reset will not
    *       trigger if it has previously fired.
    */
 
-  modifyreg32(
-      BL602_SWRST_CFG2,
-      (SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET | \
-       SWRST_CFG2_CTRL_PWRON_RST),
-      0
-  );
-
-  modifyreg32(
-      BL602_SWRST_CFG2,
-      (SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET | \
-       SWRST_CFG2_CTRL_PWRON_RST),
-      mask
-  );
+  regval = getreg32(BL602_SWRST_CFG2);
+  regval &= ~(SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET |
+        SWRST_CFG2_CTRL_PWRON_RST);
+  putreg32(regval, BL602_SWRST_CFG2);
+
+  regval = getreg32(BL602_SWRST_CFG2);
+  regval &= ~(SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET |
+        SWRST_CFG2_CTRL_PWRON_RST);
+  regval |= mask;
+  putreg32(regval, BL602_SWRST_CFG2);
+#else
+  /* When perform reset before, MUST disable interrupt */
+
+  asm volatile("csrci mstatus, 8");
+
+  ((bl602_romdrv_reset_por)(*((uint32_t *)(0x210108c4))))();

Review comment:
       Can we call the rest api with the mask argument to select which reset we want to call (sys, cpu, pwron) and then drop the code I added here that has been disabled?

##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       I noticed that I cannot set this via the wapi
   ```
   nsh> wapi country wlan0 US
   ioctl(0x8b37): Not a typewriter
   ERROR: Process command (country) failed.
   ```

##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+/****************************************************************************
+ * 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 void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+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\n", priv->net_dev.d_len);
+
+  DEBUGASSERT(priv->net_dev.d_len <= WIFI_MTU_SIZE);
+  DEBUGASSERT(priv->push_cnt < BL602_TXDESC_THRESHOLD);
+
+  if (!IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      nwarn("drop packet due to iface no carrier\n");
+      bl602_netdev_free_txbuf(priv->net_dev.d_buf -
+                              PRESERVE_80211_HEADER_LEN);
+      priv->net_dev.d_buf = NULL;
+
+      return -EPERM;
+    }
+
+  DEBUGASSERT(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)
+    {
+      DEBUGASSERT(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;
+          DEBUGASSERT(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 */
+
+          nerr("can not replay due to no tx buffer! \n");
+          PANIC();
+        }
+    }
+
+  /* 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)
+            {
+              DEBUGASSERT(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
+            {
+              nerr("can not replay due to no tx buffer!\n");
+              PANIC();
+            }
+        }
+
+      /* 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!\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\n", item->data, item->len);
+
+      /* now we have avaliable tx buffer and pending rx data, launch it */
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      DEBUGASSERT(item->data != NULL && item->len > 0);
+
+      priv->net_dev.d_buf = item->data;
+      priv->net_dev.d_len = item->len;
+      bl602_net_receive(priv);
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      net_unlock();
+
+      kmm_free(item);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_notify
+ *
+ * Description:
+ *   BL602 WiFi notify handler, similar interrupt function
+ *
+ * Input Parameters:
+ *   event: notify type, tx done or received new data
+ *   data: The data of the event, may be NULL
+ *   len: data length
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+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)
+            {
+              wlwarn("failed to alloc rx pending item, drop rx packet!\n");
+              bl_free_rx_buffer(data);
+
+              return -ENOMEM;
+            }
+
+          list_initialize(&item->node);
+
+          item->data = data;
+          item->len  = len;
+
+          wlinfo("pending rx data :%p %d\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 */
+
+          wlinfo("input stack direct:%p %d\n", data, len);
+
+          net_lock();
+          DEBUGASSERT(priv->net_dev.d_buf == NULL);
+
+          priv->net_dev.d_buf = data;
+          priv->net_dev.d_len = len;
+          bl602_net_receive(priv);
+
+          DEBUGASSERT(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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+
+  DEBUGASSERT(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))
+    {
+      work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Wireless 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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+        }
+    }
+
+  DEBUGASSERT(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 Wireless 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 Wireless 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 MAC 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 MAC 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;
+  DEBUGASSERT(para != NULL);
+  DEBUGASSERT(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;
+    }
+
+  DEBUGASSERT(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;
+  DEBUGASSERT(curr_pos != NULL);
+  DEBUGASSERT(((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;
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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)
+{
+  UNUSED(arg);
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  DEBUGASSERT(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)
+{
+  UNUSED(arg);
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      nxsig_sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      nxsig_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)
+            {
+              if (req->u.data.pointer == NULL)
+                {
+                  kmm_free(para);
+                  return -EINVAL;
+                }
+
+              para->flags = req->u.data.flags;
+              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
+            {
+              kmm_free(para);
+              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;
+
+          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;
+
+          wlinfo("set channel to %d\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)
+            {
+              wlinfo("mode not change\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);
+              wlinfo("now in station mode \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);
+              wlinfo("now in ap state\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\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));
+              wlinfo("ap bssid:%s\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);
+
+          wlinfo("essid: %s\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
+            {
+              wlinfo("unknow essid action: %d\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;
+
+          DEBUGASSERT(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;
+
+          DEBUGASSERT(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:
+      wlerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+  wifi_main_init();

Review comment:
       Are there constraints that we should be aware of here with this process?  I noticed that it seems to print to the uart which could cause unexpected behavior.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r592196598



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       No, country code need be configurable at the runtime, not compile time.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591134365



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+/****************************************************************************
+ * 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 void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+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\n", priv->net_dev.d_len);
+
+  DEBUGASSERT(priv->net_dev.d_len <= WIFI_MTU_SIZE);
+  DEBUGASSERT(priv->push_cnt < BL602_TXDESC_THRESHOLD);
+
+  if (!IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      nwarn("drop packet due to iface no carrier\n");
+      bl602_netdev_free_txbuf(priv->net_dev.d_buf -
+                              PRESERVE_80211_HEADER_LEN);
+      priv->net_dev.d_buf = NULL;
+
+      return -EPERM;
+    }
+
+  DEBUGASSERT(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)
+    {
+      DEBUGASSERT(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;
+          DEBUGASSERT(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 */
+
+          nerr("can not replay due to no tx buffer! \n");
+          PANIC();
+        }
+    }
+
+  /* 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)
+            {
+              DEBUGASSERT(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
+            {
+              nerr("can not replay due to no tx buffer!\n");
+              PANIC();
+            }
+        }
+
+      /* 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!\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\n", item->data, item->len);
+
+      /* now we have avaliable tx buffer and pending rx data, launch it */
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      DEBUGASSERT(item->data != NULL && item->len > 0);
+
+      priv->net_dev.d_buf = item->data;
+      priv->net_dev.d_len = item->len;
+      bl602_net_receive(priv);
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      net_unlock();
+
+      kmm_free(item);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_notify
+ *
+ * Description:
+ *   BL602 WiFi notify handler, similar interrupt function
+ *
+ * Input Parameters:
+ *   event: notify type, tx done or received new data
+ *   data: The data of the event, may be NULL
+ *   len: data length
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+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)
+            {
+              wlwarn("failed to alloc rx pending item, drop rx packet!\n");
+              bl_free_rx_buffer(data);
+
+              return -ENOMEM;
+            }
+
+          list_initialize(&item->node);
+
+          item->data = data;
+          item->len  = len;
+
+          wlinfo("pending rx data :%p %d\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 */
+
+          wlinfo("input stack direct:%p %d\n", data, len);
+
+          net_lock();
+          DEBUGASSERT(priv->net_dev.d_buf == NULL);
+
+          priv->net_dev.d_buf = data;
+          priv->net_dev.d_len = len;
+          bl602_net_receive(priv);
+
+          DEBUGASSERT(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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+
+  DEBUGASSERT(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))
+    {
+      work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Wireless 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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+        }
+    }
+
+  DEBUGASSERT(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 Wireless 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 Wireless 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 MAC 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 MAC 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;
+  DEBUGASSERT(para != NULL);
+  DEBUGASSERT(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;
+    }
+
+  DEBUGASSERT(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;
+  DEBUGASSERT(curr_pos != NULL);
+  DEBUGASSERT(((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;
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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)
+{
+  UNUSED(arg);
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  DEBUGASSERT(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)
+{
+  UNUSED(arg);
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      nxsig_sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      nxsig_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)
+            {
+              if (req->u.data.pointer == NULL)
+                {
+                  kmm_free(para);
+                  return -EINVAL;
+                }
+
+              para->flags = req->u.data.flags;
+              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
+            {
+              kmm_free(para);
+              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;
+
+          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;
+
+          wlinfo("set channel to %d\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)
+            {
+              wlinfo("mode not change\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);
+              wlinfo("now in station mode \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);
+              wlinfo("now in ap state\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\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));
+              wlinfo("ap bssid:%s\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);
+
+          wlinfo("essid: %s\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
+            {
+              wlinfo("unknow essid action: %d\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;
+
+          DEBUGASSERT(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;
+
+          DEBUGASSERT(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:
+      wlerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+  wifi_main_init();

Review comment:
       Then I will change it to configurable through kconfig.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r598236850



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+/****************************************************************************
+ * 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 void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+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\n", priv->net_dev.d_len);
+
+  DEBUGASSERT(priv->net_dev.d_len <= WIFI_MTU_SIZE);
+  DEBUGASSERT(priv->push_cnt < BL602_TXDESC_THRESHOLD);
+
+  if (!IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      nwarn("drop packet due to iface no carrier\n");
+      bl602_netdev_free_txbuf(priv->net_dev.d_buf -
+                              PRESERVE_80211_HEADER_LEN);
+      priv->net_dev.d_buf = NULL;
+
+      return -EPERM;
+    }
+
+  DEBUGASSERT(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)
+    {
+      DEBUGASSERT(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;
+          DEBUGASSERT(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 */
+
+          nerr("can not replay due to no tx buffer! \n");
+          PANIC();
+        }
+    }
+
+  /* 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)
+            {
+              DEBUGASSERT(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
+            {
+              nerr("can not replay due to no tx buffer!\n");
+              PANIC();
+            }
+        }
+
+      /* 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!\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\n", item->data, item->len);
+
+      /* now we have avaliable tx buffer and pending rx data, launch it */
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      DEBUGASSERT(item->data != NULL && item->len > 0);
+
+      priv->net_dev.d_buf = item->data;
+      priv->net_dev.d_len = item->len;
+      bl602_net_receive(priv);
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      net_unlock();
+
+      kmm_free(item);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_notify
+ *
+ * Description:
+ *   BL602 WiFi notify handler, similar interrupt function
+ *
+ * Input Parameters:
+ *   event: notify type, tx done or received new data
+ *   data: The data of the event, may be NULL
+ *   len: data length
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+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)
+            {
+              wlwarn("failed to alloc rx pending item, drop rx packet!\n");
+              bl_free_rx_buffer(data);
+
+              return -ENOMEM;
+            }
+
+          list_initialize(&item->node);
+
+          item->data = data;
+          item->len  = len;
+
+          wlinfo("pending rx data :%p %d\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 */
+
+          wlinfo("input stack direct:%p %d\n", data, len);
+
+          net_lock();
+          DEBUGASSERT(priv->net_dev.d_buf == NULL);
+
+          priv->net_dev.d_buf = data;
+          priv->net_dev.d_len = len;
+          bl602_net_receive(priv);
+
+          DEBUGASSERT(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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+
+  DEBUGASSERT(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))
+    {
+      work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Wireless 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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+        }
+    }
+
+  DEBUGASSERT(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 Wireless 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 Wireless 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 MAC 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 MAC 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;
+  DEBUGASSERT(para != NULL);
+  DEBUGASSERT(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;
+    }
+
+  DEBUGASSERT(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;
+  DEBUGASSERT(curr_pos != NULL);
+  DEBUGASSERT(((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;
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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)
+{
+  UNUSED(arg);
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  DEBUGASSERT(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)
+{
+  UNUSED(arg);
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      nxsig_sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      nxsig_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)
+            {
+              if (req->u.data.pointer == NULL)
+                {
+                  kmm_free(para);
+                  return -EINVAL;
+                }
+
+              para->flags = req->u.data.flags;
+              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
+            {
+              kmm_free(para);
+              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;
+
+          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;
+
+          wlinfo("set channel to %d\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)
+            {
+              wlinfo("mode not change\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);
+              wlinfo("now in station mode \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);
+              wlinfo("now in ap state\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\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));
+              wlinfo("ap bssid:%s\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);
+
+          wlinfo("essid: %s\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
+            {
+              wlinfo("unknow essid action: %d\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;
+
+          DEBUGASSERT(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;
+
+          DEBUGASSERT(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:
+      wlerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+  wifi_main_init();

Review comment:
       @xiaoxiang781216 I think this was my biggest outstanding question @Virus-V were you able to figure out how to control the debug logging from the wireless blob? Maybe I missed it in the updated PR.




-- 
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591099557



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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

Review comment:
       We'll AP and STA, respectively, as the two wlan devices, currently only supports one. 




----------------------------------------------------------------
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



[GitHub] [incubator-nuttx] Virus-V commented on pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
Virus-V commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-805533509


   > @Virus-V please normalize defconfig by:
   > 
   > ```
   > ./tools/refresh.sh --silent bl602evb:ble
   > ./tools/refresh.sh --silent bl602evb:wifi
   > ```
   
   Done


-- 
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r592171413



##########
File path: arch/risc-v/src/bl602/bl602_idle.c
##########
@@ -49,6 +49,11 @@
 
 void up_idle(void)
 {
+#if defined(CONFIG_BL602_BLE_CONTROLLER)
+  extern int ble_hci_do_rx(void);
+  ble_hci_do_rx();

Review comment:
       Reimplemented this part of the code using watchdog timer and workqueue.




----------------------------------------------------------------
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



[GitHub] [incubator-nuttx] Virus-V commented on pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
Virus-V commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-803543508


   @xiaoxiang781216 It is ready to merge


-- 
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r589867150



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

Review comment:
       It was added during debugging, and I will remove it. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-803524599


   @btashton @Virus-V is this PR ready for merge?


-- 
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591120427



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+/****************************************************************************
+ * 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 void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+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\n", priv->net_dev.d_len);
+
+  DEBUGASSERT(priv->net_dev.d_len <= WIFI_MTU_SIZE);
+  DEBUGASSERT(priv->push_cnt < BL602_TXDESC_THRESHOLD);
+
+  if (!IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      nwarn("drop packet due to iface no carrier\n");
+      bl602_netdev_free_txbuf(priv->net_dev.d_buf -
+                              PRESERVE_80211_HEADER_LEN);
+      priv->net_dev.d_buf = NULL;
+
+      return -EPERM;
+    }
+
+  DEBUGASSERT(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)
+    {
+      DEBUGASSERT(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;
+          DEBUGASSERT(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 */
+
+          nerr("can not replay due to no tx buffer! \n");
+          PANIC();
+        }
+    }
+
+  /* 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)
+            {
+              DEBUGASSERT(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
+            {
+              nerr("can not replay due to no tx buffer!\n");
+              PANIC();
+            }
+        }
+
+      /* 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!\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\n", item->data, item->len);
+
+      /* now we have avaliable tx buffer and pending rx data, launch it */
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      DEBUGASSERT(item->data != NULL && item->len > 0);
+
+      priv->net_dev.d_buf = item->data;
+      priv->net_dev.d_len = item->len;
+      bl602_net_receive(priv);
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      net_unlock();
+
+      kmm_free(item);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_notify
+ *
+ * Description:
+ *   BL602 WiFi notify handler, similar interrupt function
+ *
+ * Input Parameters:
+ *   event: notify type, tx done or received new data
+ *   data: The data of the event, may be NULL
+ *   len: data length
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+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)
+            {
+              wlwarn("failed to alloc rx pending item, drop rx packet!\n");
+              bl_free_rx_buffer(data);
+
+              return -ENOMEM;
+            }
+
+          list_initialize(&item->node);
+
+          item->data = data;
+          item->len  = len;
+
+          wlinfo("pending rx data :%p %d\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 */
+
+          wlinfo("input stack direct:%p %d\n", data, len);
+
+          net_lock();
+          DEBUGASSERT(priv->net_dev.d_buf == NULL);
+
+          priv->net_dev.d_buf = data;
+          priv->net_dev.d_len = len;
+          bl602_net_receive(priv);
+
+          DEBUGASSERT(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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+
+  DEBUGASSERT(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))
+    {
+      work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Wireless 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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+        }
+    }
+
+  DEBUGASSERT(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 Wireless 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 Wireless 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 MAC 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 MAC 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;
+  DEBUGASSERT(para != NULL);
+  DEBUGASSERT(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;
+    }
+
+  DEBUGASSERT(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;
+  DEBUGASSERT(curr_pos != NULL);
+  DEBUGASSERT(((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;
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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)
+{
+  UNUSED(arg);
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  DEBUGASSERT(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)
+{
+  UNUSED(arg);
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      nxsig_sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      nxsig_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)
+            {
+              if (req->u.data.pointer == NULL)
+                {
+                  kmm_free(para);
+                  return -EINVAL;
+                }
+
+              para->flags = req->u.data.flags;
+              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
+            {
+              kmm_free(para);
+              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;
+
+          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;
+
+          wlinfo("set channel to %d\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)
+            {
+              wlinfo("mode not change\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);
+              wlinfo("now in station mode \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);
+              wlinfo("now in ap state\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\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));
+              wlinfo("ap bssid:%s\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);
+
+          wlinfo("essid: %s\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
+            {
+              wlinfo("unknow essid action: %d\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;
+
+          DEBUGASSERT(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;
+
+          DEBUGASSERT(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:
+      wlerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+  wifi_main_init();

Review comment:
       I think they are certainly useful, I just did not see how they were attached to the logging system.  Maybe conditional them on the wireless debug kconfig?




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591102082



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+/****************************************************************************
+ * 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 void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+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\n", priv->net_dev.d_len);
+
+  DEBUGASSERT(priv->net_dev.d_len <= WIFI_MTU_SIZE);
+  DEBUGASSERT(priv->push_cnt < BL602_TXDESC_THRESHOLD);
+
+  if (!IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      nwarn("drop packet due to iface no carrier\n");
+      bl602_netdev_free_txbuf(priv->net_dev.d_buf -
+                              PRESERVE_80211_HEADER_LEN);
+      priv->net_dev.d_buf = NULL;
+
+      return -EPERM;
+    }
+
+  DEBUGASSERT(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)
+    {
+      DEBUGASSERT(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;
+          DEBUGASSERT(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 */
+
+          nerr("can not replay due to no tx buffer! \n");
+          PANIC();
+        }
+    }
+
+  /* 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)
+            {
+              DEBUGASSERT(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
+            {
+              nerr("can not replay due to no tx buffer!\n");
+              PANIC();
+            }
+        }
+
+      /* 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!\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\n", item->data, item->len);
+
+      /* now we have avaliable tx buffer and pending rx data, launch it */
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      DEBUGASSERT(item->data != NULL && item->len > 0);
+
+      priv->net_dev.d_buf = item->data;
+      priv->net_dev.d_len = item->len;
+      bl602_net_receive(priv);
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      net_unlock();
+
+      kmm_free(item);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_notify
+ *
+ * Description:
+ *   BL602 WiFi notify handler, similar interrupt function
+ *
+ * Input Parameters:
+ *   event: notify type, tx done or received new data
+ *   data: The data of the event, may be NULL
+ *   len: data length
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+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)
+            {
+              wlwarn("failed to alloc rx pending item, drop rx packet!\n");
+              bl_free_rx_buffer(data);
+
+              return -ENOMEM;
+            }
+
+          list_initialize(&item->node);
+
+          item->data = data;
+          item->len  = len;
+
+          wlinfo("pending rx data :%p %d\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 */
+
+          wlinfo("input stack direct:%p %d\n", data, len);
+
+          net_lock();
+          DEBUGASSERT(priv->net_dev.d_buf == NULL);
+
+          priv->net_dev.d_buf = data;
+          priv->net_dev.d_len = len;
+          bl602_net_receive(priv);
+
+          DEBUGASSERT(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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+
+  DEBUGASSERT(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))
+    {
+      work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Wireless 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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+        }
+    }
+
+  DEBUGASSERT(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 Wireless 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 Wireless 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 MAC 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 MAC 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;
+  DEBUGASSERT(para != NULL);
+  DEBUGASSERT(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;
+    }
+
+  DEBUGASSERT(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;
+  DEBUGASSERT(curr_pos != NULL);
+  DEBUGASSERT(((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;
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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)
+{
+  UNUSED(arg);
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  DEBUGASSERT(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)
+{
+  UNUSED(arg);
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      nxsig_sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      nxsig_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)
+            {
+              if (req->u.data.pointer == NULL)
+                {
+                  kmm_free(para);
+                  return -EINVAL;
+                }
+
+              para->flags = req->u.data.flags;
+              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
+            {
+              kmm_free(para);
+              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;
+
+          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;
+
+          wlinfo("set channel to %d\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)
+            {
+              wlinfo("mode not change\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);
+              wlinfo("now in station mode \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);
+              wlinfo("now in ap state\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\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));
+              wlinfo("ap bssid:%s\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);
+
+          wlinfo("essid: %s\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
+            {
+              wlinfo("unknow essid action: %d\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;
+
+          DEBUGASSERT(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;
+
+          DEBUGASSERT(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:
+      wlerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+  wifi_main_init();

Review comment:
       These prints are output through the syslog API, I will remove these logs.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r589866198



##########
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:
       Because the private firmware library will asynchronously call back to the bl602_netdev_free_txbuf function, which will operate g_tx_buf_indicator. 

##########
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:
       Because modifyreg32 is the code on XIP Flash, and no code on XIP Flash can be called during reset. 

##########
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:
       The ROM Driver has the same function as these commented out codes. When calling the reset code of the ROM Driver, the global interrupt needs to be turned off instead of OS-related locking. And this part of the code needs to be accessible normally when resetting, because the XIP Flash controller needs to be reset when BL602 resetting, so you can choose to put this part of the code in RAM (tcm_code section) or ROM Driver. 
   We want to save RAM space, so choose to use ROM Driver and close the global interrupt to ensure that the reset process is not interrupted. 

##########
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:
       Mentioned above

##########
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:
       Yes, it was added during debugging, and I will remove 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);
+
+      /* 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:
       I am worried that in the firmware, there will be new modifications to the results between the completion of the scan and the time I get the scan results. At least I have not encountered its assertion failure. This is added when debugging the code. I will change it to 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);
+  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:
       Mentioned above, I will remove 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);
+
+      /* 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:
       In order to save RAM space, we adapt the firmware code to the LP work queue, which is driven by events. In this way, the stack space of the work queue can be used to run firmware code instead of creating a new process and stack. 
   Since these codes are not compatible, I will remove these disabled codes. 




----------------------------------------------------------------
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



[GitHub] [incubator-nuttx] Virus-V commented on pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
Virus-V commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-794961454


   > I did some initial testing and made some more comments.
   > It seems like we may need to add some more error handling here as well. Here are some tests that I ran:
   > 
   > Tried to set the country code since I see it is currently hard coded to CN. This seems fairly important to support.
   > 
   > ```
   > nsh> wapi country wlan0 US
   > ioctl(0x8b37): Not a typewriter
   > ERROR: Process command (country) failed.
   > ```
   > 
   > I ran the config and called wapi show and crashed.
   > 
   > ```
   > nsh> wapi show wlan0
   > wlan0 Configuration:
   >        IP: 10.0.0.2
   >   NetMask: 255.255.255.0
   > Frequency: 0
   >      Flag: WAPI_FREQ_AUTO
   > ioctl(SIOCGIWRANGE): Not a typewriter
   > ERROR: wapi_freq2chan() failed: -25
   > ioctl(SIOCGIWRANGE): Not a typewriter
   > ERROR: wapi_chan2freq() failed: -25
   > irq_unexpected_isr: ERROR irq: 5
   > up_assert: Assertion failed at file:irq/irq_unexpectedisr.c line: 50 task: wapi
   > ```
   > 
   > Launching I see a lot of debug from the wireless blob. We probably need to be able to disable that. Is that possible?
   > 
   > ```
   > �Feed random number is 00000000
   > Feed random number is 00000000
   > [WF] Running Wi-Fi FW thread...
   > 1th channel,lo_vco_freq_cw=156
   > 2th channel,lo_vco_freq_cw=155
   > 3th channel,lo_vco_freq_cw=153
   > 4th channel,lo_vco_freq_cw=152
   > 5th channel,lo_vco_freq_cw=151
   > 6th channel,lo_vco_freq_cw=150
   > 7th channel,lo_vco_freq_cw=148
   > 8th channel,lo_vco_freq_cw=147
   > 9th channel,lo_vco_freq_cw=146
   > 10th channel,lo_vco_freq_cw=144
   > 11th channel,lo_vco_freq_cw=143
   > 12th channel,lo_vco_freq_cw=142
   > 13th channel,lo_vco_freq_cw=140
   > 14th channel,lo_vco_freq_cw=139
   > 15th channel,lo_vco_freq_cw=138
   > 16th channel,lo_vco_freq_cw=137
   > 17th channel,lo_vco_freq_cw=136
   > 18th channel,lo_vco_freq_cw=134
   > 19th channel,lo_vco_freq_cw=133
   > 20th channel,lo_vco_freq_cw=132
   > 21th channel,lo_vco_freq_cw=131
   > 0th channel,vco_idac_cw=9
   > 1th channel,vco_idac_cw=8
   > 2th channel,vco_idac_cw=8
   > 3th channel,vco_idac_cw=8
   > 4th channel,vco_idac_cw=8
   > 5th channel,vco_idac_cw=8
   > 6th channel,vco_idac_cw=8
   > 7th channel,vco_idac_cw=8
   > 8th channel,vco_idac_cw=8
   > 9th channel,vco_idac_cw=8
   > 10th channel,vco_idac_cw=7
   > 11th channel,vco_idac_cw=7
   > 12th channel,vco_idac_cw=8
   > 13th channel,vco_idac_cw=8
   > 14th channel,vco_idac_cw=8
   > 15th channel,vco_idac_cw=8
   > 16th channel,vco_idac_cw=8
   > 17th channel,vco_idac_cw=7
   > 18th channel,vco_idac_cw=7
   > 19th channel,vco_idac_cw=7
   > 20th channel,vco_idac_cw=7
   > LO locked 9 144
   > rosdac_i_gc3=37
   > rosdac_i_gc2=37
   > rosdac_i_gc1=37
   > rosdac_i_gc0=37
   > rosdac_q_gc3=33
   > rosdac_q_gc2=33
   > rosdac_q_gc1=33
   > rosdac_q_gc0=33
   > rbb_cap1_fc_i=26,rbb_cap2_fc_i=26,rbb_cap1_fc_q=25,rbb_cap2_fc_q=25
   > new rbb_cap1_fc_i=50,rbb_cap2_fc_i=50,rbb_cap1_fc_q=49,rbb_cap2_fc_q=49
   > LO locked 9 144
   > amp=128,step=32,adc_mean_i=250
   > amp=96,step=16,adc_mean_i=147
   > tmx_cs=0, tmxcs_pwr_avg=23706, tmxcs_pwr_avg>>10=23
   > tmx_cs=1, tmxcs_pwr_avg=32546, tmxcs_pwr_avg>>10=31
   > tmx_cs=2, tmxcs_pwr_avg=45396, tmxcs_pwr_avg>>10=44
   > tmx_cs=3, tmxcs_pwr_avg=65354, tmxcs_pwr_avg>>10=63
   > tmx_cs=4, tmxcs_pwr_avg=97771, tmxcs_pwr_avg>>10=95
   > tmx_cs=5, tmxcs_pwr_avg=150895, tmxcs_pwr_avg>>10=147
   > tmx_cs=6, tmxcs_pwr_avg=228598, tmxcs_pwr_avg>>10=223
   > tmx_cs=7, tmxcs_pwr_avg=283373, tmxcs_pwr_avg>>10=276
   > tmx_cs_max=7, tmxcs_pwr_max=283373, tmxcs_pwr_max>>10=276
   > amp=256,step=64,adc_mean_i=511
   > amp=192,step=32,adc_mean_i=511
   > amp=160,step=16,adc_mean_i=509
   > amp=144,step=8,adc_mean_i=430
   > amp=136,step=4,adc_mean_i=360
   > amp=132,step=2,adc_mean_i=319
   > tosdac_i=24,tosdac_q=32,tx_iq_gain_comp=1019,tx_iq_phase_comp=-11
   > amp=256,step=64,adc_mean_i=511
   > amp=192,step=32,adc_mean_i=511
   > amp=160,step=16,adc_mean_i=511
   > amp=144,step=8,adc_mean_i=471
   > amp=136,step=4,adc_mean_i=417
   > amp=132,step=2,adc_mean_i=392
   > amp=130,step=1,adc_mean_i=378
   > amp=129,step=0,adc_mean_i=373
   > tosdac_i=26,tosdac_q=36,tx_iq_gain_comp=1015,tx_iq_phase_comp=-10
   > amp=256,step=64,adc_mean_i=511
   > amp=192,step=32,adc_mean_i=511
   > amp=160,step=16,adc_mean_i=431
   > amp=144,step=8,adc_mean_i=357
   > amp=136,step=4,adc_mean_i=324
   > amp=132,step=2,adc_mean_i=307
   > tosdac_i=31,tosdac_q=36,tx_iq_gain_comp=1014,tx_iq_phase_comp=-7
   > amp=272,step=68,adc_mean_i=511
   > amp=204,step=34,adc_mean_i=511
   > amp=170,step=17,adc_mean_i=511
   > amp=153,step=8,adc_mean_i=511
   > amp=145,step=4,adc_mean_i=511
   > amp=141,step=2,adc_mean_i=511
   > amp=139,step=1,adc_mean_i=511
   > amp=138,step=0,adc_mean_i=511
   > tosdac_i=38,tosdac_q=30,tx_iq_gain_comp=1056,tx_iq_phase_comp=-1
   > amp=224,step=56,adc_mean_i=511
   > amp=168,step=28,adc_mean_i=511
   > amp=140,step=14,adc_mean_i=511
   > amp=126,step=7,adc_mean_i=511
   > amp=119,step=3,adc_mean_i=506
   > amp=116,step=1,adc_mean_i=500
   > amp=115,step=0,adc_mean_i=498
   > tosdac_i=26,tosdac_q=32,tx_iq_gain_comp=1016,tx_iq_phase_comp=7
   > amp=272,step=68,adc_mean_i=511
   > amp=204,step=34,adc_mean_i=511
   > amp=170,step=17,adc_mean_i=511
   > amp=153,step=8,adc_mean_i=511
   > amp=145,step=4,adc_mean_i=511
   > amp=141,step=2,adc_mean_i=511
   > amp=139,step=1,adc_mean_i=510
   > amp=138,step=0,adc_mean_i=509
   > tosdac_i=25,tosdac_q=33,tx_iq_gain_comp=1185,tx_iq_phase_comp=-66
   > amp=288,step=72,adc_mean_i=511
   > amp=216,step=36,adc_mean_i=511
   > amp=180,step=18,adc_mean_i=511
   > amp=162,step=9,adc_mean_i=511
   > amp=153,step=4,adc_mean_i=505
   > amp=149,step=2,adc_mean_i=501
   > amp=147,step=1,adc_mean_i=498
   > amp=146,step=0,adc_mean_i=497
   > tosdac_i=35,tosdac_q=38,tx_iq_gain_comp=1368,tx_iq_phase_comp=-8
   > amp=304,step=76,adc_mean_i=511
   > amp=228,step=38,adc_mean_i=511
   > amp=190,step=19,adc_mean_i=511
   > amp=171,step=9,adc_mean_i=511
   > amp=162,step=4,adc_mean_i=511
   > amp=158,step=2,adc_mean_i=511
   > amp=156,step=1,adc_mean_i=511
   > amp=155,step=0,adc_mean_i=511
   > tosdac_i=32,tosdac_q=32,tx_iq_gain_comp=1080,tx_iq_phase_comp=-8
   > Done
   > Configure IPC...Done
   > Configure MAC...[WF] [KEY] [CFG] nVAP is 2, endidx 10, startidx 8
   > ------ set default key 0x42016790, key ptr 0
   > ------ set default key 0x42016c38, key ptr 0
   > �td_init
   > �td_reset idx=0
   > �td_reset idx=1
   > Done
   > Starting loop...
   > [COEX] Skip report for the first time
   > 
   > 
   > [BL] Initi Wi-Fi with MAC #### 00:00:00:00:00:00 ####
   >      hostname: Bouffalolab_BL602-000000
   > [WF] country code CN used, num of channel 13
   > -----------------------------------------------------
   > [IPC] [TX] Low level size 204, driver size 92, total size 296
   > Enable BMX IRQ
   > 
   > [WF] [KEY] [CFG] nVAP is 2, endidx 10, startidx 8
   > ------ set default key 0x42016790, key ptr 0
   > ------ set default key 0x42016c38, key ptr 0
   > �td_init
   > �td_reset idx=0
   > �td_reset idx=1
   > [version] lmac 5.4.0.0
   > [version] version_machw_1 000055FB
   > [version] version_machw_2 000001B3
   > [version] version_phy_1 00822111
   > [version] version_phy_2 00000000
   > [version] features 001089DF
   > [ME] HT supp 1, VHT supp 0
   > bl_main_set_country_code: country code: CN
   > [WF] country code CN used, num of channel 13
   > country code:CN, support channel nums:13
   > wifi_mgmr_start_internel firtst inite = 0
   > [WF][SM] reload tsen 
   > wifi_mgmr_tsk_init
   > [WF][SM] Exiting ifaceDown state
   > [WF][SM] State Action ###ifaceDown### --->>> ###idle###
   > [WF][SM] Entering idle state
   > 
   > NuttShell (NSH) NuttX-10.0.1
   > nsh> 
   > ```
   > 
   > Scanning seemed to work great.
   > 
   > I was not able to talk on the wireless network, although I had no issues authenticating.
   > 
   > ```
   > [RX] Connection Status
   > [RX]   status_code 0
   > [RX]   connect result: sm connect ind ok
   > [RX]   MAC 1C:F2:9A:81:5F:83
   > [RX]   vif_idx 0
   > [RX]   ap_idx 0
   > [RX]   ch_idx 0
   > [RX]   qos 1
   > [RX]   acm 0
   > [RX]   assoc_req_ie_len 89
   > [RX]   assoc_rsp_ie_len 109
   > [RX]   aid 0
   > [RX]   band 0
   > [RX]   center_freq 2462
   > [RX]   width 0
   > [RX]   center_freq1 2462
   > [RX]   center_freq2 0
   > [RX] wifi_mgmr_set_connect_stat_info, wifiMgmr.wifi_mgmr_stat_info:
   > [RX]   status_code 0
   > [RX]   MAC 1C:F2:9A:81:5F:83
   > [RX]   band 0
   > [RX]   center_freq 2462
   > [RX]   type_ind 1
   > [WF][SM] Exiting connecting state
   > [WF][SM] State Action ###connecting### --->>> ###wifiConnected_ipObtaining###
   > [WF][SM] Exiting wifiConnected_ipObtaining state
   > [WF][SM] State Action ###wifiConnected_ipObtaining### --->>> ###wifiConnected_IPOK###
   > [WF][SM] Entering wifiConnected_IPOK state
   > ------------------ [FW] [PSK] [EAPOL] ----------------  diff 17523
   > nsh> ifconfig
   > wlan0	Link encap:Ethernet HWaddr b4:e8:42:02:a0:c2 at RUNNING
   > 	inet addr:10.0.0.2 DRaddr:10.0.0.1 Mask:255.255.255.0
   > 
   > nsh> ping 10.0.0.3
   > PING 10.0.0.3 56 bytes of data
   > ERROR: sendto failed at seqno 0: 114
   > nsh> 
   > ```
   
   @btashton You need to execute `renew wlan0` to request an IP from the DHCP Server 


----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591230710



##########
File path: arch/risc-v/src/bl602/bl602_idle.c
##########
@@ -49,6 +49,11 @@
 
 void up_idle(void)
 {
+#if defined(CONFIG_BL602_BLE_CONTROLLER)
+  extern int ble_hci_do_rx(void);
+  ble_hci_do_rx();

Review comment:
       I will reimplement this part of the code in the next commit




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591098044



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       We will add the WAPI command to configure the country code next. 




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r592238557



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       Ok.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r598250840



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",
+};
+
+/****************************************************************************
+ * 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 void wifi_main(int argc, char *argv[]);
+extern void wifi_mgmr_start_background(wifi_conf_t *conf);
+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\n", priv->net_dev.d_len);
+
+  DEBUGASSERT(priv->net_dev.d_len <= WIFI_MTU_SIZE);
+  DEBUGASSERT(priv->push_cnt < BL602_TXDESC_THRESHOLD);
+
+  if (!IFF_IS_RUNNING(priv->net_dev.d_flags))
+    {
+      nwarn("drop packet due to iface no carrier\n");
+      bl602_netdev_free_txbuf(priv->net_dev.d_buf -
+                              PRESERVE_80211_HEADER_LEN);
+      priv->net_dev.d_buf = NULL;
+
+      return -EPERM;
+    }
+
+  DEBUGASSERT(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)
+    {
+      DEBUGASSERT(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;
+          DEBUGASSERT(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 */
+
+          nerr("can not replay due to no tx buffer! \n");
+          PANIC();
+        }
+    }
+
+  /* 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)
+            {
+              DEBUGASSERT(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
+            {
+              nerr("can not replay due to no tx buffer!\n");
+              PANIC();
+            }
+        }
+
+      /* 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!\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\n", item->data, item->len);
+
+      /* now we have avaliable tx buffer and pending rx data, launch it */
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      DEBUGASSERT(item->data != NULL && item->len > 0);
+
+      priv->net_dev.d_buf = item->data;
+      priv->net_dev.d_len = item->len;
+      bl602_net_receive(priv);
+
+      DEBUGASSERT(priv->net_dev.d_buf == NULL);
+      net_unlock();
+
+      kmm_free(item);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_notify
+ *
+ * Description:
+ *   BL602 WiFi notify handler, similar interrupt function
+ *
+ * Input Parameters:
+ *   event: notify type, tx done or received new data
+ *   data: The data of the event, may be NULL
+ *   len: data length
+ *
+ * Returned Value:
+ *   OK on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+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)
+            {
+              wlwarn("failed to alloc rx pending item, drop rx packet!\n");
+              bl_free_rx_buffer(data);
+
+              return -ENOMEM;
+            }
+
+          list_initialize(&item->node);
+
+          item->data = data;
+          item->len  = len;
+
+          wlinfo("pending rx data :%p %d\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 */
+
+          wlinfo("input stack direct:%p %d\n", data, len);
+
+          net_lock();
+          DEBUGASSERT(priv->net_dev.d_buf == NULL);
+
+          priv->net_dev.d_buf = data;
+          priv->net_dev.d_len = len;
+          bl602_net_receive(priv);
+
+          DEBUGASSERT(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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+
+  DEBUGASSERT(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))
+    {
+      work_queue(ETHWORK, &priv->pollwork, bl602_net_poll_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: bl602_net_ifup
+ *
+ * Description:
+ *   NuttX Callback: Bring up the Wireless 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();
+  DEBUGASSERT(priv->net_dev.d_buf == NULL);
+  DEBUGASSERT(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);
+        }
+    }
+
+  DEBUGASSERT(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 Wireless 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 Wireless 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 MAC 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 MAC 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;
+  DEBUGASSERT(para != NULL);
+  DEBUGASSERT(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;
+    }
+
+  DEBUGASSERT(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;
+  DEBUGASSERT(curr_pos != NULL);
+  DEBUGASSERT(((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;
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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);
+      DEBUGASSERT(((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)
+{
+  UNUSED(arg);
+
+  /* preform connect ap */
+
+  wifi_mgmr_t *mgmr = bl602_netdev_get_wifi_mgmr(priv);
+  DEBUGASSERT(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)
+{
+  UNUSED(arg);
+
+  /* perform disconnect */
+
+  if (priv->current_mode == IW_MODE_INFRA)
+    {
+      wifi_mgmr_sta_disconnect();
+      nxsig_sleep(1);
+      wifi_mgmr_api_idle();
+    }
+  else if (priv->current_mode == IW_MODE_MASTER)
+    {
+      wifi_mgmr_api_ap_stop();
+      nxsig_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)
+            {
+              if (req->u.data.pointer == NULL)
+                {
+                  kmm_free(para);
+                  return -EINVAL;
+                }
+
+              para->flags = req->u.data.flags;
+              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
+            {
+              kmm_free(para);
+              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;
+
+          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;
+
+          wlinfo("set channel to %d\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)
+            {
+              wlinfo("mode not change\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);
+              wlinfo("now in station mode \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);
+              wlinfo("now in ap state\n");
+              priv->current_mode = IW_MODE_MASTER;
+              return OK;
+            }
+          else
+            {
+              wlwarn("WARNING: Unsupport mode:%ld\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));
+              wlinfo("ap bssid:%s\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);
+
+          wlinfo("essid: %s\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
+            {
+              wlinfo("unknow essid action: %d\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;
+
+          DEBUGASSERT(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;
+
+          DEBUGASSERT(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:
+      wlerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+      return -ENOTTY; /* Special return value for this case */
+    }
+
+  return OK;
+}
+#endif
+
+static int wifi_manager_process(int argc, FAR char *argv[])
+{
+  wifi_main_init();

Review comment:
       @btashton It is controlled by weak symbols when linking: `LDFLAGS += -defsym _wifi_log_flag=1`




-- 
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-794090013


   @Virus-V thanks for the fixes, I will do a review and some testing after work. I really appreciate all the hard work here. 


----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591125438



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       > 
   > 
   > Does this control allowed channels? If so do we need to expose this via an ioctl or to start with via Kconfig?
   
   Yes, you can directly replace the string CN with US.




----------------------------------------------------------------
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



[GitHub] [incubator-nuttx] Virus-V closed pull request #2991: risc-v/bl602: Add wifi and ble support

Posted by GitBox <gi...@apache.org>.
Virus-V closed pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991


   


----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r592168804



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       Added country code configuration parameters in Kconfig 




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591220465



##########
File path: arch/risc-v/src/bl602/bl602_idle.c
##########
@@ -49,6 +49,11 @@
 
 void up_idle(void)
 {
+#if defined(CONFIG_BL602_BLE_CONTROLLER)
+  extern int ble_hci_do_rx(void);
+  ble_hci_do_rx();

Review comment:
       Why not drive the receive by interrupt or dedicated thread? The idle thread may schedule too late to receive the data.




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#issuecomment-805527203


   @Virus-V please normalize defconfig by:
   ```
   ./tools/refresh.sh --silent bl602evb:ble
   ./tools/refresh.sh --silent bl602evb:wifi
   ```


-- 
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r592202675



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       > 
   > 
   > We will add the WAPI command to configure the country code next.
   
   @xiaoxiang781216 This feature is in progress, Kconfig only specifies the default country code




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
btashton commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591045660



##########
File path: arch/risc-v/src/bl602/bl602_systemreset.c
##########
@@ -68,25 +96,34 @@ static void bl602_chip_reset(uint32_t mask)
   putreg32(1, 0x40000ffc);
   putreg32(0, 0x40000ffc);
 
+  /* Avoid glitches  */
+
+  asm volatile("nop;nop;nop;nop");
+  asm volatile("nop;nop;nop;nop");
+
   /* Trigger reset
    * NOTE: The reset seems to be rising _edge_ triggered so the reset
    *       bit should be cleared first otherwise the reset will not
    *       trigger if it has previously fired.
    */
 
-  modifyreg32(
-      BL602_SWRST_CFG2,
-      (SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET | \
-       SWRST_CFG2_CTRL_PWRON_RST),
-      0
-  );
-
-  modifyreg32(
-      BL602_SWRST_CFG2,
-      (SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET | \
-       SWRST_CFG2_CTRL_PWRON_RST),
-      mask
-  );
+  regval = getreg32(BL602_SWRST_CFG2);
+  regval &= ~(SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET |
+        SWRST_CFG2_CTRL_PWRON_RST);
+  putreg32(regval, BL602_SWRST_CFG2);
+
+  regval = getreg32(BL602_SWRST_CFG2);
+  regval &= ~(SWRST_CFG2_CTRL_SYS_RESET | SWRST_CFG2_CTRL_CPU_RESET |
+        SWRST_CFG2_CTRL_PWRON_RST);
+  regval |= mask;
+  putreg32(regval, BL602_SWRST_CFG2);
+#else
+  /* When perform reset before, MUST disable interrupt */
+
+  asm volatile("csrci mstatus, 8");
+
+  ((bl602_romdrv_reset_por)(*((uint32_t *)(0x210108c4))))();

Review comment:
       Can we call the reset api with the mask argument to select which reset we want to call (sys, cpu, pwron) and then drop the code I added here that has been disabled?




----------------------------------------------------------------
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



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

Posted by GitBox <gi...@apache.org>.
Virus-V commented on a change in pull request #2991:
URL: https://github.com/apache/incubator-nuttx/pull/2991#discussion_r591098044



##########
File path: arch/risc-v/src/bl602/bl602_netdev.c
##########
@@ -0,0 +1,2103 @@
+/****************************************************************************
+ * 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 <nuttx/signal.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;
+
+static wifi_conf_t g_conf =
+{
+  .country_code = "CN",

Review comment:
       We will add instructions for configuring the country code next. 




----------------------------------------------------------------
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