1734 lines
64 KiB
C
1734 lines
64 KiB
C
/**
|
|
******************************************************************************
|
|
*
|
|
* @file rwnx_mod_params.c
|
|
*
|
|
* @brief Set configuration according to modules parameters
|
|
*
|
|
* Copyright (C) RivieraWaves 2012-2019
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "rwnx_defs.h"
|
|
#include "rwnx_tx.h"
|
|
#include "hal_desc.h"
|
|
#include "rwnx_cfgfile.h"
|
|
#include "rwnx_dini.h"
|
|
#include "reg_access.h"
|
|
#include "rwnx_compat.h"
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
#define COMMON_PARAM(name, default_softmac, default_fullmac) \
|
|
.name = default_fullmac,
|
|
#define SOFTMAC_PARAM(name, default)
|
|
#define FULLMAC_PARAM(name, default) .name = default,
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
struct rwnx_mod_params rwnx_mod_params = {
|
|
/* common parameters */
|
|
COMMON_PARAM(ht_on, true, true)
|
|
COMMON_PARAM(vht_on, true, true)
|
|
COMMON_PARAM(he_on, true, true)
|
|
COMMON_PARAM(mcs_map, IEEE80211_VHT_MCS_SUPPORT_0_9, IEEE80211_VHT_MCS_SUPPORT_0_9)
|
|
COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_11, IEEE80211_HE_MCS_SUPPORT_0_11)
|
|
COMMON_PARAM(he_ul_on, false, false)
|
|
COMMON_PARAM(ldpc_on, true, true)
|
|
COMMON_PARAM(stbc_on, true, true)
|
|
COMMON_PARAM(gf_rx_on, false, false)
|
|
COMMON_PARAM(phy_cfg, 2, 2)
|
|
COMMON_PARAM(uapsd_timeout, 300, 300)
|
|
COMMON_PARAM(ap_uapsd_on, true, true)
|
|
COMMON_PARAM(sgi, true, true)
|
|
COMMON_PARAM(sgi80, false, false)
|
|
COMMON_PARAM(use_2040, 1, 1)
|
|
COMMON_PARAM(nss, 1, 1)
|
|
COMMON_PARAM(amsdu_rx_max, 2, 2)
|
|
COMMON_PARAM(bfmee, true, true)
|
|
COMMON_PARAM(bfmer, false, false)
|
|
COMMON_PARAM(mesh, true, true)
|
|
COMMON_PARAM(murx, true, true)
|
|
COMMON_PARAM(mutx, true, true)
|
|
COMMON_PARAM(mutx_on, true, true)
|
|
COMMON_PARAM(use_80, false, false)
|
|
COMMON_PARAM(custregd, true, true)
|
|
COMMON_PARAM(custchan, false, false)
|
|
COMMON_PARAM(roc_dur_max, 500, 500)
|
|
COMMON_PARAM(listen_itv, 0, 0)
|
|
COMMON_PARAM(listen_bcmc, true, true)
|
|
COMMON_PARAM(lp_clk_ppm, 20, 20)
|
|
COMMON_PARAM(ps_on, true, true)
|
|
COMMON_PARAM(tx_lft, RWNX_TX_LIFETIME_MS, RWNX_TX_LIFETIME_MS)
|
|
COMMON_PARAM(amsdu_maxnb, NX_TX_PAYLOAD_MAX, NX_TX_PAYLOAD_MAX)
|
|
// By default, only enable UAPSD for Voice queue (see IEEE80211_DEFAULT_UAPSD_QUEUE comment)
|
|
COMMON_PARAM(uapsd_queues, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
|
|
COMMON_PARAM(tdls, false, false)
|
|
COMMON_PARAM(uf, false, false)
|
|
COMMON_PARAM(auto_reply, false, false)
|
|
COMMON_PARAM(ftl, "", "")
|
|
COMMON_PARAM(dpsm, false, false)
|
|
|
|
/* SOFTMAC only parameters */
|
|
SOFTMAC_PARAM(mfp_on, false)
|
|
SOFTMAC_PARAM(gf_on, false)
|
|
SOFTMAC_PARAM(bwsig_on, true)
|
|
SOFTMAC_PARAM(dynbw_on, true)
|
|
SOFTMAC_PARAM(agg_tx, true)
|
|
SOFTMAC_PARAM(amsdu_force, 2)
|
|
SOFTMAC_PARAM(rc_probes_on, false)
|
|
SOFTMAC_PARAM(cmon, true)
|
|
SOFTMAC_PARAM(hwscan, true)
|
|
SOFTMAC_PARAM(autobcn, true)
|
|
SOFTMAC_PARAM(dpsm, true)
|
|
|
|
/* FULLMAC only parameters */
|
|
FULLMAC_PARAM(ant_div, true)
|
|
};
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
/* FULLMAC specific parameters*/
|
|
module_param_named(ant_div, rwnx_mod_params.ant_div, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(ant_div, "Enable Antenna Diversity (Default: 1)");
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
module_param_named(ht_on, rwnx_mod_params.ht_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(ht_on, "Enable HT (Default: 1)");
|
|
|
|
module_param_named(vht_on, rwnx_mod_params.vht_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(vht_on, "Enable VHT (Default: 1)");
|
|
|
|
module_param_named(he_on, rwnx_mod_params.he_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(he_on, "Enable HE (Default: 1)");
|
|
|
|
module_param_named(mcs_map, rwnx_mod_params.mcs_map, int, S_IRUGO);
|
|
MODULE_PARM_DESC(mcs_map, "VHT MCS map value 0: MCS0_7, 1: MCS0_8, 2: MCS0_9"
|
|
" (Default: 2)");
|
|
|
|
module_param_named(he_mcs_map, rwnx_mod_params.he_mcs_map, int, S_IRUGO);
|
|
MODULE_PARM_DESC(he_mcs_map, "HE MCS map value 0: MCS0_7, 1: MCS0_9, 2: MCS0_11"
|
|
" (Default: 2)");
|
|
|
|
module_param_named(he_ul_on, rwnx_mod_params.he_ul_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(he_ul_on, "Enable HE OFDMA UL (Default: 0)");
|
|
|
|
module_param_named(amsdu_maxnb, rwnx_mod_params.amsdu_maxnb, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(amsdu_maxnb, "Maximum number of MSDUs inside an A-MSDU in TX: (Default: NX_TX_PAYLOAD_MAX)");
|
|
|
|
module_param_named(ps_on, rwnx_mod_params.ps_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(ps_on, "Enable PowerSaving (Default: 1-Enabled)");
|
|
|
|
module_param_named(tx_lft, rwnx_mod_params.tx_lft, int, 0644);
|
|
MODULE_PARM_DESC(tx_lft, "Tx lifetime (ms) - setting it to 0 disables retries "
|
|
"(Default: "__stringify(RWNX_TX_LIFETIME_MS)")");
|
|
|
|
module_param_named(ldpc_on, rwnx_mod_params.ldpc_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(ldpc_on, "Enable LDPC (Default: 1)");
|
|
|
|
module_param_named(stbc_on, rwnx_mod_params.stbc_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(stbc_on, "Enable STBC in RX (Default: 1)");
|
|
|
|
module_param_named(gf_rx_on, rwnx_mod_params.gf_rx_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(gf_rx_on, "Enable HT greenfield in reception (Default: 1)");
|
|
|
|
module_param_named(phycfg, rwnx_mod_params.phy_cfg, int, S_IRUGO);
|
|
MODULE_PARM_DESC(phycfg,
|
|
"0 <= phycfg <= 5 : RF Channel Conf (Default: 2(C0-A1-B2))");
|
|
|
|
module_param_named(uapsd_timeout, rwnx_mod_params.uapsd_timeout, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(uapsd_timeout,
|
|
"UAPSD Timer timeout, in ms (Default: 300). If 0, UAPSD is disabled");
|
|
|
|
module_param_named(uapsd_queues, rwnx_mod_params.uapsd_queues, int, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(uapsd_queues, "UAPSD Queues, integer value, must be seen as a bitfield\n"
|
|
" Bit 0 = VO\n"
|
|
" Bit 1 = VI\n"
|
|
" Bit 2 = BK\n"
|
|
" Bit 3 = BE\n"
|
|
" -> uapsd_queues=7 will enable uapsd for VO, VI and BK queues");
|
|
|
|
module_param_named(ap_uapsd_on, rwnx_mod_params.ap_uapsd_on, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(ap_uapsd_on, "Enable UAPSD in AP mode (Default: 1)");
|
|
|
|
module_param_named(sgi, rwnx_mod_params.sgi, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(sgi, "Advertise Short Guard Interval support (Default: 1)");
|
|
|
|
module_param_named(sgi80, rwnx_mod_params.sgi80, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(sgi80, "Advertise Short Guard Interval support for 80MHz (Default: 1)");
|
|
|
|
module_param_named(use_2040, rwnx_mod_params.use_2040, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(use_2040, "Use tweaked 20-40MHz mode (Default: 1)");
|
|
|
|
module_param_named(use_80, rwnx_mod_params.use_80, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(use_80, "Enable 80MHz (Default: 1)");
|
|
|
|
module_param_named(custregd, rwnx_mod_params.custregd, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(custregd,
|
|
"Use permissive custom regulatory rules (for testing ONLY) (Default: 0)");
|
|
|
|
module_param_named(custchan, rwnx_mod_params.custchan, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(custchan,
|
|
"Extend channel set to non-standard channels (for testing ONLY) (Default: 0)");
|
|
|
|
module_param_named(nss, rwnx_mod_params.nss, int, S_IRUGO);
|
|
MODULE_PARM_DESC(nss, "1 <= nss <= 2 : Supported number of Spatial Streams (Default: 1)");
|
|
|
|
module_param_named(amsdu_rx_max, rwnx_mod_params.amsdu_rx_max, int, S_IRUGO);
|
|
MODULE_PARM_DESC(amsdu_rx_max, "0 <= amsdu_rx_max <= 2 : Maximum A-MSDU size supported in RX\n"
|
|
" 0: 3895 bytes\n"
|
|
" 1: 7991 bytes\n"
|
|
" 2: 11454 bytes\n"
|
|
" This value might be reduced according to the FW capabilities.\n"
|
|
" Default: 2");
|
|
|
|
module_param_named(bfmee, rwnx_mod_params.bfmee, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(bfmee, "Enable Beamformee Capability (Default: 1-Enabled)");
|
|
|
|
module_param_named(bfmer, rwnx_mod_params.bfmer, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(bfmer, "Enable Beamformer Capability (Default: 0-Disabled)");
|
|
|
|
module_param_named(mesh, rwnx_mod_params.mesh, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(mesh, "Enable Meshing Capability (Default: 1-Enabled)");
|
|
|
|
module_param_named(murx, rwnx_mod_params.murx, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(murx, "Enable MU-MIMO RX Capability (Default: 1-Enabled)");
|
|
|
|
module_param_named(mutx, rwnx_mod_params.mutx, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(mutx, "Enable MU-MIMO TX Capability (Default: 1-Enabled)");
|
|
|
|
module_param_named(mutx_on, rwnx_mod_params.mutx_on, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(mutx_on, "Enable MU-MIMO transmissions (Default: 1-Enabled)");
|
|
|
|
module_param_named(roc_dur_max, rwnx_mod_params.roc_dur_max, int, S_IRUGO);
|
|
MODULE_PARM_DESC(roc_dur_max, "Maximum Remain on Channel duration");
|
|
|
|
module_param_named(listen_itv, rwnx_mod_params.listen_itv, int, S_IRUGO);
|
|
MODULE_PARM_DESC(listen_itv, "Maximum listen interval");
|
|
|
|
module_param_named(listen_bcmc, rwnx_mod_params.listen_bcmc, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(listen_bcmc, "Wait for BC/MC traffic following DTIM beacon");
|
|
|
|
module_param_named(lp_clk_ppm, rwnx_mod_params.lp_clk_ppm, int, S_IRUGO);
|
|
MODULE_PARM_DESC(lp_clk_ppm, "Low Power Clock accuracy of the local device");
|
|
|
|
module_param_named(tdls, rwnx_mod_params.tdls, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(tdls, "Enable TDLS (Default: 1-Enabled)");
|
|
|
|
module_param_named(uf, rwnx_mod_params.uf, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(uf, "Enable Unsupported HT Frame Logging (Default: 0-Disabled)");
|
|
|
|
module_param_named(auto_reply, rwnx_mod_params.auto_reply, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(auto_reply, "Enable Monitor MacAddr Auto-Reply (Default: 0-Disabled)");
|
|
|
|
module_param_named(ftl, rwnx_mod_params.ftl, charp, S_IRUGO);
|
|
MODULE_PARM_DESC(ftl, "Firmware trace level (Default: \"\")");
|
|
|
|
module_param_named(dpsm, rwnx_mod_params.dpsm, bool, S_IRUGO);
|
|
MODULE_PARM_DESC(dpsm, "Enable Dynamic PowerSaving (Default: 1-Enabled)");
|
|
|
|
|
|
#ifdef DEFAULT_COUNTRY_CODE
|
|
char default_ccode[4] = DEFAULT_COUNTRY_CODE;
|
|
#else
|
|
char default_ccode[4] = "00";
|
|
#endif
|
|
|
|
char country_code[4];
|
|
module_param_string(country_code, country_code, 4, 0600);
|
|
|
|
#if 0
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
/* Regulatory rules */
|
|
static struct ieee80211_regdomain rwnx_regdom = {
|
|
.n_reg_rules = 2,
|
|
.alpha2 = "99",
|
|
.reg_rules = {
|
|
REG_RULE(2390 - 10, 2510 + 10, 40, 0, 1000, 0),
|
|
REG_RULE(5150 - 10, 5970 + 10, 80, 0, 1000, 0),
|
|
}
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
static const int mcs_map_to_rate[4][3] = {
|
|
[PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_7] = 65,
|
|
[PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_8] = 78,
|
|
[PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_9] = 78,
|
|
[PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_7] = 135,
|
|
[PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_8] = 162,
|
|
[PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_9] = 180,
|
|
[PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_7] = 292,
|
|
[PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_8] = 351,
|
|
[PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_9] = 390,
|
|
[PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_7] = 585,
|
|
[PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_8] = 702,
|
|
[PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_9] = 780,
|
|
};
|
|
|
|
#define MAX_VHT_RATE(map, nss, bw) (mcs_map_to_rate[bw][map] * (nss))
|
|
|
|
extern struct ieee80211_regdomain *reg_regdb[];
|
|
|
|
char ccode_channels[200];
|
|
int index_for_channel_list = 0;
|
|
module_param_string(ccode_channels, ccode_channels, 200, 0600);
|
|
|
|
void rwnx_get_countrycode_channels(struct wiphy *wiphy,
|
|
struct ieee80211_regdomain *regdomain){
|
|
enum nl80211_band band;
|
|
struct ieee80211_supported_band *sband;
|
|
int channel_index;
|
|
int rule_index;
|
|
int band_num = 0;
|
|
int rule_num = regdomain->n_reg_rules;
|
|
int start_freq = 0;
|
|
int end_freq = 0;
|
|
int center_freq = 0;
|
|
char channel[4];
|
|
|
|
band_num = NUM_NL80211_BANDS;
|
|
|
|
memset(ccode_channels, 0, 200);
|
|
index_for_channel_list = 0;
|
|
|
|
for (band = 0; band < band_num; band++) {
|
|
sband = wiphy->bands[band];// bands: 0:2.4G 1:5G 2:60G
|
|
if (!sband)
|
|
continue;
|
|
|
|
for (channel_index = 0; channel_index < sband->n_channels; channel_index++) {
|
|
for(rule_index = 0; rule_index < rule_num; rule_index++){
|
|
start_freq = regdomain->reg_rules[rule_index].freq_range.start_freq_khz/1000;
|
|
end_freq = regdomain->reg_rules[rule_index].freq_range.end_freq_khz/1000;
|
|
center_freq = sband->channels[channel_index].center_freq;
|
|
if((center_freq - 10) >= start_freq && (center_freq + 10) <= end_freq){
|
|
sprintf(channel, "%d",ieee80211_frequency_to_channel(center_freq));
|
|
memcpy(ccode_channels + index_for_channel_list, channel, strlen(channel));
|
|
index_for_channel_list += strlen(channel);
|
|
memcpy(ccode_channels + index_for_channel_list, " ", 1);
|
|
index_for_channel_list += 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
AICWFDBG(LOGINFO, "%s support channel:%s\r\n", __func__, ccode_channels);
|
|
}
|
|
|
|
|
|
struct ieee80211_regdomain *getRegdomainFromRwnxDBIndex(struct wiphy *wiphy,
|
|
int index)
|
|
{
|
|
u8 idx;
|
|
|
|
idx = index;
|
|
|
|
memset(country_code, 0, 4);
|
|
country_code[0] = reg_regdb[idx]->alpha2[0];
|
|
country_code[1] = reg_regdb[idx]->alpha2[1];
|
|
|
|
printk("%s set ccode:%s \r\n", __func__, country_code);
|
|
|
|
rwnx_get_countrycode_channels(wiphy, reg_regdb[idx]);
|
|
|
|
return reg_regdb[idx];
|
|
}
|
|
|
|
|
|
struct ieee80211_regdomain *getRegdomainFromRwnxDB(struct wiphy *wiphy,
|
|
char *alpha2)
|
|
{
|
|
u8 idx;
|
|
|
|
memset(country_code, 0, 4);
|
|
|
|
AICWFDBG(LOGINFO, "%s set ccode:%s \r\n", __func__, alpha2);
|
|
idx = 0;
|
|
|
|
while (reg_regdb[idx]){
|
|
if((reg_regdb[idx]->alpha2[0] == alpha2[0]) &&
|
|
(reg_regdb[idx]->alpha2[1] == alpha2[1])){
|
|
memcpy(country_code, alpha2, 2);
|
|
rwnx_get_countrycode_channels(wiphy, reg_regdb[idx]);
|
|
return reg_regdb[idx];
|
|
}
|
|
idx++;
|
|
}
|
|
|
|
AICWFDBG(LOGERROR, "%s(): Error, wrong country = %s\n",
|
|
__func__, alpha2);
|
|
AICWFDBG(LOGINFO, "Set as default 00\n");
|
|
memcpy(country_code, default_ccode, sizeof(default_ccode));
|
|
rwnx_get_countrycode_channels(wiphy, reg_regdb[0]);
|
|
|
|
return reg_regdb[0];
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Do some sanity check
|
|
*
|
|
*/
|
|
#if 0
|
|
static int rwnx_check_fw_hw_feature(struct rwnx_hw *rwnx_hw,
|
|
struct wiphy *wiphy)
|
|
{
|
|
u32_l sys_feat = rwnx_hw->version_cfm.features;
|
|
u32_l mac_feat = rwnx_hw->version_cfm.version_machw_1;
|
|
u32_l phy_feat = rwnx_hw->version_cfm.version_phy_1;
|
|
u32_l phy_vers = rwnx_hw->version_cfm.version_phy_2;
|
|
u16_l max_sta_nb = rwnx_hw->version_cfm.max_sta_nb;
|
|
u8_l max_vif_nb = rwnx_hw->version_cfm.max_vif_nb;
|
|
int bw, res = 0;
|
|
int amsdu_rx;
|
|
|
|
if (!rwnx_hw->mod_params->custregd)
|
|
rwnx_hw->mod_params->custchan = false;
|
|
|
|
if (rwnx_hw->mod_params->custchan) {
|
|
rwnx_hw->mod_params->mesh = false;
|
|
rwnx_hw->mod_params->tdls = false;
|
|
}
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_UMAC_BIT))) {
|
|
wiphy_err(wiphy,
|
|
"Loading softmac firmware with fullmac driver\n");
|
|
res = -1;
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_ANT_DIV_BIT))) {
|
|
rwnx_hw->mod_params->ant_div = false;
|
|
}
|
|
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_VHT_BIT))) {
|
|
rwnx_hw->mod_params->vht_on = false;
|
|
}
|
|
|
|
// Check if HE is supported
|
|
if (!(sys_feat & BIT(MM_FEAT_HE_BIT))) {
|
|
rwnx_hw->mod_params->he_on = false;
|
|
rwnx_hw->mod_params->he_ul_on = false;
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_PS_BIT))) {
|
|
rwnx_hw->mod_params->ps_on = false;
|
|
}
|
|
|
|
/* AMSDU (non)support implies different shared structure definition
|
|
so insure that fw and drv have consistent compilation option */
|
|
if (sys_feat & BIT(MM_FEAT_AMSDU_BIT)) {
|
|
#ifndef CONFIG_RWNX_SPLIT_TX_BUF
|
|
wiphy_err(wiphy,
|
|
"AMSDU enabled in firmware but support not compiled in driver\n");
|
|
res = -1;
|
|
#else
|
|
if (rwnx_hw->mod_params->amsdu_maxnb > NX_TX_PAYLOAD_MAX)
|
|
rwnx_hw->mod_params->amsdu_maxnb = NX_TX_PAYLOAD_MAX;
|
|
#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
|
|
} else {
|
|
#ifdef CONFIG_RWNX_SPLIT_TX_BUF
|
|
wiphy_err(wiphy,
|
|
"AMSDU disabled in firmware but support compiled in driver\n");
|
|
res = -1;
|
|
#endif /* CONFIG_RWNX_SPLIT_TX_BUF */
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_UAPSD_BIT))) {
|
|
rwnx_hw->mod_params->uapsd_timeout = 0;
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_BFMEE_BIT))) {
|
|
rwnx_hw->mod_params->bfmee = false;
|
|
}
|
|
|
|
if ((sys_feat & BIT(MM_FEAT_BFMER_BIT))) {
|
|
#ifndef CONFIG_RWNX_BFMER
|
|
wiphy_err(wiphy,
|
|
"BFMER enabled in firmware but support not compiled in driver\n");
|
|
res = -1;
|
|
#endif /* CONFIG_RWNX_BFMER */
|
|
// Check PHY and MAC HW BFMER support and update parameter accordingly
|
|
if (!(phy_feat & MDM_BFMER_BIT) || !(mac_feat & NXMAC_BFMER_BIT)) {
|
|
rwnx_hw->mod_params->bfmer = false;
|
|
// Disable the feature in the bitfield so that it won't be displayed
|
|
sys_feat &= ~BIT(MM_FEAT_BFMER_BIT);
|
|
}
|
|
} else {
|
|
#ifdef CONFIG_RWNX_BFMER
|
|
wiphy_err(wiphy,
|
|
"BFMER disabled in firmware but support compiled in driver\n");
|
|
res = -1;
|
|
#else
|
|
rwnx_hw->mod_params->bfmer = false;
|
|
#endif /* CONFIG_RWNX_BFMER */
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_MESH_BIT))) {
|
|
rwnx_hw->mod_params->mesh = false;
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_TDLS_BIT))) {
|
|
rwnx_hw->mod_params->tdls = false;
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_UF_BIT))) {
|
|
rwnx_hw->mod_params->uf = false;
|
|
}
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
if ((sys_feat & BIT(MM_FEAT_MON_DATA_BIT))) {
|
|
#ifndef CONFIG_RWNX_MON_DATA
|
|
wiphy_err(wiphy,
|
|
"Monitor+Data interface support (MON_DATA) is enabled in firmware but support not compiled in driver\n");
|
|
res = -1;
|
|
#endif /* CONFIG_RWNX_MON_DATA */
|
|
} else {
|
|
#ifdef CONFIG_RWNX_MON_DATA
|
|
wiphy_err(wiphy,
|
|
"Monitor+Data interface support (MON_DATA) disabled in firmware but support compiled in driver\n");
|
|
res = -1;
|
|
#endif /* CONFIG_RWNX_MON_DATA */
|
|
}
|
|
#endif
|
|
|
|
// Check supported AMSDU RX size
|
|
amsdu_rx = (sys_feat >> MM_AMSDU_MAX_SIZE_BIT0) & 0x03;
|
|
if (amsdu_rx < rwnx_hw->mod_params->amsdu_rx_max) {
|
|
rwnx_hw->mod_params->amsdu_rx_max = amsdu_rx;
|
|
}
|
|
|
|
// Check supported BW
|
|
bw = (phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB;
|
|
// Check if 80MHz BW is supported
|
|
if (bw < 2) {
|
|
rwnx_hw->mod_params->use_80 = false;
|
|
}
|
|
// Check if 40MHz BW is supported
|
|
if (bw < 1)
|
|
rwnx_hw->mod_params->use_2040 = false;
|
|
|
|
// 80MHz BW shall be disabled if 40MHz is not enabled
|
|
if (!rwnx_hw->mod_params->use_2040)
|
|
rwnx_hw->mod_params->use_80 = false;
|
|
|
|
// Check if HT is supposed to be supported. If not, disable VHT/HE too
|
|
if (!rwnx_hw->mod_params->ht_on) {
|
|
rwnx_hw->mod_params->vht_on = false;
|
|
rwnx_hw->mod_params->he_on = false;
|
|
rwnx_hw->mod_params->he_ul_on = false;
|
|
rwnx_hw->mod_params->use_80 = false;
|
|
rwnx_hw->mod_params->use_2040 = false;
|
|
}
|
|
|
|
// LDPC is mandatory for HE40 and above, so if LDPC is not supported, then disable
|
|
// HE to use HT/VHT only
|
|
if (rwnx_hw->mod_params->use_2040 && !rwnx_hw->mod_params->ldpc_on) {
|
|
rwnx_hw->mod_params->he_on = false;
|
|
rwnx_hw->mod_params->he_ul_on = false;
|
|
}
|
|
|
|
// HT greenfield is not supported in modem >= 3.0
|
|
if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
|
|
rwnx_hw->mod_params->gf_rx_on = false;
|
|
}
|
|
|
|
if (!(sys_feat & BIT(MM_FEAT_MU_MIMO_RX_BIT)) ||
|
|
!rwnx_hw->mod_params->bfmee) {
|
|
rwnx_hw->mod_params->murx = false;
|
|
}
|
|
|
|
if ((sys_feat & BIT(MM_FEAT_MU_MIMO_TX_BIT))) {
|
|
#ifndef CONFIG_RWNX_MUMIMO_TX
|
|
wiphy_err(wiphy,
|
|
"MU-MIMO TX enabled in firmware but support not compiled in driver\n");
|
|
res = -1;
|
|
#endif /* CONFIG_RWNX_MUMIMO_TX */
|
|
if (!rwnx_hw->mod_params->bfmer)
|
|
rwnx_hw->mod_params->mutx = false;
|
|
// Check PHY and MAC HW MU-MIMO TX support and update parameter accordingly
|
|
else if (!(phy_feat & MDM_MUMIMOTX_BIT) || !(mac_feat & NXMAC_MU_MIMO_TX_BIT)) {
|
|
rwnx_hw->mod_params->mutx = false;
|
|
// Disable the feature in the bitfield so that it won't be displayed
|
|
sys_feat &= ~BIT(MM_FEAT_MU_MIMO_TX_BIT);
|
|
}
|
|
} else {
|
|
#ifdef CONFIG_RWNX_MUMIMO_TX
|
|
wiphy_err(wiphy,
|
|
"MU-MIMO TX disabled in firmware but support compiled in driver\n");
|
|
res = -1;
|
|
#else
|
|
rwnx_hw->mod_params->mutx = false;
|
|
#endif /* CONFIG_RWNX_MUMIMO_TX */
|
|
}
|
|
|
|
if (sys_feat & BIT(MM_FEAT_WAPI_BIT)) {
|
|
rwnx_enable_wapi(rwnx_hw);
|
|
}
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
if (sys_feat & BIT(MM_FEAT_MFP_BIT)) {
|
|
rwnx_enable_mfp(rwnx_hw);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
#define QUEUE_NAME "Broadcast/Multicast queue "
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
if (sys_feat & BIT(MM_FEAT_BCN_BIT)) {
|
|
#if NX_TXQ_CNT == 4
|
|
wiphy_err(wiphy, QUEUE_NAME
|
|
"enabled in firmware but support not compiled in driver\n");
|
|
res = -1;
|
|
#endif /* NX_TXQ_CNT == 4 */
|
|
} else {
|
|
#if NX_TXQ_CNT == 5
|
|
wiphy_err(wiphy, QUEUE_NAME
|
|
"disabled in firmware but support compiled in driver\n");
|
|
res = -1;
|
|
#endif /* NX_TXQ_CNT == 5 */
|
|
}
|
|
#undef QUEUE_NAME
|
|
|
|
#ifdef CONFIG_RWNX_RADAR
|
|
if (sys_feat & BIT(MM_FEAT_RADAR_BIT)) {
|
|
/* Enable combination with radar detection */
|
|
wiphy->n_iface_combinations++;
|
|
}
|
|
#endif /* CONFIG_RWNX_RADAR */
|
|
|
|
#ifndef CONFIG_RWNX_SDM
|
|
switch (__MDM_PHYCFG_FROM_VERS(phy_feat)) {
|
|
case MDM_PHY_CONFIG_TRIDENT:
|
|
case MDM_PHY_CONFIG_ELMA:
|
|
rwnx_hw->mod_params->nss = 1;
|
|
break;
|
|
case MDM_PHY_CONFIG_KARST:
|
|
{
|
|
int nss_supp = (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB;
|
|
if (rwnx_hw->mod_params->nss > nss_supp)
|
|
rwnx_hw->mod_params->nss = nss_supp;
|
|
}
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
#endif /* CONFIG_RWNX_SDM */
|
|
|
|
if (rwnx_hw->mod_params->nss < 1 || rwnx_hw->mod_params->nss > 2)
|
|
rwnx_hw->mod_params->nss = 1;
|
|
|
|
if (rwnx_hw->mod_params->phy_cfg < 0 || rwnx_hw->mod_params->phy_cfg > 5)
|
|
rwnx_hw->mod_params->phy_cfg = 2;
|
|
|
|
if (rwnx_hw->mod_params->mcs_map < 0 || rwnx_hw->mod_params->mcs_map > 2)
|
|
rwnx_hw->mod_params->mcs_map = 0;
|
|
|
|
wiphy_info(wiphy, "PHY features: [NSS=%d][CHBW=%d]%s%s\n",
|
|
rwnx_hw->mod_params->nss,
|
|
20 * (1 << ((phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB)),
|
|
rwnx_hw->mod_params->ldpc_on ? "[LDPC]" : "",
|
|
rwnx_hw->mod_params->he_on ? "[HE]" : "");
|
|
|
|
#define PRINT_RWNX_FEAT(feat) \
|
|
(sys_feat & BIT(MM_FEAT_##feat##_BIT) ? "["#feat"]" : "")
|
|
|
|
wiphy_info(wiphy, "FW features: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
PRINT_RWNX_FEAT(BCN),
|
|
PRINT_RWNX_FEAT(AUTOBCN),
|
|
PRINT_RWNX_FEAT(HWSCAN),
|
|
PRINT_RWNX_FEAT(CMON),
|
|
PRINT_RWNX_FEAT(MROLE),
|
|
PRINT_RWNX_FEAT(RADAR),
|
|
PRINT_RWNX_FEAT(PS),
|
|
PRINT_RWNX_FEAT(UAPSD),
|
|
PRINT_RWNX_FEAT(DPSM),
|
|
PRINT_RWNX_FEAT(AMPDU),
|
|
PRINT_RWNX_FEAT(AMSDU),
|
|
PRINT_RWNX_FEAT(CHNL_CTXT),
|
|
PRINT_RWNX_FEAT(REORD),
|
|
PRINT_RWNX_FEAT(P2P),
|
|
PRINT_RWNX_FEAT(P2P_GO),
|
|
PRINT_RWNX_FEAT(UMAC),
|
|
PRINT_RWNX_FEAT(VHT),
|
|
PRINT_RWNX_FEAT(HE),
|
|
PRINT_RWNX_FEAT(BFMEE),
|
|
PRINT_RWNX_FEAT(BFMER),
|
|
PRINT_RWNX_FEAT(WAPI),
|
|
PRINT_RWNX_FEAT(MFP),
|
|
PRINT_RWNX_FEAT(MU_MIMO_RX),
|
|
PRINT_RWNX_FEAT(MU_MIMO_TX),
|
|
PRINT_RWNX_FEAT(MESH),
|
|
PRINT_RWNX_FEAT(TDLS),
|
|
PRINT_RWNX_FEAT(ANT_DIV));
|
|
#undef PRINT_RWNX_FEAT
|
|
|
|
if (max_sta_nb != NX_REMOTE_STA_MAX) {
|
|
wiphy_err(wiphy, "Different number of supported stations between driver and FW (%d != %d)\n",
|
|
NX_REMOTE_STA_MAX, max_sta_nb);
|
|
res = -1;
|
|
}
|
|
|
|
if (max_vif_nb != NX_VIRT_DEV_MAX) {
|
|
wiphy_err(wiphy, "Different number of supported virtual interfaces between driver and FW (%d != %d)\n",
|
|
NX_VIRT_DEV_MAX, max_vif_nb);
|
|
res = -1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
|
|
static void rwnx_set_vht_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
#ifdef CONFIG_VHT_FOR_OLD_KERNEL
|
|
#ifdef USE_5G
|
|
struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
|
|
#endif
|
|
struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
|
|
|
|
int i;
|
|
int nss = rwnx_hw->mod_params->nss;
|
|
int mcs_map;
|
|
int mcs_map_max;
|
|
int bw_max;
|
|
|
|
if (!rwnx_hw->mod_params->vht_on) {
|
|
return;
|
|
}
|
|
|
|
rwnx_hw->vht_cap_2G.vht_supported = true;
|
|
if (rwnx_hw->mod_params->sgi80)
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
|
|
if (rwnx_hw->mod_params->ldpc_on)
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_RXLDPC;
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
rwnx_hw->vht_cap_2G.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
|
|
#else
|
|
rwnx_hw->vht_cap_2G.cap |= 3 << 13;
|
|
#endif
|
|
}
|
|
if (nss > 1)
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_TXSTBC;
|
|
|
|
// Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
|
|
rwnx_hw->vht_cap_2G.cap |= rwnx_hw->mod_params->amsdu_rx_max;
|
|
|
|
if (rwnx_hw->mod_params->bfmer) {
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
|
|
/* Set number of sounding dimensions */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
rwnx_hw->vht_cap_2G.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
|
|
#else
|
|
rwnx_hw->vht_cap_2G.cap |= (nss - 1) << 16;
|
|
#endif
|
|
}
|
|
if (rwnx_hw->mod_params->murx)
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
if (rwnx_hw->mod_params->mutx)
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
|
|
|
/*
|
|
* MCS map:
|
|
* This capabilities are filled according to the mcs_map module parameter.
|
|
* However currently we have some limitations due to FPGA clock constraints
|
|
* that prevent always using the range of MCS that is defined by the
|
|
* parameter:
|
|
* - in RX, 2SS, we support up to MCS7
|
|
* - in TX, 2SS, we support up to MCS8
|
|
*/
|
|
// Get max supported BW
|
|
if (rwnx_hw->mod_params->use_80)
|
|
bw_max = PHY_CHNL_BW_80;
|
|
else if (rwnx_hw->mod_params->use_2040)
|
|
bw_max = PHY_CHNL_BW_40;
|
|
else
|
|
bw_max = PHY_CHNL_BW_20;
|
|
|
|
// Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
|
|
// MCS9 is not supported in 1 and 2 SS
|
|
if (rwnx_hw->mod_params->use_2040)
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
|
|
else
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
rwnx_hw->vht_cap_2G.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
rwnx_hw->vht_cap_2G.vht_mcs.rx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
rwnx_hw->vht_cap_2G.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_VHT_MCS_SUPPORT_0_8);
|
|
}
|
|
for (; i < 8; i++) {
|
|
rwnx_hw->vht_cap_2G.vht_mcs.tx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
if (!rwnx_hw->mod_params->use_80) {
|
|
#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
|
|
#endif//CONFIG_VENDOR_RWNX_VHT_NO80
|
|
rwnx_hw->vht_cap_2G.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
}
|
|
|
|
rwnx_hw->vht_cap_2G.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
|
|
printk("%s, vht_capa_info=0x%x\n", __func__, rwnx_hw->vht_cap_2G.cap);
|
|
#ifdef USE_5G
|
|
if (rwnx_hw->band_5g_support) {
|
|
rwnx_hw->vht_cap_5G.vht_supported = true;
|
|
if (rwnx_hw->mod_params->sgi80)
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
|
|
if (rwnx_hw->mod_params->ldpc_on)
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_RXLDPC;
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
rwnx_hw->vht_cap_5G.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
|
|
#else
|
|
rwnx_hw->vht_cap_5G.cap |= 3 << 13;
|
|
#endif
|
|
}
|
|
if (nss > 1)
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_TXSTBC;
|
|
|
|
// Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
|
|
rwnx_hw->vht_cap_5G.cap |= rwnx_hw->mod_params->amsdu_rx_max;
|
|
|
|
if (rwnx_hw->mod_params->bfmer) {
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
|
|
/* Set number of sounding dimensions */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
rwnx_hw->vht_cap_5G.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
|
|
#else
|
|
rwnx_hw->vht_cap_5G.cap |= (nss - 1) << 16;
|
|
#endif
|
|
}
|
|
if (rwnx_hw->mod_params->murx)
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
if (rwnx_hw->mod_params->mutx)
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
|
|
|
/*
|
|
* MCS map:
|
|
* This capabilities are filled according to the mcs_map module parameter.
|
|
* However currently we have some limitations due to FPGA clock constraints
|
|
* that prevent always using the range of MCS that is defined by the
|
|
* parameter:
|
|
* - in RX, 2SS, we support up to MCS7
|
|
* - in TX, 2SS, we support up to MCS8
|
|
*/
|
|
// Get max supported BW
|
|
if (rwnx_hw->mod_params->use_80)
|
|
bw_max = PHY_CHNL_BW_80;
|
|
else if (rwnx_hw->mod_params->use_2040)
|
|
bw_max = PHY_CHNL_BW_40;
|
|
else
|
|
bw_max = PHY_CHNL_BW_20;
|
|
|
|
// Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
|
|
// MCS9 is not supported in 1 and 2 SS
|
|
if (rwnx_hw->mod_params->use_2040)
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
|
|
else
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
rwnx_hw->vht_cap_5G.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
rwnx_hw->vht_cap_5G.vht_mcs.rx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
rwnx_hw->vht_cap_5G.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_VHT_MCS_SUPPORT_0_8);
|
|
}
|
|
for (; i < 8; i++) {
|
|
rwnx_hw->vht_cap_5G.vht_mcs.tx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
if (!rwnx_hw->mod_params->use_80) {
|
|
#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
|
|
rwnx_hw->vht_cap_5G.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
|
|
#endif//CONFIG_VENDOR_RWNX_VHT_NO80
|
|
rwnx_hw->vht_cap_5G.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
}
|
|
|
|
}
|
|
#endif//USE_5G
|
|
return;
|
|
#else
|
|
struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
|
|
struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
|
|
|
|
int i;
|
|
int nss = rwnx_hw->mod_params->nss;
|
|
int mcs_map;
|
|
int mcs_map_max;
|
|
int bw_max;
|
|
#endif//CONFIG_VHT_FOR_OLD_KERNEL
|
|
|
|
if (!rwnx_hw->mod_params->vht_on) {
|
|
return;
|
|
}
|
|
|
|
|
|
band_2GHz->vht_cap.vht_supported = true;
|
|
if (rwnx_hw->mod_params->sgi80)
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
|
|
if (rwnx_hw->mod_params->ldpc_on)
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
band_2GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
|
|
#else
|
|
band_2GHz->vht_cap.cap |= 3 << 13;
|
|
#endif
|
|
}
|
|
if (nss > 1)
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
|
|
|
|
// Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
|
|
band_2GHz->vht_cap.cap |= rwnx_hw->mod_params->amsdu_rx_max;
|
|
|
|
if (rwnx_hw->mod_params->bfmer) {
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
|
|
/* Set number of sounding dimensions */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
band_2GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
|
|
#else
|
|
band_2GHz->vht_cap.cap |= (nss - 1) << 16;
|
|
#endif
|
|
}
|
|
if (rwnx_hw->mod_params->murx)
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
if (rwnx_hw->mod_params->mutx)
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
|
|
|
/*
|
|
* MCS map:
|
|
* This capabilities are filled according to the mcs_map module parameter.
|
|
* However currently we have some limitations due to FPGA clock constraints
|
|
* that prevent always using the range of MCS that is defined by the
|
|
* parameter:
|
|
* - in RX, 2SS, we support up to MCS7
|
|
* - in TX, 2SS, we support up to MCS8
|
|
*/
|
|
// Get max supported BW
|
|
if (rwnx_hw->mod_params->use_80)
|
|
bw_max = PHY_CHNL_BW_80;
|
|
else if (rwnx_hw->mod_params->use_2040)
|
|
bw_max = PHY_CHNL_BW_40;
|
|
else
|
|
bw_max = PHY_CHNL_BW_20;
|
|
|
|
// Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
|
|
// MCS9 is not supported in 1 and 2 SS
|
|
if (rwnx_hw->mod_params->use_2040)
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
|
|
else
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
band_2GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
band_2GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
band_2GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
band_2GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_VHT_MCS_SUPPORT_0_8);
|
|
}
|
|
for (; i < 8; i++) {
|
|
band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
if (!rwnx_hw->mod_params->use_80) {
|
|
#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
|
|
band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
|
|
#endif
|
|
band_2GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
}
|
|
|
|
if (rwnx_hw->band_5g_support) {
|
|
band_5GHz->vht_cap.vht_supported = true;
|
|
if (rwnx_hw->mod_params->sgi80)
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
|
|
if (rwnx_hw->mod_params->ldpc_on)
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
band_5GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
|
|
#else
|
|
band_5GHz->vht_cap.cap |= 3 << 13;
|
|
#endif
|
|
}
|
|
if (nss > 1)
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
|
|
|
|
// Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
|
|
band_5GHz->vht_cap.cap |= rwnx_hw->mod_params->amsdu_rx_max;
|
|
|
|
if (rwnx_hw->mod_params->bfmer) {
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
|
|
/* Set number of sounding dimensions */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
band_5GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
|
|
#else
|
|
band_5GHz->vht_cap.cap |= (nss - 1) << 16;
|
|
#endif
|
|
}
|
|
if (rwnx_hw->mod_params->murx)
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
if (rwnx_hw->mod_params->mutx)
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
|
|
|
|
/*
|
|
* MCS map:
|
|
* This capabilities are filled according to the mcs_map module parameter.
|
|
* However currently we have some limitations due to FPGA clock constraints
|
|
* that prevent always using the range of MCS that is defined by the
|
|
* parameter:
|
|
* - in RX, 2SS, we support up to MCS7
|
|
* - in TX, 2SS, we support up to MCS8
|
|
*/
|
|
// Get max supported BW
|
|
if (rwnx_hw->mod_params->use_80)
|
|
bw_max = PHY_CHNL_BW_80;
|
|
else if (rwnx_hw->mod_params->use_2040)
|
|
bw_max = PHY_CHNL_BW_40;
|
|
else
|
|
bw_max = PHY_CHNL_BW_20;
|
|
|
|
// Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
|
|
// MCS9 is not supported in 1 and 2 SS
|
|
if (rwnx_hw->mod_params->use_2040)
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
|
|
else
|
|
mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
band_5GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
band_5GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map, mcs_map_max);
|
|
band_5GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
|
|
for (i = 0; i < nss; i++) {
|
|
band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
|
|
band_5GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_VHT_MCS_SUPPORT_0_8);
|
|
}
|
|
for (; i < 8; i++) {
|
|
band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
|
|
}
|
|
|
|
if (!rwnx_hw->mod_params->use_80) {
|
|
#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
|
|
#endif
|
|
band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rwnx_set_ht_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
|
|
struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
|
|
int i;
|
|
int nss = rwnx_hw->mod_params->nss;
|
|
|
|
if (!rwnx_hw->mod_params->ht_on) {
|
|
band_2GHz->ht_cap.ht_supported = false;
|
|
if (rwnx_hw->band_5g_support)
|
|
band_5GHz->ht_cap.ht_supported = false;
|
|
return;
|
|
}
|
|
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
band_2GHz->ht_cap.cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
|
if (rwnx_hw->mod_params->ldpc_on)
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
|
|
if (rwnx_hw->mod_params->use_2040) {
|
|
band_2GHz->ht_cap.mcs.rx_mask[4] = 0x1; /* MCS32 */
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(135 * nss);
|
|
} else {
|
|
band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(65 * nss);
|
|
}
|
|
if (nss > 1)
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
|
|
|
|
// Update the AMSDU max RX size
|
|
if (rwnx_hw->mod_params->amsdu_rx_max)
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
|
|
|
|
if (rwnx_hw->mod_params->sgi) {
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
|
|
if (rwnx_hw->mod_params->use_2040) {
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
|
|
band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(150 * nss);
|
|
} else
|
|
band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(72 * nss);
|
|
}
|
|
if (rwnx_hw->mod_params->gf_rx_on)
|
|
band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
|
|
|
|
for (i = 0; i < nss; i++) {
|
|
band_2GHz->ht_cap.mcs.rx_mask[i] = 0xFF;
|
|
}
|
|
|
|
if (rwnx_hw->band_5g_support)
|
|
band_5GHz->ht_cap = band_2GHz->ht_cap;
|
|
}
|
|
|
|
#ifdef CONFIG_HE_FOR_OLD_KERNEL
|
|
extern struct ieee80211_sband_iftype_data rwnx_he_capa;
|
|
#endif
|
|
static void rwnx_set_he_capa(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
#ifdef CONFIG_HE_FOR_OLD_KERNEL
|
|
struct ieee80211_sta_he_cap *he_cap;
|
|
int i;
|
|
int nss = rwnx_hw->mod_params->nss;
|
|
int mcs_map;
|
|
|
|
he_cap = (struct ieee80211_sta_he_cap *) &rwnx_he_capa.he_cap;
|
|
he_cap->has_he = true;
|
|
he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
|
|
if (rwnx_hw->mod_params->use_2040) {
|
|
he_cap->he_cap_elem.phy_cap_info[0] |=
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
|
|
he_cap->ppe_thres[0] |= 0x10;
|
|
}
|
|
//if (rwnx_hw->mod_params->use_80)
|
|
{
|
|
he_cap->he_cap_elem.phy_cap_info[0] |=
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
|
|
}
|
|
if (rwnx_hw->mod_params->ldpc_on) {
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
|
|
} else {
|
|
// If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
|
|
// for MCS 10 and 11
|
|
rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_HE_MCS_SUPPORT_0_9);
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US
|
|
| IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
|
|
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
|
|
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
|
|
IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
|
|
IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
|
|
#endif
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
|
|
he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
|
|
IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
|
|
IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
|
|
he_cap->he_cap_elem.phy_cap_info[4] |=
|
|
IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
|
|
}
|
|
he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
|
|
IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
|
|
he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
|
|
IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
|
|
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
|
|
IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
|
|
he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
|
|
he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
|
|
he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
|
|
IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
|
|
if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80)
|
|
mcs_map = rwnx_hw->mod_params->he_mcs_map;
|
|
else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8801 || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
|
|
rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW)
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
|
|
memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
|
|
for (i = 0; i < nss; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
|
|
mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
|
|
}
|
|
mcs_map = rwnx_hw->mod_params->he_mcs_map;
|
|
for (i = 0; i < nss; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
|
|
IEEE80211_HE_MCS_SUPPORT_0_7);
|
|
}
|
|
for (; i < 8; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
|
|
}
|
|
|
|
return ;
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
|
struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
|
|
struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
|
|
int i;
|
|
int nss = rwnx_hw->mod_params->nss;
|
|
struct ieee80211_sta_he_cap *he_cap;
|
|
int mcs_map;
|
|
if (!rwnx_hw->mod_params->he_on) {
|
|
band_2GHz->iftype_data = NULL;
|
|
band_2GHz->n_iftype_data = 0;
|
|
if (rwnx_hw->band_5g_support) {
|
|
band_5GHz->iftype_data = NULL;
|
|
band_5GHz->n_iftype_data = 0;
|
|
}
|
|
return;
|
|
}
|
|
he_cap = (struct ieee80211_sta_he_cap *) &band_2GHz->iftype_data->he_cap;
|
|
he_cap->has_he = true;
|
|
he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
|
|
if (rwnx_hw->mod_params->use_2040) {
|
|
he_cap->he_cap_elem.phy_cap_info[0] |=
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
|
|
he_cap->ppe_thres[0] |= 0x10;
|
|
}
|
|
//if (rwnx_hw->mod_params->use_80)
|
|
{
|
|
he_cap->he_cap_elem.phy_cap_info[0] |=
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
|
|
}
|
|
if (rwnx_hw->mod_params->ldpc_on) {
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
|
|
} else {
|
|
// If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
|
|
// for MCS 10 and 11
|
|
rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_HE_MCS_SUPPORT_0_9);
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US
|
|
| IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
|
|
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
|
|
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
|
|
IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
|
|
IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
|
|
#endif
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
|
|
IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
|
|
IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
|
|
IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
|
|
IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
|
|
#endif
|
|
|
|
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
|
|
he_cap->he_cap_elem.phy_cap_info[4] |=
|
|
IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
|
|
}
|
|
he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
|
|
IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
|
|
IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
|
|
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
|
|
IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
|
|
IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
|
|
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
|
|
IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
|
|
#endif
|
|
|
|
he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
|
|
he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
|
|
IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
|
|
#endif
|
|
if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80){
|
|
mcs_map = rwnx_hw->mod_params->he_mcs_map;
|
|
}
|
|
else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8801 || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
|
|
rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW){
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
|
|
}
|
|
memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
|
|
for (i = 0; i < nss; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
|
|
mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
|
|
}
|
|
mcs_map = rwnx_hw->mod_params->he_mcs_map;
|
|
for (i = 0; i < nss; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
|
|
IEEE80211_HE_MCS_SUPPORT_0_7);
|
|
}
|
|
for (; i < 8; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
|
|
}
|
|
|
|
if (rwnx_hw->band_5g_support) {
|
|
he_cap = (struct ieee80211_sta_he_cap *) &band_5GHz->iftype_data->he_cap;
|
|
he_cap->has_he = true;
|
|
he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
|
|
if (rwnx_hw->mod_params->use_2040) {
|
|
he_cap->he_cap_elem.phy_cap_info[0] |=
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
|
|
he_cap->ppe_thres[0] |= 0x10;
|
|
}
|
|
//if (rwnx_hw->mod_params->use_80)
|
|
{
|
|
he_cap->he_cap_elem.phy_cap_info[0] |=
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
|
|
}
|
|
if (rwnx_hw->mod_params->ldpc_on) {
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
|
|
} else {
|
|
// If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
|
|
// for MCS 10 and 11
|
|
rwnx_hw->mod_params->he_mcs_map = min_t(int, rwnx_hw->mod_params->mcs_map,
|
|
IEEE80211_HE_MCS_SUPPORT_0_9);
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US |
|
|
IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
|
|
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
|
|
IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
|
|
IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
|
|
#endif
|
|
if (rwnx_hw->mod_params->stbc_on)
|
|
he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
|
|
IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
|
|
IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
|
|
IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1 |
|
|
IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
|
|
#endif
|
|
|
|
if (rwnx_hw->mod_params->bfmee) {
|
|
he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
|
|
he_cap->he_cap_elem.phy_cap_info[4] |=
|
|
IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
|
|
}
|
|
he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
|
|
IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
|
|
IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
|
|
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
|
|
IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
|
|
#else
|
|
he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
|
|
IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
|
|
IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
|
|
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
|
|
IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
|
|
#endif
|
|
|
|
he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
|
|
he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
|
|
IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
|
|
#endif
|
|
if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80)
|
|
mcs_map = rwnx_hw->mod_params->he_mcs_map;
|
|
else if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8801 || rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DC ||
|
|
rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800DW)
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9);
|
|
memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
|
|
for (i = 0; i < nss; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
|
|
mcs_map = IEEE80211_HE_MCS_SUPPORT_0_7;
|
|
}
|
|
for (; i < 8; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
|
|
}
|
|
mcs_map = rwnx_hw->mod_params->he_mcs_map;
|
|
for (i = 0; i < nss; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
|
|
mcs_map = min_t(int, rwnx_hw->mod_params->he_mcs_map,
|
|
IEEE80211_HE_MCS_SUPPORT_0_7);
|
|
}
|
|
for (; i < 8; i++) {
|
|
__le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
|
|
he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void rwnx_set_wiphy_params(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
|
|
struct ieee80211_regdomain *regdomain;
|
|
#endif
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
/* FULLMAC specific parameters */
|
|
wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
|
|
wiphy->max_scan_ssids = SCAN_SSID_MAX;
|
|
wiphy->max_scan_ie_len = SCANU_MAX_IE_LEN;
|
|
#endif /* CONFIG_RWNX_FULLMAC */
|
|
|
|
if (rwnx_hw->mod_params->tdls) {
|
|
/* TDLS support */
|
|
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
/* TDLS external setup support */
|
|
wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
|
|
#endif
|
|
}
|
|
|
|
if (rwnx_hw->mod_params->ap_uapsd_on)
|
|
wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
|
|
|
|
#ifdef CONFIG_RWNX_FULLMAC
|
|
if (rwnx_hw->mod_params->ps_on)
|
|
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
|
|
else
|
|
wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
|
|
#endif
|
|
|
|
if (rwnx_hw->mod_params->custregd) {
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
// Apply custom regulatory. Note that for recent kernel versions we use instead the
|
|
// REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
|
|
// function, that needs to be called after wiphy registration
|
|
memcpy(country_code, default_ccode, sizeof(default_ccode));
|
|
regdomain = getRegdomainFromRwnxDB(wiphy, default_ccode);
|
|
printk(KERN_CRIT
|
|
"\n\n%s: CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES\n\n",
|
|
__func__);
|
|
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
|
wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
|
|
wiphy_apply_custom_regulatory(wiphy, regdomain);
|
|
#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
|
|
memcpy(country_code, default_ccode, sizeof(default_ccode));
|
|
regdomain = getRegdomainFromRwnxDB(wiphy, default_ccode);
|
|
printk(KERN_CRIT"%s: Registering custom regulatory\n", __func__);
|
|
wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
|
|
wiphy_apply_custom_regulatory(wiphy, regdomain);
|
|
#endif
|
|
// Check if custom channel set shall be enabled. In such case only monitor mode is
|
|
// supported
|
|
if (rwnx_hw->mod_params->custchan) {
|
|
wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
|
|
|
|
// Enable "extra" channels
|
|
wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
|
|
//#ifdef USE_5G
|
|
if(rwnx_hw->band_5g_support){
|
|
wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
|
|
}
|
|
//#endif
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if (rwnx_hw->mod_params->custregd) {
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
|
|
// Apply custom regulatory. Note that for recent kernel versions we use instead the
|
|
// REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
|
|
// function, that needs to be called after wiphy registration
|
|
printk(KERN_CRIT
|
|
"\n\n%s: CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES\n\n",
|
|
__func__);
|
|
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
|
wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
|
|
wiphy_apply_custom_regulatory(wiphy, &rwnx_regdom);
|
|
#endif
|
|
// Check if custom channel set shall be enabled. In such case only monitor mode is
|
|
// supported
|
|
if (rwnx_hw->mod_params->custchan) {
|
|
wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
|
|
|
|
// Enable "extra" channels
|
|
wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
|
|
if (rwnx_hw->band_5g_support)
|
|
wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
static void rwnx_set_rf_params(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
#ifndef CONFIG_RWNX_SDM
|
|
struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
|
|
struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
|
|
u32 mdm_phy_cfg = __MDM_PHYCFG_FROM_VERS(rwnx_hw->version_cfm.version_phy_1);
|
|
|
|
/*
|
|
* Get configuration file depending on the RF
|
|
*/
|
|
if (mdm_phy_cfg == MDM_PHY_CONFIG_TRIDENT) {
|
|
struct rwnx_phy_conf_file phy_conf;
|
|
// Retrieve the Trident configuration
|
|
rwnx_parse_phy_configfile(rwnx_hw, RWNX_PHY_CONFIG_TRD_NAME,
|
|
&phy_conf, rwnx_hw->mod_params->phy_cfg);
|
|
memcpy(&rwnx_hw->phy.cfg, &phy_conf.trd, sizeof(phy_conf.trd));
|
|
} else if (mdm_phy_cfg == MDM_PHY_CONFIG_ELMA) {
|
|
} else if (mdm_phy_cfg == MDM_PHY_CONFIG_KARST) {
|
|
struct rwnx_phy_conf_file phy_conf;
|
|
// We use the NSS parameter as is
|
|
// Retrieve the Karst configuration
|
|
rwnx_parse_phy_configfile(rwnx_hw, RWNX_PHY_CONFIG_KARST_NAME,
|
|
&phy_conf, rwnx_hw->mod_params->phy_cfg);
|
|
|
|
memcpy(&rwnx_hw->phy.cfg, &phy_conf.karst, sizeof(phy_conf.karst));
|
|
} else {
|
|
WARN_ON(1);
|
|
}
|
|
|
|
/*
|
|
* adjust caps depending on the RF
|
|
*/
|
|
switch (mdm_phy_cfg) {
|
|
case MDM_PHY_CONFIG_TRIDENT:
|
|
{
|
|
wiphy_dbg(wiphy, "found Trident phy .. limit BW to 40MHz\n");
|
|
rwnx_hw->phy.limit_bw = true;
|
|
if (rwnx_hw->band_5g_support) {
|
|
#ifdef CONFIG_VENDOR_RWNX_VHT_NO80
|
|
band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
|
|
#endif
|
|
band_5GHz->vht_cap.cap &= ~(IEEE80211_VHT_CAP_SHORT_GI_80 |
|
|
IEEE80211_VHT_CAP_RXSTBC_MASK);
|
|
}
|
|
break;
|
|
}
|
|
case MDM_PHY_CONFIG_ELMA:
|
|
wiphy_dbg(wiphy, "found ELMA phy .. disabling 2.4GHz and greenfield rx\n");
|
|
wiphy->bands[NL80211_BAND_2GHZ] = NULL;
|
|
band_2GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
|
|
if (rwnx_hw->band_5g_support) {
|
|
band_5GHz->ht_cap.cap &= ~IEEE80211_HT_CAP_GRN_FLD;
|
|
band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK;
|
|
}
|
|
break;
|
|
case MDM_PHY_CONFIG_KARST:
|
|
{
|
|
wiphy_dbg(wiphy, "found KARST phy\n");
|
|
break;
|
|
}
|
|
default:
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
#endif /* CONFIG_RWNX_SDM */
|
|
}
|
|
#endif
|
|
|
|
int rwnx_handle_dynparams(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
#if 0
|
|
/* Check compatibility between requested parameters and HW/SW features */
|
|
int ret;
|
|
|
|
ret = rwnx_check_fw_hw_feature(rwnx_hw, wiphy);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Allocate the RX buffers according to the maximum AMSDU RX size */
|
|
ret = rwnx_ipc_rxbuf_init(rwnx_hw,
|
|
(4 * (rwnx_hw->mod_params->amsdu_rx_max + 1) + 1) * 1024);
|
|
if (ret) {
|
|
wiphy_err(wiphy, "Cannot allocate the RX buffers\n");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
|
|
rwnx_hw->mod_params->sgi80 = true;
|
|
rwnx_hw->mod_params->use_80 = true;
|
|
}
|
|
|
|
if (rwnx_hw->sdiodev->chipid == PRODUCT_ID_AIC8800D80) {
|
|
rwnx_hw->mod_params->use_80 = true;
|
|
}
|
|
|
|
/* Set wiphy parameters */
|
|
rwnx_set_wiphy_params(rwnx_hw, wiphy);
|
|
/* Set VHT capabilities */
|
|
rwnx_set_vht_capa(rwnx_hw, wiphy);
|
|
/* Set HE capabilities */
|
|
rwnx_set_he_capa(rwnx_hw, wiphy);
|
|
/* Set HT capabilities */
|
|
rwnx_set_ht_capa(rwnx_hw, wiphy);
|
|
/* Set RF specific parameters (shall be done last as it might change some
|
|
capabilities previously set) */
|
|
#if 0
|
|
rwnx_set_rf_params(rwnx_hw, wiphy);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void rwnx_custregd(struct rwnx_hw *rwnx_hw, struct wiphy *wiphy)
|
|
{
|
|
// For older kernel version, the custom regulatory is applied before the wiphy
|
|
// registration (in rwnx_set_wiphy_params()), so nothing has to be done here
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
|
|
if (!rwnx_hw->mod_params->custregd)
|
|
return;
|
|
|
|
wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
|
|
wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
|
|
|
|
rtnl_lock();
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
if (regulatory_set_wiphy_regd_sync(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){
|
|
wiphy_err(wiphy, "Failed to set custom regdomain\n");
|
|
}
|
|
#else
|
|
if (regulatory_set_wiphy_regd_sync_rtnl(wiphy, getRegdomainFromRwnxDB(wiphy, default_ccode))){
|
|
wiphy_err(wiphy, "Failed to set custom regdomain\n");
|
|
}
|
|
#endif
|
|
|
|
else{
|
|
wiphy_err(wiphy,"\n"
|
|
"*******************************************************\n"
|
|
"** CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES **\n"
|
|
"*******************************************************\n");
|
|
}
|
|
rtnl_unlock();
|
|
#endif
|
|
|
|
}
|