/****************************************************************************
 * drivers/net/wifi_sim.c
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 <debug.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <netinet/if_ether.h>

#include <nuttx/compiler.h>
#include <nuttx/kmalloc.h>
#include <nuttx/net/ip.h>
#include <nuttx/wireless/ieee80211/ieee80211.h>
#include <nuttx/virtio/virtio.h>
#include <netpacket/netlink.h>
#include <nuttx/net/netlink.h>

#include <nuttx/net/wifi_sim.h>
#include <nuttx/fs/fs.h>

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define BSS_FILE_NAME "bss"
#define BSS_FILE_PATH CONFIG_WIFI_SIM_CONFDIR "/" BSS_FILE_NAME

#define SSID_MAX_LEN 32

#define IEEE80211_CAP_PRIVACY 0x0010

/* Maximum number of supported rates (from both Supported Rates and Extended
 * Supported Rates IEs).
 */

#define WLAN_SUPP_RATES_MAX 32

#define WLAN_DEFAULT_TXPOWER 20

#define LOWERDEV2WIFIDEV(dev) \
  ((FAR struct wifi_sim_s *)((FAR struct wifi_sim_lowerhalf_s *)dev)->wifi)

#define CHAN2G(freq)              \
  {                               \
    .band = WLAN_80211_BAND_2GHZ, \
    .center_freq = (freq),        \
  }

#define CHAN5G(freq)              \
  {                               \
    .band = WLAN_80211_BAND_5GHZ, \
    .center_freq = (freq),        \
  }

/****************************************************************************
 * Private Types
 ****************************************************************************/

enum WLAN_ALG_E
{
  WLAN_ALG_NONE,
  WLAN_ALG_WEP,
  WLAN_ALG_TKIP,
  WLAN_ALG_CCMP,
  WLAN_ALG_BIP_CMAC_128,
  WLAN_ALG_GCMP,
  WLAN_ALG_SMS4,
  WLAN_ALG_KRK,
  WLAN_ALG_GCMP_256,
  WLAN_ALG_CCMP_256,
  WLAN_ALG_BIP_GMAC_128,
  WLAN_ALG_BIP_GMAC_256,
  WLAN_ALG_BIP_CMAC_256
};

enum WLAN_HW_MODE_E
{
  WLAN_HW_MODE_IEEE80211B,
  WLAN_HW_MODE_IEEE80211G,
  WLAN_HW_MODE_IEEE80211A,
  WLAN_HW_MODE_IEEE80211AD,
  WLAN_HW_MODE_IEEE80211ANY,
  WLAN_HW_MODES
};

/* enum WLAN_CHAN_WIDTH_E - Channel width definitions */

enum WLAN_CHAN_WIDTH_E
{
  WLAN_CHAN_WIDTH_20_NOHT,
  WLAN_CHAN_WIDTH_20,
  WLAN_CHAN_WIDTH_40,
  WLAN_CHAN_WIDTH_80,
  WLAN_CHAN_WIDTH_80P80,
  WLAN_CHAN_WIDTH_160,
  WLAN_CHAN_WIDTH_2160,
  WLAN_CHAN_WIDTH_4320,
  WLAN_CHAN_WIDTH_6480,
  WLAN_CHAN_WIDTH_8640,
  WLAN_CHAN_WIDTH_320,
  WLAN_CHAN_WIDTH_UNKNOWN
};

enum WLAN_80211_BAND_E
{
  WLAN_80211_BAND_2GHZ,
  WLAN_80211_BAND_5GHZ,
  WLAN_80211_BAND_6GHZ,
  WLAN_80211_BAND_NUMS
};

enum WLAN_SCAN_STATE_E
{
  WLAN_SCAN_STATE_SCANNING,
  WLAN_SCAN_STATE_DONE,
};

enum WLAN_STA_STATE_E
{
  WLAN_STA_STATE_INIT,
  WLAN_STA_STATE_CONNECTING,
  WLAN_STA_STATE_CONNECTED,
  WLAN_STA_STATE_DISCONNECTED,
};

enum WLAN_STA_CONNERR_E
{
  WLAN_STA_CONNERR_NO_BSS,
  WLAN_STA_CONNERR_NO_PSK,
  WLAN_STA_CONNERR_WRONG_KEY,
  WLAN_STA_CONNERR_FAILED,
};

/* for scan results */

struct iw_scan_result_s
{
  int      total_len;
  int       cur_len;
  FAR char *buf;                    /* iwr->u.data.pointer */
};

/* channel */

struct wlan_channel_s
{
  enum WLAN_80211_BAND_E band;
  uint32_t center_freq;
};

struct wifi_sim_bss_s
{
  char     bssid[ETH_ALEN];         /* bss BSSID */
  uint16_t capability;              /* Capability information */
  char     ssid[SSID_MAX_LEN + 1];  /* Capability information */
  uint8_t  ssid_len;                /* the length of ssid */
  int16_t  RSSI;                    /* receive signal strength (in dBm) */
  int8_t   phy_noise;               /* noise (in dBm) */
  int16_t  snr;                     /* average SNR of during frame reception */
  uint32_t freq;                    /* 802.11N BSS frequency */
  uint8_t  flags;                   /* flags */
  char     password[64];            /* password */
  char     security[128];           /* security type */
};

struct wifi_sim_sta_info_s
{
  uint16_t aid;
  char     mac_addr[ETH_ALEN];
  uint16_t capability;              /* Capability information */
  int      auth_alg;
  uint8_t  supported_rates[WLAN_SUPP_RATES_MAX];
  int      supported_rates_len;
  uint8_t  qosinfo;
  int16_t  RSSI;                    /* receive signal strength (in dBm) */
  uint32_t connected_time;          /* units: seconds */
  int16_t  deauth_reason;
  int16_t  disassoc_reason;
};

struct wifi_sim_s
{
  FAR struct netdev_lowerhalf_s *lower;

  char     ssid[SSID_MAX_LEN + 1];
  char     bssid[ETH_ALEN];
  uint8_t  mode;                        /* IW_MODE_INFRA/ IW_MODE_MASTER */
  enum WLAN_HW_MODE_E    hw_mode;       /* for hw mode */
  enum WLAN_CHAN_WIDTH_E band;
  uint16_t channel;
  uint32_t freq;
  char     password[64];
  int      key_mgmt;
  int      proto;
  int      auth_alg;
  int      auth_type;
  int      pairwise_chiper;
  int      group_cipher;

