/**************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * 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); }