diff options
Diffstat (limited to 'drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c')
-rw-r--r-- | drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c | 1201 |
1 files changed, 1201 insertions, 0 deletions
diff --git a/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c new file mode 100644 index 000000000000..de591f69d384 --- /dev/null +++ b/drivers/net/wireless/aic8800/aic8800_fdrv/aicwf_wext_linux.c @@ -0,0 +1,1201 @@ +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <net/netlink.h> +#include <linux/wireless.h> +#include <linux/nl80211.h> +#include <net/iw_handler.h> +#include <uapi/linux/if_arp.h> +#include <linux/vmalloc.h> +#include "aicwf_debug.h" +#include "rwnx_msg_tx.h" +#include "aicwf_wext_linux.h" +#include "rwnx_defs.h" + +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_PRIVACY (1<<4) + +#define IEEE80211_HT_CAP_SGI_20 0x0020 +#define IEEE80211_HT_CAP_SGI_40 0x0040 + +#define IEEE80211_HE_CH_BW_SET_160_80P80 1 << 4 +#define IEEE80211_HE_CH_BW_SET_160_IN_5G 1 << 3 +#define IEEE80211_HE_CH_BW_SET_40_AND_80_IN_5G 1 << 2 +#define IEEE80211_HE_CH_BW_SET_40_IN_2_4G 1 << 1 + +#if WIRELESS_EXT < 17 + #define IW_QUAL_QUAL_INVALID 0x10 + #define IW_QUAL_LEVEL_INVALID 0x20 + #define IW_QUAL_NOISE_INVALID 0x40 + #define IW_QUAL_QUAL_UPDATED 0x1 + #define IW_QUAL_LEVEL_UPDATED 0x2 + #define IW_QUAL_NOISE_UPDATED 0x4 +#endif + +#define MAX_WPA_IE_LEN (256) + + +/* 20/40/80, ShortGI, MCS Rate */ +const u16 VHT_MCS_DATA_RATE[3][2][30] = { + { + { + 13, 26, 39, 52, 78, 104, 117, 130, 156, 156, + 26, 52, 78, 104, 156, 208, 234, 260, 312, 312, + 39, 78, 117, 156, 234, 312, 351, 390, 468, 520 + }, /* Long GI, 20MHz */ + { + 14, 29, 43, 58, 87, 116, 130, 144, 173, 173, + 29, 58, 87, 116, 173, 231, 260, 289, 347, 347, + 43, 87, 130, 173, 260, 347, 390, 433, 520, 578 + } + }, /* Short GI, 20MHz */ + { + { + 27, 54, 81, 108, 162, 216, 243, 270, 324, 360, + 54, 108, 162, 216, 324, 432, 486, 540, 648, 720, + 81, 162, 243, 324, 486, 648, 729, 810, 972, 1080 + }, /* Long GI, 40MHz */ + { + 30, 60, 90, 120, 180, 240, 270, 300, 360, 400, + 60, 120, 180, 240, 360, 480, 540, 600, 720, 800, + 90, 180, 270, 360, 540, 720, 810, 900, 1080, 1200 + } + }, /* Short GI, 40MHz */ + { + { + 59, 117, 176, 234, 351, 468, 527, 585, 702, 780, + 117, 234, 351, 468, 702, 936, 1053, 1170, 1404, 1560, + 176, 351, 527, 702, 1053, 1404, 1580, 1755, 2106, 2340 + }, /* Long GI, 80MHz */ + { + 65, 130, 195, 260, 390, 520, 585, 650, 780, 867, + 130, 260, 390, 520, 780, 1040, 1170, 1300, 1560, 1734, + 195, 390, 585, 780, 1170, 1560, 1755, 1950, 2340, 2600 + } /* Short GI, 80MHz */ + } +}; + + +/*HE 20/40/80,MCS Rate */ +const u16 HE_MCS_DATA_RATE[3][30] = { + { + 9, 17, 26, 34, 52, 69, 77, 86, 103, 115, + 129, 143, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, /* 20MHz */ + { + 17, 34, 52, 69, 103, 138, 155, 172, 207, 229, + 258, 286, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, /* 40MHz */ + { + 36, 72, 108, 144, 216, 288, 324, 360, 432, 480, + 540, 601, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, /* 80MHz */ + +}; + + +#if WIRELESS_EXT >= 17 +struct iw_statistics iwstats; + +static struct iw_statistics *aicwf_get_wireless_stats(struct net_device *dev) +{ + int tmp_level = -100; + int tmp_qual = 0; + int tmp_noise = 0; + + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw; + struct rwnx_sta *sta = NULL; + + union rwnx_rate_ctrl_info *rate_info; + struct mm_get_sta_info_cfm cfm; + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + if(rwnx_vif->sta.ap){ + sta = rwnx_vif->sta.ap; + rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm); + rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info; + tmp_level = cfm.rssi; + } + + + iwstats.qual.level = tmp_level; + iwstats.qual.qual = tmp_qual; + iwstats.qual.noise = tmp_noise; + iwstats.qual.updated = 0x07; + iwstats.qual.updated = iwstats.qual.updated | IW_QUAL_DBM; + + return &iwstats; +} +#endif + + +static int aicwf_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw; + struct rwnx_sta *sta = NULL; + + union rwnx_rate_ctrl_info *rate_info; + struct mm_get_sta_info_cfm cfm; + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + if(rwnx_vif->sta.ap){ + sta = rwnx_vif->sta.ap; + rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm); + rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info; + + switch (rate_info->formatModTx) { + case FORMATMOD_NON_HT: + case FORMATMOD_NON_HT_DUP_OFDM: + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bg"); + break; + case FORMATMOD_HT_MF: + case FORMATMOD_HT_GF: + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11n"); + break; + case FORMATMOD_VHT: + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ac"); + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + case FORMATMOD_HE_MU: + case FORMATMOD_HE_SU: + case FORMATMOD_HE_ER: + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ax"); + break; +#else + //kernel not support he + case FORMATMOD_HE_MU: + case FORMATMOD_HE_SU: + case FORMATMOD_HE_ER: + snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11ax"); + break; +#endif + } + + + }else{ + snprintf(wrqu->name, IFNAMSIZ, "unassociated"); + } + + + return 0; +} + +static int aicwf_get_freq(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + if(rwnx_vif->sta.ap){ + wrqu->freq.m = rwnx_vif->sta.ap->center_freq * 100000; + wrqu->freq.e = 1; + wrqu->freq.i = ieee80211_frequency_to_channel(rwnx_vif->sta.ap->center_freq); + }else{ + wrqu->freq.m = 2412 * 100000; + wrqu->freq.e = 1; + wrqu->freq.i = 1; + } + + return 0; +} + +static int aicwf_get_mode(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + wrqu->mode = IW_MODE_AUTO; +#if 0 + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == _TRUE) + wrqu->mode = IW_MODE_INFRA; + else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == _TRUE) || + (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == _TRUE)) + + wrqu->mode = IW_MODE_ADHOC; + else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == _TRUE) + wrqu->mode = IW_MODE_MASTER; + else if (check_fwstate(pmlmepriv, WIFI_MONITOR_STATE) == _TRUE) + wrqu->mode = IW_MODE_MONITOR; + else + wrqu->mode = IW_MODE_AUTO; +#endif + + return 0; + +} + + +static int aicwf_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + u16 val = 0; + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + /* Let's try to keep this struct in the same order as in + * linux/include/wireless.h + */ + + /* TODO: See what values we can set, and remove the ones we can't + * set, or fill them with some default data. + */ + + /* ~5 Mb/s real (802.11b) */ + range->throughput = 5 * 1000 * 1000; + + /* TODO: Not used in 802.11b? + * range->min_nwid; Minimal NWID we are able to set */ + /* TODO: Not used in 802.11b? + * range->max_nwid; Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + /* range->old_num_channels; + * range->old_num_frequency; + * range->old_freq[6]; Filler to keep "version" at the same offset */ + + /* signal level threshold range */ + + /* Quality of link & SNR stuff */ + /* Quality range (link, level, noise) + * If the quality is absolute, it will be in the range [0 ; max_qual], + * if the quality is dBm, it will be in the range [max_qual ; 0]. + * Don't forget that we use 8 bit arithmetics... + * + * If percentage range is 0~100 + * Signal strength dbm range logical is -100 ~ 0 + * but usually value is -90 ~ -20 + */ + range->max_qual.qual = 100; +#ifdef CONFIG_SIGNAL_DISPLAY_DBM + range->max_qual.level = (u8)-100; + range->max_qual.noise = (u8)-100; + range->max_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */ + range->max_qual.updated |= IW_QUAL_DBM; +#else /* !CONFIG_SIGNAL_DISPLAY_DBM */ + /* percent values between 0 and 100. */ + range->max_qual.level = 100; + range->max_qual.noise = 100; + range->max_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */ +#endif /* !CONFIG_SIGNAL_DISPLAY_DBM */ + + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... */ + range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */ +#ifdef CONFIG_SIGNAL_DISPLAY_DBM + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = (u8)-70; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */ + range->avg_qual.updated |= IW_QUAL_DBM; +#else /* !CONFIG_SIGNAL_DISPLAY_DBM */ + /* TODO: Find real 'good' to 'bad' threshol value for RSSI */ + range->avg_qual.level = 30; + range->avg_qual.noise = 100; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED; /* Updated all three */ +#endif /* !CONFIG_SIGNAL_DISPLAY_DBM */ +#if 0 + range->num_bitrates = RATE_COUNT; + + for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) + range->bitrate[i] = rtw_rates[i]; + + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; +#endif + + range->pm_capa = 0; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 16; + + /* range->retry_capa; What retry options are supported + * range->retry_flags; How to decode max/min retry limit + * range->r_time_flags; How to decode max/min retry life + * range->min_retry; Minimal number of retries + * range->max_retry; Maximal number of retries + * range->min_r_time; Minimal retry lifetime + * range->max_r_time; Maximal retry lifetime */ +#if 0 + for (i = 0, val = 0; i < rfctl->max_chan_nums; i++) { + + /* Include only legal frequencies for some countries */ + if (rfctl->channel_set[i].ChannelNum != 0) { + range->freq[val].i = rfctl->channel_set[i].ChannelNum; + range->freq[val].m = rtw_ch2freq(rfctl->channel_set[i].ChannelNum) * 100000; + range->freq[val].e = 1; + val++; + } + + if (val == IW_MAX_FREQUENCIES) + break; + } +#endif + range->num_channels = val; + range->num_frequency = val; + + /* Commented by Albert 2009/10/13 + * The following code will proivde the security capability to network manager. + * If the driver doesn't provide this capability to network manager, + * the WPA/WPA2 routers can't be choosen in the network manager. */ + + /* + #define IW_SCAN_CAPA_NONE 0x00 + #define IW_SCAN_CAPA_ESSID 0x01 + #define IW_SCAN_CAPA_BSSID 0x02 + #define IW_SCAN_CAPA_CHANNEL 0x04 + #define IW_SCAN_CAPA_MODE 0x08 + #define IW_SCAN_CAPA_RATE 0x10 + #define IW_SCAN_CAPA_TYPE 0x20 + #define IW_SCAN_CAPA_TIME 0x40 + */ + +#if WIRELESS_EXT > 17 + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; +#endif + +#ifdef IW_SCAN_CAPA_ESSID /* WIRELESS_EXT > 21 */ + range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE | IW_SCAN_CAPA_BSSID | + IW_SCAN_CAPA_CHANNEL | IW_SCAN_CAPA_MODE | IW_SCAN_CAPA_RATE; +#endif + + + + return 0; + +} + + +static char *aicwf_get_iwe_stream_mac_addr(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + /* AP MAC address */ + iwe->cmd = SIOCGIWAP; + iwe->u.ap_addr.sa_family = ARPHRD_ETHER; + + memcpy(iwe->u.ap_addr.sa_data, scan_re->bss->bssid, ETH_ALEN); + start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_ADDR_LEN); + return start; +} + + +static inline char *aicwf_get_iwe_stream_essid(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload; + const u8 *ie = mgmt->u.beacon.variable; + u8 *ssid; + int ssid_len = 0; + + //get ssid len form ie + ssid_len = ie[1]; + + //get ssid form ie + ssid = (u8*)vmalloc(sizeof(char)* (ssid_len + 1)); + memset(ssid, 0, (ssid_len + 1)); + memcpy(ssid, &ie[2], ssid_len); + + + //AICWFDBG(LOGDEBUG, "%s len:%d ssid:%s\r\n", __func__, ssid_len, ssid); + + /* Add the ESSID */ + iwe->cmd = SIOCGIWESSID; + iwe->u.data.flags = 1; + iwe->u.data.length = min((u16)ssid_len, (u16)32); + start = iwe_stream_add_point(info, start, stop, iwe, ssid); + + vfree(ssid); + + return start; +} + + + +static inline char *aicwf_get_iwe_stream_protocol(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + u16 ht_cap = false; + u16 vht_cap = false; + u16 he_cap = false; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload; + u8 *payload = mgmt->u.beacon.variable; + const u8 *ie_content; + + /* parsing HT_CAP_IE */ + ie_content = NULL; + ie_content = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, payload, scan_re->ind->length); + if (ie_content != NULL){ + ht_cap = true; + } + + /* parsing VHT_CAP_IE */ + ie_content = NULL; + ie_content = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, payload, scan_re->ind->length); + if (ie_content != NULL){ + vht_cap = true; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL) + /* parsing HE_CAP_IE */ + ie_content = NULL; + ie_content = cfg80211_find_ie(WLAN_EID_EXTENSION, payload, scan_re->ind->length); + if (ie_content != NULL && ie_content[2] == WLAN_EID_EXT_HE_CAPABILITY){ + he_cap = true; + } +#endif + + /* Add the protocol name */ + iwe->cmd = SIOCGIWNAME; + + if (ieee80211_frequency_to_channel(scan_re->ind->center_freq) > 14) { + if (he_cap == true){ + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ax"); + }else if(vht_cap == true){ + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ac"); + }else{ + if (ht_cap == true) + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11an"); + else + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11a"); + } + } else { + if(he_cap == true){ + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11ax"); + }else if (ht_cap == true){ + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11bgn"); + }else{ + snprintf(iwe->u.name, IFNAMSIZ, "IEEE 802.11bg"); + } + } + + start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_CHAR_LEN); + return start; +} + + + +static inline char *aicwf_get_iwe_stream_rssi(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + iwe->cmd = IWEVQUAL; + + iwe->u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED + | IW_QUAL_NOISE_INVALID + | IW_QUAL_DBM; + + iwe->u.qual.level = (u8)scan_re->ind->rssi; + iwe->u.qual.qual = 100;//scan_re->bss->signal; + iwe->u.qual.noise = 0; + + start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_QUAL_LEN); + + return start; +} + +static inline char *aicwf_get_iwe_stream_chan(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + /* Add frequency/channel */ + iwe->cmd = SIOCGIWFREQ; + iwe->u.freq.m = scan_re->ind->center_freq * 100000; + iwe->u.freq.e = 1; + iwe->u.freq.i = ieee80211_frequency_to_channel(scan_re->ind->center_freq); + start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_FREQ_LEN); + + return start; +} + + +static inline char *aicwf_get_iwe_stream_mode(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + + u16 cap = scan_re->bss->capability; + /* Add mode */ + if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_ESS)) { + iwe->cmd = SIOCGIWMODE; + if (cap & WLAN_CAPABILITY_ESS) + iwe->u.mode = IW_MODE_MASTER; + else + iwe->u.mode = IW_MODE_ADHOC; + + start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_UINT_LEN); + } + return start; + +} + +static inline char *aicwf_get_iwe_stream_encryption(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload; + const u8 *ie = mgmt->u.beacon.variable; + u16 cap = scan_re->bss->capability; + u8 *ssid; + int ssid_len = 0; + + //get ssid len form ie + ssid_len = ie[1]; + + //get ssid form ie + ssid = (u8*)vmalloc(sizeof(char)* (ssid_len + 1)); + memset(ssid, 0, (ssid_len + 1)); + memcpy(ssid, &ie[2], ssid_len); + + + /* Add encryption capability */ + iwe->cmd = SIOCGIWENCODE; + if (cap & WLAN_CAPABILITY_PRIVACY) + iwe->u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe->u.data.flags = IW_ENCODE_DISABLED; + iwe->u.data.length = 0; + start = iwe_stream_add_point(info, start, stop, iwe, ssid); + vfree(ssid); + + return start; + +} + +static inline char *aicwf_get_iwe_stream_rate(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload; + u8 *payload = mgmt->u.beacon.variable; + const u8 *ie_content; + + u16 mcs_rate = 0; + u8 bw_40MHz = 0; + u8 short_GI = 0; + u16 max_rate = 0; + u8 bw_160MHz = 0; + + u16 ht_cap = false; + struct ieee80211_ht_cap *ht_capie; + + u16 vht_cap = false; + u8 tx_mcs_map[2]; + u8 tx_mcs_index = 0; + u16 vht_data_rate = 0; + + u16 he_cap = false; + u8 he_ch_width_set = 0; + u8 he_bw = 0; + + /* parsing HT_CAP_IE */ + ie_content = NULL; + ie_content = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, payload, scan_re->ind->length); + if (ie_content != NULL){ + ht_cap = true; + ht_capie = (struct ieee80211_ht_cap *)(ie_content + 2); + bw_40MHz = (ht_capie->cap_info & NL80211_CHAN_WIDTH_40) ? 1 : 0; + short_GI = (ht_capie->cap_info & (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; + memcpy(&mcs_rate, ht_capie->mcs.rx_mask, 2); + } + + + + /* parsing VHT_CAP_IE */ + ie_content = NULL; + ie_content = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, payload, scan_re->ind->length); + if (ie_content != NULL){ + + vht_cap = true; + bw_160MHz = ((*(u8*)(ie_content + 2)) >> 2) & ((u8)(0xFF >> (8 - (2)))); + if (bw_160MHz){ + short_GI = ((*(u8*)(ie_content + 2)) >> 6) & ((u8)(0xFF >> (8 - (1)))); + }else{ + short_GI = ((*(u8*)(ie_content + 2)) >> 5) & ((u8)(0xFF >> (8 - (1)))); + } + + memcpy(tx_mcs_map, ((ie_content + 2)+8), 2); + + tx_mcs_index = (tx_mcs_map[0] & 0x0F) - 1; + if(ieee80211_frequency_to_channel(scan_re->ind->center_freq) > 14){ + vht_data_rate = VHT_MCS_DATA_RATE[2][short_GI][tx_mcs_index]; + }else{ + vht_data_rate = VHT_MCS_DATA_RATE[1][short_GI][tx_mcs_index]; + } + //TO DO: + //need to counter antenna number for AC + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL) + /* parsing HE_CAP_IE */ + ie_content = NULL; + /*0xFF+len+WLAN_EID_EXTENSION+playload */ + ie_content = cfg80211_find_ie(WLAN_EID_EXTENSION, payload, scan_re->ind->length); + if (ie_content != NULL && ie_content[2] == WLAN_EID_EXT_HE_CAPABILITY){ + he_cap = true; + he_ch_width_set = ie_content[8]; + if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_160_80P80){ + he_bw = NL80211_CHAN_WIDTH_80; + }else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_160_IN_5G){ + he_bw = NL80211_CHAN_WIDTH_160; + }else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_40_AND_80_IN_5G){ + he_bw = NL80211_CHAN_WIDTH_40; + }else if(he_ch_width_set & IEEE80211_HE_CH_BW_SET_40_IN_2_4G){ + he_bw = NL80211_CHAN_WIDTH_40; + }else{ + he_bw = NL80211_CHAN_WIDTH_20; + } + //TO DO: + //need to counter antenna number for AX + } +#endif + + if(he_cap == true){ + if(he_bw == NL80211_CHAN_WIDTH_20){ + max_rate = 144; + }else if(he_bw == NL80211_CHAN_WIDTH_40){ + max_rate = 287; + }else if(he_bw == NL80211_CHAN_WIDTH_80){ + max_rate = 601; + }else if(he_bw == NL80211_CHAN_WIDTH_160){ + max_rate = 1147; + } + max_rate = max_rate * 2; + }else if(vht_cap == true){ + max_rate = vht_data_rate; + }else if (ht_cap == true) { + if (mcs_rate & 0x8000) /* MCS15 */ + max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130); + + else if (mcs_rate & 0x0080) /* MCS7 */ + max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65); + else { /* default MCS7 */ + max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65); + } + } + + iwe->cmd = SIOCGIWRATE; + iwe->u.bitrate.fixed = iwe->u.bitrate.disabled = 0; + iwe->u.bitrate.value = max_rate * 1000000; + start = iwe_stream_add_event(info, start, stop, iwe, IW_EV_PARAM_LEN); + return start ; +} + + + +int aic_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len) +{ + + u8 authmode; + u8 sec_idx; + u8 wpa_oui[4] = {0x00, 0x50, 0xf2, 0x01}; + uint cnt = 0; + + /* Search required WPA or WPA2 IE and copy to sec_ie[ ] */ + + cnt = 0; + + sec_idx = 0; + + while (cnt < in_len) { + authmode = in_ie[cnt]; + + if ((authmode == 0xdd/*_WPA_IE_ID_*/) && (memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4) == 0)) { + if (wpa_ie) + memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); + + *wpa_len = in_ie[cnt + 1] + 2; + cnt += in_ie[cnt + 1] + 2; /* get next */ + } else { + if (authmode == 0x30/*_WPA2_IE_ID_*/) { + if (rsn_ie) + memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2); + + *rsn_len = in_ie[cnt + 1] + 2; + cnt += in_ie[cnt + 1] + 2; /* get next */ + } else { + cnt += in_ie[cnt + 1] + 2; /* get next */ + } + } + + } + + + return *rsn_len + *wpa_len; + +} + + +static inline char *aicwf_get_iwe_stream_wpa_wpa2(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload; + u8 *payload = mgmt->u.beacon.variable; + int buf_size = MAX_WPA_IE_LEN * 2; + u8 *pbuf; + + u8 wpa_ie[255] = {0}, rsn_ie[255] = {0}; + u16 i, wpa_len = 0, rsn_len = 0; + u8 *p; + int out_len = 0; + + pbuf = (u8*)vmalloc(sizeof(u8) * buf_size); + + if (pbuf) { + p = pbuf; + + /* parsing WPA/WPA2 IE */ + out_len = aic_get_sec_ie(payload , scan_re->ind->length, rsn_ie, &rsn_len, wpa_ie, &wpa_len); + + if (wpa_len > 0) { + + memset(pbuf, 0, buf_size); + p += sprintf(p, "wpa_ie="); + for (i = 0; i < wpa_len; i++) + p += sprintf(p, "%02x", wpa_ie[i]); + + if (wpa_len > 100) { + printk("-----------------Len %d----------------\n", wpa_len); + for (i = 0; i < wpa_len; i++) + printk("%02x ", wpa_ie[i]); + printk("\n"); + printk("-----------------Len %d----------------\n", wpa_len); + } + + memset(iwe, 0, sizeof(*iwe)); + iwe->cmd = IWEVCUSTOM; + iwe->u.data.length = strlen(pbuf); + start = iwe_stream_add_point(info, start, stop, iwe, pbuf); + + memset(iwe, 0, sizeof(*iwe)); + iwe->cmd = IWEVGENIE; + iwe->u.data.length = wpa_len; + start = iwe_stream_add_point(info, start, stop, iwe, wpa_ie); + } + if (rsn_len > 0) { + + memset(pbuf, 0, buf_size); + p += sprintf(p, "rsn_ie="); + for (i = 0; i < rsn_len; i++) + p += sprintf(p, "%02x", rsn_ie[i]); + memset(iwe, 0, sizeof(*iwe)); + iwe->cmd = IWEVCUSTOM; + iwe->u.data.length = strlen(pbuf); + start = iwe_stream_add_point(info, start, stop, iwe, pbuf); + + memset(iwe, 0, sizeof(*iwe)); + iwe->cmd = IWEVGENIE; + iwe->u.data.length = rsn_len; + start = iwe_stream_add_point(info, start, stop, iwe, rsn_ie); + } + + vfree(pbuf); + } + return start; +} + + +u8 aicwf_get_is_wps_ie(u8 *ie_ptr, uint *wps_ielen) +{ + u8 match = false; + u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; + + if (ie_ptr == NULL) + return match; + + eid = ie_ptr[0]; + + if ((eid == 0xdd/*_WPA_IE_ID_*/) && (memcmp(&ie_ptr[2], wps_oui, 4) == 0)) { + *wps_ielen = ie_ptr[1] + 2; + match = true; + } + return match; +} + + +static inline char *aicwf_get_iwe_stream_wps(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop, struct iw_event *iwe) +{ + + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt*)scan_re->payload; + u8 *payload = mgmt->u.beacon.variable; + + /* parsing WPS IE */ + uint cnt = 0, total_ielen; + u8 *wpsie_ptr = NULL; + uint wps_ielen = 0; + u8 *ie_ptr = payload; + + total_ielen = scan_re->ind->length; + + while (cnt < total_ielen) { + if (aicwf_get_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen > 2)) { + wpsie_ptr = &ie_ptr[cnt]; + iwe->cmd = IWEVGENIE; + iwe->u.data.length = (u16)wps_ielen; + start = iwe_stream_add_point(info, start, stop, iwe, wpsie_ptr); + } + cnt += ie_ptr[cnt + 1] + 2; /* goto next */ + } + return start; +} + + +static char *translate_scan(struct rwnx_hw* rwnx_hw, + struct iw_request_info *info, struct scanu_result_wext *scan_re, + char *start, char *stop) +{ + struct iw_event iwe; + memset(&iwe, 0, sizeof(iwe)); + + + start = aicwf_get_iwe_stream_mac_addr(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_essid(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_protocol(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_chan(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_mode(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_encryption(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_rate(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_wpa_wpa2(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_wps(rwnx_hw, info, scan_re, start, stop, &iwe); + start = aicwf_get_iwe_stream_rssi(rwnx_hw, info, scan_re, start, stop, &iwe); + + return start; +} + + + +static int aicwf_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + + memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); + + if(rwnx_vif->sta.ap){ + memcpy(wrqu->ap_addr.sa_data, rwnx_vif->sta.ap->mac_addr, ETH_ALEN); + }else{ + memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); + } + + return 0; + +} + + +extern uint8_t scanning; +static int aicwf_set_scan(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw; + struct cfg80211_scan_request *request; + int index = 0; + struct wiphy *wiphy = priv_to_wiphy(rwnx_hw); + unsigned long wext_scan_timeout; + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + if(wiphy == NULL){ + printk("aic_wiphy error \r\n"); + + } + + if (rwnx_hw->wext_scan || scanning) { + AICWFDBG(LOGINFO, "is scanning, abort\n"); + ret = rwnx_send_scanu_cancel_req(rwnx_hw, NULL); + if (ret) + return ret; + msleep(150); + } + + rwnx_hw->wext_scan = 1; + + request = (struct cfg80211_scan_request *)vmalloc(sizeof(struct cfg80211_scan_request)); + + request->n_channels = rwnx_hw->support_freqs_number; + request->n_ssids = 0; + request->no_cck = false; + request->ie = NULL; + request->ie_len = 0; + + for(index = 0;index < rwnx_hw->support_freqs_number; index++){ + request->channels[index] = ieee80211_get_channel(wiphy, + rwnx_hw->support_freqs[index]); + if(request->channels[index] == NULL){ + AICWFDBG(LOGERROR, "%s ERROR!!! channels is NULL", __func__); + continue; + } + } + + if ((ret = rwnx_send_scanu_req(rwnx_hw, rwnx_vif, request))){ + return ret; + } + + rwnx_vif->rwnx_hw->scan_request = request; + wext_scan_timeout = msecs_to_jiffies(5000); + + if (!wait_for_completion_killable_timeout(&rwnx_hw->wext_scan_com, wext_scan_timeout)) { + AICWFDBG(LOGERROR, "%s WEXT scan timeout", __func__); + } + + return 0; +} + +static int aicwf_get_scan(struct net_device *dev, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw; + struct scanu_result_wext *scan_re; + struct scanu_result_wext *tmp; + char *start = extra; + char *stop = start + wrqu->data.length; + + AICWFDBG(LOGDEBUG, "%s Enter %p %p len:%d \r\n", __func__, start, stop, wrqu->data.length); + + //TODO: spinlock + list_for_each_entry_safe(scan_re, tmp, &rwnx_hw->wext_scanre_list, scanu_re_list) { + start = translate_scan(rwnx_hw, a, scan_re, start, stop); + if ((stop - start) < 768) { + return -E2BIG; + } + + } + + list_for_each_entry_safe(scan_re, tmp, &rwnx_hw->wext_scanre_list, scanu_re_list) { + list_del(&scan_re->scanu_re_list); + vfree(scan_re->payload); + vfree(scan_re->ind); + vfree(scan_re); + scan_re = NULL; + } + + + wrqu->data.length = start - extra; + wrqu->data.flags = 0; + + return ret ; + +} + + +static int aicwf_get_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw; + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + wrqu->essid.length = strlen(rwnx_hw->wext_essid); + memcpy(extra, rwnx_hw->wext_essid, strlen(rwnx_hw->wext_essid)); + wrqu->essid.flags = 1; + + + return ret; + +} + + +static int aicwf_get_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + if (extra) { + wrqu->data.length = 8; + wrqu->data.flags = 1; + memcpy(extra, "AIC@8800", 8); + } + + return 0; + +} + +static int aicwf_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u16 max_rate = 0; + struct rwnx_vif* rwnx_vif = netdev_priv(dev); + struct rwnx_hw* rwnx_hw = rwnx_vif->rwnx_hw; + struct rwnx_sta *sta = NULL; + + union rwnx_rate_ctrl_info *rate_info; + struct mm_get_sta_info_cfm cfm; + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + if(rwnx_vif->sta.ap){ + sta = rwnx_vif->sta.ap; + rwnx_send_get_sta_info_req(rwnx_hw, sta->sta_idx, &cfm); + rate_info = (union rwnx_rate_ctrl_info *)&cfm.rate_info; + + switch (rate_info->formatModTx) { + case FORMATMOD_NON_HT: + case FORMATMOD_NON_HT_DUP_OFDM: + //get bg mode datarate + max_rate = tx_legrates_lut_rate[rate_info->mcsIndexTx]/10; + break; + case FORMATMOD_HT_MF: + case FORMATMOD_HT_GF: + case FORMATMOD_VHT: + //get bg mode MCS index + max_rate = VHT_MCS_DATA_RATE[rate_info->bwTx][1][rate_info->mcsIndexTx]/2; + break; + case FORMATMOD_HE_MU: + case FORMATMOD_HE_SU: + case FORMATMOD_HE_ER: + //get bg mode MCS index + max_rate = HE_MCS_DATA_RATE[rate_info->bwTx][rate_info->mcsIndexTx]; + break; + } + } + + wrqu->bitrate.fixed = 0; /* no auto select */ + wrqu->bitrate.value = max_rate * 1000000; + + return 0; +} + + +static int aicwf_get_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + int ret = 0; + struct iw_point *erq = &(wrqu->encoding); + + AICWFDBG(LOGTRACE, "%s Enter", __func__); + + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + + + return ret; + +} + + +static iw_handler aic_handlers[] = { + NULL, /* SIOCSIWCOMMIT */ + aicwf_get_name, /* SIOCGIWNAME */ + NULL, /* SIOCSIWNWID */ + NULL, /* SIOCGIWNWID */ + NULL, /* SIOCSIWFREQ */ + aicwf_get_freq, /* SIOCGIWFREQ */ + NULL, /* SIOCSIWMODE */ + aicwf_get_mode, /* SIOCGIWMODE */ + NULL, /* SIOCSIWSENS */ + NULL, /* SIOCGIWSENS */ + NULL, /* SIOCSIWRANGE */ + aicwf_get_range, /* SIOCGIWRANGE */ + NULL, /* SIOCSIWPRIV */ + NULL, /* SIOCGIWPRIV */ + NULL, /* SIOCSIWSTATS */ + NULL, /* SIOCGIWSTATS */ + NULL, /* SIOCSIWSPY */ + NULL, /* SIOCGIWSPY */ + NULL, /* SIOCGIWTHRSPY */ + NULL, /* SIOCWIWTHRSPY */ + NULL, /* SIOCSIWAP */ + aicwf_get_wap, /* SIOCGIWAP */ + NULL, /* request MLME operation; uses struct iw_mlme */ + NULL, /* SIOCGIWAPLIST -- depricated */ + aicwf_set_scan, /* SIOCSIWSCAN */ + aicwf_get_scan, /* SIOCGIWSCAN */ + NULL, /* SIOCSIWESSID */ + aicwf_get_essid, /* SIOCGIWESSID */ + NULL, /* SIOCSIWNICKN */ + aicwf_get_nick, /* SIOCGIWNICKN */ + NULL, /* -- hole -- */ + NULL, /* -- hole -- */ + NULL, /* SIOCSIWRATE */ + aicwf_get_rate, /* SIOCGIWRATE */ + NULL, /* SIOCSIWRTS */ + NULL, /* SIOCGIWRTS */ + NULL, /* SIOCSIWFRAG */ + NULL, /* SIOCGIWFRAG */ + NULL, /* SIOCSIWTXPOW */ + NULL, /* SIOCGIWTXPOW */ + NULL, /* SIOCSIWRETRY */ + NULL, /* SIOCGIWRETRY */ + NULL, /* SIOCSIWENCODE */ + aicwf_get_enc, /* SIOCGIWENCODE */ + NULL, /* SIOCSIWPOWER */ + NULL, /* SIOCGIWPOWER */ + NULL, /*---hole---*/ + NULL, /*---hole---*/ + NULL, /* SIOCSIWGENIE */ + NULL, /* SIOCGWGENIE */ + NULL, /* SIOCSIWAUTH */ + NULL, /* SIOCGIWAUTH */ + NULL, /* SIOCSIWENCODEEXT */ + NULL, /* SIOCGIWENCODEEXT */ + NULL, /* SIOCSIWPMKSA */ + NULL, /*---hole---*/ +}; + +struct iw_handler_def aic_handlers_def = { + .standard = aic_handlers, + .num_standard = sizeof(aic_handlers) / sizeof(iw_handler), +#if WIRELESS_EXT >= 17 + .get_wireless_stats = aicwf_get_wireless_stats, +#endif +}; + +void aicwf_set_wireless_ext( struct net_device *ndev, struct rwnx_hw *rwnx_hw){ + + AICWFDBG(LOGINFO, "%s Enter", __func__); + + init_completion(&rwnx_hw->wext_scan_com); + INIT_LIST_HEAD(&rwnx_hw->wext_scanre_list); + + ndev->wireless_handlers = (struct iw_handler_def *)&aic_handlers_def; +} + + |