  uint32_t bitrate;
  int32_t  txpower;
  char     country[4];
  int8_t   sensitivity;

  bool     psk_flag;                    /* for psk, 0: unset, 1: set */
  uint8_t  ssid_flag;                   /* for ssid, 0: unset, 1: set ssid
                                         *           2: set bssid  */
  enum WLAN_STA_STATE_E state;
  enum WLAN_SCAN_STATE_E scan_state;
  uint16_t status_code;                 /* status code */
  uint16_t reason_code;                 /* deauth and disassoc reason */
  uint16_t error_code;                  /* connect error reason */

  FAR struct wifi_sim_bss_s *connected_ap;  /* for sta mode */
  mutex_t ap_lock;                          /* mutex to protect connected_ap */
};

/* for wireless event */

struct wireless_event_s
{
  struct nlmsghdr  hdr;                 /* netlink message header */
  struct ifinfomsg iface;               /* interface info */

  struct rtattr    attrevent;           /* IFLA_WIRELESS */
  struct iw_event  event;               /* wireless event */
};

struct wireless_event_list_s
{
  sq_entry_t flink;
  struct wireless_event_s payload;
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int wifidriver_connect(FAR struct netdev_lowerhalf_s *dev);
static int wifidriver_disconnect(FAR struct netdev_lowerhalf_s *dev);
static int wifidriver_essid(FAR struct netdev_lowerhalf_s *dev,
                            FAR struct iwreq *iwr, bool set);
static int wifidriver_bssid(FAR struct netdev_lowerhalf_s *dev,
                            FAR struct iwreq *iwr, bool set);
static int wifidriver_passwd(FAR struct netdev_lowerhalf_s *dev,
                             FAR struct iwreq *iwr, bool set);
static int wifidriver_mode(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set);
static int wifidriver_auth(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set);
static int wifidriver_freq(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set);
static int wifidriver_bitrate(FAR struct netdev_lowerhalf_s *dev,
                              FAR struct iwreq *iwr, bool set);
static int wifidriver_txpower(FAR struct netdev_lowerhalf_s *dev,
                              FAR struct iwreq *iwr, bool set);
static int wifidriver_country(FAR struct netdev_lowerhalf_s *dev,
                              FAR struct iwreq *iwr, bool set);
static int wifidriver_sensitivity(FAR struct netdev_lowerhalf_s *dev,
                                  FAR struct iwreq *iwr, bool set);
static int wifidriver_scan(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set);
static int wifidriver_range(FAR struct netdev_lowerhalf_s *dev,
                            FAR struct iwreq *iwr);

static int wifidriver_get_bssinfo(FAR struct wifi_sim_bss_s *bss_info,
                                  FAR char *buf, int len);
static int wifidriver_start_disconnect(FAR struct wifi_sim_s *wifidev);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct wlan_channel_s wifi_sim_channels_2ghz[] =
{
  CHAN2G(2412), /* Channel 1 */
  CHAN2G(2417), /* Channel 2 */
  CHAN2G(2422), /* Channel 3 */
  CHAN2G(2427), /* Channel 4 */
  CHAN2G(2432), /* Channel 5 */
  CHAN2G(2437), /* Channel 6 */
  CHAN2G(2442), /* Channel 7 */
  CHAN2G(2447), /* Channel 8 */
  CHAN2G(2452), /* Channel 9 */
  CHAN2G(2457), /* Channel 10 */
  CHAN2G(2462), /* Channel 11 */
  CHAN2G(2467), /* Channel 12 */
  CHAN2G(2472), /* Channel 13 */
  CHAN2G(2484), /* Channel 14 */
};

static const struct wlan_channel_s wifi_sim_channels_5ghz[] =
{
  CHAN5G(5180), /* Channel 36 */
  CHAN5G(5200), /* Channel 40 */
  CHAN5G(5220), /* Channel 44 */
  CHAN5G(5240), /* Channel 48 */
  CHAN5G(5260), /* Channel 52 */
  CHAN5G(5280), /* Channel 56 */
  CHAN5G(5300), /* Channel 60 */
  CHAN5G(5320), /* Channel 64 */
  CHAN5G(5745), /* Channel 149 */
  CHAN5G(5765), /* Channel 153 */
  CHAN5G(5785), /* Channel 157 */
  CHAN5G(5805), /* Channel 161 */
  CHAN5G(5825), /* Channel 165 */
};

static const struct wireless_ops_s g_iw_ops =
{
  wifidriver_connect,
  wifidriver_disconnect,
  wifidriver_essid,
  wifidriver_bssid,
  wifidriver_passwd,
  wifidriver_mode,
  wifidriver_auth,
  wifidriver_freq,
  wifidriver_bitrate,
  wifidriver_txpower,
  wifidriver_country,
  wifidriver_sensitivity,
  wifidriver_scan,
  wifidriver_range
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/* utils */

static int mac_addr_a2n(FAR unsigned char *mac_addr, FAR char *arg)
{
  int i;

  for (i = 0; i < ETH_ALEN ; i++)
    {
      int temp;
      FAR char *cp = strchr(arg, ':');

      if (cp)
        {
          *cp = 0;
          cp++;
        }

      if (sscanf(arg, "%x", &temp) != 1)
        {
          return -EINVAL;
        }

      if (temp < 0 || temp > 255)
        {
          return -EINVAL;
        }

      mac_addr[i] = temp;
      if (!cp)
        {
          break;
        }

      arg = cp;
    }

  if (i < ETH_ALEN - 1)
    {
      return -EINVAL;
    }

  return OK;
}

static void mac_addr_n2a(FAR char *mac_addr, FAR unsigned char *arg)
{
  int i;
  int l;

  for (l = 0, i = 0; i < ETH_ALEN; i++)
    {
      if (i == 0)
        {
          sprintf(mac_addr + l, "%02x", arg[i]);
          l += 2;
        }
      else
        {
          sprintf(mac_addr + l, ":%02x", arg[i]);
          l += 3;
        }
    }
}

/* wifi interfaces */

static int wifi_send_event(FAR struct wifi_sim_s *wifidev,
                           unsigned int cmd, FAR union iwreq_data *wrqu)
{
  FAR struct net_driver_s *dev = &wifidev->lower->netdev;
  FAR struct wireless_event_list_s *alloc;
  FAR struct wireless_event_s *wev;

  DEBUGASSERT(dev != NULL);

  int up = IFF_IS_UP(dev->d_flags);

  /* Allocate the response buffer */

  alloc = (FAR struct wireless_event_list_s *)
          kmm_zalloc(RTA_SPACE(sizeof(struct wireless_event_list_s)));
  if (alloc == NULL)
    {
      nerr("ERROR: Failed to allocate wifi event buffer.\n");
      return -ENOMEM;
    }

  /* Initialize the response buffer */

  wev                   = &alloc->payload;

  wev->hdr.nlmsg_len    = sizeof(struct wireless_event_s);
  wev->hdr.nlmsg_type   = up ? RTM_NEWLINK : RTM_DELLINK;
  wev->hdr.nlmsg_flags  = 0;
  wev->hdr.nlmsg_seq    = 0;
  wev->hdr.nlmsg_pid    = 0;

  wev->iface.ifi_family = AF_UNSPEC;
  wev->iface.ifi_type   = ARPHRD_IEEE80211;
#ifdef CONFIG_NETDEV_IFINDEX
  wev->iface.ifi_index  = dev->d_ifindex;
#endif
  wev->iface.ifi_flags  = dev->d_flags;
  wev->iface.ifi_change = 0;

  /* add wireless event info */

  wev->attrevent.rta_len  = RTA_SPACE(sizeof(struct iw_event));
  wev->attrevent.rta_type = IFLA_WIRELESS;
  wev->event.len          = sizeof(union iwreq_data);
  wev->event.cmd          = cmd;

  memset(&wev->event.u, 0, sizeof(union iwreq_data));
  memcpy(&wev->event.u, ((FAR char *) wrqu), sizeof(union iwreq_data));

  netlink_add_broadcast(RTNLGRP_LINK,
                        (FAR struct netlink_response_s *)alloc);
  return OK;
}

static int read_bss_config(FAR char *buf, size_t len, FAR char *path)
{
  struct file filep;
  int ret;

  ret = file_open(&filep, path, O_RDONLY);
  if (ret < 0)
    {
      nerr("open error");
      return ret;
    }

  ret = file_read(&filep, buf, len);
  if (ret == len)
    {
      ret = -E2BIG;
    }
  else if (ret > 0)
    {
      buf[ret] = '\0';
      ninfo("content: %s", buf);
    }

  file_close(&filep);

  return ret;
}

#if 0
uint32_t wifi_chan_to_freq_mhz(int chan, enum WLAN_80211_BAND_E band)
{
  /* see 802.11 17.3.8.3.2 and Annex J
   * there are overlapping channel numbers in 5GHz and 2GHz bands
   */

  if (chan <= 0)
    {
      return 0; /* not supported */
    }

  switch (band)
    {
      case WLAN_80211_BAND_2GHZ:
          if (chan == 14)
            {
              return (2484);
            }
          else if (chan < 14)
            {
              return (2407 + chan * 5);
            }
          break;
      case WLAN_80211_BAND_5GHZ:
          if (chan >= 182 && chan <= 196)
            {
              return (4000 + chan * 5);
            }
          else
            {
              return (5000 + chan * 5);
            }
          break;
      case WLAN_80211_BAND_6GHZ:

          /* see 802.11ax D6.1 27.3.23.2 */

          if (chan == 2)
            {
              return (5935);
            }

          if (chan <= 233)
            {
              return (5950 + chan * 5);
            }
          break;
      default:
          break;
    }

  return 0; /* not supported */
}
#endif

static int freq_to_channel(uint16_t freq)
{
  int channel = 0;

  /* If the freq is a valid channel, we think that
   * user wants to directly configure the channel.
   */

  if ((freq >= 1 && freq <= 14) ||
      (freq >= 36 && freq <= 165) ||
      (freq >= 182 && freq <= 196))
    {
      channel = freq;
      return channel;
    }

  if (freq >= 2412 && freq <= 2484)
    {
      if (freq == 2484)
        {
          channel = 14;
        }
      else
        {
          channel = freq - 2407;
          if (channel % 5)
            {
              return OK;
            }

          channel /= 5;
        }

      return channel;
    }

  if (freq >= 5005 && freq < 5900)
    {
      if (freq % 5)
        {
          return OK;
        }

      channel = (freq - 5000) / 5;
      return channel;
    }

  if (freq >= 4905 && freq < 5000)
    {
      if (freq % 5)
        {
          return OK;
        }

      channel = (freq - 4000) / 5;
      return channel;
    }

  return OK;
}

static FAR const char *get_cipherstr(int cipher)
{
  switch (cipher)
    {
      case IW_AUTH_CIPHER_NONE:
        return "NONE";

      case IW_AUTH_CIPHER_WEP40:
        return "WEP40";

      case IW_AUTH_CIPHER_WEP104:
        return "WEP104";

      case IW_AUTH_CIPHER_TKIP:
        return "TKIP";

      case IW_AUTH_CIPHER_CCMP:
        return "CCMP";

      case IW_AUTH_CIPHER_AES_CMAC:
        return "AES-128-CMAC";

      default:
        nerr("ERROR: Failed to transfer wifi cipher: %d", cipher);
        return NULL;
    }
}

static FAR const char *get_authstr(int auth)
{
  switch (auth)
    {
      case IW_AUTH_WPA_VERSION_DISABLED:
        return NULL;

      case IW_AUTH_WPA_VERSION_WPA:
        return "WPA";

      case IW_AUTH_WPA_VERSION_WPA2:
        return "WPA2";
      default:
        nerr("ERROR: Failed to transfer wifi auth: %d", auth);
        return NULL;
    }
}

/* implement iw_ops */

static FAR struct wifi_sim_bss_s *select_bss(FAR struct wifi_sim_s *sta,
                                             FAR char *bss_buf)
{
  char bss[256];
  FAR char *s;
  FAR char *p;
  struct wifi_sim_bss_s bss_info;
  FAR struct wifi_sim_bss_s *sbss = NULL;
  bool bss_found = false;

  for (p = bss_buf; *p != '\0'; p++)
    {
      s = p;
      while (*p != '\n')
        {
          p++;
        }

      memset(bss, 0, sizeof(bss));
      memcpy(bss, s, p - s + 1);
      wifidriver_get_bssinfo(&bss_info, bss, strlen(bss));

      /* compare bssid or essid, and security */

      if ((sta->ssid_flag == 2 && !memcmp(sta->bssid, bss_info.bssid, 6)) ||
          (sta->ssid_flag == 1 && !memcmp(sta->ssid, bss_info.ssid, 32)))
        {
          /* cmp security */

          if (sta->psk_flag == 0)
            {
              if (!strstr(bss_info.security, "WPA") &&
                  !strstr(bss_info.security, "WEP"))
                {
                  bss_found = true;
                }
              else
                {
                  nerr("security doesn't match");
                }

              break;
            }
          else if (get_cipherstr(sta->pairwise_chiper) &&
                   strstr(bss_info.security,
                          get_cipherstr(sta->pairwise_chiper)))
            {
              bss_found = true;
              break;
            }
          else
            {
              nerr("security doesn't match");
            }
        }
    }

  if (bss_found)
    {
      sbss = malloc(sizeof(struct wifi_sim_bss_s));
      if (sbss == NULL)
        {
          nerr("select bss  malloc failed!");
          return NULL;
        }

      memcpy(sbss, &bss_info, sizeof(struct wifi_sim_bss_s));
    }
  else
    {
      nerr("No BSS match.");
    }

  return sbss;
}

static int verify_password(FAR struct wifi_sim_s *sta,
                           FAR struct wifi_sim_bss_s *bss)
{
  int ret = ERROR;

  if (sta->psk_flag == 0)
    {
      ret = OK;
    }
  else if (!strncmp(sta->password, bss->password, sizeof(sta->password)))
    {
      ret = OK;
    }

  return ret;
}

static int get_bss_from_file(FAR char **rbuf)
{
  int ret;
  int size = 4096;
  FAR char *p;

  *rbuf = malloc(size * sizeof(char));
  if (*rbuf == NULL)
    {
      nerr("malloc failed!\n");
      return -ENOMEM;
    }

redo:
  ret = read_bss_config(*rbuf, size, BSS_FILE_PATH);
  if (ret == -E2BIG)
    {
      size += 1024;
      p = realloc(*rbuf, size);
      if (p == NULL)
        {
          nerr("read bss faied in realloc!\n");
          free(*rbuf);
          *rbuf = NULL;
          return -ENOMEM;
        }

      *rbuf = p;
      goto redo;
    }
  else if (ret < 0)
    {
      nerr("read bss failed\n");
      return ret;
    }

  (*rbuf)[ret] = '\0';

  return ret;
}

static bool wifidriver_sta_is_connected(FAR struct wifi_sim_s *wifidev)
{
  return (wifidev->state == WLAN_STA_STATE_CONNECTED);
}

static int wifidriver_start_connect(FAR struct wifi_sim_s *wifidev)
{
  int ret;
  FAR char *bss_buf = NULL;
  FAR struct wifi_sim_bss_s *bss_info;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          /* If wlan is connected, should be disconnect before connectting. */

          /* 1. check and disconnect */

          if (wifidev->state == WLAN_STA_STATE_CONNECTED)
            {
              wifidriver_start_disconnect(wifidev);
              wifidev->state = WLAN_STA_STATE_CONNECTING;
            }

          /* 2. get_bss, match (bssid/essid, security) */

          ret = get_bss_from_file(&bss_buf);
          if (ret < 0 || bss_buf == NULL)
            {
              goto error;
            }

          bss_info = select_bss(wifidev, bss_buf);
          if (bss_info == NULL)
            {
              nerr("select no bss.");
              wifidev->error_code = WLAN_STA_CONNERR_NO_BSS;

              ret = ERROR;
              goto error;
            }

          /* 3. verify the password and connect to the bss */

          ret = verify_password(wifidev, bss_info);
          if (ret == OK)
            {
              union iwreq_data wrqu;

              nxmutex_lock(&wifidev->ap_lock);
              wifidev->state        = WLAN_STA_STATE_CONNECTED;
              wifidev->status_code  = WLAN_STATUS_SUCCESS;
              wifidev->connected_ap = bss_info;

              memset(&wrqu, 0, sizeof(wrqu));
              memcpy(wrqu.ap_addr.sa_data, bss_info->bssid, ETH_ALEN);
              wifi_send_event(wifidev, SIOCGIWAP, &wrqu);

              /* If connect the AP with the bssid, copy the essid from bss */

              if (wifidev->ssid_flag == 2)
                {
                  memcpy(wifidev->ssid, bss_info->ssid,
                         strlen(bss_info->ssid));
                }

              nxmutex_unlock(&wifidev->ap_lock);
            }
          else
            {
              wifidev->reason_code = WLAN_REASON_DEAUTH_LEAVING;
              wifidev->error_code  = WLAN_STA_CONNERR_WRONG_KEY;
              free(bss_info);

              ret = WLAN_REASON_CIPHER_SUITE_REJECTED;
            }

error:
          if (bss_buf)
            {
              free(bss_buf);
            }

          if (ret != OK)
            {
              wifidev->state = WLAN_STA_STATE_INIT;
            }

          wifidev->ssid_flag = 0;

          /* Enshure that the default is non-encrypted. */

          if (wifidev->psk_flag)
            {
              wifidev->psk_flag = 0;
            }
        }
        break;

      case IW_MODE_MASTER:
      default:
        ret = -EINVAL;
        break;
    }

