From: Ben Greear <greearb@xxxxxxxxxxxxxxx> Support 'iw phy foo set_antenna 0x1' Allow re-initializing the capabilities, and then call that when antenna-change is requested Signed-off-by: Ben Greear <greearb@xxxxxxxxxxxxxxx> --- NOTE: This is against recent iwlwifi backports driver. Something similar can work against in-kernel driver. .../wireless/intel/iwlwifi/iwl-eeprom-parse.c | 5 ++ .../wireless/intel/iwlwifi/iwl-nvm-parse.c | 77 +++++++++++++++++-- .../wireless/intel/iwlwifi/iwl-nvm-parse.h | 10 +++ .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 9 +++ .../wireless/intel/iwlwifi/mvm/mld-mac80211.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 + .../net/wireless/intel/iwlwifi/mvm/utils.c | 16 ++++ 7 files changed, 114 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index 302794c7ed6a..8fbbae1a9509 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -83,8 +83,13 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans, ht_info->mcs.rx_mask[0] = 0xFF; if (rx_chains >= 2) ht_info->mcs.rx_mask[1] = 0xFF; + else + ht_info->mcs.rx_mask[1] = 0; + if (rx_chains >= 3) ht_info->mcs.rx_mask[2] = 0xFF; + else + ht_info->mcs.rx_mask[2] = 0; if (cfg->ht_params->ht_greenfield_support) ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index f4f6e9b338ee..060c9cffcca2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -964,6 +964,12 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10; } } + if (iftype_data->eht_cap.has_eht) { + struct ieee80211_eht_mcs_nss_supp *mcs_nss = + &iftype_data->eht_cap.eht_mcs_nss_supp; + + memset(mcs_nss, 0x22, sizeof(*mcs_nss)); + } } else { if (iftype_data->eht_cap.has_eht) { struct ieee80211_eht_mcs_nss_supp *mcs_nss = @@ -983,6 +989,26 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_MAX_NC_1; } + + /* If antennas were forced - make sure not declaring MIMO when + * we actually are SISO + * Recall that there are 2 bits per stream in the "HE Tx/Rx HE + * MCS NSS Support Field", so if some antenna is forced on but + * not both A and B - we should work in SISO mode, so mark the + * 2nd SS as not supported + */ + iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80 |= + cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); + iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80 |= + cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); + iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_160 |= + cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); + iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_160 |= + cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); + iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_80p80 |= + cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); + iftype_data->he_cap.he_mcs_nss_supp.tx_mcs_80p80 |= + cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); } if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap) @@ -1058,10 +1084,6 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans, struct ieee80211_sband_iftype_data *iftype_data; int i; - /* should only initialize once */ - if (WARN_ON(sband->iftype_data)) - return; - BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa)); BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa)); BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa)); @@ -1244,6 +1266,8 @@ static void iwl_init_eht_band_override(struct iwl_trans *trans, int i; for (i = 0; i < sband->n_iftype_data; i++) { + struct ieee80211_eht_mcs_nss_supp *mcs_nss; + /* we know it's writable - we set it before ourselves */ iftype_data = (void *)(uintptr_t)&sband->iftype_data[i]; @@ -1251,16 +1275,16 @@ static void iwl_init_eht_band_override(struct iwl_trans *trans, if (!iftype_data->eht_cap.has_eht) continue; + mcs_nss = &iftype_data->eht_cap.eht_mcs_nss_supp; if (trans->dbg_cfg.valid_ants && (trans->dbg_cfg.valid_ants & ANT_AB) != ANT_AB) { /* For all MCS and bandwidth, set 1 NSS for both Tx and * Rx - note we don't set the only_20mhz, but due to this * being a union, it gets set correctly anyway. */ - struct ieee80211_eht_mcs_nss_supp *mcs_nss = - &iftype_data->eht_cap.eht_mcs_nss_supp; - memset(mcs_nss, 0x11, sizeof(*mcs_nss)); + } else { + memset(mcs_nss, 0x22, sizeof(*mcs_nss)); } IWL_COPY_BIN(eht_ppe_thres, eht_cap.eht_ppe_thres); @@ -1302,6 +1326,45 @@ static void iwl_init_he_eht_override(struct iwl_trans *trans, } #endif +/* This is a subsection of the logic in iwl_init_bands */ +void iwl_reinit_capab(struct iwl_trans *trans, + struct iwl_nvm_data *data, + u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw) +{ + struct ieee80211_supported_band *sband; + + sband = &data->bands[NL80211_BAND_2GHZ]; + iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_2GHZ, + tx_chains, rx_chains); + + if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) + iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, fw); + else + sband->iftype_data = NULL; + + sband = &data->bands[NL80211_BAND_5GHZ]; + iwl_init_ht_hw_capab(trans, data, &sband->ht_cap, NL80211_BAND_5GHZ, + tx_chains, rx_chains); + if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac) + iwl_init_vht_hw_capab(trans, data, &sband->vht_cap, + tx_chains, rx_chains); + else + sband->vht_cap.vht_supported = false; + + if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) + iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, fw); + else + sband->iftype_data = NULL; + + sband = &data->bands[NL80211_BAND_6GHZ]; + + if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax) + iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains, fw); + else + sband->iftype_data = NULL; +} +IWL_EXPORT_SYMBOL(iwl_reinit_capab); + static void iwl_init_sbands(struct iwl_trans *trans, struct iwl_nvm_data *data, const void *nvm_ch_flags, u8 tx_chains, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index c79f72d54482..a4d5b19038fb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -90,4 +90,14 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_mei_nvm *mei_nvm, const struct iwl_fw *fw); +/** + * iwl_reinit_capab - Re-initialize the current HT, VHT and/or HE capabilities + * + * This should be called when changing values that affect the capabilities, such + * as number of spatial streams. + */ +void iwl_reinit_capab(struct iwl_trans *trans, + struct iwl_nvm_data *data, + u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw); + #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 622c63de4b4b..a03f93cfa73b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -319,6 +319,14 @@ int iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) return 0; } +int +iwl_mvm_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + return iwl_mvm_set_valid_ant(mvm, tx_ant, rx_ant); +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -6443,6 +6451,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, .ampdu_action = iwl_mvm_mac_ampdu_action, .get_antenna = iwl_mvm_op_get_antenna, + .set_antenna = iwl_mvm_op_set_antenna, .start = iwl_mvm_mac_start, .reconfig_complete = iwl_mvm_mac_reconfig_complete, .stop = iwl_mvm_mac_stop, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 2102cb4cbd7f..2c20f358b02c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1164,6 +1164,7 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .wake_tx_queue = iwl_mvm_mac_wake_tx_queue, .ampdu_action = iwl_mvm_mac_ampdu_action, .get_antenna = iwl_mvm_op_get_antenna, + .set_antenna = iwl_mvm_op_set_antenna, .start = iwl_mvm_mac_start, .reconfig_complete = iwl_mvm_mac_reconfig_complete, .stop = iwl_mvm_mac_stop, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 41398b42fd9e..3b414bee4e9c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1779,6 +1779,8 @@ static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm) mvm->fw->valid_rx_ant; } +int iwl_mvm_set_valid_ant(struct iwl_mvm *mvm, u32 tx_ant, u32 rx_ant); + static inline void iwl_mvm_toggle_tx_ant(struct iwl_mvm *mvm, u8 *ant) { *ant = iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), *ant); @@ -2694,6 +2696,7 @@ int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params); int iwl_mvm_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); +int iwl_mvm_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int iwl_mvm_mac_start(struct ieee80211_hw *hw); void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw, enum ieee80211_reconfig_type reconfig_type); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 2d7deb08cd45..8bff4ef84563 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -150,6 +150,22 @@ int iwl_mvm_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, return is_LB ? rate : -1; } +int iwl_mvm_set_valid_ant(struct iwl_mvm *mvm, u32 tx_ant, u32 rx_ant) +{ + if (mvm->nvm_data) { + mvm->nvm_data->valid_rx_ant = (rx_ant & ANT_ABC); + mvm->nvm_data->valid_tx_ant = (tx_ant & ANT_ABC); + + iwl_reinit_capab(mvm->trans, mvm->nvm_data, mvm->nvm_data->valid_tx_ant, + mvm->nvm_data->valid_rx_ant, mvm->fw); + + return 0; + } else { + pr_err("ERROR: iwl-mvm-set-valid-ant: mvm->nvm_data is NULL\n"); + return -EINVAL; + } +} + int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band) { -- 2.40.0