4fa5451595
Signed-off-by: liqinhui <liqinhui@xiaomi.com>
1987 lines
45 KiB
C
1987 lines
45 KiB
C
/****************************************************************************
|
|
* drivers/net/wifi_sim.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 <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,
|
|
};
|
|
|
|
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 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 */
|
|
};
|
|
|
|
/* 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;
|
|
|
|
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));
|
|
}
|
|
}
|
|
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:
|
|
{
|
|
if (wifidev->state == WLAN_STA_STATE_CONNECTED)
|
|
{
|
|
/* 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);
|
|
}
|
|
|
|
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:
|
|
{
|
|
if (wifidriver_sta_is_connected(wifidev))
|
|
{
|
|
pwrq->u.sens.value = -wifidev->connected_ap->RSSI;
|
|
}
|
|
|
|
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:
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
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:
|
|
{
|
|
if (wifidriver_sta_is_connected(wifidev))
|
|
{
|
|
memcpy(pdata, wifidev->connected_ap->bssid, ETH_ALEN);
|
|
ret = OK;
|
|
}
|
|
else
|
|
{
|
|
ret = -ENOTTY;
|
|
}
|
|
}
|
|
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;
|
|
|
|
ninfo("proto=%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)
|
|
{
|
|
switch (wifidev->mode)
|
|
{
|
|
case IW_MODE_INFRA:
|
|
break;
|
|
case IW_MODE_MASTER:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
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;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: wifi_sim_remove
|
|
****************************************************************************/
|
|
|
|
void wifi_sim_remove(FAR struct wifi_sim_lowerhalf_s *netdev)
|
|
{
|
|
FAR struct wifi_sim_s *sta = (FAR struct wifi_sim_s *)netdev->wifi;
|
|
|
|
if (sta && sta->state == WLAN_STA_STATE_CONNECTED)
|
|
{
|
|
wifidriver_start_disconnect(sta);
|
|
}
|
|
|
|
kmm_free(netdev->wifi);
|
|
}
|
|
|