  return ret;
}

static int wifidriver_start_disconnect(FAR struct wifi_sim_s *wifidev)
{
  union iwreq_data wrqu;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          nxmutex_lock(&wifidev->ap_lock);
          if (wifidev->state == WLAN_STA_STATE_CONNECTED)
            {
              wifidev->state = WLAN_STA_STATE_DISCONNECTED;

              /* free the connected_ap */

              free(wifidev->connected_ap);

              memset(&wrqu, 0, sizeof(wrqu));
              wrqu.ap_addr.sa_family = ARPHRD_ETHER;
              wifi_send_event(wifidev, SIOCGIWAP, &wrqu);
            }

          nxmutex_unlock(&wifidev->ap_lock);
          netdev_lower_carrier_off(wifidev->lower);

          if (wifidev->psk_flag == 0)
            {
              memset(wifidev->password, 0, sizeof(wifidev->password));
              wifidev->pairwise_chiper = IW_AUTH_CIPHER_NONE;
            }

          wifidev->state = WLAN_STA_STATE_INIT;
        }
        break;

      case IW_MODE_MASTER:
      default:
        break;
    }

  return OK;
}

static int wifidriver_get_mode(FAR struct wifi_sim_s *wifidev,
                               FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          pwrq->u.mode = wifidev->mode;
          ninfo("get %s mode", wifidev->lower->netdev.d_ifname);
        }
        break;

      default:
        return -EINVAL;
    }

  return OK;
}

