Commit 110b32f0 authored by Gregory Greenman's avatar Gregory Greenman Committed by Luca Coelho

iwlwifi: mvm: rs: add basic implementation of the new RS API handlers

This patch adds rate scaling configuration command and
implements a few other handlers.
Signed-off-by: default avatarGregory Greenman <gregory.greenman@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 9f66a397
......@@ -82,6 +82,11 @@ enum iwl_data_path_subcmd_ids {
*/
TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2,
/**
* @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd
*/
TLC_MNG_CONFIG_CMD = 0xF,
/**
* @STA_PM_NOTIF: &struct iwl_mvm_pm_state_notification
*/
......
......@@ -62,6 +62,173 @@
#include "mac.h"
/**
* enum iwl_tlc_mng_cfg_flags_enum - options for TLC config flags
* @IWL_TLC_MNG_CFG_FLAGS_CCK_MSK: CCK support
* @IWL_TLC_MNG_CFG_FLAGS_DD_MSK: enable DD
* @IWL_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC
* @IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK: enable LDPC
* @IWL_TLC_MNG_CFG_FLAGS_BF_MSK: enable BFER
* @IWL_TLC_MNG_CFG_FLAGS_DCM_MSK: enable DCM
*/
enum iwl_tlc_mng_cfg_flags_enum {
IWL_TLC_MNG_CFG_FLAGS_CCK_MSK = BIT(0),
IWL_TLC_MNG_CFG_FLAGS_DD_MSK = BIT(1),
IWL_TLC_MNG_CFG_FLAGS_STBC_MSK = BIT(2),
IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK = BIT(3),
IWL_TLC_MNG_CFG_FLAGS_BF_MSK = BIT(4),
IWL_TLC_MNG_CFG_FLAGS_DCM_MSK = BIT(5),
};
/**
* enum iwl_tlc_mng_cfg_cw_enum - channel width options
* @IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ: 20MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ: 40MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ: 80MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ: 160MHZ channel
* @IWL_TLC_MNG_MAX_CH_WIDTH_LAST: maximum value
*/
enum iwl_tlc_mng_cfg_cw_enum {
IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ,
IWL_TLC_MNG_MAX_CH_WIDTH_LAST = IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ,
};
/**
* enum iwl_tlc_mng_cfg_chains_enum - possible chains
* @IWL_TLC_MNG_CHAIN_A_MSK: chain A
* @IWL_TLC_MNG_CHAIN_B_MSK: chain B
* @IWL_TLC_MNG_CHAIN_C_MSK: chain C
*/
enum iwl_tlc_mng_cfg_chains_enum {
IWL_TLC_MNG_CHAIN_A_MSK = BIT(0),
IWL_TLC_MNG_CHAIN_B_MSK = BIT(1),
IWL_TLC_MNG_CHAIN_C_MSK = BIT(2),
};
/**
* enum iwl_tlc_mng_cfg_gi_enum - guard interval options
* @IWL_TLC_MNG_SGI_20MHZ_MSK: enable short GI for 20MHZ
* @IWL_TLC_MNG_SGI_40MHZ_MSK: enable short GI for 40MHZ
* @IWL_TLC_MNG_SGI_80MHZ_MSK: enable short GI for 80MHZ
* @IWL_TLC_MNG_SGI_160MHZ_MSK: enable short GI for 160MHZ
*/
enum iwl_tlc_mng_cfg_gi_enum {
IWL_TLC_MNG_SGI_20MHZ_MSK = BIT(0),
IWL_TLC_MNG_SGI_40MHZ_MSK = BIT(1),
IWL_TLC_MNG_SGI_80MHZ_MSK = BIT(2),
IWL_TLC_MNG_SGI_160MHZ_MSK = BIT(3),
};
/**
* enum iwl_tlc_mng_cfg_mode_enum - supported modes
* @IWL_TLC_MNG_MODE_CCK: enable CCK
* @IWL_TLC_MNG_MODE_OFDM_NON_HT: enable OFDM (non HT)
* @IWL_TLC_MNG_MODE_NON_HT: enable non HT
* @IWL_TLC_MNG_MODE_HT: enable HT
* @IWL_TLC_MNG_MODE_VHT: enable VHT
* @IWL_TLC_MNG_MODE_HE: enable HE
* @IWL_TLC_MNG_MODE_INVALID: invalid value
* @IWL_TLC_MNG_MODE_NUM: a count of possible modes
*/
enum iwl_tlc_mng_cfg_mode_enum {
IWL_TLC_MNG_MODE_CCK = 0,
IWL_TLC_MNG_MODE_OFDM_NON_HT = IWL_TLC_MNG_MODE_CCK,
IWL_TLC_MNG_MODE_NON_HT = IWL_TLC_MNG_MODE_CCK,
IWL_TLC_MNG_MODE_HT,
IWL_TLC_MNG_MODE_VHT,
IWL_TLC_MNG_MODE_HE,
IWL_TLC_MNG_MODE_INVALID,
IWL_TLC_MNG_MODE_NUM = IWL_TLC_MNG_MODE_INVALID,
};
/**
* enum iwl_tlc_mng_vht_he_types_enum - VHT HE types
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU: VHT HT single user
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT: VHT HT single user extended
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU: VHT HT multiple users
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED: trigger based
* @IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM: a count of possible types
*/
enum iwl_tlc_mng_vht_he_types_enum {
IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU = 0,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_SU_EXT,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_MU,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED,
IWL_TLC_MNG_VALID_VHT_HE_TYPES_NUM =
IWL_TLC_MNG_VALID_VHT_HE_TYPES_TRIG_BASED,
};
/**
* enum iwl_tlc_mng_ht_rates_enum - HT/VHT rates
* @IWL_TLC_MNG_HT_RATE_MCS0: index of MCS0
* @IWL_TLC_MNG_HT_RATE_MCS1: index of MCS1
* @IWL_TLC_MNG_HT_RATE_MCS2: index of MCS2
* @IWL_TLC_MNG_HT_RATE_MCS3: index of MCS3
* @IWL_TLC_MNG_HT_RATE_MCS4: index of MCS4
* @IWL_TLC_MNG_HT_RATE_MCS5: index of MCS5
* @IWL_TLC_MNG_HT_RATE_MCS6: index of MCS6
* @IWL_TLC_MNG_HT_RATE_MCS7: index of MCS7
* @IWL_TLC_MNG_HT_RATE_MCS8: index of MCS8
* @IWL_TLC_MNG_HT_RATE_MCS9: index of MCS9
* @IWL_TLC_MNG_HT_RATE_MAX: maximal rate for HT/VHT
*/
enum iwl_tlc_mng_ht_rates_enum {
IWL_TLC_MNG_HT_RATE_MCS0 = 0,
IWL_TLC_MNG_HT_RATE_MCS1,
IWL_TLC_MNG_HT_RATE_MCS2,
IWL_TLC_MNG_HT_RATE_MCS3,
IWL_TLC_MNG_HT_RATE_MCS4,
IWL_TLC_MNG_HT_RATE_MCS5,
IWL_TLC_MNG_HT_RATE_MCS6,
IWL_TLC_MNG_HT_RATE_MCS7,
IWL_TLC_MNG_HT_RATE_MCS8,
IWL_TLC_MNG_HT_RATE_MCS9,
IWL_TLC_MNG_HT_RATE_MAX = IWL_TLC_MNG_HT_RATE_MCS9,
};
/* Maximum supported tx antennas number */
#define MAX_RS_ANT_NUM 3
/**
* struct tlc_config_cmd - TLC configuration
* @sta_id: station id
* @reserved1: reserved
* @max_supp_ch_width: channel width
* @flags: bitmask of %IWL_TLC_MNG_CONFIG_FLAGS_ENABLE_\*
* @chains: bitmask of %IWL_TLC_MNG_CHAIN_\*
* @max_supp_ss: valid values are 0-3, 0 - spatial streams are not supported
* @valid_vht_he_types: bitmap of %IWL_TLC_MNG_VALID_VHT_HE_TYPES_\*
* @non_ht_supp_rates: bitmap of supported legacy rates
* @ht_supp_rates: bitmap of supported HT/VHT rates, valid bits are 0-9
* @mode: modulation type %IWL_TLC_MNG_MODE_\*
* @reserved2: reserved
* @he_supp_rates: bitmap of supported HE rates
* @sgi_ch_width_supp: bitmap of SGI support per channel width
* @he_gi_support: 11ax HE guard interval
* @max_ampdu_cnt: max AMPDU size (frames count)
*/
struct iwl_tlc_config_cmd {
u8 sta_id;
u8 reserved1[3];
u8 max_supp_ch_width;
u8 chains;
u8 max_supp_ss;
u8 valid_vht_he_types;
__le16 flags;
__le16 non_ht_supp_rates;
__le16 ht_supp_rates[MAX_RS_ANT_NUM];
u8 mode;
u8 reserved2;
__le16 he_supp_rates;
u8 sgi_ch_width_supp;
u8 he_gi_support;
__le32 max_ampdu_cnt;
} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_1 */
/*
* These serve as indexes into
* struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
......@@ -253,7 +420,6 @@ enum {
#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | \
RATE_MCS_ANT_C_MSK)
#define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK
#define RATE_MCS_ANT_NUM 3
/* Bit 17: (0) SS, (1) SS*2 */
#define RATE_MCS_STBC_POS 17
......
......@@ -61,9 +61,196 @@
#include "iwl-op-mode.h"
#include "mvm.h"
static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta)
{
switch (sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
return IWL_TLC_MNG_MAX_CH_WIDTH_160MHZ;
case IEEE80211_STA_RX_BW_80:
return IWL_TLC_MNG_MAX_CH_WIDTH_80MHZ;
case IEEE80211_STA_RX_BW_40:
return IWL_TLC_MNG_MAX_CH_WIDTH_40MHZ;
case IEEE80211_STA_RX_BW_20:
default:
return IWL_TLC_MNG_MAX_CH_WIDTH_20MHZ;
}
}
static u8 rs_fw_set_active_chains(u8 chains)
{
u8 fw_chains = 0;
if (chains & ANT_A)
fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK;
if (chains & ANT_B)
fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK;
if (chains & ANT_C)
fw_chains |= IWL_TLC_MNG_CHAIN_C_MSK;
return fw_chains;
}
static u8 rs_fw_sgi_cw_support(struct ieee80211_sta *sta)
{
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
u8 supp = 0;
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20)
supp |= IWL_TLC_MNG_SGI_20MHZ_MSK;
if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40)
supp |= IWL_TLC_MNG_SGI_40MHZ_MSK;
if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80)
supp |= IWL_TLC_MNG_SGI_80MHZ_MSK;
if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160)
supp |= IWL_TLC_MNG_SGI_160MHZ_MSK;
return supp;
}
static u16 rs_fw_set_config_flags(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
bool vht_ena = vht_cap && vht_cap->vht_supported;
u16 flags = IWL_TLC_MNG_CFG_FLAGS_CCK_MSK |
IWL_TLC_MNG_CFG_FLAGS_DCM_MSK |
IWL_TLC_MNG_CFG_FLAGS_DD_MSK;
if (mvm->cfg->ht_params->stbc &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) ||
(vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))))
flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK;
if (mvm->cfg->ht_params->ldpc &&
((ht_cap && (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) ||
(vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))))
flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK;
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
(num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) &&
(vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE))
flags |= IWL_TLC_MNG_CFG_FLAGS_BF_MSK;
return flags;
}
static
int rs_fw_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap,
int nss)
{
u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) &
(0x3 << (2 * (nss - 1)));
rx_mcs >>= (2 * (nss - 1));
switch (rx_mcs) {
case IEEE80211_VHT_MCS_SUPPORT_0_7:
return IWL_TLC_MNG_HT_RATE_MCS7;
case IEEE80211_VHT_MCS_SUPPORT_0_8:
return IWL_TLC_MNG_HT_RATE_MCS8;
case IEEE80211_VHT_MCS_SUPPORT_0_9:
return IWL_TLC_MNG_HT_RATE_MCS9;
default:
WARN_ON_ONCE(1);
break;
}
return 0;
}
static void rs_fw_vht_set_enabled_rates(struct ieee80211_sta *sta,
struct ieee80211_sta_vht_cap *vht_cap,
struct iwl_tlc_config_cmd *cmd)
{
u16 supp;
int i, highest_mcs;
for (i = 0; i < sta->rx_nss; i++) {
if (i == MAX_RS_ANT_NUM)
break;
highest_mcs = rs_fw_vht_highest_rx_mcs_index(vht_cap, i + 1);
if (!highest_mcs)
continue;
supp = BIT(highest_mcs + 1) - 1;
if (sta->bandwidth == IEEE80211_STA_RX_BW_20)
supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9);
cmd->ht_supp_rates[i] = cpu_to_le16(supp);
}
}
static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband,
struct iwl_tlc_config_cmd *cmd)
{
int i;
unsigned long tmp;
unsigned long supp; /* must be unsigned long for for_each_set_bit */
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
/* non HT rates */
supp = 0;
tmp = sta->supp_rates[sband->band];
for_each_set_bit(i, &tmp, BITS_PER_LONG)
supp |= BIT(sband->bitrates[i].hw_value);
cmd->non_ht_supp_rates = cpu_to_le16(supp);
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
/* HT/VHT rates */
if (vht_cap && vht_cap->vht_supported) {
cmd->mode = IWL_TLC_MNG_MODE_VHT;
rs_fw_vht_set_enabled_rates(sta, vht_cap, cmd);
} else if (ht_cap && ht_cap->ht_supported) {
cmd->mode = IWL_TLC_MNG_MODE_HT;
cmd->ht_supp_rates[0] = cpu_to_le16(ht_cap->mcs.rx_mask[0]);
cmd->ht_supp_rates[1] = cpu_to_le16(ht_cap->mcs.rx_mask[1]);
}
}
void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band)
{
struct ieee80211_hw *hw = mvm->hw;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
u32 cmd_id = iwl_cmd_id(TLC_MNG_CONFIG_CMD, DATA_PATH_GROUP, 0);
struct ieee80211_supported_band *sband;
struct iwl_tlc_config_cmd cfg_cmd = {
.sta_id = mvmsta->sta_id,
.max_supp_ch_width = rs_fw_bw_from_sta_bw(sta),
.flags = cpu_to_le16(rs_fw_set_config_flags(mvm, sta)),
.chains = rs_fw_set_active_chains(iwl_mvm_get_valid_tx_ant(mvm)),
.max_supp_ss = sta->rx_nss,
.max_ampdu_cnt = cpu_to_le32(mvmsta->max_agg_bufsize),
.sgi_ch_width_supp = rs_fw_sgi_cw_support(sta),
};
int ret;
memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
#ifdef CONFIG_IWLWIFI_DEBUGFS
iwl_mvm_reset_frame_stats(mvm);
#endif
sband = hw->wiphy->bands[band];
rs_fw_set_supp_rates(sta, sband, &cfg_cmd);
ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cfg_cmd), &cfg_cmd);
if (ret)
IWL_ERR(mvm, "Failed to send rate scale config (%d)\n", ret);
}
int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
{
/* TODO: need to introduce a new FW cmd since LQ cmd is not relevant */
IWL_DEBUG_RATE(mvm, "tx protection - not implemented yet.\n");
return 0;
}
void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta)
......
......@@ -4061,15 +4061,8 @@ void iwl_mvm_rate_control_unregister(void)
ieee80211_rate_control_unregister(&rs_mvm_ops_drv);
}
/**
* iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
* Tx protection, according to this request and previous requests,
* and send the LQ command.
* @mvmsta: The station
* @enable: Enable Tx protection?
*/
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
static int rs_drv_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
{
struct iwl_lq_cmd *lq = &mvmsta->lq_sta.rs_drv.lq;
......@@ -4087,3 +4080,17 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
return iwl_mvm_send_lq_cmd(mvm, lq, false);
}
/**
* iwl_mvm_tx_protection - ask FW to enable RTS/CTS protection
* @mvmsta: The station
* @enable: Enable Tx protection?
*/
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable)
{
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TLC_OFFLOAD))
return rs_fw_tx_protection(mvm, mvmsta, enable);
else
return rs_drv_tx_protection(mvm, mvmsta, enable);
}
......@@ -452,4 +452,6 @@ void rs_remove_sta_debugfs(void *mvm, void *mvm_sta);
void iwl_mvm_rs_add_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta);
void rs_fw_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
enum nl80211_band band);
int rs_fw_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable);
#endif /* __rs__ */
......@@ -278,8 +278,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
u8 ind = last_idx;
int i;
for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
ind = (ind + 1) % RATE_MCS_ANT_NUM;
for (i = 0; i < MAX_RS_ANT_NUM; i++) {
ind = (ind + 1) % MAX_RS_ANT_NUM;
if (valid & BIT(ind))
return ind;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment