Files
aic8800d80/drivers/aic8800/aic8800_fdrv/aicwf_wext_linux.c
Shen MintaoandClaude Opus 4.7 d66e5cb859 feat: update to SDK v5.0 with comprehensive kernel and device support
Sync WiFi driver, firmware, and DKMS configuration from the bluetooth
branch (which itself is radxa USB driver_fw + Linux 6.16/6.17/6.19
kernel compat). aic_btusb and Bluetooth-specific install.sh logic
are intentionally excluded from main.

Driver updates:
- Add Linux kernel 6.12-6.19 compatibility support
- Add Linux 6.16 timer_container_of and wakelock API support
- Add Debian 6.1.0-26 backport support
- Add USB device ID 368b:8d81 support
- Add AIC8800DW device support
- Add TP-Link Archer TX1U Nano support (USB_VENDOR_ID_TENDA_V2:0x0110)
- Add TENDA series device support (U2, U11, U11 PRO)
- Add AIC8800FC_CUS1-6 series support
- Add AIC8800M80_CUS0-8 series support
- Add UGREEN AIC8800D80 adapter support
- Add Mercury (TP-Link OEM) support
- Allow 1 or 3 USB interfaces (BT-disabled SKUs report 1)

New compat modules:
- aicwf_compat_8800d80n.{c,h}
- aicwf_compat_8800dln.{c,h}

Firmware:
- New: fw/aic8800/, fw/aic8800D80N/, fw/aic8800D80X2/, fw/aic8800DLN/
- Updated: fw/aic8800D80/, fw/aic8800DC/

install.sh:
- Loop over all fw/aic8800* variants instead of hard-coding aic8800D80
- Install usb_modeswitch/1111_1111 config for "Pandora" clone adapter
- Verify firmware install by counting variants

dkms.conf: pass KDIR explicitly to make for cross-kernel builds.

Excluded from this commit (BT-only, kept on bluetooth branch):
- drivers/aic8800/aic_btusb/
- modprobe/aic8800-bt.conf
- diagnose_bt.sh
- install.sh btusb auto-load / hciconfig / bluez status checks

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 14:02:08 +08:00

1227 lines
33 KiB
C

#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;
if(scan_re->bss){
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_bw = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)|| defined(CONFIG_HE_FOR_OLD_KERNEL)
u8 he_ch_width_set = 0;
#endif
/* 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));
if(scan_re->bss){
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;
} else{
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;
}
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 || rwnx_hw->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 WIRELESS_EXT >= 17
if (wrqu->data.length == sizeof(struct iw_scan_req)) {
struct iw_scan_req *req = (struct iw_scan_req *)extra;
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
int len = min((int)req->essid_len, 32);
request->ssids = kmalloc(sizeof(struct cfg80211_ssid), GFP_KERNEL);
if(!request->ssids){
AICWFDBG(LOGERROR, "%s Failied to alloc memory for ssids", __func__);
return -ENOMEM;
}
memset(request->ssids, 0, sizeof(struct cfg80211_ssid));
memcpy(request->ssids[0].ssid, req->essid, len);
request->ssids[0].ssid_len = len;
request->n_ssids = 1;
AICWFDBG(LOGDEBUG,"IW_SCAN_THIS_ESSID, ssid=%s, len=%d\n", req->essid, req->essid_len);
} else if (req->scan_type == IW_SCAN_TYPE_PASSIVE)
AICWFDBG(LOGDEBUG,"aic_set_scan, req->scan_type == IW_SCAN_TYPE_PASSIVE\n");
}
#endif
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;
}