static int wifidriver_set_mode(FAR struct wifi_sim_s *wifidev,
                               FAR struct iwreq *pwrq)
{
  switch (pwrq->u.mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          wifidev->mode = pwrq->u.mode;

          /* default country code CN */

          memcpy(wifidev->country, "CN", 2);
        }
        break;

      default:
        return -EINVAL;
    }

  return OK;
}

static int wifidriver_set_country(FAR struct wifi_sim_s *wifidev,
                                  FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          memcpy(wifidev->country, pwrq->u.data.pointer,
                 pwrq->u.data.length);
          ninfo("set country is %s\n", wifidev->country);
        }
        break;

      default:
        return -EINVAL;
    }

  return OK;
}

static int wifidriver_get_country(FAR struct wifi_sim_s *wifidev,
                                  FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          pwrq->u.data.length = strlen(wifidev->country);
          memcpy(pwrq->u.data.pointer, wifidev->country,
                 pwrq->u.data.length);
          ninfo("set country is %s\n", wifidev->country);
        }
        break;

      default:
        return -EINVAL;
    }

  return OK;
}

static int wifidriver_get_sensitivity(FAR struct wifi_sim_s *wifidev,
                                      FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          nxmutex_lock(&wifidev->ap_lock);
          if (wifidriver_sta_is_connected(wifidev))
            {
              pwrq->u.sens.value = -wifidev->connected_ap->RSSI;
            }

          nxmutex_unlock(&wifidev->ap_lock);
          ninfo("get rssi is %" PRId32 "\n", pwrq->u.sens.value);
        }
        break;

      default:
        return -EINVAL;
    }

  return OK;
}

int wifidriver_set_freq(FAR struct wifi_sim_s *wifidev,
                        FAR struct iwreq *pwrq)
{
  int ret;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          ret = freq_to_channel(pwrq->u.freq.m);
          if (ret > 0)
            {
              wifidev->channel = ret;
              wifidev->freq = pwrq->u.freq.m;
              ninfo("set channel is %d\n", ret);
            }
        }
        break;

      default:
        ret = -EINVAL;
        break;
    }

  return ret;
}

static int wifidriver_get_freq(FAR struct wifi_sim_s *wifidev,
                               FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          nxmutex_lock(&wifidev->ap_lock);
          if (wifidriver_sta_is_connected(wifidev))
            {
              pwrq->u.freq.flags = IW_FREQ_FIXED;
              pwrq->u.freq.e     = 0;
              pwrq->u.freq.m     = wifidev->connected_ap->freq;
            }
          else
            {
              pwrq->u.freq.flags = IW_FREQ_AUTO;
              pwrq->u.freq.e     = 0;
              pwrq->u.freq.m     = 2412;
            }

          nxmutex_unlock(&wifidev->ap_lock);
        }
        break;

      case IW_MODE_MASTER:
        {
          pwrq->u.freq.flags = IW_FREQ_FIXED;
          pwrq->u.freq.e     = 0;
          pwrq->u.freq.m     = wifidev->freq;
        }
        break;

      default:
        return -EINVAL;
    }

  return OK;
}

int wifidriver_set_txpower(FAR struct wifi_sim_s *wifidev,
                           FAR struct iwreq *pwrq)
{
  int ret = OK;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        wifidev->txpower = pwrq->u.txpower.value;
        break;

      default:
        ret = -EINVAL;
        break;
    }

  return ret;
}

static int wifidriver_get_txpower(FAR struct wifi_sim_s *wifidev,
                                  FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          pwrq->u.txpower.value    = wifidev->txpower ? wifidev->txpower :
                                     WLAN_DEFAULT_TXPOWER;

          pwrq->u.txpower.fixed    = 0;
          pwrq->u.txpower.disabled = 0;
          pwrq->u.txpower.flags    = IW_TXPOW_DBM;
        }
        break;

      case IW_MODE_MASTER:
        break;
      default:
        return -EINVAL;
        break;
    }

  return OK;
}

static int wifidriver_get_bitrate(FAR struct wifi_sim_s *wifidev,
                                  FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        if (wifidev->state != WLAN_STA_STATE_CONNECTED)
          {
            pwrq->u.bitrate.fixed = IW_FREQ_AUTO;
          }
        else
          {
            pwrq->u.bitrate.fixed = IW_FREQ_FIXED;

            /* default 2streams 40MHz */

            pwrq->u.bitrate.value = 150;
          }
        break;

      case IW_MODE_MASTER:
      default:
        return -EINVAL;
        break;
    }

  return OK;
}

static int wifidriver_get_range(FAR struct wifi_sim_s *wifidev,
                                FAR struct iwreq *pwrq)
{
  int k;
  int i;
  FAR struct iw_range *range = (FAR struct iw_range *)pwrq->u.data.pointer;

  /* default in china. */

  /* Add 2.4G channel */

  range->num_frequency = nitems(wifi_sim_channels_2ghz);
  for (k = 1; k <= range->num_frequency; k++)
    {
      range->freq[k - 1].i = k;
      range->freq[k - 1].e = 0;
      range->freq[k - 1].m = wifi_sim_channels_2ghz[k - 1].center_freq;
    }

  /* Add 5GHz channels */

  range->num_frequency += nitems(wifi_sim_channels_5ghz);
  for (i = 0; k <= range->num_frequency; k++)
    {
      range->freq[k - 1].i = k;
      range->freq[k - 1].e = 0;
      range->freq[k - 1].m = wifi_sim_channels_5ghz[i++].center_freq;
    }

  return OK;
}

static int wifidriver_start_scan(FAR struct wifi_sim_s *wifidev,
                                 FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          wifidev->scan_state = WLAN_SCAN_STATE_SCANNING;
          ninfo("scan %s\n", BSS_FILE_PATH);
        }
        break;

      case IW_MODE_MASTER:
      default:
        return -EINVAL;
        break;
    }

  return OK;
}

static int copy_scan_results(FAR struct iw_scan_result_s *scan_req,
                             FAR struct wifi_sim_bss_s *info)
{
  int need_len;
  FAR char *pointer;
  FAR struct iw_event *iwe;

  need_len = IW_EV_LEN(ap_addr) + IW_EV_LEN(qual) +
             IW_EV_LEN(freq) + IW_EV_LEN(data) + IW_EV_LEN(essid);

  if (scan_req->cur_len + need_len > scan_req->total_len)
    {
      scan_req->cur_len += need_len;
      return -E2BIG;
    }

  /* Copy scan result */

  pointer = scan_req->buf + scan_req->cur_len;

  /* 1.Copy BSSID */

  iwe                      = (FAR struct iw_event *)pointer;
  iwe->cmd                 = SIOCGIWAP;
  iwe->u.ap_addr.sa_family = ARPHRD_ETHER;
  memcpy(&iwe->u.ap_addr.sa_data, info->bssid, IFHWADDRLEN);
  iwe->len                 = IW_EV_LEN(ap_addr);
  pointer                 += iwe->len;

  /* 2.Copy ESSID */

  iwe                  = (FAR struct iw_event *)pointer;
  iwe->cmd             = SIOCGIWESSID;
  iwe->u.essid.flags   = 0;
  iwe->u.essid.length  = MIN(info->ssid_len, 32);
  iwe->u.essid.pointer = (FAR void *)sizeof(iwe->u.essid);
  memcpy(&iwe->u.essid + 1, info->ssid, iwe->u.essid.length);
  iwe->len             = IW_EV_LEN(essid) + ((iwe->u.essid.length + 3) & ~3);
  pointer             += iwe->len;

  /* 3.Copy link quality info */

  iwe                  = (FAR struct iw_event *)pointer;
  iwe->cmd             = IWEVQUAL;
  iwe->u.qual.qual     = info->snr;
  iwe->u.qual.level    = info->RSSI;
  iwe->u.qual.noise    = info->phy_noise;
  iwe->u.qual.updated  = IW_QUAL_DBM | IW_QUAL_ALL_UPDATED;
  iwe->len             = IW_EV_LEN(qual);
  pointer             += iwe->len;

  /* 4.Copy AP control channel */

  iwe                  = (FAR struct iw_event *)pointer;
  iwe->cmd             = SIOCGIWFREQ;
  iwe->u.freq.e        = -1;
  iwe->u.freq.m        = info->freq * 10;
  iwe->len             = IW_EV_LEN(freq);
  pointer             += iwe->len;

  /* 5.Copy AP encryption mode */

  iwe                  = (FAR struct iw_event *)pointer;
  iwe->cmd             = SIOCGIWENCODE;
  iwe->u.data.flags    = info->capability & IEEE80211_CAP_PRIVACY ?
                         IW_ENCODE_ENABLED | IW_ENCODE_NOKEY :
                         IW_ENCODE_DISABLED;
  iwe->u.data.length   = 0;
  iwe->u.essid.pointer = NULL;
  iwe->len             = IW_EV_LEN(data);
  pointer             += iwe->len;

  scan_req->cur_len    = pointer - scan_req->buf;

  return OK;
}

static int wifidriver_get_bssinfo(FAR struct wifi_sim_bss_s *bss_info,
                                  FAR char *buf, int len)
{
  unsigned char bssid[ETH_ALEN];
  char str[128];
  FAR char *p = NULL;
  FAR char *s = NULL;
  int i = 0;

  memset(bss_info, 0, sizeof(*bss_info));
  for (i = 0, p = buf; p - buf < len; p++, i++)
    {
      ninfo("  var_idx%d: ", i);
      s = p;
      while (*p != '\n' && *p != ',' && *p != '\0')
        {
          p++;
        }

      memset(str, 0, sizeof(str));
      memcpy(str, s, p - s);
      ninfo("%s", str);

      switch (i)
        {
          case 0:
            {
              /* bssid */

              mac_addr_a2n(bssid, str);
              memcpy(bss_info->bssid, bssid, IFHWADDRLEN);
            }
            break;

          case 1:
            {
              /* freq */

              bss_info->freq = atoi(str);
            }
            break;

          case 2:
            {
              /* signal */

              bss_info->RSSI = atoi(str);
            }
            break;

          case 3:
            {
              /* security */

              bss_info->capability |= strlen(str) > strlen("[ESS]") ?
                                      IEEE80211_CAP_PRIVACY : 0x01;
              memcpy(bss_info->security, str, p - s);
            }
            break;

          case 4:
            {
              /* ssid */

              memcpy(bss_info->ssid, str, p - s);
              bss_info->ssid_len = p - s;
            }
            break;

          case 5:
            {
              /* password */

              memcpy(bss_info->password, str, p - s);
            }
            break;

          default:
              break;
        }
    }

  ninfo("\n");

  return OK;
}

static int get_scan_results(FAR struct wifi_sim_s *wifidev,
                            FAR struct iw_scan_result_s *scan_reqs)
{
  int ret;
  FAR char *rbuf;
  FAR char *p;
  FAR char *s;
  struct wifi_sim_bss_s bss_info;

  ret = get_bss_from_file(&rbuf);
  if (ret < 0)
    {
      if (rbuf)
        {
          free(rbuf);
        }

      return ret;
    }

  ret = -EAGAIN;
  for (p = rbuf; *p != '\0'; p++)
    {
      s = p;
      while (*p != '\n')
        {
          p++;
        }

      wifidriver_get_bssinfo(&bss_info, s, p - s + 1);
      ret = copy_scan_results(scan_reqs, &bss_info);
      if (ret < 0)
        {
          break;
        }
    }

  free(rbuf);

  return ret;
}

static int wifidriver_scan_result(FAR struct wifi_sim_s *wifidev,
                                  FAR struct iwreq *pwrq)
{
  struct iw_scan_result_s scan_req;
  int ret = 0;

  if (wifidev->mode == IW_MODE_MASTER)
    {
      return OK;
    }
  else if (wifidev->mode == IW_MODE_INFRA)
    {
      if (wifidev->scan_state == WLAN_SCAN_STATE_SCANNING)
        {
          wifidev->scan_state = WLAN_SCAN_STATE_DONE;
          return -EAGAIN;
        }

      scan_req.buf       = pwrq->u.data.pointer;
      scan_req.total_len = pwrq->u.data.length;
      scan_req.cur_len   = 0;

      ret = get_scan_results(wifidev, &scan_req);

      pwrq->u.data.length = scan_req.cur_len;
    }

  return ret;
}

static int wifidriver_set_essid(FAR struct wifi_sim_s *wifidev,
                                FAR struct iwreq *pwrq)
{
  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
      case IW_MODE_MASTER:
        {
          memset(wifidev->ssid, 0, sizeof(wifidev->ssid));
          memcpy(wifidev->ssid, pwrq->u.essid.pointer, pwrq->u.essid.length);
          wifidev->ssid_flag = 1;
          ninfo("set essid = %s\n", wifidev->ssid);
        }
        break;

      default:
        return -EINVAL;
        break;
    }

  return OK;
}

static int wifidriver_get_essid(FAR struct wifi_sim_s *wifidev,
                                FAR struct iwreq *pwrq)
{
  FAR struct iw_point *essid = &pwrq->u.essid;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          /* get essid */

          pwrq->u.essid.length = strlen(wifidev->ssid);
          memcpy(pwrq->u.essid.pointer, wifidev->ssid, pwrq->u.essid.length);

          ninfo("get essid = %s\n", wifidev->ssid);

          /* get state */

          if (wifidev->state == WLAN_STA_STATE_CONNECTED)
            {
              essid->flags = IW_ESSID_ON;
            }
          else
            {
              essid->flags = IW_ESSID_OFF;
            }
        }
        break;

      case IW_MODE_MASTER:
      default:
        return -EINVAL;
    }

  return OK;
}

static int wifidriver_set_bssid(FAR struct wifi_sim_s *wifidev,
                                FAR struct iwreq *pwrq)
{
  FAR struct sockaddr *sockaddr = &pwrq->u.ap_addr;
  FAR unsigned char *pdata = (FAR unsigned char *)sockaddr->sa_data;
  char bssid_str[20];
  int ret;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          memcpy(wifidev->bssid, pdata, ETH_ALEN);
          mac_addr_n2a(bssid_str, pdata);
          ninfo("set bssid = %s\n", bssid_str);

          wifidev->ssid_flag = 2;
          ret = OK;
        }
        break;

      case IW_MODE_MASTER:
        ret = -ENOSYS;
        break;

      default:
        ret = -EINVAL;
        break;
    }

  return ret;
}

static int wifidriver_get_bssid(FAR struct wifi_sim_s *wifidev,
                                FAR struct iwreq *pwrq)
{
  FAR struct sockaddr *sockaddr = &pwrq->u.ap_addr;
  FAR unsigned char *pdata = (FAR unsigned char *)sockaddr->sa_data;
  int ret;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          nxmutex_lock(&wifidev->ap_lock);
          if (wifidriver_sta_is_connected(wifidev))
            {
              memcpy(pdata, wifidev->connected_ap->bssid, ETH_ALEN);
              ret = OK;
            }
          else
            {
              ret = -ENOTTY;
            }

          nxmutex_unlock(&wifidev->ap_lock);
        }
        break;

      case IW_MODE_MASTER:
        ret = -ENOSYS;
        break;

      default:
        ret = -EINVAL;
        break;
    }

  return ret;
}

static int wifidriver_set_auth(FAR struct wifi_sim_s *wifidev,
                               FAR struct iwreq *pwrq)
{
  int flag  = pwrq->u.param.flags & IW_AUTH_INDEX;
  int value = pwrq->u.param.value;

  if (wifidev->mode != IW_MODE_INFRA)
    {
      return -ENOSYS;
    }

  switch (flag)
    {
      case IW_AUTH_WPA_VERSION:
        {
          /* record the value */

          wifidev->proto = value >> 1;
          wifidev->auth_type = value;

          ninfo("auth_type=%s\n", get_authstr(value));
        }
        break;

      case IW_AUTH_CIPHER_PAIRWISE:
        {
          /* record the value */

          wifidev->pairwise_chiper = value;

          ninfo("pairwise=%s\n", get_cipherstr(value));
        }
        break;

      default:
        nerr("ERROR: Unknown cmd %d\n", flag);
        return -ENOSYS;
    }

  return OK;
}

static int wifidriver_get_auth(FAR struct wifi_sim_s *wifidev,
                               FAR struct iwreq *pwrq)
{
  int flag = pwrq->u.param.flags & IW_AUTH_INDEX;

  switch (flag)
    {
      case IW_AUTH_WPA_VERSION:
        pwrq->u.param.value = wifidev->auth_type;
        break;
      case IW_AUTH_CIPHER_PAIRWISE:
        pwrq->u.param.value = wifidev->pairwise_chiper;
        break;
      default:
        nerr("ERROR: Unknown cmd %d\n", flag);
        return -ENOSYS;
    }

  return OK;
}

static int wifidriver_set_psk(FAR struct wifi_sim_s *wifidev,
                              FAR struct iwreq *pwrq)
{
  FAR struct iw_encode_ext *ext;
  int ret = OK;

  ext = (FAR struct iw_encode_ext *)pwrq->u.encoding.pointer;

  /* set auth_alg */

  switch (ext->alg)
    {
      case IW_ENCODE_ALG_TKIP:
      case IW_ENCODE_ALG_CCMP:
        break;
      case IW_ENCODE_ALG_NONE:
      case IW_ENCODE_ALG_WEP:
      default:
        return -ENOSYS;
    }

  wifidev->auth_alg = ext->alg;

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        {
          wifidev->auth_alg = ext->alg;
          memset(wifidev->password, 0, sizeof(wifidev->password));
          memcpy(wifidev->password, ext->key, ext->key_len);

          ninfo("psk=%s, key_len= %d, alg=%u\n", wifidev->password,
                ext->key_len, ext->alg);

          /* Set the psk flag for security ap. */

          wifidev->psk_flag = 1;
        }
        break;

      case IW_MODE_MASTER:
      default:
        ret = -ENOSYS;
        break;
    }

  return ret ;
}

static int wifidriver_get_psk(FAR struct wifi_sim_s *wifidev,
                              FAR struct iwreq *pwrq)
{
  FAR struct iw_encode_ext *ext;
  int ret = OK;
  int len;
  int size;

  ext = (FAR struct iw_encode_ext *)pwrq->u.encoding.pointer;
  len = pwrq->u.encoding.length - sizeof(*ext);

  switch (wifidev->mode)
    {
      case IW_MODE_INFRA:
        size = strnlen(wifidev->password, 64);
        if (len < size)
        {
          return -EINVAL;
        }
        else
        {
          ext->key_len = size;
          memcpy(ext->key, wifidev->password, ext->key_len);
          ext->alg = wifidev->auth_alg;
        }
        break;

      case IW_MODE_MASTER:
      default:
        ret = -ENOSYS;
        break;
    }

  return ret;
}

/* iw_ops */

static int wifidriver_connect(FAR struct netdev_lowerhalf_s *dev)
{
  int ret;

  ret = wifidriver_start_connect(LOWERDEV2WIFIDEV(dev));
  if (ret == OK)
    {
      netdev_lower_carrier_on(dev);
    }

  return ret;
}

static int wifidriver_disconnect(FAR struct netdev_lowerhalf_s *dev)
{
  return wifidriver_start_disconnect(LOWERDEV2WIFIDEV(dev));
}

static int wifidriver_essid(FAR struct netdev_lowerhalf_s *dev,
                            FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_set_essid(wifidev, iwr);
    }
  else
    {
      return wifidriver_get_essid(wifidev, iwr);
    }
}

static int wifidriver_bssid(FAR struct netdev_lowerhalf_s *dev,
                            FAR struct iwreq *iwr, bool set)
{
  int ret;
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      ret = wifidriver_set_bssid(wifidev, iwr);
    }
  else
    {
      ret = wifidriver_get_bssid(wifidev, iwr);
    }

  return ret;
}

static int wifidriver_passwd(FAR struct netdev_lowerhalf_s *dev,
                             FAR struct iwreq *iwr, bool set)
{
  if (set)
    {
      return wifidriver_set_psk(LOWERDEV2WIFIDEV(dev), iwr);
    }
  else
    {
      return wifidriver_get_psk(LOWERDEV2WIFIDEV(dev), iwr);
    }
}

static int wifidriver_mode(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_set_mode(wifidev, iwr);
    }
  else
    {
      return wifidriver_get_mode(wifidev, iwr);
    }
}

static int wifidriver_auth(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_set_auth(wifidev, iwr);
    }
  else
    {
      return wifidriver_get_auth(wifidev, iwr);
    }
}

static int wifidriver_freq(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_set_freq(wifidev, iwr);
    }
  else
    {
      return wifidriver_get_freq(wifidev, iwr);
    }
}

static int wifidriver_bitrate(FAR struct netdev_lowerhalf_s *dev,
                              FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return -ENOTTY;
    }
  else
    {
      return wifidriver_get_bitrate(wifidev, iwr);
    }
}

static int wifidriver_txpower(FAR struct netdev_lowerhalf_s *dev,
                              FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_set_txpower(wifidev, iwr);
    }
  else
    {
      return wifidriver_get_txpower(wifidev, iwr);
    }
}

static int wifidriver_country(FAR struct netdev_lowerhalf_s *dev,
                              FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_set_country(wifidev, iwr);
    }
  else
    {
      return wifidriver_get_country(wifidev, iwr);
    }
}

static int wifidriver_sensitivity(FAR struct netdev_lowerhalf_s *dev,
                                  FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return -ENOTTY;
    }
  else
    {
      return wifidriver_get_sensitivity(wifidev, iwr);
    }
}

static int wifidriver_scan(FAR struct netdev_lowerhalf_s *dev,
                           FAR struct iwreq *iwr, bool set)
{
  FAR struct wifi_sim_s *wifidev = LOWERDEV2WIFIDEV(dev);

  if (set)
    {
      return wifidriver_start_scan(wifidev, iwr);
    }
  else
    {
      return wifidriver_scan_result(wifidev, iwr);
    }
}

static int wifidriver_range(FAR struct netdev_lowerhalf_s *dev,
                            FAR struct iwreq *iwr)
{
  return wifidriver_get_range(LOWERDEV2WIFIDEV(dev), iwr);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: wifi_sim_init
 ****************************************************************************/

int wifi_sim_init(FAR struct wifi_sim_lowerhalf_s *netdev)
{
  FAR struct wifi_sim_s *priv;

  priv = kmm_zalloc(sizeof(*priv));
  if (priv == NULL)
    {
      nerr("virt wifi driver priv alloc failed\n");
      return -ENOMEM;
    }

  netdev->lower.iw_ops = &g_iw_ops;
  priv->lower          = &netdev->lower;
  netdev->wifi         = priv;

  priv->mode           = IW_MODE_AUTO;

  nxmutex_init(&priv->ap_lock);

  return OK;
}

/****************************************************************************
 * Name: wifi_sim_remove
 ****************************************************************************/

void wifi_sim_remove(FAR struct wifi_sim_lowerhalf_s *netdev)
{
  FAR struct wifi_sim_s *wifidev = (FAR struct wifi_sim_s *)netdev->wifi;

  if (wifidev)
    {
      if (wifidev->state == WLAN_STA_STATE_CONNECTED)
        {
          wifidriver_start_disconnect(wifidev);
        }

      nxmutex_destroy(&wifidev->ap_lock);
      kmm_free(wifidev);
    }
}

/****************************************************************************
 * Name: wifi_sim_connected
 ****************************************************************************/

bool wifi_sim_connected(FAR struct wifi_sim_lowerhalf_s *dev)
{
  FAR struct wifi_sim_s *wifidev = (FAR struct wifi_sim_s *)dev->wifi;

  if (wifidev->mode == IW_MODE_MASTER)
    {
      return true;
    }
  else if (wifidev->mode == IW_MODE_INFRA)
    {
      return wifidev->state == WLAN_STA_STATE_CONNECTED;
    }

  return false;
}